From e74863249cad05d3afdd16d0d6e3107040c62e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20H=C3=BCbner?= Date: Wed, 11 Mar 2026 09:22:20 +0000 Subject: [PATCH 01/97] 8378559: Add setting of captured states like errno Reviewed-by: jvernee, fbredberg --- .../cpu/aarch64/downcallLinker_aarch64.cpp | 50 ++++++++++++++---- src/hotspot/cpu/ppc/downcallLinker_ppc.cpp | 47 +++++++++++++---- .../cpu/riscv/downcallLinker_riscv.cpp | 49 ++++++++++++++---- src/hotspot/cpu/s390/downcallLinker_s390.cpp | 45 ++++++++++++---- src/hotspot/cpu/x86/downcallLinker_x86_64.cpp | 48 +++++++++++++---- src/hotspot/share/prims/downcallLinker.cpp | 37 ++++++++++---- src/hotspot/share/prims/downcallLinker.hpp | 7 +-- .../classes/java/lang/foreign/Linker.java | 32 +++++++----- .../native/libfallbackLinker/fallbackLinker.c | 43 ++++++++++++---- .../TestCaptureCallState.java | 51 ++++++++++++------- .../capturecallstate/libCaptureCallState.c | 14 ++++- 11 files changed, 319 insertions(+), 104 deletions(-) diff --git a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp index 65d448f908cc..130d29498007 100644 --- a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.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. * Copyright (c) 2019, Arm Limited. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -146,10 +146,10 @@ void DowncallLinker::StubGenerator::generate() { bool should_save_return_value = !_needs_return_buffer; RegSpiller out_reg_spiller(_output_registers); - int spill_offset = -1; + int out_spill_offset = -1; if (should_save_return_value) { - spill_offset = 0; + out_spill_offset = 0; // spill area can be shared with shadow space and out args, // since they are only used before the call, // and spill area is only used after. @@ -174,6 +174,9 @@ void DowncallLinker::StubGenerator::generate() { // FP-> | | // |---------------------| = frame_bottom_offset = frame_size // | (optional) | + // | in_reg_spiller area | + // |---------------------| + // | (optional) | // | capture state buf | // |---------------------| = StubLocations::CAPTURED_STATE_BUFFER // | (optional) | @@ -187,6 +190,19 @@ void DowncallLinker::StubGenerator::generate() { GrowableArray out_regs = ForeignGlobals::replace_place_holders(_input_registers, locs); ArgumentShuffle arg_shuffle(filtered_java_regs, out_regs, shuffle_reg); + // Need to spill for state capturing runtime call. + // The area spilled into is distinct from the capture state buffer. + RegSpiller in_reg_spiller(out_regs); + int in_spill_offset = -1; + if (_captured_state_mask != 0) { + // The spill area cannot be shared with the out_spill since + // spilling needs to happen before the call. Allocate a new + // region in the stack for this spill space. + in_spill_offset = allocated_frame_size; + allocated_frame_size += in_reg_spiller.spill_size_bytes(); + } + + #ifndef PRODUCT LogTarget(Trace, foreign, downcall) lt; if (lt.is_enabled()) { @@ -228,6 +244,20 @@ void DowncallLinker::StubGenerator::generate() { arg_shuffle.generate(_masm, shuffle_reg, 0, _abi._shadow_space_bytes); __ block_comment("} argument shuffle"); + if (_captured_state_mask != 0) { + assert(in_spill_offset != -1, "must be"); + __ block_comment("{ load initial thread local"); + in_reg_spiller.generate_spill(_masm, in_spill_offset); + + // Copy the contents of the capture state buffer into thread local + __ ldr(c_rarg0, Address(sp, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); + __ movw(c_rarg1, _captured_state_mask); + __ rt_call(CAST_FROM_FN_PTR(address, DowncallLinker::capture_state_pre), tmp1); + + in_reg_spiller.generate_fill(_masm, in_spill_offset); + __ block_comment("} load initial thread local"); + } + __ blr(as_Register(locs.get(StubLocations::TARGET_ADDRESS))); // this call is assumed not to have killed rthread @@ -254,15 +284,15 @@ void DowncallLinker::StubGenerator::generate() { __ block_comment("{ save thread local"); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ ldr(c_rarg0, Address(sp, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); __ movw(c_rarg1, _captured_state_mask); - __ rt_call(CAST_FROM_FN_PTR(address, DowncallLinker::capture_state), tmp1); + __ rt_call(CAST_FROM_FN_PTR(address, DowncallLinker::capture_state_post), tmp1); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ block_comment("} save thread local"); @@ -321,7 +351,7 @@ void DowncallLinker::StubGenerator::generate() { if (should_save_return_value) { // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ mov(c_rarg0, rthread); @@ -330,7 +360,7 @@ void DowncallLinker::StubGenerator::generate() { __ blr(tmp1); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ b(L_after_safepoint_poll); @@ -342,13 +372,13 @@ void DowncallLinker::StubGenerator::generate() { __ bind(L_reguard); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages), tmp1); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ b(L_after_reguard); diff --git a/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp b/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp index f12d25ac6115..d149fc33ac38 100644 --- a/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp +++ b/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2025 SAP SE. All rights reserved. - * 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 @@ -135,10 +135,10 @@ void DowncallLinker::StubGenerator::generate() { bool should_save_return_value = !_needs_return_buffer; RegSpiller out_reg_spiller(_output_registers); - int spill_offset = -1; + int out_spill_offset = -1; if (should_save_return_value) { - spill_offset = frame::native_abi_reg_args_size; + out_spill_offset = frame::native_abi_reg_args_size; // Spill area can be shared with additional out args (>8), // since it is only used after the call. int frame_size_including_spill_area = frame::native_abi_reg_args_size + out_reg_spiller.spill_size_bytes(); @@ -170,6 +170,18 @@ void DowncallLinker::StubGenerator::generate() { ArgumentShuffle arg_shuffle(filtered_java_regs, out_regs, _abi._scratch1); + // Need to spill for state capturing runtime call. + // The area spilled into is distinct from the capture state buffer. + RegSpiller in_reg_spiller(out_regs); + int in_spill_offset = -1; + if (_captured_state_mask != 0) { + // The spill area cannot be shared with the out_spill since + // spilling needs to happen before the call. Allocate a new + // region in the stack for this spill space. + in_spill_offset = allocated_frame_size; + allocated_frame_size += in_reg_spiller.spill_size_bytes(); + } + #ifndef PRODUCT LogTarget(Trace, foreign, downcall) lt; if (lt.is_enabled()) { @@ -211,6 +223,21 @@ void DowncallLinker::StubGenerator::generate() { arg_shuffle.generate(_masm, as_VMStorage(callerSP), frame::jit_out_preserve_size, frame::native_abi_minframe_size); __ block_comment("} argument shuffle"); + if (_captured_state_mask != 0) { + assert(in_spill_offset != -1, "must be"); + __ block_comment("{ load initial thread local"); + in_reg_spiller.generate_spill(_masm, in_spill_offset); + + // Copy the contents of the capture state buffer into thread local + __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, DowncallLinker::capture_state_pre), R0); + __ ld(R3_ARG1, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER), R1_SP); + __ load_const_optimized(R4_ARG2, _captured_state_mask, R0); + __ call_c(call_target_address); + + in_reg_spiller.generate_fill(_masm, in_spill_offset); + __ block_comment("} load initial thread local"); + } + __ call_c(call_target_address); if (_needs_return_buffer) { @@ -247,16 +274,16 @@ void DowncallLinker::StubGenerator::generate() { __ block_comment("{ save thread local"); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } - __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, DowncallLinker::capture_state), R0); + __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, DowncallLinker::capture_state_post), R0); __ ld(R3_ARG1, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER), R1_SP); __ load_const_optimized(R4_ARG2, _captured_state_mask, R0); __ call_c(call_target_address); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ block_comment("} save thread local"); @@ -310,7 +337,7 @@ void DowncallLinker::StubGenerator::generate() { if (should_save_return_value) { // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, JavaThread::check_special_condition_for_native_trans), R0); @@ -318,7 +345,7 @@ void DowncallLinker::StubGenerator::generate() { __ call_c(call_target_address); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ b(L_after_safepoint_poll); @@ -330,14 +357,14 @@ void DowncallLinker::StubGenerator::generate() { __ bind(L_reguard); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, SharedRuntime::reguard_yellow_pages), R0); __ call_c(call_target_address); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ b(L_after_reguard); diff --git a/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp b/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp index cc685645ec5b..f9d7ce78ff0e 100644 --- a/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp +++ b/src/hotspot/cpu/riscv/downcallLinker_riscv.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. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -140,10 +140,10 @@ void DowncallLinker::StubGenerator::generate() { bool should_save_return_value = !_needs_return_buffer; RegSpiller out_reg_spiller(_output_registers); - int spill_offset = -1; + int out_spill_offset = -1; if (should_save_return_value) { - spill_offset = 0; + out_spill_offset = 0; // spill area can be shared with shadow space and out args, // since they are only used before the call, // and spill area is only used after. @@ -168,6 +168,9 @@ void DowncallLinker::StubGenerator::generate() { // FP-> | | // |---------------------| = frame_bottom_offset = frame_size // | (optional) | + // | in_reg_spiller area | + // |---------------------| + // | (optional) | // | capture state buf | // |---------------------| = StubLocations::CAPTURED_STATE_BUFFER // | (optional) | @@ -181,6 +184,18 @@ void DowncallLinker::StubGenerator::generate() { GrowableArray out_regs = ForeignGlobals::replace_place_holders(_input_registers, locs); ArgumentShuffle arg_shuffle(filtered_java_regs, out_regs, shuffle_reg); + // Need to spill for state capturing runtime call. + // The area spilled into is distinct from the capture state buffer. + RegSpiller in_reg_spiller(out_regs); + int in_spill_offset = -1; + if (_captured_state_mask != 0) { + // The spill area cannot be shared with the out_spill since + // spilling needs to happen before the call. Allocate a new + // region in the stack for this spill space. + in_spill_offset = allocated_frame_size; + allocated_frame_size += in_reg_spiller.spill_size_bytes(); + } + #ifndef PRODUCT LogTarget(Trace, foreign, downcall) lt; if (lt.is_enabled()) { @@ -226,6 +241,20 @@ void DowncallLinker::StubGenerator::generate() { arg_shuffle.generate(_masm, shuffle_reg, 0, _abi._shadow_space_bytes); __ block_comment("} argument shuffle"); + if (_captured_state_mask != 0) { + assert(in_spill_offset != -1, "must be"); + __ block_comment("{ load initial thread local"); + in_reg_spiller.generate_spill(_masm, in_spill_offset); + + // Copy the contents of the capture state buffer into thread local + __ ld(c_rarg0, Address(sp, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); + __ mv(c_rarg1, _captured_state_mask); + __ rt_call(CAST_FROM_FN_PTR(address, DowncallLinker::capture_state_pre)); + + in_reg_spiller.generate_fill(_masm, in_spill_offset); + __ block_comment("} load initial thread local"); + } + __ jalr(as_Register(locs.get(StubLocations::TARGET_ADDRESS))); // this call is assumed not to have killed xthread @@ -254,15 +283,15 @@ void DowncallLinker::StubGenerator::generate() { __ block_comment("{ save thread local"); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ ld(c_rarg0, Address(sp, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); __ mv(c_rarg1, _captured_state_mask); - __ rt_call(CAST_FROM_FN_PTR(address, DowncallLinker::capture_state)); + __ rt_call(CAST_FROM_FN_PTR(address, DowncallLinker::capture_state_post)); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ block_comment("} save thread local"); @@ -319,7 +348,7 @@ void DowncallLinker::StubGenerator::generate() { if (should_save_return_value) { // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ mv(c_rarg0, xthread); @@ -327,7 +356,7 @@ void DowncallLinker::StubGenerator::generate() { __ rt_call(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans)); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ j(L_after_safepoint_poll); __ block_comment("} L_safepoint_poll_slow_path"); @@ -339,13 +368,13 @@ void DowncallLinker::StubGenerator::generate() { if (should_save_return_value) { // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); } __ rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages)); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); } __ j(L_after_reguard); diff --git a/src/hotspot/cpu/s390/downcallLinker_s390.cpp b/src/hotspot/cpu/s390/downcallLinker_s390.cpp index ccd8002da370..f1c41d05b5cf 100644 --- a/src/hotspot/cpu/s390/downcallLinker_s390.cpp +++ b/src/hotspot/cpu/s390/downcallLinker_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -129,7 +129,7 @@ void DowncallLinker::StubGenerator::generate() { assert(!_needs_return_buffer, "unexpected needs_return_buffer"); RegSpiller out_reg_spiller(_output_registers); - int spill_offset = allocated_frame_size; + int out_spill_offset = allocated_frame_size; allocated_frame_size += BytesPerWord; StubLocations locs; @@ -153,6 +153,18 @@ void DowncallLinker::StubGenerator::generate() { GrowableArray out_regs = ForeignGlobals::replace_place_holders(_input_registers, locs); ArgumentShuffle arg_shuffle(filtered_java_regs, out_regs, _abi._scratch1); + // Need to spill for state capturing runtime call. + // The area spilled into is distinct from the capture state buffer. + RegSpiller in_reg_spiller(out_regs); + int in_spill_offset = -1; + if (_captured_state_mask != 0) { + // The spill area cannot be shared with the out_spill since + // spilling needs to happen before the call. Allocate a new + // region in the stack for this spill space. + in_spill_offset = allocated_frame_size; + allocated_frame_size += in_reg_spiller.spill_size_bytes(); + } + #ifndef PRODUCT LogTarget(Trace, foreign, downcall) lt; if (lt.is_enabled()) { @@ -192,6 +204,21 @@ void DowncallLinker::StubGenerator::generate() { arg_shuffle.generate(_masm, shuffle_reg, frame::z_jit_out_preserve_size, _abi._shadow_space_bytes); __ block_comment("} argument_shuffle"); + if (_captured_state_mask != 0) { + assert(in_spill_offset != -1, "must be"); + __ block_comment("{ load initial thread local"); + in_reg_spiller.generate_spill(_masm, in_spill_offset); + + // Copy the contents of the capture state buffer into thread local + __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, DowncallLinker::capture_state_pre)); + __ z_lg(Z_ARG1, Address(Z_SP, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); + __ load_const_optimized(Z_ARG2, _captured_state_mask); + __ call(call_target_address); + + in_reg_spiller.generate_fill(_masm, in_spill_offset); + __ block_comment("} load initial thread local"); + } + __ call(as_Register(locs.get(StubLocations::TARGET_ADDRESS))); ////////////////////////////////////////////////////////////////////////////// @@ -199,14 +226,14 @@ void DowncallLinker::StubGenerator::generate() { if (_captured_state_mask != 0) { __ block_comment("save_thread_local {"); - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); - __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, DowncallLinker::capture_state)); + __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, DowncallLinker::capture_state_post)); __ z_lg(Z_ARG1, Address(Z_SP, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); __ load_const_optimized(Z_ARG2, _captured_state_mask); __ call(call_target_address); - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); __ block_comment("} save_thread_local"); } @@ -259,13 +286,13 @@ void DowncallLinker::StubGenerator::generate() { __ bind(L_safepoint_poll_slow_path); // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, JavaThread::check_special_condition_for_native_trans)); __ z_lgr(Z_ARG1, Z_thread); __ call(call_target_address); - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); __ z_bru(L_after_safepoint_poll); __ block_comment("} L_safepoint_poll_slow_path"); @@ -275,12 +302,12 @@ void DowncallLinker::StubGenerator::generate() { __ bind(L_reguard); // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); + out_reg_spiller.generate_spill(_masm, out_spill_offset); __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, SharedRuntime::reguard_yellow_pages)); __ call(call_target_address); - out_reg_spiller.generate_fill(_masm, spill_offset); + out_reg_spiller.generate_fill(_masm, out_spill_offset); __ z_bru(L_after_reguard); diff --git a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp b/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp index c48940198ea8..e3bf5f17fe9b 100644 --- a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp +++ b/src/hotspot/cpu/x86/downcallLinker_x86_64.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 @@ -145,10 +145,10 @@ void DowncallLinker::StubGenerator::generate() { // when we don't use a return buffer we need to spill the return value around our slow path calls bool should_save_return_value = !_needs_return_buffer; RegSpiller out_reg_spiller(_output_registers); - int spill_rsp_offset = -1; + int out_spill_rsp_offset = -1; if (should_save_return_value) { - spill_rsp_offset = 0; + out_spill_rsp_offset = 0; // spill area can be shared with shadow space and out args, // since they are only used before the call, // and spill area is only used after. @@ -173,6 +173,9 @@ void DowncallLinker::StubGenerator::generate() { // FP-> | | // |---------------------| = frame_bottom_offset = frame_size // | (optional) | + // | in_reg_spiller area | + // |---------------------| + // | (optional) | // | capture state buf | // |---------------------| = StubLocations::CAPTURED_STATE_BUFFER // | (optional) | @@ -188,6 +191,18 @@ void DowncallLinker::StubGenerator::generate() { VMStorage shuffle_reg = as_VMStorage(rbx); ArgumentShuffle arg_shuffle(filtered_java_regs, out_regs, shuffle_reg); + // Need to spill for state capturing runtime call. + // The area spilled into is distinct from the capture state buffer. + RegSpiller in_reg_spiller(out_regs); + int in_spill_rsp_offset = -1; + if (_captured_state_mask != 0) { + // The spill area cannot be shared with the shadow/out args space + // since spilling needs to happen before the call. Allocate a new + // region in the stack for this spill space. + in_spill_rsp_offset = allocated_frame_size; + allocated_frame_size += in_reg_spiller.spill_size_bytes(); + } + #ifndef PRODUCT LogTarget(Trace, foreign, downcall) lt; if (lt.is_enabled()) { @@ -232,6 +247,19 @@ void DowncallLinker::StubGenerator::generate() { arg_shuffle.generate(_masm, shuffle_reg, 0, _abi._shadow_space_bytes); __ block_comment("} argument shuffle"); + if (_captured_state_mask != 0) { + assert(in_spill_rsp_offset != -1, "must be"); + __ block_comment("{ load initial thread local"); + in_reg_spiller.generate_spill(_masm, in_spill_rsp_offset); + + // Copy the contents of the capture state buffer into thread local + __ movptr(c_rarg0, Address(rsp, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); + __ movl(c_rarg1, _captured_state_mask); + runtime_call(_masm, CAST_FROM_FN_PTR(address, DowncallLinker::capture_state_pre)); + + in_reg_spiller.generate_fill(_masm, in_spill_rsp_offset); + __ block_comment("} load initial thread local"); + } __ call(as_Register(locs.get(StubLocations::TARGET_ADDRESS))); assert(!_abi.is_volatile_reg(r15_thread), "Call assumed not to kill r15"); @@ -258,15 +286,15 @@ void DowncallLinker::StubGenerator::generate() { __ block_comment("{ save thread local"); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_rsp_offset); + out_reg_spiller.generate_spill(_masm, out_spill_rsp_offset); } __ movptr(c_rarg0, Address(rsp, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER))); __ movl(c_rarg1, _captured_state_mask); - runtime_call(_masm, CAST_FROM_FN_PTR(address, DowncallLinker::capture_state)); + runtime_call(_masm, CAST_FROM_FN_PTR(address, DowncallLinker::capture_state_post)); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_rsp_offset); + out_reg_spiller.generate_fill(_masm, out_spill_rsp_offset); } __ block_comment("} save thread local"); @@ -319,14 +347,14 @@ void DowncallLinker::StubGenerator::generate() { __ bind(L_safepoint_poll_slow_path); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_rsp_offset); + out_reg_spiller.generate_spill(_masm, out_spill_rsp_offset); } __ mov(c_rarg0, r15_thread); runtime_call(_masm, CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans)); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_rsp_offset); + out_reg_spiller.generate_fill(_masm, out_spill_rsp_offset); } __ jmp(L_after_safepoint_poll); @@ -338,13 +366,13 @@ void DowncallLinker::StubGenerator::generate() { __ bind(L_reguard); if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_rsp_offset); + out_reg_spiller.generate_spill(_masm, out_spill_rsp_offset); } runtime_call(_masm, CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages)); if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_rsp_offset); + out_reg_spiller.generate_fill(_masm, out_spill_rsp_offset); } __ jmp(L_after_reguard); diff --git a/src/hotspot/share/prims/downcallLinker.cpp b/src/hotspot/share/prims/downcallLinker.cpp index 5dde825d75f0..cbef98416523 100644 --- a/src/hotspot/share/prims/downcallLinker.cpp +++ b/src/hotspot/share/prims/downcallLinker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,15 +30,34 @@ #include #endif +// keep in synch with jdk.internal.foreign.abi.CapturableState +enum PreservableValues { + NONE = 0, + GET_LAST_ERROR = 1, + WSA_GET_LAST_ERROR = 1 << 1, + ERRNO = 1 << 2 +}; + +// We call this from _thread_in_native, right before a downcall +JVM_LEAF(void, DowncallLinker::capture_state_pre(int32_t* value_ptr, int captured_state_mask)) +#ifdef _WIN64 + if (captured_state_mask & GET_LAST_ERROR) { + SetLastError(*value_ptr); + } + value_ptr++; + if (captured_state_mask & WSA_GET_LAST_ERROR) { + WSASetLastError(*value_ptr); + *value_ptr = WSAGetLastError(); + } + value_ptr++; +#endif + if (captured_state_mask & ERRNO) { + errno = *value_ptr; + } +JVM_END + // We call this from _thread_in_native, right after a downcall -JVM_LEAF(void, DowncallLinker::capture_state(int32_t* value_ptr, int captured_state_mask)) - // keep in synch with jdk.internal.foreign.abi.CapturableState - enum PreservableValues { - NONE = 0, - GET_LAST_ERROR = 1, - WSA_GET_LAST_ERROR = 1 << 1, - ERRNO = 1 << 2 - }; +JVM_LEAF(void, DowncallLinker::capture_state_post(int32_t* value_ptr, int captured_state_mask)) #ifdef _WIN64 if (captured_state_mask & GET_LAST_ERROR) { *value_ptr = GetLastError(); diff --git a/src/hotspot/share/prims/downcallLinker.hpp b/src/hotspot/share/prims/downcallLinker.hpp index 01ee5c567766..2c2cf0530333 100644 --- a/src/hotspot/share/prims/downcallLinker.hpp +++ b/src/hotspot/share/prims/downcallLinker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,8 +41,9 @@ class DowncallLinker: AllStatic { int captured_state_mask, bool needs_transition); - // This is defined as JVM_LEAF which adds the JNICALL modifier. - static void JNICALL capture_state(int32_t* value_ptr, int captured_state_mask); + // These are defined as JVM_LEAF which adds the JNICALL modifier. + static void JNICALL capture_state_pre(int32_t* value_ptr, int captured_state_mask); + static void JNICALL capture_state_post(int32_t* value_ptr, int captured_state_mask); class StubGenerator : public StubCodeGenerator { BasicType* _signature; diff --git a/src/java.base/share/classes/java/lang/foreign/Linker.java b/src/java.base/share/classes/java/lang/foreign/Linker.java index cfa030902994..1a7d33266aad 100644 --- a/src/java.base/share/classes/java/lang/foreign/Linker.java +++ b/src/java.base/share/classes/java/lang/foreign/Linker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -813,25 +813,28 @@ static Option firstVariadicArg(int index) { } /** - * {@return a linker option used to save portions of the execution state - * immediately after calling a foreign function associated with a - * downcall method handle, before it can be overwritten by the Java - * runtime, or read through conventional means} + * {@return a linker option used to initialize portions of the execution + * state immediately before, and save portions of the execution + * state immediately after calling a foreign function associated + * with a downcall method handle, before it can be overwritten by the + * Java runtime, or read through conventional means} *

- * Execution state is captured by a downcall method handle on invocation, by - * writing it to a native segment provided by the user to the downcall method - * handle. For this purpose, a downcall method handle linked with this option - * will feature an additional {@link MemorySegment} parameter directly following - * the target address, and optional {@link SegmentAllocator} parameters. This - * parameter, the capture state segment, represents the native segment - * into which the captured state is written. + * Execution state is initialized from, or saved to a native segment provided by + * the user to the downcall method handle. For this purpose, a downcall method + * handle linked with this option will feature an additional {@link MemorySegment} + * parameter directly following the target address, and optional {@link SegmentAllocator} + * parameters. This parameter, the capture state segment, represents the + * native segment from which the capture state is initialized, and into which the + * capture state is saved. *

* The capture state segment must have size and alignment compatible with the * layout returned by {@linkplain #captureStateLayout}. This layout is a struct * layout which has a named field for each captured value. *

- * Captured state can be retrieved from the capture state segment by constructing - * var handles from the {@linkplain #captureStateLayout capture state layout}. + * Captured state can be stored in, or retrieved from the capture state segment by + * constructing var handles from the {@linkplain #captureStateLayout capture state layout}. + * Some functions require this state the be initialized to a particular value before + * invoking the downcall. *

* The following example demonstrates the use of this linker option: * {@snippet lang = "java": @@ -843,6 +846,7 @@ static Option firstVariadicArg(int index) { * VarHandle errnoHandle = capturedStateLayout.varHandle(PathElement.groupElement("errno")); * try (Arena arena = Arena.ofConfined()) { * MemorySegment capturedState = arena.allocate(capturedStateLayout); + * errnoHandle.set(capturedState, 0L, 0); // set errno to 0 * handle.invoke(capturedState); * int errno = (int) errnoHandle.get(capturedState, 0L); * // use errno diff --git a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c index 3f57a2b97cf4..7519efd93bbb 100644 --- a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c +++ b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, 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 @@ -87,14 +87,32 @@ Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1get_1struct_1offsets(JNI return ffi_get_struct_offsets((ffi_abi) abi, jlong_to_ptr(type), jlong_to_ptr(offsets)); } -static void do_capture_state(int32_t* value_ptr, int captured_state_mask) { - // keep in synch with jdk.internal.foreign.abi.CapturableState - enum PreservableValues { - NONE = 0, - GET_LAST_ERROR = 1, - WSA_GET_LAST_ERROR = 1 << 1, - ERRNO = 1 << 2 - }; +// keep in synch with jdk.internal.foreign.abi.CapturableState +enum PreservableValues { + NONE = 0, + GET_LAST_ERROR = 1, + WSA_GET_LAST_ERROR = 1 << 1, + ERRNO = 1 << 2 +}; + +static void do_capture_state_pre(int32_t* value_ptr, int captured_state_mask) { +#ifdef _WIN64 + if (captured_state_mask & GET_LAST_ERROR) { + SetLastError(*value_ptr); + } + value_ptr++; + if (captured_state_mask & WSA_GET_LAST_ERROR) { + WSASetLastError(*value_ptr); + *value_ptr = WSAGetLastError(); + } + value_ptr++; +#endif + if (captured_state_mask & ERRNO) { + errno = *value_ptr; + } +} + +static void do_capture_state_post(int32_t* value_ptr, int captured_state_mask) { #ifdef _WIN64 if (captured_state_mask & GET_LAST_ERROR) { *value_ptr = GetLastError(); @@ -142,10 +160,15 @@ Java_jdk_internal_foreign_abi_fallback_LibFallback_doDowncall(JNIEnv* env, jclas } } + if (captured_state_mask != 0) { + // Copy the contents of the capture state buffer into thread local + do_capture_state_pre(captured_state_addr, captured_state_mask); + } + ffi_call(jlong_to_ptr(cif), jlong_to_ptr(fn), jlong_to_ptr(rvalue), jlong_to_ptr(avalues)); if (captured_state_mask != 0) { - do_capture_state(captured_state_addr, captured_state_mask); + do_capture_state_post(captured_state_addr, captured_state_mask); } if (heapBases != NULL) { diff --git a/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java b/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java index 2c69c5691b87..8ef5483bd82a 100644 --- a/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java +++ b/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -73,7 +73,7 @@ public void testApiContracts() { } private record SaveValuesCase(String nativeTarget, FunctionDescriptor nativeDesc, String threadLocalName, - Consumer resultCheck, boolean critical) {} + Consumer resultCheck, boolean expectTestValue, boolean critical) {} @Test(dataProvider = "cases") public void testSavedThreadLocal(SaveValuesCase testCase) throws Throwable { @@ -90,14 +90,21 @@ public void testSavedThreadLocal(SaveValuesCase testCase) throws Throwable { try (Arena arena = Arena.ofConfined()) { MemorySegment saveSeg = arena.allocate(capturedStateLayout); + // The existing value and the value that it should be overwritten with + int prevValue = 24; int testValue = 42; + errnoHandle.set(saveSeg, 0L, prevValue); boolean needsAllocator = testCase.nativeDesc().returnLayout().map(StructLayout.class::isInstance).orElse(false); Object result = needsAllocator ? handle.invoke(arena, saveSeg, testValue) : handle.invoke(saveSeg, testValue); testCase.resultCheck().accept(result); int savedErrno = (int) errnoHandle.get(saveSeg, 0L); - assertEquals(savedErrno, testValue); + if (testCase.expectTestValue()) { + assertEquals(savedErrno, testValue); + } else { + assertEquals(savedErrno, prevValue); + } } } @@ -127,37 +134,45 @@ public static Object[][] cases() { for (boolean critical : new boolean[]{ true, false }) { cases.add(new SaveValuesCase("set_errno_V", FunctionDescriptor.ofVoid(JAVA_INT), - "errno", o -> {}, critical)); + "errno", o -> {}, true, critical)); + cases.add(new SaveValuesCase("noset_errno_V", FunctionDescriptor.ofVoid(JAVA_INT), + "errno", o -> {}, false, critical)); cases.add(new SaveValuesCase("set_errno_I", FunctionDescriptor.of(JAVA_INT, JAVA_INT), - "errno", o -> assertEquals((int) o, 42), critical)); + "errno", o -> assertEquals((int) o, 42), true, critical)); cases.add(new SaveValuesCase("set_errno_D", FunctionDescriptor.of(JAVA_DOUBLE, JAVA_INT), - "errno", o -> assertEquals((double) o, 42.0), critical)); + "errno", o -> assertEquals((double) o, 42.0), true, critical)); - cases.add(structCase("SL", Map.of(JAVA_LONG.withName("x"), 42L), critical)); + cases.add(structCase("SL", Map.of(JAVA_LONG.withName("x"), 42L), true, critical)); cases.add(structCase("SLL", Map.of(JAVA_LONG.withName("x"), 42L, - JAVA_LONG.withName("y"), 42L), critical)); + JAVA_LONG.withName("y"), 42L), true, critical)); + cases.add(structCase("SLLL", Map.of(JAVA_LONG.withName("x"), 42L, + JAVA_LONG.withName("y"), 42L, + JAVA_LONG.withName("z"), 42L), true, critical)); cases.add(structCase("SLLL", Map.of(JAVA_LONG.withName("x"), 42L, JAVA_LONG.withName("y"), 42L, - JAVA_LONG.withName("z"), 42L), critical)); - cases.add(structCase("SD", Map.of(JAVA_DOUBLE.withName("x"), 42D), critical)); + JAVA_LONG.withName("z"), 42L), false, critical)); + cases.add(structCase("SD", Map.of(JAVA_DOUBLE.withName("x"), 42D), true, critical)); cases.add(structCase("SDD", Map.of(JAVA_DOUBLE.withName("x"), 42D, - JAVA_DOUBLE.withName("y"), 42D), critical)); + JAVA_DOUBLE.withName("y"), 42D), true, critical)); cases.add(structCase("SDDD", Map.of(JAVA_DOUBLE.withName("x"), 42D, JAVA_DOUBLE.withName("y"), 42D, - JAVA_DOUBLE.withName("z"), 42D), critical)); + JAVA_DOUBLE.withName("z"), 42D), true, critical)); if (IS_WINDOWS) { cases.add(new SaveValuesCase("SetLastError", FunctionDescriptor.ofVoid(JAVA_INT), - "GetLastError", o -> {}, critical)); + "GetLastError", o -> {}, true, critical)); cases.add(new SaveValuesCase("WSASetLastError", FunctionDescriptor.ofVoid(JAVA_INT), - "WSAGetLastError", o -> {}, critical)); + "WSAGetLastError", o -> {}, true, critical)); } } return cases.stream().map(tc -> new Object[] {tc}).toArray(Object[][]::new); } - static SaveValuesCase structCase(String name, Map fields, boolean critical) { + static SaveValuesCase structCase(String name, + Map fields, + boolean expectTestValue, + boolean critical) { StructLayout layout = MemoryLayout.structLayout(fields.keySet().toArray(MemoryLayout[]::new)); Consumer check = o -> {}; @@ -167,9 +182,9 @@ static SaveValuesCase structCase(String name, Map fields, Object value = field.getValue(); check = check.andThen(o -> assertEquals(fieldHandle.get(o, 0L), value)); } - - return new SaveValuesCase("set_errno_" + name, FunctionDescriptor.of(layout, JAVA_INT), - "errno", check, critical); + String prefix = expectTestValue ? "set_errno_" : "noset_errno_"; + return new SaveValuesCase(prefix + name, FunctionDescriptor.of(layout, JAVA_INT), + "errno", check, expectTestValue, critical); } @DataProvider diff --git a/test/jdk/java/foreign/capturecallstate/libCaptureCallState.c b/test/jdk/java/foreign/capturecallstate/libCaptureCallState.c index d76c9d7bedae..fe0d144cb5be 100644 --- a/test/jdk/java/foreign/capturecallstate/libCaptureCallState.c +++ b/test/jdk/java/foreign/capturecallstate/libCaptureCallState.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,10 @@ EXPORT void set_errno_V(int value) { errno = value; } +EXPORT int noset_errno_V(int value) { + return 42; +} + EXPORT int set_errno_I(int value) { errno = value; return 42; @@ -78,6 +82,14 @@ EXPORT struct SLLL set_errno_SLLL(int value) { return s; } +EXPORT struct SLLL noset_errno_SLLL(int value) { + struct SLLL s; + s.x = 42; + s.y = 42; + s.z = 42; + return s; +} + struct SD { double x; }; From 256512147d4ecabfddf29f8391640743d63ebc21 Mon Sep 17 00:00:00 2001 From: Martin Doerr Date: Wed, 11 Mar 2026 09:29:29 +0000 Subject: [PATCH 02/97] 8379448: [PPC64] Build without C2 broken after 8373595 Reviewed-by: rrich, aboldtch, mbaesken, dbriemann --- .../ppc/gc/shared/barrierSetAssembler_ppc.cpp | 12 ++++---- .../ppc/gc/shared/barrierSetAssembler_ppc.hpp | 10 +++++-- .../shenandoahBarrierSetAssembler_ppc.cpp | 14 ++++------ .../shenandoahBarrierSetAssembler_ppc.hpp | 7 ++--- .../cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp | 28 +++++++++---------- .../cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp | 6 ++-- src/hotspot/cpu/ppc/macroAssembler_ppc.cpp | 4 +-- 7 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp index 82d06f6c685f..3692b2479891 100644 --- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018, 2025 SAP SE. All rights reserved. + * Copyright (c) 2018, 2026 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -179,6 +179,11 @@ void BarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Re __ ld(dst, 0, dst); // Resolve (untagged) jobject. } +void BarrierSetAssembler::try_resolve_weak_handle(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { + // Load the oop from the weak handle. + __ ld(obj, 0, obj); +} + void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm, Register tmp) { BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod(); assert_different_registers(tmp, R0); @@ -275,11 +280,6 @@ OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Na return opto_reg; } -void BarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { - // Load the oop from the weak handle. - __ ld(obj, 0, obj); -} - #undef __ #define __ _masm-> diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp index d78071f2ee05..8112542d761d 100644 --- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018, 2022 SAP SE. All rights reserved. + * Copyright (c) 2018, 2026 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -70,6 +70,12 @@ class BarrierSetAssembler: public CHeapObj { virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register dst, Register jni_env, Register obj, Register tmp, Label& slowpath); + // Can be used in nmethods including native wrappers. + // Attention: obj will only be valid until next safepoint (no SATB barrier). + // TODO: maybe rename to try_peek_weak_handle on all platforms (try: operation may fail, peek: obj is not kept alive) + // (other platforms currently use it for C2 only: try_resolve_weak_handle_in_c2) + virtual void try_resolve_weak_handle(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); + virtual void barrier_stubs_init() {} virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::stw_instruction_and_data_patch; } @@ -81,8 +87,6 @@ class BarrierSetAssembler: public CHeapObj { #ifdef COMPILER2 OptoReg::Name refine_register(const Node* node, OptoReg::Name opto_reg) const; - virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, - Register tmp, Label& slow_path); #endif // COMPILER2 }; diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index e1f0416d65df..8e99d23cc993 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2025, Red Hat, Inc. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. 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 @@ -663,17 +663,16 @@ void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler __ block_comment("} try_resolve_jobject_in_native (shenandoahgc)"); } -#ifdef COMPILER2 -void ShenandoahBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler *masm, Register obj, - Register tmp, Label &slow_path) { - __ block_comment("try_resolve_weak_handle_in_c2 (shenandoahgc) {"); +void ShenandoahBarrierSetAssembler::try_resolve_weak_handle(MacroAssembler *masm, Register obj, + Register tmp, Label &slow_path) { + __ block_comment("try_resolve_weak_handle (shenandoahgc) {"); assert_different_registers(obj, tmp); Label done; // Resolve weak handle using the standard implementation. - BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path); + BarrierSetAssembler::try_resolve_weak_handle(masm, obj, tmp, slow_path); // Check if the reference is null, and if it is, take the fast path. __ cmpdi(CR0, obj, 0); @@ -686,9 +685,8 @@ void ShenandoahBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler __ bne(CR0, slow_path); __ bind(done); - __ block_comment("} try_resolve_weak_handle_in_c2 (shenandoahgc)"); + __ block_comment("} try_resolve_weak_handle (shenandoahgc)"); } -#endif // Special shenandoah CAS implementation that handles false negatives due // to concurrent evacuation. That is, the CAS operation is intended to succeed in diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp index 672f8122bcbe..58180c496424 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2022, Red Hat, Inc. All rights reserved. - * Copyright (c) 2012, 2022 SAP SE. 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 @@ -122,9 +122,8 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register dst, Register jni_env, Register obj, Register tmp, Label& slowpath); -#ifdef COMPILER2 - virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); -#endif + + virtual void try_resolve_weak_handle(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); }; #endif // CPU_PPC_GC_SHENANDOAH_SHENANDOAHBARRIERSETASSEMBLER_PPC_HPP diff --git a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp index bfa3c87c1794..3e74dfb88cb9 100644 --- a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2021, 2025 SAP SE. All rights reserved. + * Copyright (c) 2021, 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 @@ -627,6 +627,19 @@ void ZBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, R __ block_comment("} try_resolve_jobject_in_native (zgc)"); } +void ZBarrierSetAssembler::try_resolve_weak_handle(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { + // Resolve weak handle using the standard implementation. + BarrierSetAssembler::try_resolve_weak_handle(masm, obj, tmp, slow_path); + + // Check if the oop is bad, in which case we need to take the slow path. + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatMarkBadMask); + __ andi_(R0, obj, barrier_Relocation::unpatched); + __ bne(CR0, slow_path); + + // Oop is okay, so we uncolor it. + __ srdi(obj, obj, ZPointerLoadShift); +} + #undef __ #ifdef COMPILER1 @@ -950,19 +963,6 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ b(*stub->continuation()); } -void ZBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { - // Resolve weak handle using the standard implementation. - BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path); - - // Check if the oop is bad, in which case we need to take the slow path. - __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatMarkBadMask); - __ andi_(R0, obj, barrier_Relocation::unpatched); - __ bne(CR0, slow_path); - - // Oop is okay, so we uncolor it. - __ srdi(obj, obj, ZPointerLoadShift); -} - #undef __ #endif // COMPILER2 diff --git a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp index e31817370d90..655184cf6a35 100644 --- a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2021, 2022 SAP SE. All rights reserved. + * Copyright (c) 2021, 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 @@ -72,6 +72,8 @@ class ZBarrierSetAssembler : public ZBarrierSetAssemblerBase { virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register dst, Register jni_env, Register obj, Register tmp, Label& slowpath); + virtual void try_resolve_weak_handle(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); + virtual void check_oop(MacroAssembler *masm, Register obj, const char* msg); virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::conc_instruction_and_data_patch; } @@ -108,8 +110,6 @@ class ZBarrierSetAssembler : public ZBarrierSetAssemblerBase { void generate_c2_load_barrier_stub(MacroAssembler* masm, ZLoadBarrierStubC2* stub) const; void generate_c2_store_barrier_stub(MacroAssembler* masm, ZStoreBarrierStubC2* stub) const; - - void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); #endif // COMPILER2 void store_barrier_fast(MacroAssembler* masm, diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index 986dd3358161..f3050cbabb46 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. 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 @@ -2800,7 +2800,7 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register // Check if object matches. ld(tmp3, in_bytes(ObjectMonitor::object_offset()), monitor); BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); - bs_asm->try_resolve_weak_handle_in_c2(this, tmp3, tmp2, slow_path); + bs_asm->try_resolve_weak_handle(this, tmp3, tmp2, slow_path); cmpd(CR0, tmp3, obj); bne(CR0, slow_path); From acbd1febdd8662f857a0ff140e85d59ce80f6d6a Mon Sep 17 00:00:00 2001 From: Leo Korinth Date: Wed, 11 Mar 2026 10:12:37 +0000 Subject: [PATCH 03/97] 8377658: Cleanup files for enabling conversion warnings for g1ConcurrentMark.cpp Reviewed-by: stefank, tschatzl, jsjolen --- .../share/gc/g1/g1ConcurrentMarkThread.inline.hpp | 4 ++-- src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp | 2 +- src/hotspot/share/oops/bsmAttribute.hpp | 12 ++++++------ src/hotspot/share/oops/bsmAttribute.inline.hpp | 4 ++-- src/hotspot/share/oops/compressedKlass.hpp | 4 ++-- src/hotspot/share/oops/constantPool.hpp | 6 +++--- src/hotspot/share/oops/fieldInfo.hpp | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp index 254eaf62bb24..e80c5023405c 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,7 @@ // Total virtual time so far. inline double G1ConcurrentMarkThread::total_mark_cpu_time_s() { - return os::thread_cpu_time(this) + worker_threads_cpu_time_s(); + return static_cast(os::thread_cpu_time(this)) + worker_threads_cpu_time_s(); } // Marking virtual time so far diff --git a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp index 4dcdd33846e0..df76147f4b13 100644 --- a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp +++ b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp @@ -95,7 +95,7 @@ class G1RegionMarkStatsCache { // Evict a given element of the statistics cache. void evict(uint idx); - size_t _num_cache_entries_mask; + const uint _num_cache_entries_mask; uint hash(uint idx) { return idx & _num_cache_entries_mask; diff --git a/src/hotspot/share/oops/bsmAttribute.hpp b/src/hotspot/share/oops/bsmAttribute.hpp index a28d2757fb07..32bc58b5b077 100644 --- a/src/hotspot/share/oops/bsmAttribute.hpp +++ b/src/hotspot/share/oops/bsmAttribute.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,18 +61,18 @@ class BSMAttributeEntry { _argv_offset = 2 }; - int bootstrap_method_index() const { + u2 bootstrap_method_index() const { return _bootstrap_method_index; } - int argument_count() const { + u2 argument_count() const { return _argument_count; } - int argument(int n) const { - assert(checked_cast(n) < _argument_count, "oob"); + u2 argument(u2 n) const { + assert(n < _argument_count, "oob"); return argument_indexes()[n]; } - void set_argument(int index, u2 value) { + void set_argument(u2 index, u2 value) { assert(index >= 0 && index < argument_count(), "invariant"); argument_indexes()[index] = value; } diff --git a/src/hotspot/share/oops/bsmAttribute.inline.hpp b/src/hotspot/share/oops/bsmAttribute.inline.hpp index e678c280c26e..8e048704e08f 100644 --- a/src/hotspot/share/oops/bsmAttribute.inline.hpp +++ b/src/hotspot/share/oops/bsmAttribute.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,7 +47,7 @@ inline BSMAttributeEntry* BSMAttributeEntries::InsertionIterator::reserve_new_en inline void BSMAttributeEntry::copy_args_into(BSMAttributeEntry* entry) const { assert(entry->argument_count() == this->argument_count(), "must be same"); - for (int i = 0; i < argument_count(); i++) { + for (u2 i = 0; i < argument_count(); i++) { entry->set_argument(i, this->argument(i)); } } diff --git a/src/hotspot/share/oops/compressedKlass.hpp b/src/hotspot/share/oops/compressedKlass.hpp index 64b9fcf9c821..98a9eafdbf4e 100644 --- a/src/hotspot/share/oops/compressedKlass.hpp +++ b/src/hotspot/share/oops/compressedKlass.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -231,7 +231,7 @@ class CompressedKlassPointers : public AllStatic { // Returns the alignment a Klass* is guaranteed to have. // Note: *Not* the same as 1 << shift ! Klass are always guaranteed to be at least 64-bit aligned, // so this will return 8 even if shift is 0. - static int klass_alignment_in_bytes() { return nth_bit(MAX2(3, _shift)); } + static int klass_alignment_in_bytes() { return static_cast(nth_bit(MAX2(3, _shift))); } static int klass_alignment_in_words() { return klass_alignment_in_bytes() / BytesPerWord; } // Returns the highest possible narrowKlass value given the current Klass range diff --git a/src/hotspot/share/oops/constantPool.hpp b/src/hotspot/share/oops/constantPool.hpp index 6c519945f4d1..b4cff2bbbe6d 100644 --- a/src/hotspot/share/oops/constantPool.hpp +++ b/src/hotspot/share/oops/constantPool.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -554,8 +554,8 @@ class ConstantPool : public Metadata { u2 bootstrap_argument_index_at(int cp_index, int j) { int bsmai = bootstrap_methods_attribute_index(cp_index); BSMAttributeEntry* bsme = bsm_attribute_entry(bsmai); - assert((uint)j < (uint)bsme->argument_count(), "oob"); - return bsm_attribute_entry(bsmai)->argument(j); + assert(j < bsme->argument_count(), "oob"); + return bsm_attribute_entry(bsmai)->argument(checked_cast(j)); } // The following methods (name/signature/klass_ref_at, klass_ref_at_noresolve, diff --git a/src/hotspot/share/oops/fieldInfo.hpp b/src/hotspot/share/oops/fieldInfo.hpp index b6d9c4d34e5e..88c982e9d1f7 100644 --- a/src/hotspot/share/oops/fieldInfo.hpp +++ b/src/hotspot/share/oops/fieldInfo.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -302,7 +302,7 @@ class FieldStatus { // boilerplate: u1 _flags; - static constexpr u1 flag_mask(FieldStatusBitPosition pos) { return (u1)1 << (int)pos; } + static constexpr u1 flag_mask(FieldStatusBitPosition pos) { return checked_cast(1 << pos); } bool test_flag(FieldStatusBitPosition pos) { return (_flags & flag_mask(pos)) != 0; } // this performs an atomic update on a live status byte! void update_flag(FieldStatusBitPosition pos, bool z); From c904a0edae3d1807d4c6f13e41d5186caae71187 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 11 Mar 2026 11:14:59 +0000 Subject: [PATCH 04/97] 8378338: Shenandoah: Heap-used generic verification error after update-refs Reviewed-by: wkemper --- src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index 225339a32198..afef11640c96 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -1086,7 +1086,7 @@ void ShenandoahVerifier::verify_generic(ShenandoahGeneration* generation, Verify _verify_cset_disable, // cset may be inconsistent _verify_liveness_disable, // no reliable liveness data _verify_regions_disable, // no reliable region data - _verify_size_exact, // expect generation and heap sizes to match exactly + _verify_size_disable, // no reliable sizing data _verify_gcstate_disable // no data about gcstate ); } From 97f9060abec87a201a2858bde06595f500c871b6 Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Wed, 11 Mar 2026 13:24:20 +0000 Subject: [PATCH 05/97] 8379181: Convert ObjectMonitorTable to use Atomic Reviewed-by: kbarrett, tschatzl --- .../share/runtime/objectMonitorTable.cpp | 111 +++++++++--------- .../share/runtime/objectMonitorTable.hpp | 3 +- 2 files changed, 60 insertions(+), 54 deletions(-) diff --git a/src/hotspot/share/runtime/objectMonitorTable.cpp b/src/hotspot/share/runtime/objectMonitorTable.cpp index 8431b6dd0d42..1b79d1bf1c41 100644 --- a/src/hotspot/share/runtime/objectMonitorTable.cpp +++ b/src/hotspot/share/runtime/objectMonitorTable.cpp @@ -31,6 +31,7 @@ #include "runtime/thread.hpp" #include "runtime/timerTrace.hpp" #include "runtime/trimNativeHeap.hpp" +#include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" // ----------------------------------------------------------------------------- @@ -117,17 +118,17 @@ // requirements. Don't change it for fun, it might backfire. // ----------------------------------------------------------------------------- -ObjectMonitorTable::Table* volatile ObjectMonitorTable::_curr; +Atomic ObjectMonitorTable::_curr; class ObjectMonitorTable::Table : public CHeapObj { friend class ObjectMonitorTable; DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, 0); const size_t _capacity_mask; // One less than its power-of-two capacity - Table* volatile _prev; // Set while rehashing - Entry volatile* _buckets; // The payload + Atomic _prev; // Set while rehashing + Atomic* _buckets; // The payload DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(_capacity_mask) + sizeof(_prev) + sizeof(_buckets)); - volatile size_t _items_count; + Atomic _items_count; DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, sizeof(_items_count)); static Entry as_entry(ObjectMonitor* monitor) { @@ -156,11 +157,11 @@ class ObjectMonitorTable::Table : public CHeapObj { // Make sure we leave space for previous versions to relocate too. bool try_inc_items_count() { for (;;) { - size_t population = AtomicAccess::load(&_items_count); + size_t population = _items_count.load_relaxed(); if (should_grow(population)) { return false; } - if (AtomicAccess::cmpxchg(&_items_count, population, population + 1, memory_order_relaxed) == population) { + if (_items_count.compare_set(population, population + 1, memory_order_relaxed)) { return true; } } @@ -171,31 +172,31 @@ class ObjectMonitorTable::Table : public CHeapObj { } void inc_items_count() { - AtomicAccess::inc(&_items_count, memory_order_relaxed); + _items_count.add_then_fetch(1u, memory_order_relaxed); } void dec_items_count() { - AtomicAccess::dec(&_items_count, memory_order_relaxed); + _items_count.sub_then_fetch(1u, memory_order_relaxed); } public: Table(size_t capacity, Table* prev) : _capacity_mask(capacity - 1), _prev(prev), - _buckets(NEW_C_HEAP_ARRAY(Entry, capacity, mtObjectMonitor)), + _buckets(NEW_C_HEAP_ARRAY(Atomic, capacity, mtObjectMonitor)), _items_count(0) { for (size_t i = 0; i < capacity; ++i) { - _buckets[i] = empty(); + ::new (_buckets + i) Atomic(empty()); } } ~Table() { - FREE_C_HEAP_ARRAY(Entry, _buckets); + FREE_C_HEAP_ARRAY(Atomic, _buckets); } Table* prev() { - return AtomicAccess::load(&_prev); + return _prev.load_relaxed(); } size_t capacity() { @@ -207,12 +208,12 @@ class ObjectMonitorTable::Table : public CHeapObj { } bool should_grow() { - return should_grow(AtomicAccess::load(&_items_count)); + return should_grow(_items_count.load_relaxed()); } size_t total_items() { - size_t current_items = AtomicAccess::load(&_items_count); - Table* prev = AtomicAccess::load(&_prev); + size_t current_items = _items_count.load_relaxed(); + Table* prev = _prev.load_relaxed(); if (prev != nullptr) { return prev->total_items() + current_items; } @@ -221,7 +222,7 @@ class ObjectMonitorTable::Table : public CHeapObj { ObjectMonitor* get(oop obj, intptr_t hash) { // Acquire tombstones and relocations in case prev transitioned to null - Table* prev = AtomicAccess::load_acquire(&_prev); + Table* prev = _prev.load_acquire(); if (prev != nullptr) { ObjectMonitor* result = prev->get(obj, hash); if (result != nullptr) { @@ -233,8 +234,8 @@ class ObjectMonitorTable::Table : public CHeapObj { size_t index = start_index; for (;;) { - Entry volatile* bucket = _buckets + index; - Entry entry = AtomicAccess::load_acquire(bucket); + Atomic& bucket = _buckets[index]; + Entry entry = bucket.load_acquire(); if (entry == tombstone() || entry == empty()) { // Not found @@ -257,7 +258,7 @@ class ObjectMonitorTable::Table : public CHeapObj { ObjectMonitor* prepare_insert(oop obj, intptr_t hash) { // Acquire any tombstones and relocations if prev transitioned to null. - Table* prev = AtomicAccess::load_acquire(&_prev); + Table* prev = _prev.load_acquire(); if (prev != nullptr) { ObjectMonitor* result = prev->prepare_insert(obj, hash); if (result != nullptr) { @@ -269,13 +270,13 @@ class ObjectMonitorTable::Table : public CHeapObj { size_t index = start_index; for (;;) { - Entry volatile* bucket = _buckets + index; - Entry entry = AtomicAccess::load_acquire(bucket); + Atomic& bucket = _buckets[index]; + Entry entry = bucket.load_acquire(); if (entry == empty()) { // Found an empty slot to install the new monitor in. // To avoid concurrent inserts succeeding, place a tombstone here. - Entry result = AtomicAccess::cmpxchg(bucket, entry, tombstone(), memory_order_relaxed); + Entry result = bucket.compare_exchange(entry, tombstone(), memory_order_relaxed); if (result == entry) { // Success! Nobody will try to insert here again, except reinsert from rehashing. return nullptr; @@ -300,7 +301,7 @@ class ObjectMonitorTable::Table : public CHeapObj { ObjectMonitor* get_set(oop obj, Entry new_monitor, intptr_t hash) { // Acquire any tombstones and relocations if prev transitioned to null. - Table* prev = AtomicAccess::load_acquire(&_prev); + Table* prev = _prev.load_acquire(); if (prev != nullptr) { // Sprinkle tombstones in previous tables to force concurrent inserters // to the latest table. We only really want to try inserting in the @@ -315,14 +316,14 @@ class ObjectMonitorTable::Table : public CHeapObj { size_t index = start_index; for (;;) { - Entry volatile* bucket = _buckets + index; - Entry entry = AtomicAccess::load_acquire(bucket); + Atomic& bucket = _buckets[index]; + Entry entry = bucket.load_acquire(); if (entry == empty()) { // Empty slot to install the new monitor if (try_inc_items_count()) { // Succeeding in claiming an item. - Entry result = AtomicAccess::cmpxchg(bucket, entry, new_monitor, memory_order_acq_rel); + Entry result = bucket.compare_exchange(entry, new_monitor, memory_order_acq_rel); if (result == entry) { // Success - already incremented. return as_monitor(new_monitor); @@ -334,7 +335,7 @@ class ObjectMonitorTable::Table : public CHeapObj { } else { // Out of allowance; leaving place for rehashing to succeed. // To avoid concurrent inserts succeeding, place a tombstone here. - Entry result = AtomicAccess::cmpxchg(bucket, entry, tombstone(), memory_order_acq_rel); + Entry result = bucket.compare_exchange(entry, tombstone(), memory_order_acq_rel); if (result == entry) { // Success; nobody will try to insert here again, except reinsert from rehashing. return nullptr; @@ -366,8 +367,8 @@ class ObjectMonitorTable::Table : public CHeapObj { size_t index = start_index; for (;;) { - Entry volatile* bucket = _buckets + index; - Entry entry = AtomicAccess::load_acquire(bucket); + Atomic& bucket = _buckets[index]; + Entry entry = bucket.load_acquire(); if (entry == empty()) { // The monitor does not exist in this table. @@ -381,8 +382,8 @@ class ObjectMonitorTable::Table : public CHeapObj { if (entry == old_monitor) { // Found matching entry; remove it - Entry result = AtomicAccess::cmpxchg(bucket, entry, removed(), memory_order_relaxed); - assert(result == entry, "should not fail"); + bool result = bucket.compare_set(entry, removed(), memory_order_relaxed); + assert(result, "should not fail"); break; } @@ -395,8 +396,8 @@ class ObjectMonitorTable::Table : public CHeapObj { // still not being a monitor, instead of flickering back to being there. // Only the deflation thread rebuilds and unlinks tables, so we do not need // any concurrency safe prev read below. - if (_prev != nullptr) { - _prev->remove(obj, old_monitor, hash); + if (_prev.load_relaxed() != nullptr) { + _prev.load_relaxed()->remove(obj, old_monitor, hash); } } @@ -407,12 +408,12 @@ class ObjectMonitorTable::Table : public CHeapObj { size_t index = start_index; for (;;) { - Entry volatile* bucket = _buckets + index; - Entry entry = AtomicAccess::load_acquire(bucket); + Atomic& bucket = _buckets[index]; + Entry entry = bucket.load_acquire(); if (entry == empty()) { // Empty slot to install the new monitor. - Entry result = AtomicAccess::cmpxchg(bucket, entry, new_monitor, memory_order_acq_rel); + Entry result = bucket.compare_exchange(entry, new_monitor, memory_order_acq_rel); if (result == entry) { // Success - unconditionally increment. inc_items_count(); @@ -426,7 +427,7 @@ class ObjectMonitorTable::Table : public CHeapObj { if (entry == tombstone()) { // A concurrent inserter did not get enough allowance in the table. // But reinsert always succeeds - we will take the spot. - Entry result = AtomicAccess::cmpxchg(bucket, entry, new_monitor, memory_order_acq_rel); + Entry result = bucket.compare_exchange(entry, new_monitor, memory_order_acq_rel); if (result == entry) { // Success - unconditionally increment. inc_items_count(); @@ -439,7 +440,7 @@ class ObjectMonitorTable::Table : public CHeapObj { if (entry == removed()) { // A removed entry can be flipped back with reinsertion. - Entry result = AtomicAccess::cmpxchg(bucket, entry, new_monitor, memory_order_release); + Entry result = bucket.compare_exchange(entry, new_monitor, memory_order_release); if (result == entry) { // Success - but don't increment; the initial entry did that for us. return; @@ -459,7 +460,7 @@ class ObjectMonitorTable::Table : public CHeapObj { } void rebuild() { - Table* prev = _prev; + Table* prev = _prev.load_relaxed(); if (prev == nullptr) { // Base case for recursion - no previous version. return; @@ -477,12 +478,12 @@ class ObjectMonitorTable::Table : public CHeapObj { ThreadBlockInVM tbivm(current); } - Entry volatile* bucket = prev->_buckets + index; - Entry entry = AtomicAccess::load_acquire(bucket); + Atomic& bucket = prev->_buckets[index]; + Entry entry = bucket.load_acquire(); if (entry == empty()) { // Empty slot; put a tombstone there. - Entry result = AtomicAccess::cmpxchg(bucket, entry, tombstone(), memory_order_acq_rel); + Entry result = bucket.compare_exchange(entry, tombstone(), memory_order_acq_rel); if (result == empty()) { // Success; move to next entry. continue; @@ -508,17 +509,17 @@ class ObjectMonitorTable::Table : public CHeapObj { } // Unlink this table, releasing the tombstones and relocations. - AtomicAccess::release_store(&_prev, (Table*)nullptr); + _prev.release_store(nullptr); } }; void ObjectMonitorTable::create() { - _curr = new Table(128, nullptr); + _curr.store_relaxed(new Table(128, nullptr)); } ObjectMonitor* ObjectMonitorTable::monitor_get(oop obj) { const intptr_t hash = obj->mark().hash(); - Table* curr = AtomicAccess::load_acquire(&_curr); + Table* curr = _curr.load_acquire(); ObjectMonitor* monitor = curr->get(obj, hash); return monitor; } @@ -526,7 +527,7 @@ ObjectMonitor* ObjectMonitorTable::monitor_get(oop obj) { // Returns a new table to try inserting into. ObjectMonitorTable::Table* ObjectMonitorTable::grow_table(Table* curr) { Table* result; - Table* new_table = AtomicAccess::load_acquire(&_curr); + Table* new_table = _curr.load_acquire(); if (new_table != curr) { // Table changed; no need to try further return new_table; @@ -537,14 +538,14 @@ ObjectMonitorTable::Table* ObjectMonitorTable::grow_table(Table* curr) { // attempt to allocate the new table. MonitorLocker ml(MonitorDeflation_lock, Mutex::_no_safepoint_check_flag); - new_table = AtomicAccess::load_acquire(&_curr); + new_table = _curr.load_acquire(); if (new_table != curr) { // Table changed; no need to try further return new_table; } new_table = new Table(curr->capacity() << 1, curr); - result = AtomicAccess::cmpxchg(&_curr, curr, new_table, memory_order_acq_rel); + result = _curr.compare_exchange(curr, new_table, memory_order_acq_rel); if (result == curr) { log_info(monitorinflation)("Growing object monitor table (capacity: %zu)", new_table->capacity()); @@ -565,7 +566,7 @@ ObjectMonitorTable::Table* ObjectMonitorTable::grow_table(Table* curr) { ObjectMonitor* ObjectMonitorTable::monitor_put_get(ObjectMonitor* monitor, oop obj) { const intptr_t hash = obj->mark().hash(); - Table* curr = AtomicAccess::load_acquire(&_curr); + Table* curr = _curr.load_acquire(); for (;;) { // Curr is the latest table and is reasonably loaded. @@ -585,7 +586,7 @@ void ObjectMonitorTable::remove_monitor_entry(ObjectMonitor* monitor) { return; } const intptr_t hash = obj->mark().hash(); - Table* curr = AtomicAccess::load_acquire(&_curr); + Table* curr = _curr.load_acquire(); curr->remove(obj, curr->as_entry(monitor), hash); assert(monitor_get(obj) != monitor, "should have been removed"); } @@ -620,13 +621,13 @@ void ObjectMonitorTable::rebuild(GrowableArray* delete_list) { // given the growing threshold of 12.5%, it is impossible for the // tables to reach a load factor above 50%. Which is more than // enough to guarantee the function of this concurrent hash table. - Table* curr = AtomicAccess::load_acquire(&_curr); + Table* curr = _curr.load_acquire(); size_t need_to_accomodate = curr->total_items(); size_t new_capacity = curr->should_grow(need_to_accomodate) ? curr->capacity() << 1 : curr->capacity(); new_table = new Table(new_capacity, curr); - Table* result = AtomicAccess::cmpxchg(&_curr, curr, new_table, memory_order_acq_rel); + Table* result = _curr.compare_exchange(curr, new_table, memory_order_acq_rel); if (result != curr) { // Somebody else racingly started rehashing. Delete the // new_table and treat somebody else's table as the new one. @@ -653,7 +654,7 @@ void ObjectMonitorTable::destroy(GrowableArray* delete_list) { } address ObjectMonitorTable::current_table_address() { - return (address)(&_curr); + return reinterpret_cast
(&_curr) + _curr.value_offset_in_bytes(); } ByteSize ObjectMonitorTable::table_capacity_mask_offset() { @@ -661,5 +662,9 @@ ByteSize ObjectMonitorTable::table_capacity_mask_offset() { } ByteSize ObjectMonitorTable::table_buckets_offset() { + // Assumptions made from the emitted code about the layout. + STATIC_ASSERT(sizeof(Atomic) == sizeof(Entry*)); + STATIC_ASSERT(Atomic::value_offset_in_bytes() == 0); + return byte_offset_of(Table, _buckets); } diff --git a/src/hotspot/share/runtime/objectMonitorTable.hpp b/src/hotspot/share/runtime/objectMonitorTable.hpp index 0a56b6960698..1ec372883b25 100644 --- a/src/hotspot/share/runtime/objectMonitorTable.hpp +++ b/src/hotspot/share/runtime/objectMonitorTable.hpp @@ -27,6 +27,7 @@ #include "memory/allStatic.hpp" #include "oops/oopsHierarchy.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/sizes.hpp" @@ -44,7 +45,7 @@ class ObjectMonitorTable : AllStatic { class Table; private: - static Table* volatile _curr; + static Atomic _curr; static Table* grow_table(Table* curr); enum class Entry : uintptr_t { From d4960980cc837cec26b33d40727fe05345b06ff2 Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Wed, 11 Mar 2026 14:33:19 +0000 Subject: [PATCH 06/97] 8373143: C2: verify that adr_type and adr's type match in LoadNode::make and StoreNode::make Reviewed-by: qamai, bmaillard --- src/hotspot/share/opto/arraycopynode.cpp | 26 ++++++++++++++--- src/hotspot/share/opto/buildOopMap.cpp | 3 ++ src/hotspot/share/opto/callnode.cpp | 6 +++- src/hotspot/share/opto/library_call.cpp | 3 +- src/hotspot/share/opto/macro.cpp | 4 ++- src/hotspot/share/opto/macro.hpp | 11 ++++---- src/hotspot/share/opto/macroArrayCopy.cpp | 34 ++++++++++++----------- src/hotspot/share/opto/memnode.cpp | 28 +++++++++++++++---- src/hotspot/share/opto/memnode.hpp | 8 ++++++ 9 files changed, 90 insertions(+), 33 deletions(-) diff --git a/src/hotspot/share/opto/arraycopynode.cpp b/src/hotspot/share/opto/arraycopynode.cpp index 4ee6107fe54d..3b006ae6b11a 100644 --- a/src/hotspot/share/opto/arraycopynode.cpp +++ b/src/hotspot/share/opto/arraycopynode.cpp @@ -426,7 +426,13 @@ Node* ArrayCopyNode::array_copy_forward(PhaseGVN *phase, for (int i = 1; i < count; i++) { Node* off = phase->MakeConX(type2aelembytes(copy_type) * i); Node* next_src = phase->transform(new AddPNode(base_src,adr_src,off)); + // We may have narrowed the type of next_src right before calling this method but because this runs with + // PhaseIterGVN::_delay_transform true, explicitly update the type of the AddP so it's consistent with its + // base and load() picks the right memory slice. + phase->set_type(next_src, next_src->Value(phase)); Node* next_dest = phase->transform(new AddPNode(base_dest,adr_dest,off)); + // Same as above + phase->set_type(next_dest, next_dest->Value(phase)); v = load(bs, phase, forward_ctl, mm, next_src, atp_src, value_type, copy_type); store(bs, phase, forward_ctl, mm, next_dest, atp_dest, v, value_type, copy_type); } @@ -464,7 +470,12 @@ Node* ArrayCopyNode::array_copy_backward(PhaseGVN *phase, for (int i = count-1; i >= 1; i--) { Node* off = phase->MakeConX(type2aelembytes(copy_type) * i); Node* next_src = phase->transform(new AddPNode(base_src,adr_src,off)); + // We may have narrowed the type of next_src right before calling this method but because this runs with + // PhaseIterGVN::_delay_transform true, explicitly update the type of the AddP so it's consistent with its + // base and store() picks the right memory slice. + phase->set_type(next_src, next_src->Value(phase)); Node* next_dest = phase->transform(new AddPNode(base_dest,adr_dest,off)); + phase->set_type(next_dest, next_dest->Value(phase)); Node* v = load(bs, phase, backward_ctl, mm, next_src, atp_src, value_type, copy_type); store(bs, phase, backward_ctl, mm, next_dest, atp_dest, v, value_type, copy_type); } @@ -592,6 +603,17 @@ Node *ArrayCopyNode::Ideal(PhaseGVN *phase, bool can_reshape) { const Type* value_type = nullptr; bool disjoint_bases = false; + Node* src = in(ArrayCopyNode::Src); + Node* dest = in(ArrayCopyNode::Dest); + // EA may have moved an input to a new slice. EA stores the new address types in the ArrayCopy node itself + // (_src_type/_dest_type). phase->type(src) and _src_type or phase->type(dest) and _dest_type may be different + // when this transformation runs if igvn hasn't had a chance to propagate the new types yet. Make sure the new + // types are taken into account so new Load/Store nodes are created on the right slice. + const TypePtr* atp_src = get_address_type(phase, _src_type, src); + const TypePtr* atp_dest = get_address_type(phase, _dest_type, dest); + phase->set_type(src, phase->type(src)->join_speculative(atp_src)); + phase->set_type(dest, phase->type(dest)->join_speculative(atp_dest)); + if (!prepare_array_copy(phase, can_reshape, adr_src, base_src, adr_dest, base_dest, copy_type, value_type, disjoint_bases)) { @@ -600,10 +622,6 @@ Node *ArrayCopyNode::Ideal(PhaseGVN *phase, bool can_reshape) { return nullptr; } - Node* src = in(ArrayCopyNode::Src); - Node* dest = in(ArrayCopyNode::Dest); - const TypePtr* atp_src = get_address_type(phase, _src_type, src); - const TypePtr* atp_dest = get_address_type(phase, _dest_type, dest); Node* in_mem = in(TypeFunc::Memory); if (can_reshape) { diff --git a/src/hotspot/share/opto/buildOopMap.cpp b/src/hotspot/share/opto/buildOopMap.cpp index 675113163e84..e3a94f78d9ce 100644 --- a/src/hotspot/share/opto/buildOopMap.cpp +++ b/src/hotspot/share/opto/buildOopMap.cpp @@ -377,6 +377,9 @@ OopMap *OopFlow::build_oop_map( Node *n, int max_reg, PhaseRegAlloc *regalloc, i worklist.push(u); } } + if (m->is_SpillCopy()) { + worklist.push(m->in(1)); + } } } #endif diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index 9b3d7b38d15c..d4c832359d63 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -844,6 +844,10 @@ bool CallNode::may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) { } } guarantee(dest != nullptr, "Call had only one ptr in, broken IR!"); + if (phase->type(dest)->isa_rawptr()) { + // may happen for an arraycopy that initializes a newly allocated object. Conservatively return true; + return true; + } if (!dest->is_top() && may_modify_arraycopy_helper(phase->type(dest)->is_oopptr(), t_oop, phase)) { return true; } @@ -1742,7 +1746,7 @@ Node *AllocateNode::make_ideal_mark(PhaseGVN* phase, Node* control, Node* mem) { if (UseCompactObjectHeaders) { Node* klass_node = in(AllocateNode::KlassNode); Node* proto_adr = phase->transform(new AddPNode(phase->C->top(), klass_node, phase->MakeConX(in_bytes(Klass::prototype_header_offset())))); - mark_node = LoadNode::make(*phase, control, mem, proto_adr, TypeRawPtr::BOTTOM, TypeX_X, TypeX_X->basic_type(), MemNode::unordered); + mark_node = LoadNode::make(*phase, control, mem, proto_adr, phase->type(proto_adr)->is_ptr(), TypeX_X, TypeX_X->basic_type(), MemNode::unordered); } else { // For now only enable fast locking for non-array types mark_node = phase->MakeConX(markWord::prototype().value()); diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 3627d06a87a2..b3ee060d75f2 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -3076,7 +3076,8 @@ bool LibraryCallKit::inline_native_vthread_start_transition(address funcAddr, co Node* global_disable_addr = makecon(TypeRawPtr::make((address)MountUnmountDisabler::global_vthread_transition_disable_count_address())); Node* global_disable = ideal.load(ideal.ctrl(), global_disable_addr, TypeInt::INT, T_INT, Compile::AliasIdxRaw, true /*require_atomic_access*/); Node* vt_disable_addr = basic_plus_adr(vt_oop, java_lang_Thread::vthread_transition_disable_count_offset()); - Node* vt_disable = ideal.load(ideal.ctrl(), vt_disable_addr, TypeInt::INT, T_INT, Compile::AliasIdxRaw, true /*require_atomic_access*/); + const TypePtr* vt_disable_addr_t = _gvn.type(vt_disable_addr)->is_ptr(); + Node* vt_disable = ideal.load(ideal.ctrl(), vt_disable_addr, TypeInt::INT, T_INT, C->get_alias_index(vt_disable_addr_t), true /*require_atomic_access*/); Node* disabled = _gvn.transform(new AddINode(global_disable, vt_disable)); ideal.if_then(disabled, BoolTest::ne, ideal.ConI(0)); { diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 510531c95cd7..b9045ddcf4c7 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -1313,7 +1313,9 @@ void PhaseMacroExpand::expand_allocate_common( initial_slow_test = nullptr; } - bool allocation_has_use = (alloc->result_cast() != nullptr); + // ArrayCopyNode right after an allocation operates on the raw result projection for the Allocate node so it's not + // safe to remove such an allocation even if it has no result cast. + bool allocation_has_use = (alloc->result_cast() != nullptr) || (alloc->initialization() != nullptr && alloc->initialization()->is_complete_with_arraycopy()); if (!allocation_has_use) { InitializeNode* init = alloc->initialization(); if (init != nullptr) { diff --git a/src/hotspot/share/opto/macro.hpp b/src/hotspot/share/opto/macro.hpp index 0f8d8fb51726..070bfbe14785 100644 --- a/src/hotspot/share/opto/macro.hpp +++ b/src/hotspot/share/opto/macro.hpp @@ -40,14 +40,15 @@ class PhaseMacroExpand : public Phase { public: // Helper methods roughly modeled after GraphKit: - Node* basic_plus_adr(Node* base, int offset) { - return (offset == 0)? base: basic_plus_adr(base, MakeConX(offset)); + Node* basic_plus_adr(Node* ptr, int offset, bool raw_base = false) { + return (offset == 0)? ptr: basic_plus_adr(ptr, MakeConX(offset), raw_base); } Node* basic_plus_adr(Node* base, Node* ptr, int offset) { return (offset == 0)? ptr: basic_plus_adr(base, ptr, MakeConX(offset)); } - Node* basic_plus_adr(Node* base, Node* offset) { - return basic_plus_adr(base, base, offset); + Node* basic_plus_adr(Node* ptr, Node* offset, bool raw_base = false) { + Node* base = raw_base ? top() : ptr; + return basic_plus_adr(base, ptr, offset); } Node* basic_plus_adr(Node* base, Node* ptr, Node* offset) { Node* adr = new AddPNode(base, ptr, offset); @@ -109,7 +110,7 @@ class PhaseMacroExpand : public Phase { // More helper methods modeled after GraphKit for array copy void insert_mem_bar(Node** ctrl, Node** mem, int opcode, int alias_idx, Node* precedent = nullptr); - Node* array_element_address(Node* ary, Node* idx, BasicType elembt); + Node* array_element_address(Node* ary, Node* idx, BasicType elembt, bool raw_base); Node* ConvI2L(Node* offset); // helper methods modeled after LibraryCallKit for array copy diff --git a/src/hotspot/share/opto/macroArrayCopy.cpp b/src/hotspot/share/opto/macroArrayCopy.cpp index 0719ffc45a54..344561606b58 100644 --- a/src/hotspot/share/opto/macroArrayCopy.cpp +++ b/src/hotspot/share/opto/macroArrayCopy.cpp @@ -55,10 +55,10 @@ void PhaseMacroExpand::insert_mem_bar(Node** ctrl, Node** mem, int opcode, int a } } -Node* PhaseMacroExpand::array_element_address(Node* ary, Node* idx, BasicType elembt) { +Node* PhaseMacroExpand::array_element_address(Node* ary, Node* idx, BasicType elembt, bool raw_base) { uint shift = exact_log2(type2aelembytes(elembt)); uint header = arrayOopDesc::base_offset_in_bytes(elembt); - Node* base = basic_plus_adr(ary, header); + Node* base = basic_plus_adr(ary, header, raw_base); #ifdef _LP64 // see comment in GraphKit::array_element_address int index_max = max_jint - 1; // array size is max_jint, index is one less @@ -67,7 +67,7 @@ Node* PhaseMacroExpand::array_element_address(Node* ary, Node* idx, BasicType el #endif Node* scale = new LShiftXNode(idx, intcon(shift)); transform_later(scale); - return basic_plus_adr(ary, base, scale); + return basic_plus_adr(raw_base ? top() : ary, base, scale); } Node* PhaseMacroExpand::ConvI2L(Node* offset) { @@ -379,6 +379,7 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode* bool disjoint_bases, bool length_never_negative, RegionNode* slow_region) { + Node* orig_dest = dest; if (slow_region == nullptr) { slow_region = new RegionNode(1); transform_later(slow_region); @@ -411,6 +412,7 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode* assert(dest->is_CheckCastPP(), "sanity"); assert(dest->in(0)->in(0) == init, "dest pinned"); adr_type = TypeRawPtr::BOTTOM; // all initializations are into raw memory + dest = dest->in(1); // writing to raw memory requires a raw base // From this point on, every exit path is responsible for // initializing any non-copied parts of the object to zero. // Also, if this flag is set we make sure that arraycopy interacts properly @@ -771,7 +773,7 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode* local_mem = generate_slow_arraycopy(ac, &local_ctrl, local_mem, &local_io, adr_type, - src, src_offset, dest, dest_offset, + src, src_offset, orig_dest, dest_offset, copy_length, /*dest_uninitialized*/false); result_region->init_req(slow_call_path, local_ctrl); @@ -839,7 +841,7 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode* _igvn.replace_node(_callprojs.fallthrough_catchproj, *ctrl); #ifdef ASSERT - const TypeOopPtr* dest_t = _igvn.type(dest)->is_oopptr(); + const TypeOopPtr* dest_t = _igvn.type(orig_dest)->is_oopptr(); if (dest_t->is_known_instance()) { ArrayCopyNode* ac = nullptr; assert(ArrayCopyNode::may_modify(dest_t, (*ctrl)->in(0)->as_MemBar(), &_igvn, ac), "dependency on arraycopy lost"); @@ -915,12 +917,12 @@ void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem, if (start_con >= 0 && end_con >= 0) { // Constant start and end. Simple. mem = ClearArrayNode::clear_memory(ctrl, mem, dest, - start_con, end_con, false, &_igvn); + start_con, end_con, adr_type == TypeRawPtr::BOTTOM, &_igvn); } else if (start_con >= 0 && dest_size != top()) { // Constant start, pre-rounded end after the tail of the array. Node* end = dest_size; mem = ClearArrayNode::clear_memory(ctrl, mem, dest, - start_con, end, false, &_igvn); + start_con, end, adr_type == TypeRawPtr::BOTTOM, &_igvn); } else if (start_con >= 0 && slice_len != top()) { // Constant start, non-constant end. End needs rounding up. // End offset = round_up(abase + ((slice_idx_con + slice_len) << scale), 8) @@ -933,7 +935,7 @@ void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem, end = transform_later(new AddXNode(end, MakeConX(end_base)) ); end = transform_later(new AndXNode(end, MakeConX(~end_round)) ); mem = ClearArrayNode::clear_memory(ctrl, mem, dest, - start_con, end, false, &_igvn); + start_con, end, adr_type == TypeRawPtr::BOTTOM, &_igvn); } else if (start_con < 0 && dest_size != top()) { // Non-constant start, pre-rounded end after the tail of the array. // This is almost certainly a "round-to-end" operation. @@ -960,14 +962,14 @@ void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem, if (bump_bit != 0) { // Store a zero to the immediately preceding jint: Node* x1 = transform_later(new AddXNode(start, MakeConX(-bump_bit)) ); - Node* p1 = basic_plus_adr(dest, x1); + Node* p1 = basic_plus_adr(dest, x1, adr_type == TypeRawPtr::BOTTOM); mem = StoreNode::make(_igvn, ctrl, mem, p1, adr_type, intcon(0), T_INT, MemNode::unordered); mem = transform_later(mem); } } Node* end = dest_size; // pre-rounded mem = ClearArrayNode::clear_memory(ctrl, mem, dest, - start, end, false, &_igvn); + start, end, adr_type == TypeRawPtr::BOTTOM, &_igvn); } else { // Non-constant start, unrounded non-constant end. // (Nobody zeroes a random midsection of an array using this routine.) @@ -1009,7 +1011,7 @@ bool PhaseMacroExpand::generate_block_arraycopy(Node** ctrl, MergeMemNode** mem, if (((src_off | dest_off) & (BytesPerLong-1)) == BytesPerInt && ((src_off ^ dest_off) & (BytesPerLong-1)) == 0) { Node* sptr = basic_plus_adr(src, src_off); - Node* dptr = basic_plus_adr(dest, dest_off); + Node* dptr = basic_plus_adr(dest, dest_off, adr_type == TypeRawPtr::BOTTOM); const TypePtr* s_adr_type = _igvn.type(sptr)->is_ptr(); assert(s_adr_type->isa_aryptr(), "impossible slice"); uint s_alias_idx = C->get_alias_index(s_adr_type); @@ -1037,7 +1039,7 @@ bool PhaseMacroExpand::generate_block_arraycopy(Node** ctrl, MergeMemNode** mem, // Do this copy by giant steps. Node* sptr = basic_plus_adr(src, src_off); - Node* dptr = basic_plus_adr(dest, dest_off); + Node* dptr = basic_plus_adr(dest, dest_off, adr_type == TypeRawPtr::BOTTOM); Node* countx = dest_size; countx = transform_later(new SubXNode(countx, MakeConX(dest_off))); countx = transform_later(new URShiftXNode(countx, intcon(LogBytesPerLong))); @@ -1134,8 +1136,8 @@ Node* PhaseMacroExpand::generate_checkcast_arraycopy(Node** ctrl, MergeMemNode** Node* check_offset = ConvI2X(transform_later(n3)); Node* check_value = dest_elem_klass; - Node* src_start = array_element_address(src, src_offset, T_OBJECT); - Node* dest_start = array_element_address(dest, dest_offset, T_OBJECT); + Node* src_start = array_element_address(src, src_offset, T_OBJECT, false); + Node* dest_start = array_element_address(dest, dest_offset, T_OBJECT, adr_type == TypeRawPtr::BOTTOM); const TypeFunc* call_type = OptoRuntime::checkcast_arraycopy_Type(); Node* call = make_leaf_call(*ctrl, *mem, call_type, copyfunc_addr, "checkcast_arraycopy", adr_type, @@ -1190,8 +1192,8 @@ void PhaseMacroExpand::generate_unchecked_arraycopy(Node** ctrl, MergeMemNode** Node* src_start = src; Node* dest_start = dest; if (src_offset != nullptr || dest_offset != nullptr) { - src_start = array_element_address(src, src_offset, basic_elem_type); - dest_start = array_element_address(dest, dest_offset, basic_elem_type); + src_start = array_element_address(src, src_offset, basic_elem_type, false); + dest_start = array_element_address(dest, dest_offset, basic_elem_type, adr_type == TypeRawPtr::BOTTOM); } // Figure out which arraycopy runtime method to call. diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index f7da9a96d345..6cb14444f6bb 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -1010,6 +1010,7 @@ bool LoadNode::is_immutable_value(Node* adr) { Node* LoadNode::make(PhaseGVN& gvn, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt, BasicType bt, MemOrd mo, ControlDependency control_dependency, bool require_atomic_access, bool unaligned, bool mismatched, bool unsafe, uint8_t barrier_data) { Compile* C = gvn.C; + assert(adr->is_top() || C->get_alias_index(gvn.type(adr)->is_ptr()) == C->get_alias_index(adr_type), "adr and adr_type must agree"); // sanity check the alias category against the created node type assert(!(adr_type->isa_oopptr() && @@ -1411,8 +1412,12 @@ Node* LoadNode::convert_to_unsigned_load(PhaseGVN& gvn) { assert(false, "no unsigned variant: %s", Name()); return nullptr; } + const Type* mem_t = gvn.type(in(MemNode::Address)); + if (mem_t == Type::TOP) { + return gvn.C->top(); + } return LoadNode::make(gvn, in(MemNode::Control), in(MemNode::Memory), in(MemNode::Address), - raw_adr_type(), rt, bt, _mo, _control_dependency, + mem_t->is_ptr(), rt, bt, _mo, _control_dependency, false /*require_atomic_access*/, is_unaligned_access(), is_mismatched_access()); } @@ -1431,8 +1436,12 @@ Node* LoadNode::convert_to_signed_load(PhaseGVN& gvn) { assert(false, "no signed variant: %s", Name()); return nullptr; } + const Type* mem_t = gvn.type(in(MemNode::Address)); + if (mem_t == Type::TOP) { + return gvn.C->top(); + } return LoadNode::make(gvn, in(MemNode::Control), in(MemNode::Memory), in(MemNode::Address), - raw_adr_type(), rt, bt, _mo, _control_dependency, + mem_t->is_ptr(), rt, bt, _mo, _control_dependency, false /*require_atomic_access*/, is_unaligned_access(), is_mismatched_access()); } @@ -1459,8 +1468,12 @@ Node* LoadNode::convert_to_reinterpret_load(PhaseGVN& gvn, const Type* rt) { const int op = Opcode(); bool require_atomic_access = (op == Op_LoadL && ((LoadLNode*)this)->require_atomic_access()) || (op == Op_LoadD && ((LoadDNode*)this)->require_atomic_access()); + const Type* mem_t = gvn.type(in(MemNode::Address)); + if (mem_t == Type::TOP) { + return gvn.C->top(); + } return LoadNode::make(gvn, in(MemNode::Control), in(MemNode::Memory), in(MemNode::Address), - raw_adr_type(), rt, bt, _mo, _control_dependency, + mem_t->is_ptr(), rt, bt, _mo, _control_dependency, require_atomic_access, is_unaligned_access(), is_mismatched); } @@ -1482,8 +1495,12 @@ Node* StoreNode::convert_to_reinterpret_store(PhaseGVN& gvn, Node* val, const Ty const int op = Opcode(); bool require_atomic_access = (op == Op_StoreL && ((StoreLNode*)this)->require_atomic_access()) || (op == Op_StoreD && ((StoreDNode*)this)->require_atomic_access()); + const Type* mem_t = gvn.type(in(MemNode::Address)); + if (mem_t == Type::TOP) { + return gvn.C->top(); + } StoreNode* st = StoreNode::make(gvn, in(MemNode::Control), in(MemNode::Memory), in(MemNode::Address), - raw_adr_type(), val, bt, _mo, require_atomic_access); + mem_t->is_ptr(), val, bt, _mo, require_atomic_access); bool is_mismatched = is_mismatched_access(); const TypeRawPtr* raw_type = gvn.type(in(MemNode::Memory))->isa_rawptr(); @@ -2778,6 +2795,7 @@ Node* LoadRangeNode::Identity(PhaseGVN* phase) { StoreNode* StoreNode::make(PhaseGVN& gvn, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val, BasicType bt, MemOrd mo, bool require_atomic_access) { assert((mo == unordered || mo == release), "unexpected"); Compile* C = gvn.C; + assert(adr_type == nullptr || adr->is_top() || C->get_alias_index(gvn.type(adr)->is_ptr()) == C->get_alias_index(adr_type), "adr and adr_type must agree"); assert(C->get_alias_index(adr_type) != Compile::AliasIdxRaw || ctl != nullptr, "raw memory operations should have control edge"); @@ -5070,7 +5088,7 @@ Node* InitializeNode::capture_store(StoreNode* st, intptr_t start, else ins_req(i, C->top()); // build a new edge } - Node* new_st = st->clone(); + Node* new_st = st->clone_with_adr_type(TypeRawPtr::BOTTOM); BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); new_st->set_req(MemNode::Control, in(Control)); new_st->set_req(MemNode::Memory, prev_mem); diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index 2f26c67f0a8f..30d44e820162 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -173,6 +173,14 @@ class MemNode : public Node { static void dump_adr_type(const TypePtr* adr_type, outputStream* st); virtual void dump_spec(outputStream *st) const; #endif + + MemNode* clone_with_adr_type(const TypePtr* adr_type) const { + MemNode* new_node = clone()->as_Mem(); +#ifdef ASSERT + new_node->_adr_type = adr_type; +#endif + return new_node; + } }; // Analyze a MemNode to try to prove that it is independent from other memory accesses From ef55947162c1853e453b79b66e2e971c8625a638 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Wed, 11 Mar 2026 15:18:32 +0000 Subject: [PATCH 07/97] 8379416: AIX build fails if system (not GNU) date tool is in PATH Reviewed-by: erikj, clanger --- make/autoconf/basic_tools.m4 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/make/autoconf/basic_tools.m4 b/make/autoconf/basic_tools.m4 index 8e42f9205a45..66ef94d48a8b 100644 --- a/make/autoconf/basic_tools.m4 +++ b/make/autoconf/basic_tools.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -369,6 +369,10 @@ AC_DEFUN_ONCE([BASIC_SETUP_COMPLEX_TOOLS], IS_GNU_DATE=yes else AC_MSG_RESULT([no]) + # Likely at the AIX provided version of the date utility here, which is not compatible + if test "x$OPENJDK_TARGET_OS" = "xaix"; then + AC_MSG_ERROR([gnu date from AIX toolbox is required]) + fi IS_GNU_DATE=no fi AC_SUBST(IS_GNU_DATE) From 775f07e67190dc848cf4e466673090311d168015 Mon Sep 17 00:00:00 2001 From: "Daniel D. Daugherty" Date: Wed, 11 Mar 2026 16:37:46 +0000 Subject: [PATCH 08/97] 8379435: More proposed cleanups for JDK-8373595 A new ObjectMonitorTable implementation Reviewed-by: coleenp, fbredberg --- .../share/runtime/objectMonitorTable.cpp | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/hotspot/share/runtime/objectMonitorTable.cpp b/src/hotspot/share/runtime/objectMonitorTable.cpp index 1b79d1bf1c41..bc173992d7a7 100644 --- a/src/hotspot/share/runtime/objectMonitorTable.cpp +++ b/src/hotspot/share/runtime/objectMonitorTable.cpp @@ -69,7 +69,7 @@ // allocate a new table (twice as large as the old one), and then copy all the // old monitor pointers from the old table to the new. // -// But since the OMT is a concurrent hash table and things needs to work for +// But since the OMT is a concurrent hash table and things need to work for // other clients of the OMT while we grow it, it gets a bit more // complicated. // @@ -125,7 +125,7 @@ class ObjectMonitorTable::Table : public CHeapObj { DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, 0); const size_t _capacity_mask; // One less than its power-of-two capacity - Atomic _prev; // Set while rehashing + Atomic _prev; // Set while growing/rebuilding Atomic* _buckets; // The payload DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(_capacity_mask) + sizeof(_prev) + sizeof(_buckets)); Atomic _items_count; @@ -251,8 +251,8 @@ class ObjectMonitorTable::Table : public CHeapObj { assert(index != start_index, "invariant"); } - // Rehashing could have started by now, but if a monitor has been inserted in a - // newer table, it was inserted after the get linearization point. + // Rebuilding could have started by now, but if a monitor has been inserted + // in a newer table, it was inserted after the get linearization point. return nullptr; } @@ -278,7 +278,7 @@ class ObjectMonitorTable::Table : public CHeapObj { // To avoid concurrent inserts succeeding, place a tombstone here. Entry result = bucket.compare_exchange(entry, tombstone(), memory_order_relaxed); if (result == entry) { - // Success! Nobody will try to insert here again, except reinsert from rehashing. + // Success! Nobody will try to insert here again, except reinsert from rebuilding. return nullptr; } entry = result; @@ -333,11 +333,11 @@ class ObjectMonitorTable::Table : public CHeapObj { dec_items_count(); entry = result; } else { - // Out of allowance; leaving place for rehashing to succeed. + // Out of allowance; leave space for rebuilding to succeed. // To avoid concurrent inserts succeeding, place a tombstone here. Entry result = bucket.compare_exchange(entry, tombstone(), memory_order_acq_rel); if (result == entry) { - // Success; nobody will try to insert here again, except reinsert from rehashing. + // Success; nobody will try to insert here again, except reinsert from rebuilding. return nullptr; } entry = result; @@ -558,7 +558,7 @@ ObjectMonitorTable::Table* ObjectMonitorTable::grow_table(Table* curr) { } } - // Somebody else started rehashing; restart in new table. + // Somebody else started rebuilding; restart in their new table. delete new_table; return result; @@ -591,7 +591,7 @@ void ObjectMonitorTable::remove_monitor_entry(ObjectMonitor* monitor) { assert(monitor_get(obj) != monitor, "should have been removed"); } -// Before handshake; rehash and unlink tables. +// Before handshake; rebuild and unlink tables. void ObjectMonitorTable::rebuild(GrowableArray* delete_list) { Table* new_table; { @@ -629,7 +629,7 @@ void ObjectMonitorTable::rebuild(GrowableArray* delete_list) { new_table = new Table(new_capacity, curr); Table* result = _curr.compare_exchange(curr, new_table, memory_order_acq_rel); if (result != curr) { - // Somebody else racingly started rehashing. Delete the + // Somebody else racingly started rebuilding. Delete the // new_table and treat somebody else's table as the new one. delete new_table; new_table = result; From c315d1cd2b0a09cfbd4b8263a0578537558be101 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Wed, 11 Mar 2026 16:39:57 +0000 Subject: [PATCH 09/97] 8357404: jpackage should attempt to get a package version from the JDK's release file if the --version option is not specified Reviewed-by: almatvee --- .../jpackage/internal/LinuxFromOptions.java | 17 + .../jdk/jpackage/internal/MacFromOptions.java | 11 +- .../jpackage/internal/ApplicationBuilder.java | 62 +- .../jdk/jpackage/internal/FromOptions.java | 17 +- .../jdk/jpackage/internal/ModuleInfo.java | 32 +- .../resources/MainResources.properties | 2 + .../internal/util/RuntimeReleaseFile.java | 130 +++ .../jdk/jpackage/internal/WinFromOptions.java | 12 +- test/jdk/tools/jpackage/TEST.properties | 2 +- .../jdk/jpackage/test/AppImageFile.java | 3 +- .../jdk/jpackage/test/JPackageCommand.java | 116 +- .../jdk/jpackage/test/PackageType.java | 40 +- .../internal/model/DottedVersionTest.java | 46 +- .../internal/util/RuntimeReleaseFileTest.java | 172 +++ .../tools/jpackage/share/AppVersionTest.java | 1031 ++++++++++++++++- .../jpackage/share/RuntimePackageTest.java | 12 +- 16 files changed, 1563 insertions(+), 142 deletions(-) create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RuntimeReleaseFile.java create mode 100644 test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RuntimeReleaseFileTest.java diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java index 0791c79c6628..1f9540364311 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java @@ -27,6 +27,7 @@ import static jdk.jpackage.internal.FromOptions.buildApplicationBuilder; import static jdk.jpackage.internal.FromOptions.createPackageBuilder; import static jdk.jpackage.internal.LinuxPackagingPipeline.APPLICATION_LAYOUT; +import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_LINUX_RPM; import static jdk.jpackage.internal.cli.StandardOption.LINUX_APP_CATEGORY; import static jdk.jpackage.internal.cli.StandardOption.LINUX_DEB_MAINTAINER_EMAIL; import static jdk.jpackage.internal.cli.StandardOption.LINUX_MENU_GROUP; @@ -39,6 +40,7 @@ import static jdk.jpackage.internal.model.StandardPackageType.LINUX_RPM; import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.model.DottedVersion; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.LinuxApplication; import jdk.jpackage.internal.model.LinuxDebPackage; @@ -67,6 +69,10 @@ static LinuxApplication createLinuxApplication(Options options) { appBuilder.launchers().map(LinuxPackagingPipeline::normalizeShortcuts).ifPresent(appBuilder::launchers); + if (OptionUtils.bundlingOperation(options) == CREATE_LINUX_RPM) { + appBuilder.derivedVersionNormalizer(LinuxFromOptions::normalizeRpmVersion); + } + return LinuxApplication.create(appBuilder.create()); } @@ -118,4 +124,15 @@ private static LinuxPackageBuilder createLinuxPackageBuilder(Options options, Li return pkgBuilder; } + private static String normalizeRpmVersion(String version) { + // RPM does not support "-" symbol in version. In some case + // we might have "-" from "release" file version. + // Normalize version if it has "-" symbols. All other supported version + // formats by "release" file should be supported by RPM. + if (version.contains("-")) { + return DottedVersion.lazy(version).toComponentsString(); + } + + return version; + } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java index be9519c57a66..20dcbcf57427 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java @@ -31,7 +31,6 @@ import static jdk.jpackage.internal.MacRuntimeValidator.validateRuntimeHasNoBinDir; import static jdk.jpackage.internal.OptionUtils.isBundlingOperation; import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_MAC_PKG; -import static jdk.jpackage.internal.cli.StandardBundlingOperation.SIGN_MAC_APP_IMAGE; import static jdk.jpackage.internal.cli.StandardOption.APPCLASS; import static jdk.jpackage.internal.cli.StandardOption.ICON; import static jdk.jpackage.internal.cli.StandardOption.MAC_APP_CATEGORY; @@ -65,6 +64,7 @@ import jdk.jpackage.internal.cli.Options; import jdk.jpackage.internal.cli.StandardFaOption; import jdk.jpackage.internal.model.ApplicationLaunchers; +import jdk.jpackage.internal.model.DottedVersion; import jdk.jpackage.internal.model.ExternalApplication; import jdk.jpackage.internal.model.FileAssociation; import jdk.jpackage.internal.model.Launcher; @@ -234,7 +234,7 @@ private static ApplicationBuilder createApplicationBuilder(Options options) { superAppBuilder.launchers(new ApplicationLaunchers(MacLauncher.create(mainLauncher), launchers.additionalLaunchers())); } - final var app = superAppBuilder.create(); + superAppBuilder.derivedVersionNormalizer(MacFromOptions::normalizeVersion); return superAppBuilder; } @@ -389,4 +389,11 @@ private static boolean hasAppImageSignIdentity(Options options) { private static boolean hasPkgInstallerSignIdentity(Options options) { return options.contains(MAC_SIGNING_KEY_NAME) || options.contains(MAC_INSTALLER_SIGN_IDENTITY); } + + private static String normalizeVersion(String version) { + // macOS requires 1, 2 or 3 components version string. + // When reading from release file it can be 1 or 3 or maybe more. + // We will always normalize to 3 components if needed. + return DottedVersion.lazy(version).trim(3).toComponentsString(); + } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java index ebfae9f8d4c4..0cca59f7c199 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java @@ -36,17 +36,20 @@ import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.UnaryOperator; import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLaunchers; import jdk.jpackage.internal.model.ExternalApplication; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.LauncherIcon; +import jdk.jpackage.internal.model.LauncherModularStartupInfo; import jdk.jpackage.internal.model.LauncherStartupInfo; import jdk.jpackage.internal.model.ResourceDirLauncherIcon; import jdk.jpackage.internal.model.RuntimeBuilder; import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.util.RootedPath; +import jdk.jpackage.internal.util.RuntimeReleaseFile; final class ApplicationBuilder { @@ -65,6 +68,8 @@ final class ApplicationBuilder { appImageLayout = other.appImageLayout; runtimeBuilder = other.runtimeBuilder; launchers = other.launchers; + runtimeReleaseFile = other.runtimeReleaseFile; + derivedVersionNormalizer = other.derivedVersionNormalizer; } ApplicationBuilder copy() { @@ -86,7 +91,7 @@ Application create() { return new Application.Stub( effectiveName, Optional.ofNullable(description).orElse(effectiveName), - Optional.ofNullable(version).orElseGet(DEFAULTS::version), + validatedVersion(), Optional.ofNullable(vendor).orElseGet(DEFAULTS::vendor), Optional.ofNullable(copyright).orElseGet(DEFAULTS::copyright), Optional.ofNullable(appDirSources).orElseGet(List::of), @@ -147,6 +152,11 @@ ApplicationBuilder version(String v) { Optional version() { return Optional.ofNullable(version); + } + + ApplicationBuilder runtimeReleaseFile(Path v) { + runtimeReleaseFile = v; + return this; } ApplicationBuilder vendor(String v) { @@ -169,6 +179,54 @@ ApplicationBuilder contentDirSources(Collection v) { return this; } + ApplicationBuilder derivedVersionNormalizer(UnaryOperator v) { + derivedVersionNormalizer = v; + return this; + } + + private String validatedVersion() { + return Optional.ofNullable(version).or(() -> { + // Application version has not been specified explicitly. Derive it. + var derivedVersion = derivedVersion(); + if (derivedVersionNormalizer != null) { + derivedVersion = derivedVersion.map(v -> { + var mappedVersion = derivedVersionNormalizer.apply(v); + if (!mappedVersion.equals(v)) { + Log.verbose(I18N.format("message.version-normalized", mappedVersion, v)); + } + return mappedVersion; + }); + } + return derivedVersion; + }).orElseGet(DEFAULTS::version); + } + + private Optional derivedVersion() { + if (appImageLayout instanceof RuntimeLayout && runtimeReleaseFile != null) { + try { + var releaseVersion = new RuntimeReleaseFile(runtimeReleaseFile).getJavaVersion().toString(); + Log.verbose(I18N.format("message.release-version", releaseVersion)); + return Optional.of(releaseVersion); + } catch (Exception ex) { + Log.verbose(ex); + return Optional.empty(); + } + } else if (launchers != null) { + return launchers.mainLauncher().startupInfo() + .filter(LauncherModularStartupInfo.class::isInstance) + .map(LauncherModularStartupInfo.class::cast) + .flatMap(modularStartupInfo -> { + var moduleVersion = modularStartupInfo.moduleVersion(); + moduleVersion.ifPresent(v -> { + Log.verbose(I18N.format("message.module-version", v, modularStartupInfo.moduleName())); + }); + return moduleVersion; + }); + } else { + return Optional.empty(); + } + } + static ApplicationLaunchers normalizeIcons( ApplicationLaunchers appLaunchers, Optional resourceDir, BiFunction launcherOverrideCtor) { @@ -333,6 +391,8 @@ String copyright() { private AppImageLayout appImageLayout; private RuntimeBuilder runtimeBuilder; private ApplicationLaunchers launchers; + private Path runtimeReleaseFile; + private UnaryOperator derivedVersionNormalizer; private static final Defaults DEFAULTS = new Defaults( "1.0", diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java index 4f590850328b..4b4e6f9dbac3 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java @@ -59,10 +59,10 @@ import jdk.jpackage.internal.model.ApplicationLaunchers; import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.model.Launcher; -import jdk.jpackage.internal.model.LauncherModularStartupInfo; import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.util.RootedPath; +import jdk.jpackage.internal.util.RuntimeReleaseFile; final class FromOptions { @@ -212,19 +212,6 @@ private static ApplicationBuilder createApplicationBuilder( runtimeBuilderBuilder.modulePath(ensureBaseModuleInModulePath(MODULE_PATH.findIn(options).orElseGet(List::of))); - if (!APP_VERSION.containsIn(options)) { - // Version is not specified explicitly. Try to get it from the app's module. - launchers.mainLauncher().startupInfo().ifPresent(startupInfo -> { - if (startupInfo instanceof LauncherModularStartupInfo modularStartupInfo) { - modularStartupInfo.moduleVersion().ifPresent(moduleVersion -> { - appBuilder.version(moduleVersion); - Log.verbose(I18N.format("message.module-version", - moduleVersion, modularStartupInfo.moduleName())); - }); - } - }); - } - predefinedRuntimeDirectory.ifPresentOrElse(runtimeBuilderBuilder::forRuntime, () -> { final var startupInfos = launchers.asList().stream() .map(Launcher::startupInfo) @@ -239,6 +226,8 @@ private static ApplicationBuilder createApplicationBuilder( } } + predefinedRuntimeDirectory.map(RuntimeReleaseFile::releaseFilePathInRuntime).ifPresent(appBuilder::runtimeReleaseFile); + return appBuilder; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleInfo.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleInfo.java index 71d1a66659e0..9555423db62c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleInfo.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleInfo.java @@ -24,17 +24,13 @@ */ package jdk.jpackage.internal; -import java.io.IOException; -import java.io.Reader; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleReference; import java.net.URI; -import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Properties; +import jdk.jpackage.internal.util.RuntimeReleaseFile; record ModuleInfo(String name, Optional version, Optional mainClass, Optional location) { @@ -56,30 +52,16 @@ static Optional fromCookedRuntime(String moduleName, Path cookedRunt // We can't extract info about version and main class of a module // linked in external runtime without running ModuleFinder in that // runtime. But this is too much work as the runtime might have been - // coocked without native launchers. So just make sure the module - // is linked in the runtime by simply analysing the data + // cooked without native launchers. So just make sure the module + // is linked in the runtime by simply analyzing the data // of `release` file. - final Path releaseFile = cookedRuntime.resolve("release"); - - try (Reader reader = Files.newBufferedReader(releaseFile)) { - Properties props = new Properties(); - props.load(reader); - String moduleList = props.getProperty("MODULES"); - if (moduleList == null) { - return Optional.empty(); - } - - if ((moduleList.startsWith("\"") && moduleList.endsWith("\"")) - || (moduleList.startsWith("\'") && moduleList.endsWith( - "\'"))) { - moduleList = moduleList.substring(1, moduleList.length() - 1); - } - - if (!List.of(moduleList.split("\\s+")).contains(moduleName)) { + try { + var cookedRuntimeModules = RuntimeReleaseFile.loadFromRuntime(cookedRuntime).getModules(); + if (!cookedRuntimeModules.contains(moduleName)) { return Optional.empty(); } - } catch (IOException|IllegalArgumentException ex) { + } catch (Exception ex) { Log.verbose(ex); return Optional.empty(); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties index 233067d64573..02784a52acc7 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties @@ -52,6 +52,8 @@ message.app-image-created=Succeeded in building output application image directo message.debug-working-directory=Kept working directory for debug: {0} message.module-version=Using version "{0}" from module "{1}" as application version +message.release-version=Using version "{0}" from "release" file of the predefined runtime as a package version +message.version-normalized=Using version "{0}" normalized to platform supported format from "{1}" message.error-header=Error: {0} message.advice-header=Advice to fix: {0} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RuntimeReleaseFile.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RuntimeReleaseFile.java new file mode 100644 index 000000000000..e055417f391d --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RuntimeReleaseFile.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal.util; + +import java.io.IOException; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.Properties; + +public final class RuntimeReleaseFile { + + public RuntimeReleaseFile(Path releaseFilePath) throws IOException { + // The implementation is based on the behavior of + // jdk.tools.jlink.internal.plugins.ReleaseInfoPlugin, which + // uses java.util.Properties to read/write the "release" file. + try (Reader reader = Files.newBufferedReader(releaseFilePath)) { + props = new Properties(); + props.load(reader); + } + } + + /** + * Returns path to the "runtime" file in the specified runtime directory. + * + * @param runtimeDir the path to a directory with the standard Java runtime + * structure + */ + public static Path releaseFilePathInRuntime(Path runtimeDir) { + return runtimeDir.resolve("release"); + } + + /** + * Creates the instance form the "runtime" file in the specified runtime + * directory. + *

+ * Uses {@link #releaseFilePathInRuntime(Path)} to get the path to the "runtime" + * file in the specified runtime directory. + * + * @param runtimeDir the path to a directory with the standard Java runtime + * structure + */ + public static RuntimeReleaseFile loadFromRuntime(Path runtimeDir) throws IOException { + return new RuntimeReleaseFile(releaseFilePathInRuntime(runtimeDir)); + } + + /** + * Returns verbatim value of the property with the specified name or an empty + * {@code Optional} if there is no property with the specified name. + *

+ * Property values in the "release" file are enclosed in double quotes. + * This method returns the value with the double quotes. + * + * @param propertyName the property name + */ + public Optional findRawProperty(String propertyName) { + return Optional.ofNullable(props.getProperty(propertyName)); + } + + /** + * Returns unquoted value of the property with the specified name or an empty + * {@code Optional} if there is no property with the specified name. + *

+ * Property values in the "release" file are enclosed in double quotes. This + * method returns the value without the double quotes. + * + * @param propertyName the property name + */ + public Optional findProperty(String propertyName) { + return findRawProperty(propertyName).map(v -> { + if (v.charAt(0) == '"' && v.charAt(v.length() - 1) == '"') { + return v.substring(1, v.length() - 1); + } else { + return v; + } + }); + } + + /** + * Returns the value of the "JAVA_VERSION" property. + *

+ * Will throw {@code NoSuchElementException} if there is no such property. Will + * use {@link Runtime.Version#parse(String)} method to parse version string. Any + * exception that it may yield will be passed to the caller verbatim. + * + * @throws NoSuchElementException if there is no such property + */ + public Runtime.Version getJavaVersion() { + return findProperty("JAVA_VERSION").map(Runtime.Version::parse).orElseThrow(NoSuchElementException::new); + } + + /** + * Returns the value of the "MODULES" property. + * + * @throws NoSuchElementException if there is no such property + */ + public List getModules() { + return findProperty("MODULES").map(v -> { + return List.of(v.split("\\s+")); + }).orElseThrow(NoSuchElementException::new); + } + + private final Properties props; +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java index 6009e2f67243..f6080523e89b 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.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 @@ -42,6 +42,7 @@ import static jdk.jpackage.internal.model.StandardPackageType.WIN_MSI; import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.model.DottedVersion; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.WinApplication; import jdk.jpackage.internal.model.WinExePackage; @@ -73,6 +74,8 @@ static WinApplication createWinApplication(Options options) { appBuilder.launchers().map(WinPackagingPipeline::normalizeShortcuts).ifPresent(appBuilder::launchers); + appBuilder.derivedVersionNormalizer(WinFromOptions::normalizeVersion); + return WinApplication.create(appBuilder.create()); } @@ -112,4 +115,11 @@ static WinExePackage createWinExePackage(Options options) { return pkgBuilder.create(); } + + private static String normalizeVersion(String version) { + // Windows requires between 2 and 4 components version string. + // When reading from release file it can be 1 or 3 or maybe more. + // One component will be normalized to 2 and more then 4 will be trim to 4. + return DottedVersion.lazy(version).trim(4).pad(2).toComponentsString(); + } } diff --git a/test/jdk/tools/jpackage/TEST.properties b/test/jdk/tools/jpackage/TEST.properties index 5cc4aa7a1b90..99af197b989b 100644 --- a/test/jdk/tools/jpackage/TEST.properties +++ b/test/jdk/tools/jpackage/TEST.properties @@ -8,7 +8,7 @@ requires.properties = \ jpackage.test.SQETest \ jpackage.test.MacSignTests -maxOutputSize = 2000000 +maxOutputSize = 10000000 # Run jpackage tests on windows platform sequentially. # Having "share" directory in the list affects tests on other platforms. diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java index 2dcccc673a9d..ce2cb6f6b575 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java @@ -26,6 +26,7 @@ import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; +import static jdk.jpackage.test.JPackageCommand.DEFAULT_VERSION; import java.io.IOException; import java.nio.file.Path; @@ -65,7 +66,7 @@ public static Path getPathInAppImage(Path appImageDir) { } public AppImageFile(String mainLauncherName, Optional mainLauncherClassName) { - this(mainLauncherName, mainLauncherClassName, "1.0", false, Map.of(mainLauncherName, Map.of())); + this(mainLauncherName, mainLauncherClassName, DEFAULT_VERSION, false, Map.of(mainLauncherName, Map.of())); } public AppImageFile(String mainLauncherName, String mainLauncherClassName) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java index 67c0ba41e3f5..6b1f67d50f41 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -43,6 +43,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; @@ -60,7 +61,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import jdk.jpackage.internal.model.DottedVersion; import jdk.jpackage.internal.util.MacBundle; +import jdk.jpackage.internal.util.Result; +import jdk.jpackage.internal.util.RuntimeReleaseFile; import jdk.jpackage.internal.util.function.ExceptionBox; import jdk.jpackage.internal.util.function.ThrowingConsumer; import jdk.jpackage.internal.util.function.ThrowingFunction; @@ -102,6 +106,7 @@ private JPackageCommand(JPackageCommand cmd, boolean immutable) { executeInDirectory = cmd.executeInDirectory; winMsiLogFile = cmd.winMsiLogFile; unpackedPackageDirectory = cmd.unpackedPackageDirectory; + explicitVersion = cmd.explicitVersion; } JPackageCommand createImmutableCopy() { @@ -246,22 +251,100 @@ public Path inputDir() { public String version() { return PropertyFinder.findAppProperty(this, - PropertyFinder.cmdlineOptionWithValue("--app-version").or(cmd -> { - if (cmd.isRuntime() && PackageType.MAC.contains(cmd.packageType())) { - // This is a macOS runtime bundle. - var predefinedRuntimeBundle = MacBundle.fromPath(Path.of(cmd.getArgumentValue("--runtime-image"))); - if (predefinedRuntimeBundle.isPresent()) { - // This is a macOS runtime bundle created from the predefined runtime bundle (not a predefined runtime directory). - // The version of this bundle should be copied from the Info.plist file of the predefined runtime bundle. - return MacHelper.readPList(predefinedRuntimeBundle.get().infoPlistFile()).findValue("CFBundleVersion"); - } - } - return Optional.empty(); - }), + PropertyFinder.of(Optional.ofNullable(explicitVersion)) + .or(PropertyFinder.cmdlineOptionWithValue("--app-version")) + .or(JPackageCommand::derivedVersion), PropertyFinder.appImageFile(appImageFile -> { return appImageFile.version(); }) - ).orElse("1.0"); + ).orElse(DEFAULT_VERSION); + } + + private Optional derivedVersion() { + if (isRuntime()) { + var predefinedRuntimePath = Path.of(getArgumentValue("--runtime-image")); + if (TKit.isOSX()) { + // This is a macOS runtime bundle. + return MacBundle.fromPath(predefinedRuntimePath).map(predefinedRuntimeBundle -> { + return Result.>of(() -> { + // This is a macOS runtime bundle created from the predefined runtime bundle (not a predefined runtime directory). + // The version of this bundle should be copied from the Info.plist file of the predefined runtime bundle. + return MacHelper.readPList(predefinedRuntimeBundle.infoPlistFile()).findValue("CFBundleVersion"); + }).value().flatMap(x -> x).or(() -> { + // Failed to read version from the Info.plist file of the predefined runtime bundle. + // Try to read it from the "release" file of the predefined runtime directory. + return normalizedVersionFromRuntimeReleaseFile(predefinedRuntimeBundle.homeDir()); + }); + }).orElseGet(() -> { + return normalizedVersionFromRuntimeReleaseFile(predefinedRuntimePath); + }); + } else { + return normalizedVersionFromRuntimeReleaseFile(predefinedRuntimePath); + } + } else { + return Optional.empty(); + } + } + + private Optional normalizedVersionFromRuntimeReleaseFile(Path runtimeDir) { + return Result.of(() -> { + return RuntimeReleaseFile.loadFromRuntime(runtimeDir).getJavaVersion().toString(); + }, Exception.class).value().map(JPackageCommand::normalizeDerivedVersion).map(map -> { + return Objects.requireNonNull(map.get(packageType())); + }); + } + + public static Map normalizeDerivedVersion(String version) { + var dotted = DottedVersion.lazy(version); + + var map = new HashMap(); + + // Linux + map.put(PackageType.LINUX_IMAGE, version); + map.put(PackageType.LINUX_DEB, version); + if (dotted.getUnprocessedSuffix().contains("-")) { + map.put(PackageType.LINUX_RPM, dotted.toComponentsString()); + } else { + map.put(PackageType.LINUX_RPM, version); + } + + // macOS + PackageType.ALL_MAC.forEach(type -> { + map.put(type, dotted.trim(3).pad(1).toComponentsString()); + }); + + // Windows + PackageType.ALL_WINDOWS.forEach(type -> { + DottedVersion ver; + if (dotted.getComponentsCount() < 2) { + ver = dotted.pad(2); + } else { + ver = dotted.trim(4); + } + map.put(type, ver.toComponentsString()); + }); + + map.put(PackageType.IMAGE, Objects.requireNonNull(map.get(PackageType.appImageForOS(PackageType.IMAGE.os())))); + + return map; + } + + /** + * Sets application version. + *

+ * Use this method to explicitly set the application version. Normally, the + * application version can be derived from the command line, but sometimes, when + * jpackage derives it from other sources, the {@code JPackageCommand} class + * can't get it correctly. Use this method in these uncommon cases. + * + * @param v the application version or {@code null} to reset previously set + * value + * @return this + */ + public JPackageCommand version(String v) { + verifyMutable(); + explicitVersion = v; + return this; } public String name() { @@ -1930,6 +2013,7 @@ private enum Mode { private Path executeInDirectory; private Path winMsiLogFile; private Path unpackedPackageDirectory; + private String explicitVersion; private Set readOnlyPathAsserts = Set.of(ReadOnlyPathAssert.values()); private Set standardAsserts = Set.of(StandardAssert.values()); private List> validators = new ArrayList<>(); @@ -1938,7 +2022,9 @@ private enum DefaultToolProviderKey { VALUE } - private static final Map PACKAGE_TYPES = Stream.of(PackageType.values()).collect(toMap(PackageType::getType, x -> x)); + private static final Map PACKAGE_TYPES = Stream.of(PackageType.values()).filter(type -> { + return type.isNative() || type == PackageType.IMAGE; + }).collect(toMap(PackageType::getType, x -> x)); // Set the property to the path of run-time image to speed up // building app images and platform bundles by avoiding running jlink. @@ -1947,6 +2033,8 @@ private enum DefaultToolProviderKey { // `--runtime-image` parameter set. public static final Path DEFAULT_RUNTIME_IMAGE = Optional.ofNullable(TKit.getConfigProperty("runtime-image")).map(Path::of).orElse(null); + public final static String DEFAULT_VERSION = "1.0"; + // [HH:mm:ss.SSS] private static final Pattern TIMESTAMP_REGEXP = Pattern.compile( "^\\[\\d\\d:\\d\\d:\\d\\d.\\d\\d\\d\\] "); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java index 085e782ea40a..df020d39fc80 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java @@ -48,16 +48,22 @@ public enum PackageType { LINUX_RPM(".rpm", OperatingSystem.LINUX), MAC_DMG(".dmg", OperatingSystem.MACOS), MAC_PKG(".pkg", OperatingSystem.MACOS), - IMAGE; - - PackageType() { + IMAGE(OperatingSystem.current()), + WIN_IMAGE(OperatingSystem.WINDOWS), + LINUX_IMAGE(OperatingSystem.LINUX), + MAC_IMAGE(OperatingSystem.MACOS), + ; + + PackageType(OperatingSystem os) { + this.os = Objects.requireNonNull(os); type = "app-image"; suffix = null; - supported = true; - enabled = true; + supported = (os == OperatingSystem.current()); + enabled = supported; } PackageType(String packageName, String bundleSuffix, OperatingSystem os) { + this.os = Objects.requireNonNull(os); type = Objects.requireNonNull(packageName); suffix = Objects.requireNonNull(bundleSuffix); supported = isSupported(new BundlingOperationDescriptor(os, type)); @@ -88,10 +94,29 @@ public boolean isEnabled() { return enabled; } + public boolean isAppImage() { + return type.equals(IMAGE.type); + } + + public boolean isNative() { + return !isAppImage(); + } + public String getType() { return type; } + public OperatingSystem os() { + return os; + } + + public static PackageType appImageForOS(OperatingSystem os) { + Objects.requireNonNull(os); + return Stream.of(LINUX_IMAGE, MAC_IMAGE, WIN_IMAGE).filter(type -> { + return type.os() == os; + }).findFirst().orElseThrow(); + } + public static RuntimeException throwSkippedExceptionIfNativePackagingUnavailable() { if (NATIVE.stream().noneMatch(PackageType::isSupported)) { TKit.throwSkippedException("None of the native packagers supported in this environment"); @@ -115,6 +140,7 @@ private static Set orderedSet(PackageType... types) { return new LinkedHashSet<>(List.of(types)); } + private final OperatingSystem os; private final String type; private final String suffix; private final boolean enabled; @@ -126,6 +152,10 @@ private static Set orderedSet(PackageType... types) { public static final Set NATIVE = Stream.of(LINUX, WINDOWS, MAC) .flatMap(Collection::stream).collect(Collectors.toUnmodifiableSet()); + public static final Set ALL_LINUX = orderedSet(LINUX_IMAGE, LINUX_DEB, LINUX_RPM); + public static final Set ALL_WINDOWS = orderedSet(WIN_IMAGE, WIN_MSI, WIN_EXE); + public static final Set ALL_MAC = orderedSet(MAC_IMAGE, MAC_DMG, MAC_PKG); + private static final class Inner { private static final Set DISABLED_PACKAGERS = Optional.ofNullable( TKit.tokenizeConfigProperty("disabledPackagers")).orElse( diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/DottedVersionTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/DottedVersionTest.java index df020f1a34c5..65f9c71a9107 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/DottedVersionTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/DottedVersionTest.java @@ -22,18 +22,19 @@ */ package jdk.jpackage.internal.model; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.Function; 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.assertThrowsExactly; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertNotSame; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -54,14 +55,6 @@ public record TestConfig(String input, this(input, type.createVersion, "", expectedComponentCount, input); } - static TestConfig greedy(String input, int expectedComponentCount, String expectedToComponent) { - return new TestConfig(input, Type.GREEDY.createVersion, "", expectedComponentCount, expectedToComponent); - } - - static TestConfig greedy(String input, int expectedComponentCount) { - return new TestConfig(input, Type.GREEDY.createVersion, "", expectedComponentCount, input); - } - static TestConfig lazy(String input, String expectedSuffix, int expectedComponentCount, String expectedToComponent) { return new TestConfig(input, Type.LAZY.createVersion, expectedSuffix, expectedComponentCount, expectedToComponent); } @@ -74,6 +67,7 @@ public void testValid(TestConfig cfg) { assertEquals(cfg.expectedSuffix(), dv.getUnprocessedSuffix()); assertEquals(cfg.expectedComponentCount(), dv.getComponents().length); assertEquals(cfg.expectedToComponent(), dv.toComponentsString()); + assertEquals(dv.toString(), cfg.input()); } private static List testValid() { @@ -123,7 +117,7 @@ private static List testValid() { @ParameterizedTest @MethodSource - public void testTrim(DottedVersion ver, String expectedStr, int limit) { + public void test_trim(DottedVersion ver, String expectedStr, int limit) { var expected = DottedVersion.lazy(expectedStr); var actual = ver.trim(limit); assertEquals(expected, actual); @@ -136,14 +130,14 @@ public void testTrim(DottedVersion ver, String expectedStr, int limit) { } @ParameterizedTest - @MethodSource - public void testTrimNegative(DottedVersion ver, int limit) { + @MethodSource("test_trim_pad_negative") + public void test_trim_negative(DottedVersion ver, int limit) { assertThrowsExactly(IllegalArgumentException.class, () -> { ver.trim(limit); }); } - private static Stream testTrim() { + private static Stream test_trim() { var testCases = new ArrayList(); @@ -160,15 +154,9 @@ private static Stream testTrim() { return testCases.stream().map(DottedVersionTest::mapFirstStringToDottedVersion); } - private static Stream testTrimNegative() { - return Stream.of( - Arguments.of("10.5.foo", -1) - ).map(DottedVersionTest::mapFirstStringToDottedVersion); - } - @ParameterizedTest @MethodSource - public void testPad(DottedVersion ver, String expectedStr, int limit) { + public void test_pad(DottedVersion ver, String expectedStr, int limit) { var expected = DottedVersion.lazy(expectedStr); var actual = ver.pad(limit); assertEquals(expected, actual); @@ -181,14 +169,14 @@ public void testPad(DottedVersion ver, String expectedStr, int limit) { } @ParameterizedTest - @MethodSource - public void testPadNegative(DottedVersion ver, int limit) { + @MethodSource("test_trim_pad_negative") + public void test_pad_negative(DottedVersion ver, int limit) { assertThrowsExactly(IllegalArgumentException.class, () -> { ver.pad(limit); }); } - private static Stream testPad() { + private static Stream test_pad() { var testCases = new ArrayList(); @@ -206,7 +194,7 @@ private static Stream testPad() { return testCases.stream().map(DottedVersionTest::mapFirstStringToDottedVersion); } - private static Stream testPadNegative() { + private static Stream test_trim_pad_negative() { return Stream.of( Arguments.of("10.5.foo", -1) ).map(DottedVersionTest::mapFirstStringToDottedVersion); diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RuntimeReleaseFileTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RuntimeReleaseFileTest.java new file mode 100644 index 000000000000..097f63d51ab5 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RuntimeReleaseFileTest.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.Properties; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class RuntimeReleaseFileTest { + + @Test + void test_invalid_input(@TempDir Path workdir) { + assertThrows(IOException.class, () -> { + new RuntimeReleaseFile(workdir); + }); + } + + @Test + void test_findRawProperty(@TempDir Path workdir) throws IOException { + var releaseFile = new RuntimeReleaseFile(createPropFile(workdir, Map.of("Name", "John", "Company", "\"Acme LTD\""))); + + assertEquals(Optional.empty(), releaseFile.findRawProperty("foo")); + assertEquals(Optional.of("John"), releaseFile.findRawProperty("Name")); + assertEquals(Optional.of("\"Acme LTD\""), releaseFile.findRawProperty("Company")); + } + + @Test + void test_findProperty(@TempDir Path workdir) throws IOException { + var releaseFile = new RuntimeReleaseFile(createPropFile(workdir, Map.of("Name", "John", "Company", "\"Acme LTD\""))); + + assertEquals(Optional.empty(), releaseFile.findProperty("foo")); + assertEquals(Optional.of("John"), releaseFile.findProperty("Name")); + assertEquals(Optional.of("Acme LTD"), releaseFile.findProperty("Company")); + } + + @ParameterizedTest + @CsvSource({ + "foo, foo", + "\"foo\", foo", + "'foo', 'foo'", + "\"f\"o\"o\", f\"o\"o", + "\"foo, \"foo", + "foo\", foo\"", + }) + void test_findProperty(String rawValue, String expectedValue, @TempDir Path workdir) throws IOException { + var releaseFile = new RuntimeReleaseFile(createPropFile(workdir, Map.of("FOO", rawValue))); + + assertEquals(expectedValue, releaseFile.findProperty("FOO").orElseThrow()); + } + + @ParameterizedTest + @CsvSource({ + "\"27.1.2\", 27.1.2", + "27.1.2, 27.1.2", + "\"27.1.2-ea\", 27.1.2-ea", + "27.1.2-ea, 27.1.2-ea", + "\"27.1.2+15\", 27.1.2+15", + "27.1.2+15, 27.1.2+15", + }) + void test_getJavaVersion(String version, String expectedVersion, @TempDir Path workdir) throws IOException { + var releaseFile = new RuntimeReleaseFile(createPropFileWithValue(workdir, "JAVA_VERSION", version)); + + final var value = releaseFile.getJavaVersion(); + + assertEquals(expectedVersion, value.toString()); + } + + @ParameterizedTest + @CsvSource({ + "\"7.1.2+foo\"", + "\"foo\"", + "\"\"", + "7.1.2+foo", + "foo", + "''" + }) + void test_getJavaVersion_invalid(String version, @TempDir Path workdir) throws IOException { + var releaseFile = new RuntimeReleaseFile(createPropFileWithValue(workdir, "JAVA_VERSION", version)); + + var ex = assertThrows(RuntimeException.class, releaseFile::getJavaVersion); + + assertFalse(NoSuchElementException.class.isInstance(ex)); + } + + @Test + void test_without_version(@TempDir Path workdir) throws IOException { + var releaseFile = new RuntimeReleaseFile(createPropFileWithValue(workdir, "JDK_VERSION", "\"27.1.2\"")); + + assertThrowsExactly(NoSuchElementException.class, releaseFile::getJavaVersion); + } + + @Test + void test_getModules(@TempDir Path workdir) throws IOException { + var releaseFile = new RuntimeReleaseFile(createPropFileWithValue(workdir, "MODULES", "foo bar\t buz ")); + + assertEquals(List.of("foo", "bar", "buz"), releaseFile.getModules()); + } + + @Test + void test_current() throws IOException { + var releaseFile = new RuntimeReleaseFile(Path.of(System.getProperty("java.home")).resolve("release")); + + final var expectedVersion = Runtime.version(); + final var actualVersion = releaseFile.getJavaVersion(); + + assertEquals(expectedVersion.version(), actualVersion.version()); + + final var expectedModules = ModuleFinder.ofSystem().findAll().stream() + .map(ModuleReference::descriptor).map(ModuleDescriptor::name).sorted().toList(); + final var actualModules = releaseFile.getModules().stream().sorted().toList(); + + assertEquals(expectedModules, actualModules); + } + + private Path createPropFileWithValue(Path workdir, String name, String value) { + return createPropFile(workdir, Map.of(name, value)); + } + + private Path createPropFile(Path workdir, Map input) { + Path releaseFile = workdir.resolve("foo"); + Properties props = new Properties(); + props.putAll(input); + + try (Writer writer = Files.newBufferedWriter(releaseFile)) { + props.store(writer, null); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + + return releaseFile; + } +} diff --git a/test/jdk/tools/jpackage/share/AppVersionTest.java b/test/jdk/tools/jpackage/share/AppVersionTest.java index ad96a9b9e0ca..3b6ac5a92412 100644 --- a/test/jdk/tools/jpackage/share/AppVersionTest.java +++ b/test/jdk/tools/jpackage/share/AppVersionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,15 +22,50 @@ */ +import static java.util.stream.Collectors.toUnmodifiableMap; +import static java.util.stream.Collectors.toUnmodifiableSet; +import static jdk.jpackage.internal.util.PListWriter.writeDict; +import static jdk.jpackage.internal.util.PListWriter.writePList; +import static jdk.jpackage.internal.util.XmlUtils.createXml; +import static jdk.jpackage.internal.util.XmlUtils.toXmlConsumer; +import static jdk.jpackage.test.JPackageCommand.DEFAULT_VERSION; +import static jdk.jpackage.test.JPackageCommand.normalizeDerivedVersion; +import static jdk.jpackage.test.JPackageCommand.RuntimeImageType.RUNTIME_TYPE_FAKE; + import java.io.IOException; -import java.util.Collection; +import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; -import javax.xml.xpath.XPathExpressionException; -import jdk.jpackage.test.AppImageFile; -import jdk.jpackage.test.Annotations.Parameters; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.util.MacBundle; +import jdk.jpackage.internal.util.RuntimeReleaseFile; +import jdk.jpackage.internal.util.Slot; +import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.AppImageFile; +import jdk.jpackage.test.CannedFormattedString; +import jdk.jpackage.test.ConfigurationTarget; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JPackageCommand.StandardAssert; +import jdk.jpackage.test.JPackageOutputValidator; +import jdk.jpackage.test.JPackageStringBundle; +import jdk.jpackage.test.JavaAppDesc; +import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.RunnablePackageTest.Action; import jdk.jpackage.test.TKit; /* @@ -45,54 +80,954 @@ public final class AppVersionTest { - @Parameters - public static Collection input() { - List data = new ArrayList<>(); - - data.addAll(List.of(new Object[][]{ - // Default jpackage version - {"1.0", "Hello", null}, - {"1.0", "com.other/com.other.Hello", null}, - // Version should be picked from --app-version - {"3.1", "Hello", new String[]{"--app-version", "3.1"}}, - {"3.2", "com.other/com.other.Hello", new String[]{"--app-version", - "3.2"}}, - // Version should be picked from the last --app-version - {"3.3", "Hello", new String[]{"--app-version", "4", "--app-version", - "3.3"}}, - {"7.8", "com.other/com.other.Hello", new String[]{"--app-version", - "4", "--app-version", "7.8"}}, - // Pick version from jar - {"3.10.17", "com.other/com.other.Hello@3.10.17", null}, - // Ignore version in jar if --app-version given - {"7.5.81", "com.other/com.other.Hello@3.10.17", new String[]{ - "--app-version", "7.5.81"}} - })); - - return data; - } - - public AppVersionTest(String expectedVersion, String javaAppDesc, - String[] jpackageArgs) { - this.expectedVersion = expectedVersion; - this.javaAppDesc = javaAppDesc; - this.jpackageArgs = jpackageArgs; + @Test + @ParameterSupplier + public static void testApp(AppTestSpec testSpec) { + + var moduleVersionSource = testSpec.findVersionSource(ModuleVersionSource.class); + + ConfigurationTarget cfg; + if (testSpec.isImagePackageType()) { + var cmd = moduleVersionSource.map(ModuleVersionSource::appDesc) + .map(JPackageCommand::helloAppImage) + .orElseGet(JPackageCommand::helloAppImage); + cfg = new ConfigurationTarget(cmd); + } else { + var nativeTest = new PackageTest().forTypes(testSpec.spec().expected().keySet()); + moduleVersionSource + .map(ModuleVersionSource::appDesc) + .ifPresentOrElse(nativeTest::configureHelloApp, nativeTest::configureHelloApp); + + cfg = new ConfigurationTarget(nativeTest); + } + + cfg.addInitializer(JPackageCommand::setFakeRuntime); + + cfg.addInitializer(testSpec::applyTo); + + cfg.cmd().ifPresent(JPackageCommand::executeAndAssertHelloAppImageCreated); + + testSpec.validateVersion(cfg); + + cfg.test().ifPresent(pkg -> { + pkg.run(testSpec.spec().packageTestActions()); + }); } @Test - public void test() throws XPathExpressionException, IOException { - JPackageCommand cmd = JPackageCommand.helloAppImage(javaAppDesc); - if (jpackageArgs != null) { - cmd.addArguments(jpackageArgs); + @ParameterSupplier + @ParameterSupplier(value = "testMacPredefinedRuntimeBundle", ifOS = OperatingSystem.MACOS) + public static void testRuntime(RuntimeTestSpec testSpec) { + + var predefinedRuntimeDir = Slot.createEmpty(); + new PackageTest() + .forTypes(testSpec.spec().expected().keySet()) + .addRunOnceInitializer(() -> { + predefinedRuntimeDir.set(testSpec.createRuntime()); + }) + .addInitializer(cmd -> { + cmd.removeArgumentWithValue("--input").setArgumentValue("--runtime-image", predefinedRuntimeDir.get()); + }) + .addInitializer(testSpec::applyTo) + .mutate(test -> { + testSpec.validateVersion(new ConfigurationTarget(test)); + }) + .run(testSpec.spec().packageTestActions()); + } + + public static Collection testApp() { + + List testCases = new ArrayList<>(); + + for (var modular : List.of(true, false)) { + String appDesc; + if (modular) { + appDesc = "com.other/com.other.Hello"; + } else { + appDesc = "Hello"; + } + + // Default version. + AppTestSpec.create(appDesc, TestSpec.build().expectDefaultVersion(), testCases::add); + + // Pick version from the command line. + AppTestSpec.create(appDesc, TestSpec.build().versionFromCmdline("3.1"), testCases::add); + } + + // Pick version from the modular jar. + AppTestSpec.create(TestSpec.build() + .versionFromAppModule("com.other/com.other.Hello@3.10.16"), testCases::add); + + // Pick version from the command line, ignore version of the modular jar. + AppTestSpec.create(TestSpec.build() + .versionFromAppModule("com.other/com.other.Hello@3.10.18") + .versionFromCmdline("7.5.81"), testCases::add); + + // Pick version from the modular jar. Apply package-specific normalization. + for (var ver : List.of( + "30.10.17.204.899-foo" + )) { + var versionSource = new ModuleVersionSource("com.other/com.other.Hello@" + ver); + + var builder = TestSpec.build().versionSource(versionSource); + for (var e : TestSpec.Builder.getExpectedVersions(versionSource).entrySet()) { + builder.withTypes(e.getKey()).expect(e.getValue()); + } + + AppTestSpec.create(builder, testCases::add); + } + + return testCases.stream().map(v -> { + return new Object[] {v}; + }).toList(); + } + + public static Collection testRuntime() { + + List testCases = new ArrayList<>(); + + // Default version. + RuntimeTestSpec.create(TestSpec.build().expectDefaultVersion(), testCases::add); + + // Pick version from the command line. + RuntimeTestSpec.create(TestSpec.build().versionFromCmdline("3.1"), testCases::add); + + // Invalid versions. + for (var ver : List.of("foo", "", "17.21.3+foo")) { + RuntimeTestSpec.create(TestSpec.build().versionFromReleaseFile(ver).expectDefaultVersion(), testCases::add); + } + + // Valid version values (see java.lang.Runtime.Version javadoc and https://openjdk.org/jeps/223) + for (var suffix : jep223VersionSuffixes()) { + for (var vnum : List.of("17", "17.1", "17.1.2", "17.1.2.3", "17.1.2.3.5")) { + var ver = new RuntimeReleaseFileVersionSource(vnum + suffix); + + var builder = TestSpec.build().versionSource(ver); + for (var e : TestSpec.Builder.getExpectedVersions(ver).entrySet()) { + builder.withTypes(e.getKey()).expect(e.getValue()); + } + + RuntimeTestSpec.create(builder, testCases::add); + } + } + + return testCases.stream().map(v -> { + return new Object[] {v}; + }).toList(); + } + + public static Collection testMacPredefinedRuntimeBundle() { + + List testCases = new ArrayList<>(); + + var appendTestCases = skipImagePackageType(testSpec -> { + for (var runtimeType : List.of( + RuntimeType.MAC_BUNDLE_PLIST_FILE_MALFORMED, + RuntimeType.MAC_BUNDLE_PLIST_WITHOUT_VERSION + )) { + testCases.add(new RuntimeTestSpec(runtimeType, testSpec)); + } + }); + + // Invalid version. + TestSpec.build().versionFromReleaseFile("foo").expectDefaultVersion().create(appendTestCases); + + // Valid versions. + for (var suffix : List.of("", "-foo")) { + for (var vnum : List.of("17", "17.1", "17.1.2", "17.1.2.3")) { + var ver = new RuntimeReleaseFileVersionSource(vnum + suffix); + + var builder = TestSpec.build().versionSource(ver); + var allBundleTypes = TestSpec.Builder.getExpectedVersions(ver); + for (var bundleType : PackageType.MAC) { + builder.withTypes(bundleType).expect(Objects.requireNonNull(allBundleTypes.get(bundleType))); + } + + builder.create(appendTestCases); + } + } + + return testCases.stream().map(v -> { + return new Object[] {v}; + }).toList(); + } + + private static List jep223VersionSuffixes() { + var suffixes = new HashSet(); + for (var pre : List.of("", "-ea")) { + for (var build : List.of("", "+5678")) { + for (var opt : List.of("", "-foo", "-12.UZ3")) { + if (pre.isEmpty() && build.isEmpty() && !opt.isEmpty()) { + suffixes.add("+" + opt); + } else { + suffixes.add(pre + build + opt); + } + } + } + } + return suffixes.stream().sorted().peek(suffix -> { + // Validate version suffixes. + Runtime.Version.parse("11" + suffix); + }).toList(); + } + + enum Message { + VERSION_FROM_MODULE("message.module-version", "version", "module"), + VERSION_FROM_RELEASE_FILE("message.release-version", "version"), + VERSION_NORMALIZED("message.version-normalized", "version", "version"), + ; + + Message(String key, Object ... args) { + this.key = Objects.requireNonNull(key); + this.args = args; + } + + CannedFormattedString cannedFormattedString(Object ... args) { + return JPackageStringBundle.MAIN.cannedFormattedString(key, args); } - cmd.executeAndAssertHelloAppImageCreated(); - String actualVersion = AppImageFile.load(cmd.outputBundle()).version(); - TKit.assertEquals(expectedVersion, actualVersion, - "Check application version"); + TKit.TextStreamVerifier negateFind() { + var pattern = JPackageStringBundle.MAIN.cannedFormattedStringAsPattern(key, _ -> { + return Pattern.compile(".*"); + }, args); + return TKit.assertTextStream(pattern).negate(); + } + + String key() { + return key; + } + + private final String key; + private final Object[] args; + } + + sealed interface VersionSource { + String version(); + + VersionSource copyWithVersion(String v); + } + + enum DefaultVersionSource implements VersionSource { + INSTANCE; + + @Override + public String version() { + return DEFAULT_VERSION; + } + + @Override + public VersionSource copyWithVersion(String v) { + return this; + } + } + + record ModuleVersionSource(String appDesc) implements VersionSource { + + ModuleVersionSource { + Objects.requireNonNull(appDesc); + if (JavaAppDesc.parse(appDesc).moduleVersion() == null) { + throw new IllegalArgumentException(); + } + } + + @Override + public String version() { + return JavaAppDesc.parse(appDesc).moduleVersion(); + } + + @Override + public VersionSource copyWithVersion(String v) { + return new ModuleVersionSource(moduleName() + "@" + Objects.requireNonNull(v)); + } + + String moduleName() { + return JavaAppDesc.parse(appDesc).moduleName(); + } + + @Override + public String toString() { + return appDesc; + } + } + + record CmdlineVersionSource(String version) implements VersionSource { + + CmdlineVersionSource { + Objects.requireNonNull(version); + } + + @Override + public VersionSource copyWithVersion(String v) { + return new CmdlineVersionSource(v); + } + + @Override + public String toString() { + return String.format("--app-version=[%s]", version); + } + } + + record RuntimeReleaseFileVersionSource(String version) implements VersionSource { + + RuntimeReleaseFileVersionSource { + Objects.requireNonNull(version); + } + + @Override + public VersionSource copyWithVersion(String v) { + return new RuntimeReleaseFileVersionSource(v); + } + + @Override + public String toString() { + return String.format("JAVA_VERSION=[%s]", version); + } + } + + record Expected(String version, List messages) { + + Expected { + Objects.requireNonNull(version); + Objects.requireNonNull(messages); + } + + void applyTo(JPackageCommand cmd) { + Objects.requireNonNull(cmd); + new JPackageOutputValidator().expectMatchingStrings(messages).matchTimestamps().stripTimestamps().applyTo(cmd); + cmd.version(version); + + var expectMessageKeys = messages.stream().map(CannedFormattedString::key).toList(); + Stream.of(Message.values()).filter(message -> { + return !expectMessageKeys.contains(message.key()); + }).map(Message::negateFind).forEach(validator -> { + new JPackageOutputValidator().add(validator).applyTo(cmd); + }); + } + + @Override + public String toString() { + var sb = new StringBuilder(); + sb.append(version); + if (!messages.isEmpty()) { + sb.append("; ").append(messages); + } + return sb.toString(); + } + + static Builder build() { + return new Builder(); + } + + static final class Builder { + + Expected create() { + return new Expected(version, List.copyOf(messages)); + } + + Builder version(String v) { + version = v; + return this; + } + + Builder messages(List v) { + messages.addAll(v); + return this; + } + + Builder messages(CannedFormattedString... v) { + return messages(List.of(v)); + } + + Builder message(Message message, Object ... args) { + return messages(message.cannedFormattedString(args)); + } + + private String version; + private final List messages = new ArrayList<>(); + } } - private final String expectedVersion; - private final String javaAppDesc; - private final String[] jpackageArgs; + record TestSpec(Collection versions, Map expected) { + + TestSpec { + Objects.requireNonNull(versions); + Objects.requireNonNull(expected); + + if (expected.isEmpty()) { + throw new IllegalArgumentException(); + } + + if (expected.keySet().contains(PackageType.IMAGE) && !Collections.disjoint(expected.keySet(), PackageType.NATIVE)) { + throw new IllegalArgumentException("Mixing of native and app image packaging"); + } + + if (expected.keySet().stream().map(PackageType::os).distinct().count() != 1) { + throw new IllegalArgumentException("All package types must be for the same OS"); + } + } + + Optional findVersionSource(Class versionSourceType) { + Objects.requireNonNull(versionSourceType); + return versions.stream().filter(versionSourceType::isInstance).map(versionSourceType::cast).findFirst(); + } + + void applyTo(JPackageCommand cmd) { + Objects.requireNonNull(cmd); + findVersionSource(CmdlineVersionSource.class).ifPresent(ver -> { + cmd.setArgumentValue("--app-version", ver.version()); + }); + expected.get(cmd.packageType()).applyTo(cmd); + } + + void validateVersion(ConfigurationTarget cfg) { + cfg.cmd().ifPresent(cmd -> { + var actualVersion = AppImageFile.load(cmd.outputBundle()).version(); + TKit.assertEquals(expected.get(cmd.packageType()).version(), actualVersion, "Check application version"); + }); + cfg.test().ifPresent(test -> { + expected.entrySet().forEach(e -> { + nativeBundleVersionPropertyName(e.getKey()).ifPresent(propertyName -> { + test.forTypes(e.getKey(), _ -> { + test.addBundlePropertyVerifier(propertyName, e.getValue().version()); + }); + }); + }); + }); + + if (os() == OperatingSystem.MACOS) { + cfg.addInstallVerifier(cmd -> { + final var bundleRoot = cmd.isImagePackageType() ? cmd.outputBundle() + : cmd.pathToUnpackedPackageFile(cmd.appInstallationDirectory()); + var plist = MacHelper.readPListFromAppImage(bundleRoot); + var expectedVersion = expected.get(cmd.packageType()).version(); + for (var prop : List.of("CFBundleVersion", "CFBundleShortVersionString")) { + TKit.assertEquals(expectedVersion, plist.queryValue(prop), + String.format("Check the value of '%s' property in [%s] bundle", prop, bundleRoot)); + } + }); + } + } + + boolean isImagePackageType() { + return expected.keySet().contains(PackageType.IMAGE); + } + + Action[] packageTestActions() { + if (os() == OperatingSystem.MACOS) { + return Action.CREATE_AND_UNPACK; + } else { + return new Action[] {Action.CREATE}; + } + } + + OperatingSystem os() { + return expected.keySet().iterator().next().os(); + } + + @Override + public String toString() { + var sb = new StringBuilder(); + + switch (versions.size()) { + case 0 -> { + } + case 1 -> { + sb.append(versions.iterator().next()).append("; "); + } + default -> { + sb.append("versions=").append(versions).append("; "); + } + } + + sb.append("expect="); + if (expected.values().stream().distinct().count() == 1) { + sb.append(expected.keySet().stream().sorted().toList()).append(":"); + sb.append(expected.values().iterator().next()); + } else { + sb.append('[').append(expected).append(']'); + } + + return sb.toString(); + } + + private static Optional nativeBundleVersionPropertyName(PackageType type) { + switch (type) { + case LINUX_DEB, LINUX_RPM -> { + return Optional.of("Version"); + } + case WIN_MSI -> { + return Optional.of("ProductVersion"); + } + default -> { + return Optional.empty(); + } + } + } + + static Builder build() { + return new Builder(); + } + + static final class Builder { + + Builder() { + } + + Builder(Builder other) { + currentTypes = other.currentTypes; + currentExpectedVersion = other.currentExpectedVersion; + pendingCommit = other.pendingCommit; + versions.addAll(other.versions); + expected.putAll(other.expected); + } + + Builder copy() { + return new Builder(this); + } + + void create(Consumer sink) { + Objects.requireNonNull(sink); + if (pendingCommit) { + copy().expect(expectedVersion()).commit().create(sink); + } else { + var types = expected.keySet(); + var copiedVersions = List.copyOf(versions); + if (types.contains(PackageType.IMAGE) && !Collections.disjoint(types, PackageType.NATIVE)) { + sink.accept(new TestSpec(copiedVersions, Map.of(PackageType.IMAGE, expected.get(PackageType.IMAGE)))); + var copy = new HashMap<>(expected); + copy.remove(PackageType.IMAGE); + sink.accept(new TestSpec(copiedVersions, copy)); + } else { + new TestSpec(copiedVersions, Map.copyOf(expected)); + } + } + } + + Builder versionSource(VersionSource v) { + if (Objects.requireNonNull(v) == DefaultVersionSource.INSTANCE) { + throw new IllegalArgumentException(); + } + versions.add(v); + pendingCommit = true; + return this; + } + + Builder versionFromCmdline(String v) { + return versionSource(new CmdlineVersionSource(v)); + } + + Builder versionFromAppModule(String v) { + return versionSource(new ModuleVersionSource(v)); + } + + Builder versionFromReleaseFile(String v) { + return versionSource(new RuntimeReleaseFileVersionSource(v)); + } + + Builder expect(VersionSource v) { + currentExpectedVersion = v; + pendingCommit = true; + return this; + } + + Builder expectDefaultVersion() { + return expect(DefaultVersionSource.INSTANCE); + } + + Builder expectVersionFromCmdline(String v) { + return expect(new CmdlineVersionSource(v)); + } + + Builder expectVersionFromAppModule(String appDesc) { + return expect(new ModuleVersionSource(appDesc)); + } + + Builder expectVersionFromReleaseFile(String v) { + return expect(new RuntimeReleaseFileVersionSource(v)); + } + + Builder withTypes(Set types) { + if (types.isEmpty()) { + types = ALL_TYPES; + } + + if (!currentTypes.equals(types)) { + commit(); + currentTypes = types; + } + + return this; + } + + Builder withTypes(PackageType ... types) { + return withTypes(Set.of(types)); + } + + static Map getExpectedVersions(VersionSource versionSource) { + var map = new HashMap(normalizeDerivedVersion(versionSource.version()).entrySet() + .stream() + .collect(toUnmodifiableMap(Map.Entry::getKey, e -> { + return versionSource.copyWithVersion(e.getValue()); + }) + )); + + ALL_TYPES.forEach(type -> { + map.putIfAbsent(type, DefaultVersionSource.INSTANCE); + }); + + return map; + } + + private VersionSource expectedVersion() { + return Optional.ofNullable(currentExpectedVersion).or(() -> { + return versions.stream().filter(CmdlineVersionSource.class::isInstance).findFirst(); + }).or(() -> { + if (versions.size() == 1) { + return Optional.of(versions.getFirst()); + } else { + return Optional.empty(); + } + }).orElseThrow(IllegalStateException::new); + } + + private Builder commit() { + pendingCommit = false; + + if (versions.isEmpty() && currentExpectedVersion == null) { + // Nothing to commit. + return this; + } + + var filteredTypes = normalize(currentTypes); + if (filteredTypes.isEmpty()) { + // Version configuration is not supported on the current OS. + return this; + } + + VersionSource expectedVersion = expectedVersion(); + + VersionSource versionSource; + if (expectedVersion == DefaultVersionSource.INSTANCE) { + versionSource = expectedVersion; + } else { + versionSource = versions.stream().filter(expectedVersion.getClass()::isInstance).findFirst().orElseThrow(); + } + + var expectedBuilder = Expected.build().version(expectedVersion.version()); + switch (versionSource) { + case ModuleVersionSource ver -> { + expectedBuilder.message(Message.VERSION_FROM_MODULE, ver.version(), ver.moduleName()); + } + case RuntimeReleaseFileVersionSource ver -> { + expectedBuilder.message(Message.VERSION_FROM_RELEASE_FILE, ver.version()); + } + default -> { + // NOP + } + } + + if (!versionSource.version().equals(expectedVersion.version())) { + expectedBuilder.message(Message.VERSION_NORMALIZED, expectedVersion.version(), versionSource.version()); + } + + var expectedValue = expectedBuilder.create(); + filteredTypes.forEach(type -> { + expected.put(type, expectedValue); + }); + + return this; + } + + private static Set normalize(Collection types) { + return types.stream().filter(type -> { + return type.os() == OperatingSystem.current(); + }) + // Filter out "exe" packaging as it is a duplicate of "msi" packaging and + // the testing lib can't validate properties of embedded msi file. + .filter(Predicate.isEqual(PackageType.WIN_EXE).negate()) + .map(type -> { + if (type.isAppImage()) { + return PackageType.IMAGE; + } else { + return type; + } + }).collect(toUnmodifiableSet()); + } + + private Set currentTypes = ALL_TYPES; + private VersionSource currentExpectedVersion; + private boolean pendingCommit; + private final List versions = new ArrayList<>(); + private final Map expected = new HashMap<>(); + + private final static Set ALL_TYPES = Set.of(PackageType.values()); + } + } + + record AppTestSpec(String appDesc, TestSpec spec) { + + AppTestSpec { + Objects.requireNonNull(appDesc); + Objects.requireNonNull(spec); + } + + AppTestSpec(TestSpec spec) { + this(spec.findVersionSource(ModuleVersionSource.class).orElseThrow().appDesc(), spec); + } + + static void create(String appDesc, TestSpec.Builder specBuilder, Consumer sink) { + Objects.requireNonNull(appDesc); + Objects.requireNonNull(sink); + specBuilder.create(spec -> { + sink.accept(new AppTestSpec(appDesc, spec)); + }); + } + + static void create(TestSpec.Builder specBuilder, Consumer sink) { + Objects.requireNonNull(sink); + specBuilder.create(spec -> { + sink.accept(new AppTestSpec(spec)); + }); + } + + Optional findVersionSource(Class versionSourceType) { + return spec.findVersionSource(versionSourceType); + } + + void applyTo(JPackageCommand cmd) { + spec.applyTo(cmd); + } + + void validateVersion(ConfigurationTarget cfg) { + spec.validateVersion(cfg); + } + + boolean isImagePackageType() { + return spec.isImagePackageType(); + } + + @Override + public String toString() { + var sb = new StringBuilder(); + if (spec.findVersionSource(ModuleVersionSource.class).map(ModuleVersionSource::appDesc).filter(appDesc::equals).isEmpty()) { + sb.append("app-desc=").append(appDesc()).append("; "); + } + sb.append(spec); + return sb.toString(); + } + } + + /** + * Type of the predefined runtime. + */ + enum RuntimeType { + /** + * A directory with the standard Java runtime structure. + */ + IMAGE, + /** + * macOS bundle with valid Info.plist file. + */ + MAC_BUNDLE, + /** + * macOS bundle with malformed Info.plist file. + */ + MAC_BUNDLE_PLIST_FILE_MALFORMED, + /** + * macOS bundle with Info.plist file without version. + */ + MAC_BUNDLE_PLIST_WITHOUT_VERSION, + ; + } + + record RuntimeTestSpec(RuntimeType type, TestSpec spec) { + + RuntimeTestSpec { + Objects.requireNonNull(type); + Objects.requireNonNull(spec); + if (spec.isImagePackageType()) { + throw new IllegalArgumentException(); + } + if (type == RuntimeType.MAC_BUNDLE && spec.os() != OperatingSystem.MACOS) { + throw new IllegalArgumentException("Bundle runtime is supported for macOS native packaging only"); + } + } + + static void create(TestSpec.Builder specBuilder, Consumer sink) { + Objects.requireNonNull(sink); + specBuilder.create(skipImagePackageType(spec -> { + if (spec.os() != OperatingSystem.MACOS) { + sink.accept(new RuntimeTestSpec(RuntimeType.IMAGE, spec)); + } else { + sink.accept(new RuntimeTestSpec(RuntimeType.IMAGE, spec)); + + if (spec.findVersionSource(CmdlineVersionSource.class).isPresent()) { + // Disable "AppVersionTest.testRuntime(mac_bundle; versions=[--app-version=[3.1], plist=[1.22.333]]; expect=[MAC_DMG, MAC_PKG]:3.1)" test for now + // as it will fail because jpackage halfway ignores the "--app-version" + // and any option in general that makes its way into the Info.plist file. + // When building a runtime from the predefined runtime bundle, + // jpackage copies the Info.plist file from the predefined runtime bundle into the output bundle verbatim. + return; + } + + var plistVersionSource = new InheritPListVersionSource(MAC_PREDEFINED_RUNTIME_BUNDLE_VERSION); + var specBuilderCopy = specBuilder.copy().versionSource(plistVersionSource).withTypes(PackageType.ALL_MAC); + if (spec.findVersionSource(CmdlineVersionSource.class).isEmpty()) { + specBuilderCopy.expect(plistVersionSource); + } + specBuilderCopy.create(skipImagePackageType(augmentedSpec -> { + sink.accept(new RuntimeTestSpec(RuntimeType.MAC_BUNDLE, augmentedSpec)); + })); + } + })); + } + + Optional findVersionSource(Class versionSourceType) { + return spec.findVersionSource(versionSourceType); + } + + void applyTo(JPackageCommand cmd) { + switch (type) { + case MAC_BUNDLE_PLIST_FILE_MALFORMED, MAC_BUNDLE_PLIST_WITHOUT_VERSION -> { + // Don't validate signature of the output bundle. If the Info.plist file is malformed, it will fail with the error: + // + // [22:59:25.374] TRACE: Command [/usr/bin/codesign --verify --deep --strict --verbose=2 RuntimeAppVersionTest.jdk](6) exited with exit code 3 and the following output: + // [22:59:25.375] TRACE: 2026-03-05 22:59:25.353 codesign[11351:61024] There was an error parsing the Info.plist for the bundle at URL <0x7f81e140ae70>: NSCocoaErrorDomain - 3840 + // [22:59:25.376] TRACE: 2026-03-05 22:59:25.370 codesign[11351:61024] There was an error parsing the Info.plist for the bundle at URL <0x7f81e1413b90>: NSCocoaErrorDomain - 3840 + // [22:59:25.376] TRACE: --prepared:RuntimeAppVersionTest.jdk/Contents/MacOS/libjli.dylib + // [22:59:25.377] TRACE: --validated:RuntimeAppVersionTest.jdk/Contents/MacOS/libjli.dylib + // + cmd.excludeStandardAsserts(StandardAssert.MAC_BUNDLE_UNSIGNED_SIGNATURE); + // This one will also fail. + cmd.excludeStandardAsserts(StandardAssert.MAC_RUNTIME_PLIST_JDK_KEY); + } + default -> { + // NOP + } + } + spec.applyTo(cmd); + } + + void validateVersion(ConfigurationTarget cfg) { + switch (type) { + case MAC_BUNDLE_PLIST_FILE_MALFORMED, MAC_BUNDLE_PLIST_WITHOUT_VERSION -> { + cfg.addInstallVerifier(cmd -> { + final var bundleRoot = cmd.isImagePackageType() ? cmd.outputBundle() + : cmd.pathToUnpackedPackageFile(cmd.appInstallationDirectory()); + final var infoPlist = new MacBundle(bundleRoot).infoPlistFile(); + TKit.assertFileExists(infoPlist); + TKit.trace(String.format("Bundle version property in [%s] file is unavailable. Skip validation", infoPlist)); + }); + } + default -> { + spec.validateVersion(cfg); + } + } + } + + Path createRuntime() throws IOException { + return createRuntime(type, findVersionSource(RuntimeReleaseFileVersionSource.class).map(VersionSource::version)); + } + + @Override + public String toString() { + return new StringBuilder().append(type.name().toLowerCase()).append("; ").append(spec).toString(); + } + + private record InheritPListVersionSource(String version) implements VersionSource { + + InheritPListVersionSource { + Objects.requireNonNull(version); + } + + @Override + public VersionSource copyWithVersion(String v) { + return new InheritPListVersionSource(v); + } + + @Override + public String toString() { + return String.format("plist=[%s]", version); + } + } + + private Path createRuntime(RuntimeType type, Optional releaseFileVersion) throws IOException { + Objects.requireNonNull(type); + Objects.requireNonNull(releaseFileVersion); + + Path predefinedRuntimeDir; + Path runtimeDir = switch (type) { + case IMAGE -> { + predefinedRuntimeDir = JPackageCommand.createInputRuntimeImage(RUNTIME_TYPE_FAKE); + yield predefinedRuntimeDir; + } + case MAC_BUNDLE -> { + releaseFileVersion.ifPresent(ver -> { + if (ver.equals(MAC_PREDEFINED_RUNTIME_BUNDLE_VERSION)) { + // The value of `JAVA_VERSION` property in the "release" file of the runtime + // and the value of the `CFBundleVersion` property in the plist file should be different + // to test corner cases of version picking logic. + throw new IllegalArgumentException(); + } + }); + + // Create macOS bundle with `MAC_PREDEFINED_RUNTIME_BUNDLE_VERSION` version. + // The idea is to have different values of `JAVA_VERSION` property in the "release" file + // of the runtime and the value of the `CFBundleVersion` property in the plist file. + predefinedRuntimeDir = MacHelper.buildRuntimeBundle().type(RUNTIME_TYPE_FAKE).mutator(cmd -> { + cmd.setArgumentValue("--app-version", MAC_PREDEFINED_RUNTIME_BUNDLE_VERSION); + }).create(); + yield MacBundle.fromPath(predefinedRuntimeDir).orElseThrow().homeDir(); + } + case MAC_BUNDLE_PLIST_FILE_MALFORMED, MAC_BUNDLE_PLIST_WITHOUT_VERSION -> { + predefinedRuntimeDir = MacHelper.buildRuntimeBundle().type(RUNTIME_TYPE_FAKE).create(); + var plistFile = new MacBundle(predefinedRuntimeDir).infoPlistFile(); + + switch (type) { + case MAC_BUNDLE_PLIST_FILE_MALFORMED -> { + TKit.trace(String.format("Create invalid plist file [%s]", plistFile)); + } + case MAC_BUNDLE_PLIST_WITHOUT_VERSION -> { + TKit.trace(String.format("Create empty plist file [%s]", plistFile)); + } + default -> { + throw new AssertionError(); + } + } + + createXml(plistFile, xml -> { + writePList(xml, toXmlConsumer(() -> { + if (type == RuntimeType.MAC_BUNDLE_PLIST_WITHOUT_VERSION) { + writeDict(xml, toXmlConsumer(() -> { + })); + } + })); + }); + yield MacBundle.fromPath(predefinedRuntimeDir).orElseThrow().homeDir(); + } + default -> { + throw new AssertionError(); + } + }; + + releaseFileVersion.ifPresent(ver -> { + TKit.createPropertiesFile( + RuntimeReleaseFile.releaseFilePathInRuntime(runtimeDir), + Map.of("JAVA_VERSION", "\"" + ver + "\"")); + }); + + return predefinedRuntimeDir; + } + + final static String MAC_PREDEFINED_RUNTIME_BUNDLE_VERSION = "1.22.333"; + } + + private static Consumer skipImagePackageType(Consumer consumer) { + Objects.requireNonNull(consumer); + return spec -> { + if (!spec.isImagePackageType()) { + consumer.accept(spec); + } + }; + } } diff --git a/test/jdk/tools/jpackage/share/RuntimePackageTest.java b/test/jdk/tools/jpackage/share/RuntimePackageTest.java index a18efce62c6d..87fd3496ba59 100644 --- a/test/jdk/tools/jpackage/share/RuntimePackageTest.java +++ b/test/jdk/tools/jpackage/share/RuntimePackageTest.java @@ -23,6 +23,7 @@ import static jdk.internal.util.OperatingSystem.LINUX; import static jdk.internal.util.OperatingSystem.MACOS; +import static jdk.jpackage.test.JPackageCommand.DEFAULT_VERSION; import static jdk.jpackage.test.TKit.assertFalse; import static jdk.jpackage.test.TKit.assertTrue; @@ -81,7 +82,16 @@ public class RuntimePackageTest { @Test public static void test() { - init().run(); + init() + .addInitializer(cmd -> { + // JDK-8357404 enables jpackage to pick a version from the "release" file + // of the predefined runtime when bundling the runtime package. + // This makes the output of this test dependent on the version of the running JDK + // and will be an inconvenience for SQE testing. + // Explicitly specify the package version to fulfill expectations of SQE. + cmd.setArgumentValue("--app-version", DEFAULT_VERSION); + }) + .run(); } @Test(ifOS = MACOS) From 30e569d206dbb87349927682f194f5ba1294e44d Mon Sep 17 00:00:00 2001 From: William Kemper Date: Wed, 11 Mar 2026 16:47:34 +0000 Subject: [PATCH 10/97] 8379021: Shenandoah: Speedup ShenandoahSimpleBitMapTest Reviewed-by: shade --- .../gtest/gc/shenandoah/test_shenandoahMarkBitMap.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahMarkBitMap.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahMarkBitMap.cpp index 3dbb7c621226..18cf3b3333f9 100644 --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahMarkBitMap.cpp +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahMarkBitMap.cpp @@ -165,8 +165,8 @@ class ShenandoahMarkBitMapTest: public ::testing::Test { static bool run_test() { ShenandoahHeap* heap = ShenandoahHeap::heap(); - size_t heap_size = heap->max_capacity(); - size_t heap_size_words = heap_size / HeapWordSize; + size_t test_heap_size = MIN2(32 * M, heap->max_capacity()); + size_t heap_size_words = test_heap_size / HeapWordSize; HeapWord* my_heap_memory = heap->base(); HeapWord* end_of_my_heap = my_heap_memory + heap_size_words; MemRegion heap_descriptor(my_heap_memory, heap_size_words); @@ -175,7 +175,7 @@ class ShenandoahMarkBitMapTest: public ::testing::Test { _assertion_failures = 0; size_t bitmap_page_size = UseLargePages ? os::large_page_size() : os::vm_page_size(); - size_t bitmap_size_orig = ShenandoahMarkBitMap::compute_size(heap_size); + size_t bitmap_size_orig = ShenandoahMarkBitMap::compute_size(test_heap_size); size_t bitmap_size = align_up(bitmap_size_orig, bitmap_page_size); size_t bitmap_word_size = (bitmap_size + HeapWordSize - 1) / HeapWordSize; From 814b7f5d2a76288b6a8d6510a10c0241fdb727d0 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Wed, 11 Mar 2026 17:54:32 +0000 Subject: [PATCH 11/97] 8378920: Remove AppContext from SequencedEvent Reviewed-by: psadhukhan, tr --- .../classes/java/awt/SequencedEvent.java | 43 +---- .../MultipleContextsFunctionalTest.java | 175 ------------------ .../MultipleContextsUnitTest.java | 167 ----------------- 3 files changed, 6 insertions(+), 379 deletions(-) delete mode 100644 test/jdk/java/awt/event/SequencedEvent/MultipleContextsFunctionalTest.java delete mode 100644 test/jdk/java/awt/event/SequencedEvent/MultipleContextsUnitTest.java diff --git a/src/java.desktop/share/classes/java/awt/SequencedEvent.java b/src/java.desktop/share/classes/java/awt/SequencedEvent.java index 13ec53178227..25fe72e1787d 100644 --- a/src/java.desktop/share/classes/java/awt/SequencedEvent.java +++ b/src/java.desktop/share/classes/java/awt/SequencedEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,12 +29,11 @@ import java.util.LinkedList; import sun.awt.AWTAccessor; -import sun.awt.AppContext; import sun.awt.SunToolkit; /** * A mechanism for ensuring that a series of AWTEvents are executed in a - * precise order, even across multiple AppContexts. The nested events will be + * precise order. The nested events will be * dispatched in the order in which their wrapping SequencedEvents were * constructed. The only exception to this rule is if the peer of the target of * the nested event was destroyed (with a call to Component.removeNotify) @@ -57,7 +56,6 @@ class SequencedEvent extends AWTEvent implements ActiveEvent { private final AWTEvent nested; @SuppressWarnings("serial") // Not statically typed as Serializable - private AppContext appContext; private boolean disposed; private final LinkedList pendingEvents = new LinkedList<>(); @@ -145,7 +143,6 @@ public void run() { * dispatched or disposed. If this method is invoked before all previous nested events * have been dispatched, then this method blocks until such a point is * reached. - * While waiting disposes nested events to disposed AppContext * * NOTE: Locking protocol. Since dispose() can get EventQueue lock, * dispatch() shall never call dispose() while holding the lock on the list, @@ -154,8 +151,6 @@ public void run() { */ public final void dispatch() { try { - appContext = AppContext.getAppContext(); - if (getFirst() != this) { if (EventQueue.isDispatchThread()) { if (Thread.currentThread() instanceof EventDispatchThread) { @@ -201,19 +196,6 @@ public final void dispatch() { } } - /** - * true only if event exists and nested source appContext is disposed. - */ - private static final boolean isOwnerAppContextDisposed(SequencedEvent se) { - if (se != null) { - Object target = se.nested.getSource(); - if (target instanceof Component) { - return ((Component)target).appContext.isDisposed(); - } - } - return false; - } - /** * Sequenced events are dispatched in order, so we cannot dispatch * until we are the first sequenced event in the queue (i.e. it's our @@ -224,26 +206,13 @@ public final boolean isFirstOrDisposed() { if (disposed) { return true; } - // getFirstWithContext can dispose this - return this == getFirstWithContext() || disposed; + return this == getFirst(); } private static final synchronized SequencedEvent getFirst() { return list.getFirst(); } - /* Disposes all events from disposed AppContext - * return first valid event - */ - private static final SequencedEvent getFirstWithContext() { - SequencedEvent first = getFirst(); - while(isOwnerAppContextDisposed(first)) { - first.dispose(); - first = getFirst(); - } - return first; - } - /** * Disposes of this instance. This method is invoked once the nested event * has been dispatched and handled, or when the peer of the target of the @@ -283,12 +252,12 @@ final void dispose() { } } // Wake up waiting threads - if (next != null && next.appContext != null) { - SunToolkit.postEvent(next.appContext, new SentEvent()); + if (next != null) { + SunToolkit.postEvent(new SentEvent()); } for(AWTEvent e : pendingEvents) { - SunToolkit.postEvent(appContext, e); + SunToolkit.postEvent(e); } } } diff --git a/test/jdk/java/awt/event/SequencedEvent/MultipleContextsFunctionalTest.java b/test/jdk/java/awt/event/SequencedEvent/MultipleContextsFunctionalTest.java deleted file mode 100644 index 9af56ba044b1..000000000000 --- a/test/jdk/java/awt/event/SequencedEvent/MultipleContextsFunctionalTest.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * @test - * @bug 8204142 - * @key headful - * @summary Deadlock when queueing SequencedEvent of different AppContexts - * @author Laurent Bourges - * @modules java.desktop/sun.awt - * @run main/othervm/timeout=30 MultipleContextsFunctionalTest - */ - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicReference; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.SwingUtilities; -import javax.swing.Timer; - -public final class MultipleContextsFunctionalTest { - - private static final long serialVersionUID = 1L; - - private static final int NUM_WINDOW = 2; - private static final int INTERVAL = 50; - private static final int MAX_TIME = 10000; // 10s - private static final int TOLERANCE = 10000;// 10s - private static final int CHECK_LAPSE = 100; - private static final int MAX_COUNT = MAX_TIME / INTERVAL; - private static final int EXPECTED = MAX_COUNT * NUM_WINDOW; - private static final List WINDOWS = new CopyOnWriteArrayList<>(); - - public static void main(String[] args) { - for (int i = 0; i < NUM_WINDOW; i++) { - createWin(i); - } - - int total = 0; - int waitingTime = MAX_TIME + TOLERANCE; - while (waitingTime > 0 && total != EXPECTED) { - try { - Thread.sleep(CHECK_LAPSE); - } catch (InterruptedException e) { - e.printStackTrace(); - } - waitingTime -= CHECK_LAPSE; - - total = 0; - for (TestWindow window : WINDOWS) { - total += window.getCounter(); - } - } - - // Failure if AWT hanging: assert - System.out.println("Total [" + total + "] - Expected [" + EXPECTED + "]"); - if (total == EXPECTED) { - System.out.println("Test PASSED"); - return; - } - System.out.println("Test FAILED"); - Runtime.getRuntime().halt(-1); - } - - private static void createWin(int tgNum) { - new Thread(new ThreadGroup("TG " + tgNum), - new Runnable() { - @Override - public void run() { - sun.awt.SunToolkit.createNewAppContext(); - - final AtomicReference ref = - new AtomicReference(); - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - final TestWindow window = new TestWindow(tgNum); - window.setVisible(true); - ref.set(window); - WINDOWS.add(window); - } - }); - - // Wait for window to show - TestWindow window = ref.get(); - while (window == null) { - try { - Thread.sleep(100); - } catch (InterruptedException ie) { - ie.printStackTrace(); - } - window = ref.get(); - } - window.enableTimer(true); - } - }).start(); - } - - private static final class TestWindow extends JFrame implements ActionListener { - - private final JButton btn; - private volatile int counter = 0; - private final Timer t; - - TestWindow(final int num) { - super("Test Window [" + num + "]"); - setMinimumSize(new Dimension(300, 200)); - setLocation(100 + 400 * (num - 1), 100); - - setLayout(new BorderLayout()); - JLabel textBlock = new JLabel("Lorem ipsum dolor sit amet..."); - add(textBlock); - - btn = new JButton("TEST"); - add(btn, BorderLayout.SOUTH); - - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - pack(); - - t = new Timer(INTERVAL, this); - t.setRepeats(false); - } - - @Override - public void actionPerformed(ActionEvent e) { - this.toFront(); - btn.setText("TEST " + (++counter)); - this.toBack(); - if (counter < MAX_COUNT) { - enableTimer(true); - } else { - dispose(); - } - } - - void enableTimer(boolean enable) { - if (enable) { - t.start(); - } else { - t.stop(); - } - } - - int getCounter() { - return counter; - } - } -} diff --git a/test/jdk/java/awt/event/SequencedEvent/MultipleContextsUnitTest.java b/test/jdk/java/awt/event/SequencedEvent/MultipleContextsUnitTest.java deleted file mode 100644 index 8c7062f4abf0..000000000000 --- a/test/jdk/java/awt/event/SequencedEvent/MultipleContextsUnitTest.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * 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.awt.AWTEvent; -import java.awt.event.InvocationEvent; -import java.lang.reflect.Constructor; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; - -import sun.awt.AppContext; -import sun.awt.SunToolkit; - -/** - * @test - * @bug 8204142 - * @author Sergey Bylokhov - * @key headful - * @modules java.desktop/java.awt:open java.desktop/sun.awt - * @run main/othervm/timeout=30 MultipleContextsUnitTest - */ -public final class MultipleContextsUnitTest { - - private static final int COUNT = 20; - private static final AppContext[] apps = new AppContext[COUNT]; - private static final CountDownLatch go = new CountDownLatch(1); - private static final CountDownLatch end = new CountDownLatch(COUNT); - - private static volatile int createSENumber = 0; - private static volatile int dispatchSENumber = 0; - - public static void main(final String[] args) throws Exception { - for (int i = 0; i < COUNT; i++) { - Thread t = testThread(i); - t.start(); - t.join(); - } - - for (AppContext app : apps) { - SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { - try { - go.await(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - })); - } - - // eventOne - created first, but posted last - AWTEvent eventOne = getSequencedEvent(); - { - // eventTwo and eventThree - posted in the reverse order - AppContext app = apps[1]; - AWTEvent eventTwo = getSequencedEvent(); - AWTEvent eventThree = getSequencedEvent(); - SunToolkit.postEvent(app, eventThree); - SunToolkit.postEvent(app, eventTwo); - SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { - System.err.println(AppContext.getAppContext()); - end.countDown(); - })); - } - - for (int i = 2; i < apps.length; i++) { - // eventTwo and eventThree - posted in the correct order - AppContext app = apps[i]; - - AWTEvent eventTwo = getSequencedEvent(); - SunToolkit.postEvent(app, eventTwo); - - AtomicReference called1 = new AtomicReference(false); - AtomicReference called2 = new AtomicReference(false); - int num1 = createSENumber; - SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { - if (dispatchSENumber < num1) { - throw new RuntimeException("Dispatched too early"); - } - called1.set(true); - if (called2.get()) { - throw new RuntimeException("Second event is called before first"); - } - })); - AWTEvent eventThree = getSequencedEvent(); - SunToolkit.postEvent(app, eventThree); - int num2 = createSENumber; - SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { - if (dispatchSENumber < num2) { - throw new RuntimeException("Dispatched too early"); - } - called2.set(true); - if (!called1.get()) { - throw new RuntimeException("First event is not called before second"); - } - System.err.println(AppContext.getAppContext()); - end.countDown(); - })); - } - - - - // eventOne should flush all EDT - SunToolkit.postEvent(apps[0], eventOne); - SunToolkit.postEvent(apps[0], new InvocationEvent(new Object(), () -> { - System.err.println(AppContext.getAppContext()); - end.countDown(); - })); - - go.countDown(); - - System.err.println("Start to wait"); - end.await(); - System.err.println("End to wait"); - } - - private static Thread testThread(int index) { - final ThreadGroup group = new ThreadGroup("TG " + index); - return new Thread(group, () -> { - apps[index] = SunToolkit.createNewAppContext(); - }); - } - - private static AWTEvent getSequencedEvent() - { - int num = createSENumber++; - - InvocationEvent wrapMe = new InvocationEvent(new Object(), () -> { - if (num != dispatchSENumber++) { - System.err.println("num: " + num); - System.err.println("dispatchSENumber: " + dispatchSENumber); - throw new RuntimeException("Wrong order"); - } - }); - - try { - /* - * SequencedEvent is a package private class, which cannot be instantiated - * by importing. So use reflection to create an instance. - */ - Class seqClass = (Class) Class.forName("java.awt.SequencedEvent"); - Constructor - seqConst = seqClass.getConstructor(AWTEvent.class); - seqConst.setAccessible(true); - return seqConst.newInstance(wrapMe); - } catch (Throwable err) { - throw new RuntimeException("Unable to instantiate SequencedEvent",err); - } - } -} From 39a25663e3c4b4fcc22b9aaa3bb89bf6ef6a01c2 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Thu, 12 Mar 2026 06:19:05 +0000 Subject: [PATCH 12/97] 8378740: Suppressed warnings reported when implicit compilation is combined with annotation processing Reviewed-by: liach, vromero, dbeaumont --- .../sun/tools/javac/main/JavaCompiler.java | 4 +- .../APImplicitClassesWarnings.java | 163 ++++++++++++++++++ 2 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 test/langtools/tools/javac/implicitCompile/APImplicitClassesWarnings.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index 94292d9a348e..269d2f5de62c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.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 @@ -823,9 +823,9 @@ public void readSourceFile(JCCompilationUnit tree, ClassSymbol c) throws Complet c, () -> diagFactory.fragment(Fragments.UserSelectedCompletionFailure), dcfh); } JavaFileObject filename = c.classfile; - JavaFileObject prev = log.useSource(filename); if (tree == null) { + JavaFileObject prev = log.useSource(filename); try { tree = parse(filename, filename.getCharContent(false)); } catch (IOException e) { diff --git a/test/langtools/tools/javac/implicitCompile/APImplicitClassesWarnings.java b/test/langtools/tools/javac/implicitCompile/APImplicitClassesWarnings.java new file mode 100644 index 000000000000..dc60c6cc78af --- /dev/null +++ b/test/langtools/tools/javac/implicitCompile/APImplicitClassesWarnings.java @@ -0,0 +1,163 @@ +/* + * 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 8378740 + * @summary Verify warnings are properly suppressed for the combination of + * annotation processing and implicit compilation + * @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 APImplicitClassesWarnings + */ + + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; +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 APImplicitClassesWarnings { + + final ToolBox tb = new ToolBox(); + Path base; + + @Test + public void testCorrectSource() throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + package test; + + @Deprecated(forRemoval=true) + public class Depr { + } + """, + """ + package test; + public class Use { + Implicit implicit; + Depr depr; + } + """, + """ + package test; + public interface Implicit {} + """); + Files.createDirectories(classes); + + List log = new JavacTask(tb) + .options("-d", classes.toString(), + "-XDrawDiagnostics", + "-implicit:class", + "-sourcepath", src.toString()) + .files(src.resolve("test").resolve("Depr.java"), + src.resolve("test").resolve("Use.java")) + .processors(new ProcessorImpl()) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = List.of( + "Use.java:4:5: compiler.warn.has.been.deprecated.for.removal: test.Depr, test", + "Use.java:4:5: compiler.warn.has.been.deprecated.for.removal: test.Depr, test", + "Use.java:4:5: compiler.warn.has.been.deprecated.for.removal: test.Depr, test", + "3 warnings" + ); + + tb.checkEqual(expected, log); + } + + @Test + public void testCorrectSuppress() throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + //note the added @SuppressWarnings("removal"): + """ + package test; + + @Deprecated(forRemoval=true) + public class Depr { + } + """, + """ + package test; + public class Use { + Implicit implicit; + @SuppressWarnings("removal") + Depr depr; + } + """, + """ + package test; + public interface Implicit {} + """); + Files.createDirectories(classes); + + new JavacTask(tb) + .options("-d", classes.toString(), + "-Werror", + "-implicit:class", + "-sourcepath", src.toString()) + .files(src.resolve("test").resolve("Depr.java"), + src.resolve("test").resolve("Use.java")) + .processors(new ProcessorImpl()) + .run() + .writeAll(); + } + + @SupportedAnnotationTypes("*") + private static class ProcessorImpl extends AbstractProcessor { + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + return false; + } + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod().orElseThrow().getName()); + } +} From 26bb357fa2ffa5a919b4be4fdf732fd7b97b767e Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Thu, 12 Mar 2026 07:32:43 +0000 Subject: [PATCH 13/97] 8305250: Unnecessary "unknown enum constant" warning emitted by javac when dependency has optional annotations with enums Reviewed-by: vromero --- .../com/sun/tools/javac/jvm/ClassReader.java | 55 +++-- .../tools/javac/resources/compiler.properties | 2 + .../tools/javac/annotations/6365854/test1.out | 2 - .../CrashOnUnknownTargetTypeTest.java | 2 +- .../CrashOnUnknownTargetTypeTest.out | 4 +- .../tools/javac/classreader/Annotations.java | 220 ++++++++++++++++++ 6 files changed, 262 insertions(+), 23 deletions(-) create mode 100644 test/langtools/tools/javac/classreader/Annotations.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index b7bf48b4a121..017d740dc0af 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.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 @@ -69,6 +69,7 @@ import com.sun.tools.javac.util.ByteBuffer.UnderflowException; import com.sun.tools.javac.util.DefinedBy.Api; import com.sun.tools.javac.util.JCDiagnostic.Fragment; +import com.sun.tools.javac.util.Log.DeferredDiagnosticHandler; import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Kinds.Kind.*; @@ -2043,15 +2044,27 @@ List deproxyCompoundList(List pl) { } Attribute.Compound deproxyCompound(CompoundAnnotationProxy a) { - Type annotationType = resolvePossibleProxyType(a.type); - ListBuffer> buf = new ListBuffer<>(); - for (List> l = a.values; - l.nonEmpty(); - l = l.tail) { - MethodSymbol meth = findAccessMethod(annotationType, l.head.fst); - buf.append(new Pair<>(meth, deproxy(meth.type.getReturnType(), l.head.snd))); + DeferredDiagnosticHandler deferred = log.new DeferredDiagnosticHandler(); + Type annotationType = syms.objectType; + try { + annotationType = resolvePossibleProxyType(a.type); + ListBuffer> buf = new ListBuffer<>(); + for (List> l = a.values; + l.nonEmpty(); + l = l.tail) { + MethodSymbol meth = findAccessMethod(annotationType, l.head.fst); + buf.append(new Pair<>(meth, deproxy(meth.type.getReturnType(), l.head.snd))); + } + return new Attribute.Compound(annotationType, buf.toList()); + } finally { + if (!annotationType.tsym.type.hasTag(TypeTag.ERROR)) { + //if the annotation type does not exists + //throw away warnings reported while de-proxying the annotation, + //as the annotation's library is probably missing from the classpath: + deferred.reportDeferredDiagnostics(); + } + log.popDiagnosticHandler(deferred); } - return new Attribute.Compound(annotationType, buf.toList()); } MethodSymbol findAccessMethod(Type container, Name name) { @@ -2146,15 +2159,21 @@ public void visitEnumAttributeProxy(EnumAttributeProxy proxy) { failure = ex; } if (enumerator == null) { - if (failure != null) { - log.warning(Warnings.UnknownEnumConstantReason(currentClassFile, - enumTypeSym, - proxy.enumerator, - failure.getDiagnostic())); - } else { - log.warning(Warnings.UnknownEnumConstant(currentClassFile, - enumTypeSym, - proxy.enumerator)); + // The enumerator wasn't found: emit a warning and recover + JavaFileObject prevSource = log.useSource(requestingOwner.classfile); + try { + if (failure != null) { + log.warning(LintWarnings.UnknownEnumConstantReason(currentClassFile, + enumTypeSym, + proxy.enumerator, + failure.getDiagnostic())); + } else { + log.warning(LintWarnings.UnknownEnumConstant(currentClassFile, + enumTypeSym, + proxy.enumerator)); + } + } finally { + log.useSource(prevSource); } result = new Attribute.Enum(enumTypeSym.type, new VarSymbol(0, proxy.enumerator, syms.botType, enumTypeSym)); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index 915d7f8a8d80..58a5333ce4c1 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -2521,10 +2521,12 @@ compiler.err.cant.attach.type.annotations=\ {3} # 0: file object, 1: symbol, 2: name +# lint: classfile compiler.warn.unknown.enum.constant=\ unknown enum constant {1}.{2} # 0: file object, 1: symbol, 2: name, 3: message segment +# lint: classfile compiler.warn.unknown.enum.constant.reason=\ unknown enum constant {1}.{2}\n\ reason: {3} diff --git a/test/langtools/tools/javac/annotations/6365854/test1.out b/test/langtools/tools/javac/annotations/6365854/test1.out index c8bf69b095da..e69de29bb2d1 100644 --- a/test/langtools/tools/javac/annotations/6365854/test1.out +++ b/test/langtools/tools/javac/annotations/6365854/test1.out @@ -1,2 +0,0 @@ -TestCore.class:-:-: compiler.warn.annotation.method.not.found.reason: test.annotation.TestAnnotation, test, (compiler.misc.class.file.not.found: test.annotation.TestAnnotation) -1 warning diff --git a/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.java b/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.java index 7f5d49e38c9f..d0c19c3ec09b 100644 --- a/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.java +++ b/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.java @@ -3,7 +3,7 @@ * @summary compiler is crashing with AssertionError for annotations with unknown target type * @bug 8296010 * @build A - * @compile/fail/ref=CrashOnUnknownTargetTypeTest.out -XDrawDiagnostics CrashOnUnknownTargetTypeTest.java + * @compile/fail/ref=CrashOnUnknownTargetTypeTest.out -XDrawDiagnostics -Xlint:classfile CrashOnUnknownTargetTypeTest.java */ public class CrashOnUnknownTargetTypeTest { diff --git a/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.out b/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.out index 8e6925a0e0db..d46ea56acef2 100644 --- a/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.out +++ b/test/langtools/tools/javac/annotations/crashOnUnknownAttr/CrashOnUnknownTargetTypeTest.out @@ -1,5 +1,5 @@ -- compiler.warn.unknown.enum.constant: ElementType.class, java.lang.annotation.ElementType, NO_SUCH -- compiler.warn.unknown.enum.constant: String.class, java.lang.annotation.ElementType, NO_SUCH +A.class:-:-: compiler.warn.unknown.enum.constant: ElementType.class, java.lang.annotation.ElementType, NO_SUCH +A.class:-:-: compiler.warn.unknown.enum.constant: String.class, java.lang.annotation.ElementType, NO_SUCH CrashOnUnknownTargetTypeTest.java:10:5: compiler.err.annotation.unrecognized.attribute.name: A, NO_SUCH CrashOnUnknownTargetTypeTest.java:11:5: compiler.err.annotation.unrecognized.attribute.name: A, NO_SUCH CrashOnUnknownTargetTypeTest.java:12:14: compiler.err.annotation.unrecognized.attribute.name: A, NO_SUCH diff --git a/test/langtools/tools/javac/classreader/Annotations.java b/test/langtools/tools/javac/classreader/Annotations.java new file mode 100644 index 000000000000..30d0ae48b9a7 --- /dev/null +++ b/test/langtools/tools/javac/classreader/Annotations.java @@ -0,0 +1,220 @@ +/* + * 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 8305250 + * @summary Check behavior w.r.t. annotations missing from the classpath. + * @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 Annotations + */ + +import java.nio.file.Files; +import java.util.List; +import java.nio.file.Path; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import toolbox.ToolBox; +import toolbox.JavacTask; +import toolbox.Task; + +public class Annotations { + private ToolBox tb = new ToolBox(); + private Path base; + + @Test + public void testParameterModifiersNotVisible() throws Exception { + Path ann = base.resolve("annotations"); + Path annSrc = ann.resolve("src"); + Path annClasses = ann.resolve("classes"); + + tb.writeJavaFiles(annSrc, + """ + package annotations; + public @interface Ann { + public E e(); + } + """, + """ + package annotations; + public enum E { + A; + } + """); + + Files.createDirectories(annClasses); + + new JavacTask(tb) + .outdir(annClasses) + .files(tb.findJavaFiles(annSrc)) + .run() + .writeAll(); + + Path lib = base.resolve("lib"); + Path libSrc = lib.resolve("src"); + Path libClasses = lib.resolve("classes"); + + tb.writeJavaFiles(libSrc, + """ + package lib; + import annotations.*; + @Ann(e = E.A) + public class Lib { + } + """); + + Files.createDirectories(libClasses); + + new JavacTask(tb) + .outdir(libClasses) + .classpath(annClasses) + .files(tb.findJavaFiles(libSrc)) + .run() + .writeAll(); + + Path test = base.resolve("test"); + Path testSrc = test.resolve("src"); + Path testClasses = test.resolve("classes"); + + tb.writeJavaFiles(testSrc, + """ + package test; + import lib.*; + public class Test { + Lib l; + } + """); + + Files.createDirectories(testClasses); + + //annotations available, no errors/warnings: + new JavacTask(tb) + .outdir(testClasses) + .classpath(libClasses, annClasses) + .options("-Werror", "-Xlint:classfile") + .files(tb.findJavaFiles(testSrc)) + .run() + .writeAll(); + + //annotation and enum missing, no errors/warnings: + new JavacTask(tb) + .outdir(testClasses) + .classpath(libClasses) + .options("-Werror", "-Xlint:classfile") + .files(tb.findJavaFiles(testSrc)) + .run() + .writeAll(); + + tb.writeJavaFiles(annSrc, + """ + package annotations; + public enum E { + B; + } + """); + + Files.createDirectories(annClasses); + + new JavacTask(tb) + .outdir(annClasses) + .files(tb.findJavaFiles(annSrc)) + .run() + .writeAll(); + + List log; + + //enum missing the enum constant recorded in the classfile, report warning: + log = new JavacTask(tb) + .outdir(testClasses) + .classpath(libClasses, annClasses) + .options("-Xlint:classfile", "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + tb.checkEqual(log, + List.of("Lib.class:-:-: compiler.warn.unknown.enum.constant: E.class, annotations.E, A", + "1 warning")); + + //enum is missing, but the annotation is not, report warning: + Files.delete(annClasses.resolve("annotations").resolve("E.class")); + + log = new JavacTask(tb) + .outdir(testClasses) + .classpath(libClasses, annClasses) + .options("-Xlint:classfile", "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + tb.checkEqual(log, + List.of("Lib.class:-:-: compiler.warn.unknown.enum.constant.reason: Ann.class, annotations.E, A, (compiler.misc.class.file.not.found: annotations.E)", + "1 warning")); + + tb.writeJavaFiles(annSrc, + """ + package annotations; + public @interface Ann { + public E nue(); + } + """, + """ + package annotations; + public enum E { + A; + } + """); + + new JavacTask(tb) + .outdir(annClasses) + .files(tb.findJavaFiles(annSrc)) + .run() + .writeAll(); + + //enum is OK and the annotation exists, but the annotation is missing the required attribute method, report warning: + log = new JavacTask(tb) + .outdir(testClasses) + .classpath(libClasses, annClasses) + .options("-Xlint:classfile", "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + tb.checkEqual(log, + List.of("Lib.class:-:-: compiler.warn.annotation.method.not.found: annotations.Ann, e", + "1 warning")); + } + + @BeforeEach + public void setup(TestInfo ti) { + base = Path.of(".").resolve(ti.getTestMethod().orElseThrow().getName()); + } +} From 73424d14baa9e439f0825507233a7c2247894195 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Thu, 12 Mar 2026 08:31:43 +0000 Subject: [PATCH 14/97] 8379506: Parallel: Move Parallel specific flags to parallel_globals.hpp Reviewed-by: tschatzl, phubner --- src/hotspot/os/linux/os_linux.cpp | 2 ++ src/hotspot/share/gc/parallel/mutableNUMASpace.hpp | 1 + src/hotspot/share/gc/parallel/parallel_globals.hpp | 13 +++++++++++++ src/hotspot/share/runtime/globals.hpp | 13 ------------- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 9c2fbab7535d..17cd0d05f9ad 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -4549,6 +4549,7 @@ void os::Linux::numa_init() { FLAG_SET_ERGO_IF_DEFAULT(UseNUMAInterleaving, true); } +#if INCLUDE_PARALLELGC if (UseParallelGC && UseNUMA && UseLargePages && !can_commit_large_page_memory()) { // With static large pages we cannot uncommit a page, so there's no way // we can make the adaptive lgrp chunk resizing work. If the user specified both @@ -4560,6 +4561,7 @@ void os::Linux::numa_init() { UseAdaptiveNUMAChunkSizing = false; } } +#endif } void os::Linux::disable_numa(const char* reason, bool warning) { diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp index 4b8bca430f8f..36de38c34bb9 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp @@ -26,6 +26,7 @@ #define SHARE_GC_PARALLEL_MUTABLENUMASPACE_HPP #include "gc/parallel/mutableSpace.hpp" +#include "gc/shared/gc_globals.hpp" #include "gc/shared/gcUtil.hpp" #include "runtime/globals.hpp" #include "utilities/growableArray.hpp" diff --git a/src/hotspot/share/gc/parallel/parallel_globals.hpp b/src/hotspot/share/gc/parallel/parallel_globals.hpp index 64e2effdeae1..ba4a79f92542 100644 --- a/src/hotspot/share/gc/parallel/parallel_globals.hpp +++ b/src/hotspot/share/gc/parallel/parallel_globals.hpp @@ -31,6 +31,19 @@ product_pd, \ range, \ constraint) \ + product(uintx, NUMAChunkResizeWeight, 20, \ + "Percentage (0-100) used to weight the current sample when " \ + "computing exponentially decaying average for " \ + "AdaptiveNUMAChunkSizing") \ + range(0, 100) \ + \ + product(size_t, NUMASpaceResizeRate, 1*G, \ + "Do not reallocate more than this amount per collection") \ + range(0, max_uintx) \ + \ + product(bool, UseAdaptiveNUMAChunkSizing, true, \ + "Enable adaptive chunk sizing for NUMA") \ + \ product(bool, UseMaximumCompactionOnSystemGC, true, \ "Use maximum compaction in the Parallel Old garbage collector " \ "for a system GC") diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 6d4b9908e1c3..559071cae685 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -199,19 +199,6 @@ const int ObjectAlignmentInBytes = 8; "Granularity to use for NUMA interleaving on Windows OS") \ constraint(NUMAInterleaveGranularityConstraintFunc, AtParse) \ \ - product(uintx, NUMAChunkResizeWeight, 20, \ - "Percentage (0-100) used to weight the current sample when " \ - "computing exponentially decaying average for " \ - "AdaptiveNUMAChunkSizing") \ - range(0, 100) \ - \ - product(size_t, NUMASpaceResizeRate, 1*G, \ - "Do not reallocate more than this amount per collection") \ - range(0, max_uintx) \ - \ - product(bool, UseAdaptiveNUMAChunkSizing, true, \ - "Enable adaptive chunk sizing for NUMA") \ - \ product(bool, NUMAStats, false, \ "Print NUMA stats in detailed heap information") \ \ From fd80329bfdbc023140376e396a02ad6dc0c5bf09 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 12 Mar 2026 08:39:41 +0000 Subject: [PATCH 15/97] 8379260: C2: Separate volatile barrier and full barrier Reviewed-by: fyang, mdoerr, amitkumar, aph, dlong --- src/hotspot/cpu/aarch64/aarch64.ad | 29 +++++++++++ src/hotspot/cpu/arm/arm.ad | 24 ++++++++++ src/hotspot/cpu/ppc/ppc.ad | 24 ++++++++++ src/hotspot/cpu/riscv/riscv.ad | 64 +++++++++++++++++++++++++ src/hotspot/cpu/s390/s390.ad | 18 +++++++ src/hotspot/cpu/x86/x86.ad | 30 ++++++++++++ src/hotspot/share/adlc/formssel.cpp | 2 + src/hotspot/share/opto/classes.hpp | 2 + src/hotspot/share/opto/library_call.cpp | 4 +- src/hotspot/share/opto/memnode.cpp | 2 + src/hotspot/share/opto/memnode.hpp | 15 ++++++ 11 files changed, 212 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 51bb15e0f59c..3989c5a17f02 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -8024,6 +8024,21 @@ instruct membar_release_lock() %{ ins_pipe(pipe_serial); %} +instruct membar_storeload() %{ + match(MemBarStoreLoad); + ins_cost(VOLATILE_REF_COST*100); + + format %{ "MEMBAR-store-load\n\t" + "dmb ish" %} + + ins_encode %{ + __ block_comment("membar_storeload"); + __ membar(Assembler::StoreLoad); + %} + + ins_pipe(pipe_serial); +%} + instruct unnecessary_membar_volatile() %{ predicate(unnecessary_volatile(n)); match(MemBarVolatile); @@ -8053,6 +8068,20 @@ instruct membar_volatile() %{ ins_pipe(pipe_serial); %} +instruct membar_full() %{ + match(MemBarFull); + ins_cost(VOLATILE_REF_COST*100); + + format %{ "membar_full\n\t" + "dmb ish" %} + ins_encode %{ + __ block_comment("membar_full"); + __ membar(Assembler::AnyAny); + %} + + ins_pipe(pipe_serial); +%} + // ============================================================================ // Cast/Convert Instructions diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 87c609be5a7f..7d0d31c1f799 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -4440,6 +4440,18 @@ instruct membar_release_lock() %{ ins_pipe(empty); %} +instruct membar_storeload() %{ + match(MemBarStoreLoad); + ins_cost(4*MEMORY_REF_COST); + + size(4); + format %{ "MEMBAR-storeload" %} + ins_encode %{ + __ membar(MacroAssembler::StoreLoad, noreg); + %} + ins_pipe(long_memory_op); +%} + instruct membar_volatile() %{ match(MemBarVolatile); ins_cost(4*MEMORY_REF_COST); @@ -4463,6 +4475,18 @@ instruct unnecessary_membar_volatile() %{ ins_pipe(empty); %} +instruct membar_full() %{ + match(MemBarFull); + ins_cost(4*MEMORY_REF_COST); + + size(4); + format %{ "MEMBAR-full" %} + ins_encode %{ + __ membar(MacroAssembler::StoreLoad, noreg); + %} + ins_pipe(long_memory_op); +%} + //----------Register Move Instructions----------------------------------------- // Cast Index to Pointer for unsafe natives diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 7e3cd04171d5..057015d3c395 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -7163,6 +7163,18 @@ instruct membar_release_lock() %{ ins_pipe(pipe_class_default); %} +instruct membar_storeload() %{ + match(MemBarStoreLoad); + ins_cost(4*MEMORY_REF_COST); + + format %{ "MEMBAR-store-load" %} + size(4); + ins_encode %{ + __ fence(); + %} + ins_pipe(pipe_class_default); +%} + instruct membar_volatile() %{ match(MemBarVolatile); ins_cost(4*MEMORY_REF_COST); @@ -7205,6 +7217,18 @@ instruct membar_volatile() %{ // ins_pipe(pipe_class_default); //%} +instruct membar_full() %{ + match(MemBarFull); + ins_cost(4*MEMORY_REF_COST); + + format %{ "MEMBAR-full" %} + size(4); + ins_encode %{ + __ fence(); + %} + ins_pipe(pipe_class_default); +%} + instruct membar_CPUOrder() %{ match(MemBarCPUOrder); ins_cost(0); diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 54ea81683fcc..e140052d168a 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -8156,6 +8156,22 @@ instruct unnecessary_membar_rvtso() %{ ins_pipe(real_empty); %} +instruct membar_storeload_rvtso() %{ + predicate(UseZtso); + match(MemBarStoreLoad); + ins_cost(VOLATILE_REF_COST); + + format %{ "#@membar_storeload_rvtso\n\t" + "fence w, r"%} + + ins_encode %{ + __ block_comment("membar_storeload_rvtso"); + __ membar(MacroAssembler::StoreLoad); + %} + + ins_pipe(pipe_slow); +%} + instruct membar_volatile_rvtso() %{ predicate(UseZtso); match(MemBarVolatile); @@ -8186,6 +8202,22 @@ instruct unnecessary_membar_volatile_rvtso() %{ ins_pipe(real_empty); %} +instruct membar_full_rvtso() %{ + predicate(UseZtso); + match(MemBarFull); + ins_cost(VOLATILE_REF_COST); + + format %{ "#@membar_full_rvtso\n\t" + "fence rw, rw" %} + + ins_encode %{ + __ block_comment("membar_full_rvtso"); + __ membar(MacroAssembler::AnyAny); + %} + + ins_pipe(pipe_slow); +%} + // RVWMO instruct membar_aqcuire_rvwmo() %{ @@ -8235,6 +8267,22 @@ instruct membar_storestore_rvwmo() %{ ins_pipe(pipe_serial); %} +instruct membar_storeload_rvwmo() %{ + predicate(!UseZtso); + match(MemBarStoreLoad); + ins_cost(VOLATILE_REF_COST); + + format %{ "#@membar_storeload_rvwmo\n\t" + "fence w, r"%} + + ins_encode %{ + __ block_comment("membar_storeload_rvwmo"); + __ membar(MacroAssembler::StoreLoad); + %} + + ins_pipe(pipe_serial); +%} + instruct membar_volatile_rvwmo() %{ predicate(!UseZtso); match(MemBarVolatile); @@ -8279,6 +8327,22 @@ instruct unnecessary_membar_volatile_rvwmo() %{ ins_pipe(real_empty); %} +instruct membar_full_rvwmo() %{ + predicate(!UseZtso); + match(MemBarFull); + ins_cost(VOLATILE_REF_COST); + + format %{ "#@membar_full_rvwmo\n\t" + "fence rw, rw" %} + + ins_encode %{ + __ block_comment("membar_full_rvwmo"); + __ membar(MacroAssembler::AnyAny); + %} + + ins_pipe(pipe_serial); +%} + instruct spin_wait() %{ predicate(UseZihintpause); match(OnSpinWait); diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 1521edde40cb..b9982c795cde 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -5239,6 +5239,15 @@ instruct membar_release_lock() %{ ins_pipe(pipe_class_dummy); %} +instruct membar_storeload() %{ + match(MemBarStoreLoad); + ins_cost(4 * MEMORY_REF_COST); + size(2); + format %{ "MEMBAR-storeload" %} + ins_encode %{ __ z_fence(); %} + ins_pipe(pipe_class_dummy); +%} + instruct membar_volatile() %{ match(MemBarVolatile); ins_cost(4 * MEMORY_REF_COST); @@ -5258,6 +5267,15 @@ instruct unnecessary_membar_volatile() %{ ins_pipe(pipe_class_dummy); %} +instruct membar_full() %{ + match(MemBarFull); + ins_cost(4 * MEMORY_REF_COST); + size(2); + format %{ "MEMBAR-full" %} + ins_encode %{ __ z_fence(); %} + ins_pipe(pipe_class_dummy); +%} + instruct membar_CPUOrder() %{ match(MemBarCPUOrder); ins_cost(0); diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index ed380105565e..8b90655c53c4 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -8852,6 +8852,21 @@ instruct membar_release_lock() ins_pipe(empty); %} +instruct membar_storeload(rFlagsReg cr) %{ + match(MemBarStoreLoad); + effect(KILL cr); + ins_cost(400); + + format %{ + $$template + $$emit$$"lock addl [rsp + #0], 0\t! membar_storeload" + %} + ins_encode %{ + __ membar(Assembler::StoreLoad); + %} + ins_pipe(pipe_slow); +%} + instruct membar_volatile(rFlagsReg cr) %{ match(MemBarVolatile); effect(KILL cr); @@ -8879,6 +8894,21 @@ instruct unnecessary_membar_volatile() ins_pipe(empty); %} +instruct membar_full(rFlagsReg cr) %{ + match(MemBarFull); + effect(KILL cr); + ins_cost(400); + + format %{ + $$template + $$emit$$"lock addl [rsp + #0], 0\t! membar_full" + %} + ins_encode %{ + __ membar(Assembler::StoreLoad); + %} + ins_pipe(pipe_slow); +%} + instruct membar_storestore() %{ match(MemBarStoreStore); match(StoreStoreFence); diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index 182587d2f2fd..4dd2bff7c897 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -4276,7 +4276,9 @@ bool MatchRule::is_ideal_membar() const { !strcmp(_opType,"LoadFence" ) || !strcmp(_opType,"StoreFence") || !strcmp(_opType,"StoreStoreFence") || + !strcmp(_opType,"MemBarStoreLoad") || !strcmp(_opType,"MemBarVolatile") || + !strcmp(_opType,"MemBarFull") || !strcmp(_opType,"MemBarCPUOrder") || !strcmp(_opType,"MemBarStoreStore") || !strcmp(_opType,"OnSpinWait"); diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index abd93fdd876c..719b90ad6ddd 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -239,8 +239,10 @@ macro(MemBarRelease) macro(StoreFence) macro(StoreStoreFence) macro(MemBarReleaseLock) +macro(MemBarStoreLoad) macro(MemBarVolatile) macro(MemBarStoreStore) +macro(MemBarFull) macro(MergeMem) macro(MinI) macro(MinL) diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index b3ee060d75f2..8d3c5a98ea00 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -2900,7 +2900,7 @@ bool LibraryCallKit::inline_unsafe_fence(vmIntrinsics::ID id) { insert_mem_bar(Op_StoreStoreFence); return true; case vmIntrinsics::_fullFence: - insert_mem_bar(Op_MemBarVolatile); + insert_mem_bar(Op_MemBarFull); return true; default: fatal_unexpected_iid(id); @@ -3070,7 +3070,7 @@ bool LibraryCallKit::inline_native_vthread_start_transition(address funcAddr, co Node* vt_addr = basic_plus_adr(vt_oop, java_lang_Thread::is_in_vthread_transition_offset()); access_store_at(nullptr, jt_addr, _gvn.type(jt_addr)->is_ptr(), ideal.ConI(1), TypeInt::BOOL, T_BOOLEAN, IN_NATIVE | MO_UNORDERED); access_store_at(nullptr, vt_addr, _gvn.type(vt_addr)->is_ptr(), ideal.ConI(1), TypeInt::BOOL, T_BOOLEAN, IN_NATIVE | MO_UNORDERED); - insert_mem_bar(Op_MemBarVolatile); + insert_mem_bar(Op_MemBarStoreLoad); ideal.sync_kit(this); Node* global_disable_addr = makecon(TypeRawPtr::make((address)MountUnmountDisabler::global_vthread_transition_disable_count_address())); diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 6cb14444f6bb..85bc41e71b9c 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -4349,7 +4349,9 @@ MemBarNode* MemBarNode::make(Compile* C, int opcode, int atp, Node* pn) { case Op_StoreStoreFence: return new StoreStoreFenceNode(C, atp, pn); case Op_MemBarAcquireLock: return new MemBarAcquireLockNode(C, atp, pn); case Op_MemBarReleaseLock: return new MemBarReleaseLockNode(C, atp, pn); + case Op_MemBarStoreLoad: return new MemBarStoreLoadNode(C, atp, pn); case Op_MemBarVolatile: return new MemBarVolatileNode(C, atp, pn); + case Op_MemBarFull: return new MemBarFullNode(C, atp, pn); case Op_MemBarCPUOrder: return new MemBarCPUOrderNode(C, atp, pn); case Op_OnSpinWait: return new OnSpinWaitNode(C, atp, pn); case Op_Initialize: return new InitializeNode(C, atp, pn); diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index 30d44e820162..7fa238f574dc 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -1321,6 +1321,13 @@ class StoreStoreFenceNode: public MemBarNode { virtual int Opcode() const; }; +class MemBarStoreLoadNode : public MemBarNode { +public: + MemBarStoreLoadNode(Compile* C, int alias_idx, Node* precedent) + : MemBarNode(C, alias_idx, precedent) {} + virtual int Opcode() const; +}; + // Ordering between a volatile store and a following volatile load. // Requires multi-CPU visibility? class MemBarVolatileNode: public MemBarNode { @@ -1330,6 +1337,14 @@ class MemBarVolatileNode: public MemBarNode { virtual int Opcode() const; }; +// A full barrier blocks all loads and stores from moving across it +class MemBarFullNode : public MemBarNode { +public: + MemBarFullNode(Compile* C, int alias_idx, Node* precedent) + : MemBarNode(C, alias_idx, precedent) {} + virtual int Opcode() const; +}; + // Ordering within the same CPU. Used to order unsafe memory references // inside the compiler when we lack alias info. Not needed "outside" the // compiler because the CPU does all the ordering for us. From c6afd3ced68d231a1d466bd62403409db9e8a96e Mon Sep 17 00:00:00 2001 From: Kerem Kat Date: Thu, 12 Mar 2026 09:24:55 +0000 Subject: [PATCH 16/97] 8379460: C2: Notify AddI/AddL to URShiftI/URShiftL users Reviewed-by: qamai, dfenacci, mchevalier --- src/hotspot/share/opto/phaseX.cpp | 7 ++ .../c2/igvn/TestURShiftAddNotification.java | 89 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/c2/igvn/TestURShiftAddNotification.java diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index c77316e7d0b1..082746f80b73 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2581,6 +2581,13 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ return u->Opcode() == Op_CmpU; }); } + // If changed AddI/AddL inputs, check URShift users for + // "((X << z) + Y) >>> z" optimization in URShift{I,L}Node::Ideal. + if (use_op == Op_AddI || use_op == Op_AddL) { + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL; + }); + } // If changed AndI/AndL inputs, check RShift/URShift users for "(x & mask) >> shift" optimization opportunity if (use_op == Op_AndI || use_op == Op_AndL) { add_users_to_worklist_if(worklist, use, [](Node* u) { diff --git a/test/hotspot/jtreg/compiler/c2/igvn/TestURShiftAddNotification.java b/test/hotspot/jtreg/compiler/c2/igvn/TestURShiftAddNotification.java new file mode 100644 index 000000000000..8ca192ea38b3 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/igvn/TestURShiftAddNotification.java @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.c2.igvn; + +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; +import jdk.test.lib.Platform; +import jdk.test.lib.Utils; +import java.util.Random; + +/* + * @test + * @bug 8379460 + * @key randomness + * @summary When AddI/AddL inputs change during IGVN, URShift users must be re-added + * to the IGVN worklist so they can re-check the ((X << z) + Y) >>> z optimization. + * @library /test/lib / + * @run driver ${test.main.class} + */ +public class TestURShiftAddNotification { + + private static final Random RANDOM = Utils.getRandomInstance(); + + public static void main(String[] args) { + var framework = new TestFramework(); + framework.addScenarios(new Scenario(0)); + if (Platform.isDebugBuild()) { + framework.addScenarios(new Scenario(1, "-XX:VerifyIterativeGVN=1110")); + } + framework.start(); + } + + // The trick: a loop whose exit value is only known after loop optimization. + // During initial GVN, i is a Phi, so (x << C) * i stays as MulI — URShift + // can't see the LShiftI input through the MulI. After loop opts resolve + // i = 1, MulI identity-folds to LShiftI (same type, no cascade), and + // without the fix URShift is never re-notified about the new LShiftI input. + + @Run(test = {"testI", "testL"}) + public void runTests() { + int xi = RANDOM.nextInt(); + int yi = RANDOM.nextInt(); + long xl = RANDOM.nextLong(); + long yl = RANDOM.nextLong(); + + Asserts.assertEQ(((xi << 3) + yi) >>> 3, testI(xi, yi)); + Asserts.assertEQ(((xl << 9) + yl) >>> 9, testL(xl, yl)); + } + + @Test + @IR(failOn = {IRNode.LSHIFT_I, IRNode.MUL_I}, + counts = {IRNode.URSHIFT_I, "1", IRNode.AND_I, "1"}) + static int testI(int x, int y) { + int i; + for (i = -10; i < 1; i++) { } + int c = (x << 3) * i; + return (c + y) >>> 3; + } + + @Test + @IR(failOn = {IRNode.LSHIFT_L, IRNode.MUL_L}, + counts = {IRNode.URSHIFT_L, "1", IRNode.AND_L, "1"}) + static long testL(long x, long y) { + int i; + for (i = -10; i < 1; i++) { } + long c = (x << 9) * i; + return (c + y) >>> 9; + } +} From bd2972d2b0c49925362df29962baa86259467fca Mon Sep 17 00:00:00 2001 From: Kuai Wei Date: Thu, 12 Mar 2026 09:31:08 +0000 Subject: [PATCH 17/97] 8379502: Remove unused PhaseOutput::need_register_stack_bang() Reviewed-by: chagedorn --- src/hotspot/share/opto/output.cpp | 9 --------- src/hotspot/share/opto/output.hpp | 1 - 2 files changed, 10 deletions(-) diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index 136fc8ac864f..edfd6189fb98 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -363,15 +363,6 @@ bool PhaseOutput::need_stack_bang(int frame_size_in_bytes) const { DEBUG_ONLY(|| true))); } -bool PhaseOutput::need_register_stack_bang() const { - // Determine if we need to generate a register stack overflow check. - // This is only used on architectures which have split register - // and memory stacks. - // Bang if the method is not a stub function and has java calls - return (C->stub_function() == nullptr && C->has_java_calls()); -} - - // Compute the size of first NumberOfLoopInstrToAlign instructions at the top // of a loop. When aligning a loop we need to provide enough instructions // in cpu's fetch buffer to feed decoders. The loop alignment could be diff --git a/src/hotspot/share/opto/output.hpp b/src/hotspot/share/opto/output.hpp index 432ad3638b23..5cca59ea0e4e 100644 --- a/src/hotspot/share/opto/output.hpp +++ b/src/hotspot/share/opto/output.hpp @@ -109,7 +109,6 @@ class PhaseOutput : public Phase { // Convert Nodes to instruction bits and pass off to the VM void Output(); bool need_stack_bang(int frame_size_in_bytes) const; - bool need_register_stack_bang() const; void compute_loop_first_inst_sizes(); void install_code(ciMethod* target, From 83f323d6fda09f95616de4b962415226259a0fd6 Mon Sep 17 00:00:00 2001 From: Amit Kumar Date: Thu, 12 Mar 2026 09:59:04 +0000 Subject: [PATCH 18/97] 8379704: [s390x] Build without C2 broken after 8373595 Reviewed-by: mdoerr, aph --- .../cpu/s390/gc/shared/barrierSetAssembler_s390.cpp | 10 +++++----- .../cpu/s390/gc/shared/barrierSetAssembler_s390.hpp | 7 +++++-- src/hotspot/cpu/s390/macroAssembler_s390.cpp | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp index 7617c7a49e84..9fac231df473 100644 --- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp @@ -169,6 +169,11 @@ void BarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Re __ z_lg(obj, 0, obj); // Resolve (untagged) jobject. } +void BarrierSetAssembler::try_resolve_weak_handle(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { + // Load the oop from the weak handle. + __ z_lg(obj, Address(obj)); +} + void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm) { BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod(); __ align(4, __ offset() + OFFSET_TO_PATCHABLE_DATA); // must align the following block which requires atomic updates @@ -206,11 +211,6 @@ OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Na return opto_reg; } -void BarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { - // Load the oop from the weak handle. - __ z_lg(obj, Address(obj)); -} - #undef __ #define __ _masm-> diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp index d56824504147..8e76ec2f4b48 100644 --- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp @@ -58,6 +58,11 @@ class BarrierSetAssembler: public CHeapObj { virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath); + // Can be used in nmethods including native wrappers. + // Attention: obj will only be valid until next safepoint (no SATB barrier). + // (other platforms currently use it for C2 only: try_resolve_weak_handle_in_c2) + virtual void try_resolve_weak_handle(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); + virtual void nmethod_entry_barrier(MacroAssembler* masm); virtual void barrier_stubs_init() {} @@ -65,8 +70,6 @@ class BarrierSetAssembler: public CHeapObj { #ifdef COMPILER2 OptoReg::Name refine_register(const Node* node, OptoReg::Name opto_reg) const; - virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, - Register tmp, Label& slow_path); #endif // COMPILER2 static const int OFFSET_TO_PATCHABLE_DATA_INSTRUCTION = 6 + 6 + 6; // iihf(6) + iilf(6) + lg(6) diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index 78779a9098ae..6e132f895bd2 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. - * Copyright 2024 IBM Corporation. All rights reserved. + * Copyright 2024, 2026 IBM Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -6413,7 +6413,7 @@ void MacroAssembler::compiler_fast_lock_object(Register obj, Register box, Regis // Check if object matches. z_lg(tmp2, Address(tmp1_monitor, ObjectMonitor::object_offset())); BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); - bs_asm->try_resolve_weak_handle_in_c2(this, tmp2, Z_R0_scratch, slow_path); + bs_asm->try_resolve_weak_handle(this, tmp2, Z_R0_scratch, slow_path); z_cgr(obj, tmp2); z_brne(slow_path); From 34a0235ed30141e92f064d30fe4378709ea0e135 Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Thu, 12 Mar 2026 14:45:18 +0000 Subject: [PATCH 19/97] 8379548: java/util/logging/ParentLoggersTest.java failed with missing myParentLogger value Reviewed-by: jpai --- .../classes/java/util/logging/LogManager.java | 84 +++++++++++-------- .../java/util/logging/ParentLoggersTest.java | 2 +- 2 files changed, 48 insertions(+), 38 deletions(-) diff --git a/src/java.logging/share/classes/java/util/logging/LogManager.java b/src/java.logging/share/classes/java/util/logging/LogManager.java index 9c9c708a0624..102f4bac6e41 100644 --- a/src/java.logging/share/classes/java/util/logging/LogManager.java +++ b/src/java.logging/share/classes/java/util/logging/LogManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -736,41 +736,56 @@ synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNe logger.setLevel(level); } - // instantiation of the handler is done in the LogManager.addLogger - // implementation as a handler class may be only visible to LogManager - // subclass for the custom log manager case - processParentHandlers(logger, name, VisitedLoggers.NEVER); - - // Find the new node and its parent. - LogNode node = getNode(name); - node.loggerRef = ref; - Logger parent = null; - LogNode nodep = node.parent; - while (nodep != null) { - LoggerWeakRef nodeRef = nodep.loggerRef; - if (nodeRef != null) { - parent = nodeRef.get(); - if (parent != null) { - break; + // We need to make sure that loggers created by processParentHandlers + // will not be garbage collected before the child/parent + // pointers are updated. We use an ArrayList to temporarily + // store these loggers, until the parent/child relationship + // have been updated + final List saved = new ArrayList<>(); + try { + + // always return false, to make sure we process all loggers from + // root to child. + Predicate visited = (l) -> saved.add(l) && false; + + // instantiation of the handler is done in the LogManager.addLogger + // implementation as a handler class may be only visible to LogManager + // subclass for the custom log manager case + processParentHandlers(logger, name, visited); + + // Find the new node and its parent. + LogNode node = getNode(name); + node.loggerRef = ref; + Logger parent = null; + LogNode nodep = node.parent; + while (nodep != null) { + LoggerWeakRef nodeRef = nodep.loggerRef; + if (nodeRef != null) { + parent = nodeRef.get(); + if (parent != null) { + break; + } } + nodep = nodep.parent; } - nodep = nodep.parent; - } - if (parent != null) { - logger.setParent(parent); + if (parent != null) { + logger.setParent(parent); + } + // Walk over the children and tell them we are their new parent. + node.walkAndSetParent(logger); + // new LogNode is ready so tell the LoggerWeakRef about it + ref.setNode(node); + + // Do not publish 'ref' in namedLoggers before the logger tree + // is fully updated - because the named logger will be visible as + // soon as it is published in namedLoggers (findLogger takes + // benefit of the ConcurrentHashMap implementation of namedLoggers + // to avoid synchronizing on retrieval when that is possible). + namedLoggers.put(name, ref); + } finally { + saved.clear(); } - // Walk over the children and tell them we are their new parent. - node.walkAndSetParent(logger); - // new LogNode is ready so tell the LoggerWeakRef about it - ref.setNode(node); - - // Do not publish 'ref' in namedLoggers before the logger tree - // is fully updated - because the named logger will be visible as - // soon as it is published in namedLoggers (findLogger takes - // benefit of the ConcurrentHashMap implementation of namedLoggers - // to avoid synchronizing on retrieval when that is possible). - namedLoggers.put(name, ref); return true; } @@ -1647,11 +1662,6 @@ public boolean test(Logger logger) { public void clear() { if (visited != null) visited.clear(); } - - // An object that considers that no logger has ever been visited. - // This is used when processParentHandlers is called from - // LoggerContext.addLocalLogger - static final VisitedLoggers NEVER = new VisitedLoggers(null); } diff --git a/test/jdk/java/util/logging/ParentLoggersTest.java b/test/jdk/java/util/logging/ParentLoggersTest.java index 8aae7e95d743..55a93249ca81 100644 --- a/test/jdk/java/util/logging/ParentLoggersTest.java +++ b/test/jdk/java/util/logging/ParentLoggersTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6498300 8377457 + * @bug 6498300 8377457 8379548 * * @summary regression: parent loggers are not properly registered * @author ss45998 From 8444fdae4afd99369a321bda95d1ee034f1835f6 Mon Sep 17 00:00:00 2001 From: Daniel Gredler Date: Thu, 12 Mar 2026 14:49:35 +0000 Subject: [PATCH 20/97] 8379778: SunGraphics2D improvements Reviewed-by: avu, prr --- .../classes/sun/java2d/SunGraphics2D.java | 153 +++--------------- 1 file changed, 25 insertions(+), 128 deletions(-) diff --git a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java index 9815d657eeed..891a15f24dec 100644 --- a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java +++ b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -250,11 +250,6 @@ public final class SunGraphics2D private FontInfo glyphVectorFontInfo; private FontRenderContext glyphVectorFRC; - private static final int slowTextTransformMask = - AffineTransform.TYPE_GENERAL_TRANSFORM - | AffineTransform.TYPE_MASK_ROTATION - | AffineTransform.TYPE_FLIP; - static { if (PerformanceLogger.loggingEnabled()) { PerformanceLogger.setTime("SunGraphics2D static initialization"); @@ -453,13 +448,13 @@ public void validatePipe() { * or whether that shape must be "kept" unmodified. */ Shape intersectShapes(Shape s1, Shape s2, boolean keep1, boolean keep2) { - if (s1 instanceof Rectangle && s2 instanceof Rectangle) { - return ((Rectangle) s1).intersection((Rectangle) s2); + if (s1 instanceof Rectangle r1 && s2 instanceof Rectangle r2) { + return r1.intersection(r2); } - if (s1 instanceof Rectangle2D) { - return intersectRectShape((Rectangle2D) s1, s2, keep1, keep2); - } else if (s2 instanceof Rectangle2D) { - return intersectRectShape((Rectangle2D) s2, s1, keep2, keep1); + if (s1 instanceof Rectangle2D r1) { + return intersectRectShape(r1, s2, keep1, keep2); + } else if (s2 instanceof Rectangle2D r2) { + return intersectRectShape(r2, s1, keep2, keep1); } return intersectByArea(s1, s2, keep1, keep2); } @@ -473,8 +468,7 @@ Shape intersectShapes(Shape s1, Shape s2, boolean keep1, boolean keep2) { */ Shape intersectRectShape(Rectangle2D r, Shape s, boolean keep1, boolean keep2) { - if (s instanceof Rectangle2D) { - Rectangle2D r2 = (Rectangle2D) s; + if (s instanceof Rectangle2D r2) { Rectangle2D outrect; if (!keep1) { outrect = r; @@ -596,12 +590,10 @@ public FontInfo checkFontInfo(FontInfo info, Font font, } float ptSize = font.getSize2D(); - int txFontType; AffineTransform devAt, textAt=null; if (font.isTransformed()) { textAt = font.getTransform(); textAt.scale(ptSize, ptSize); - txFontType = textAt.getType(); info.originX = (float)textAt.getTranslateX(); info.originY = (float)textAt.getTranslateY(); textAt.translate(-info.originX, -info.originY); @@ -621,7 +613,6 @@ public FontInfo checkFontInfo(FontInfo info, Font font, } info.pixelHeight = (int)(Math.abs(scaley)+0.5); } else { - txFontType = AffineTransform.TYPE_IDENTITY; info.originX = info.originY = 0; if (transformState >= TRANSFORM_TRANSLATESCALE) { transform.getMatrix(info.devTx = new double[4]); @@ -783,18 +774,6 @@ public FontInfo checkFontInfo(FontInfo info, Font font, return info; } - public static boolean isRotated(double [] mtx) { - if ((mtx[0] == mtx[3]) && - (mtx[1] == 0.0) && - (mtx[2] == 0.0) && - (mtx[0] > 0.0)) - { - return false; - } - - return true; - } - public void setFont(Font font) { /* replacing the reference equality test font != this.font with * !font.equals(this.font) did not yield any measurable difference @@ -944,8 +923,7 @@ public void setComposite(Composite comp) { } int newCompState; CompositeType newCompType; - if (comp instanceof AlphaComposite) { - AlphaComposite alphacomp = (AlphaComposite) comp; + if (comp instanceof AlphaComposite alphacomp) { newCompType = CompositeType.forAlphaComposite(alphacomp); if (newCompType == CompositeType.SrcOverNoEa) { if (paintState == PAINT_OPAQUECOLOR || @@ -1000,8 +978,8 @@ public void setComposite(Composite comp) { * @see TexturePaint */ public void setPaint(Paint paint) { - if (paint instanceof Color) { - setColor((Color) paint); + if (paint instanceof Color c) { + setColor(c); return; } if (paint == null || this.paint == paint) { @@ -1162,8 +1140,8 @@ public void setStroke(Stroke s) { } int saveStrokeState = strokeState; stroke = s; - if (s instanceof BasicStroke) { - validateBasicStroke((BasicStroke) s); + if (s instanceof BasicStroke bs) { + validateBasicStroke(bs); } else { strokeState = STROKE_CUSTOM; } @@ -1193,11 +1171,10 @@ public void setRenderingHint(Key hintKey, Object hintValue) { throw new IllegalArgumentException (hintValue+" is not compatible with "+hintKey); } - if (hintKey instanceof SunHints.Key) { + if (hintKey instanceof SunHints.Key sunKey) { boolean stateChanged; boolean textStateChanged = false; boolean recognized = true; - SunHints.Key sunKey = (SunHints.Key) hintKey; int newHint; if (sunKey == SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST) { newHint = ((Integer)hintValue).intValue(); @@ -1297,7 +1274,6 @@ public void setRenderingHint(Key hintKey, Object hintValue) { hints.put(hintKey, hintValue); } - /** * Returns the preferences for the rendering algorithms. * @param hintKey The category of hint to be set. The strings @@ -1310,10 +1286,10 @@ public Object getRenderingHint(Key hintKey) { if (hints != null) { return hints.get(hintKey); } - if (!(hintKey instanceof SunHints.Key)) { + if (!(hintKey instanceof SunHints.Key shk)) { return null; } - int keyindex = ((SunHints.Key)hintKey).getIndex(); + int keyindex = shk.getIndex(); switch (keyindex) { case SunHints.INTKEY_RENDERING: return SunHints.Value.get(SunHints.INTKEY_RENDERING, @@ -1822,8 +1798,8 @@ public Rectangle getClipBounds() { public Rectangle getClipBounds(Rectangle r) { if (clipState != CLIP_DEVICE) { if (transformState <= TRANSFORM_INT_TRANSLATE) { - if (usrClip instanceof Rectangle) { - r.setBounds((Rectangle) usrClip); + if (usrClip instanceof Rectangle usrClipRect) { + r.setBounds(usrClipRect); } else { r.setFrame(usrClip.getBounds2D()); } @@ -1970,8 +1946,7 @@ protected static Shape transformShape(int tx, int ty, Shape s) { r.translate(tx, ty); return r; } - if (s instanceof Rectangle2D) { - Rectangle2D rect = (Rectangle2D) s; + if (s instanceof Rectangle2D rect) { return new Rectangle2D.Double(rect.getX() + tx, rect.getY() + ty, rect.getWidth(), @@ -1991,10 +1966,9 @@ protected static Shape transformShape(AffineTransform tx, Shape clip) { return null; } - if (clip instanceof Rectangle2D && + if (clip instanceof Rectangle2D rect && (tx.getType() & NON_RECTILINEAR_TRANSFORM_MASK) == 0) { - Rectangle2D rect = (Rectangle2D) clip; double[] matrix = new double[4]; matrix[0] = rect.getX(); matrix[1] = rect.getY(); @@ -2180,65 +2154,6 @@ private void doCopyArea(int x, int y, int w, int h, int dx, int dy) { } } - /* - public void XcopyArea(int x, int y, int w, int h, int dx, int dy) { - Rectangle rect = new Rectangle(x, y, w, h); - rect = transformBounds(rect, transform); - Point2D point = new Point2D.Float(dx, dy); - Point2D root = new Point2D.Float(0, 0); - point = transform.transform(point, point); - root = transform.transform(root, root); - int fdx = (int)(point.getX()-root.getX()); - int fdy = (int)(point.getY()-root.getY()); - - Rectangle r = getCompBounds().intersection(rect.getBounds()); - - if (r.isEmpty()) { - return; - } - - // Begin Rasterizer for Clip Shape - boolean skipClip = true; - byte[] clipAlpha = null; - - if (clipState == CLIP_SHAPE) { - - int box[] = new int[4]; - - clipRegion.getBounds(box); - Rectangle devR = new Rectangle(box[0], box[1], - box[2] - box[0], - box[3] - box[1]); - if (!devR.isEmpty()) { - OutputManager mgr = getOutputManager(); - RegionIterator ri = clipRegion.getIterator(); - while (ri.nextYRange(box)) { - int spany = box[1]; - int spanh = box[3] - spany; - while (ri.nextXBand(box)) { - int spanx = box[0]; - int spanw = box[2] - spanx; - mgr.copyArea(this, null, - spanw, 0, - spanx, spany, - spanw, spanh, - fdx, fdy, - null); - } - } - } - return; - } - // End Rasterizer for Clip Shape - - getOutputManager().copyArea(this, null, - r.width, 0, - r.x, r.y, r.width, - r.height, fdx, fdy, - null); - } - */ - public void drawLine(int x1, int y1, int x2, int y2) { try { drawpipe.drawLine(this, x1, y1, x2, y2); @@ -2465,8 +2380,8 @@ private void revalidateAll() { if (paintState <= PAINT_ALPHACOLOR) { validateColor(); } - if (composite instanceof XORComposite) { - Color c = ((XORComposite) composite).getXorColor(); + if (composite instanceof XORComposite xorComp) { + Color c = xorComp.getXorColor(); setComposite(new XORComposite(c, surfaceData)); } validatePipe(); @@ -2668,8 +2583,7 @@ public void drawRenderedImage(RenderedImage img, } // BufferedImage case: use a simple drawImage call - if (img instanceof BufferedImage) { - BufferedImage bufImg = (BufferedImage)img; + if (img instanceof BufferedImage bufImg) { drawImage(bufImg,xform,null); return; } @@ -2905,21 +2819,6 @@ public void drawRenderableImage(RenderableImage img, drawRenderedImage(rendering,reverseTransform); } - - - /* - * Transform the bounding box of the BufferedImage - */ - protected Rectangle transformBounds(Rectangle rect, - AffineTransform tx) { - if (tx.isIdentity()) { - return rect; - } - - Shape s = transformShape(tx, rect); - return s.getBounds(); - } - // text rendering methods public void drawString(String str, int x, int y) { if (str == null) { @@ -3130,13 +3029,12 @@ private Boolean drawHiDPIImage(Image img, invalidateTransform(); } return result; - } else if (img instanceof MultiResolutionImage) { + } else if (img instanceof MultiResolutionImage mrImage) { // get scaled destination image size int width = img.getWidth(observer); int height = img.getHeight(observer); - MultiResolutionImage mrImage = (MultiResolutionImage) img; Image resolutionVariant = getResolutionVariant(mrImage, width, height, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, @@ -3311,8 +3209,7 @@ private Image getResolutionVariant(MultiResolutionImage img, Image resolutionVariant = img.getResolutionVariant(destImageWidth, destImageHeight); - if (resolutionVariant instanceof ToolkitImage - && ((ToolkitImage) resolutionVariant).hasError()) { + if (resolutionVariant instanceof ToolkitImage tki && tki.hasError()) { return null; } From ebcfd7d62342c4c9da4d418b9e679d520ab1eb1e Mon Sep 17 00:00:00 2001 From: Mikhail Yankelevich Date: Thu, 12 Mar 2026 17:33:52 +0000 Subject: [PATCH 21/97] 8378267: Test sun/security/util/HexDumpEncoderTests.java fails when unexpected warning appears Reviewed-by: weijun --- .../security/util/HexDumpEncoderTests.java | 53 ++++++++++++++----- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/test/jdk/sun/security/util/HexDumpEncoderTests.java b/test/jdk/sun/security/util/HexDumpEncoderTests.java index 9ff08cdb1921..53f9001ae7a9 100644 --- a/test/jdk/sun/security/util/HexDumpEncoderTests.java +++ b/test/jdk/sun/security/util/HexDumpEncoderTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,10 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; import java.util.Arrays; /* @@ -38,7 +42,6 @@ */ public class HexDumpEncoderTests { - private static String[] getTestCommand(final String encoding) { return new String[]{ "--add-modules", "java.base", @@ -54,24 +57,34 @@ public static void main(String[] args) throws Exception { final var resultIso = ProcessTools.executeTestJava(testCommandIso); resultIso.shouldHaveExitValue(0); + final String latin1ResultFromFile = Files.readString( + Path.of("ISO-8859-1.txt")); - // This will take all available StandardCharsets and test them all comparing to the ISO_8859_1 - // Dome im parallel, as this is significantly faster + // This will take all available StandardCharsets and test them all + // comparing to the ISO_8859_1. + // Done im parallel, as this is significantly faster Arrays.stream(StandardCharsets.class.getDeclaredFields()) .parallel() .forEach(field -> { - if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) { + if (java.lang.reflect.Modifier + .isStatic(field.getModifiers())) { try { - final var charset = (Charset) field.get(StandardCharsets.ISO_8859_1); // getting the charset to test + // getting the charset to test + final var charset = (Charset) field.get(null); - final var testCommand = getTestCommand(charset.name()); + final var testCommand = + getTestCommand(charset.name()); - final var result = ProcessTools.executeTestJava(testCommand); + final var result = + ProcessTools.executeTestJava(testCommand); result.shouldHaveExitValue(0); + final String resultFromFile = Files.readString( + Path.of(charset.name()+".txt")); - // The outputs of the ISO encoding must be identical to the one tested - Asserts.assertEquals(resultIso.getStdout(), - result.getStdout(), + // The outputs of the ISO encoding must be identical + // to the one tested + Asserts.assertEquals(latin1ResultFromFile, + resultFromFile, "Encoding " + charset.name()); } catch (Exception e) { throw new RuntimeException(e); @@ -86,14 +99,26 @@ public static class HexDumpEncoderTest { * This will test the encode and encode buffer functions at once, * as they are both representing the string in LATIN_1 *

- * The output is put as a system.out + * The output is put to the file in scratch dir with corresponding + * encoding name */ public static void main(String[] args) throws Exception { final var encoder = new HexDumpEncoder(); - System.out.printf("\nCert Encoded With Encode Buffer: %s\n", encoder.encodeBuffer(new byte[100])); - System.out.printf("\nCert Encoded With Encode: %s\n", encoder.encode(new byte[100])); + final String encodeBufferResult = + encoder.encodeBuffer(new byte[100]); + final String encodeResult = encoder.encode(new byte[100]); + + final String content = String.format(""" + Cert Encoded With Encode Buffer: %s + Cert Encoded With Encode: %s""", + encodeBufferResult, encodeResult); + Files.writeString( + Paths.get(Charset.defaultCharset().displayName() + ".txt"), + content, + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); } } } From a6f7089798238e2d0ba5ec168c19acf355403e3d Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Thu, 12 Mar 2026 17:58:06 +0000 Subject: [PATCH 22/97] 8379799: Upstream redundant diffs fixed in Valhalla - langtool tests 1 Reviewed-by: vromero --- .../combo/tools/javac/combo/CompilationTestCase.java | 6 +++++- .../lib/combo/tools/javac/combo/Diagnostics.java | 8 +++++++- .../tools/javac/combo/JavacTemplateTestBase.java | 8 ++++++++ .../BridgeShouldHaveNoInteriorAnnotationsTest.java | 4 ++-- test/langtools/tools/javac/diags/CheckExamples.java | 12 +++++++++++- .../tools/javac/launcher/SourceLauncherTest.java | 1 + .../tools/javac/patterns/BreakAndLoops.java | 4 ++-- .../tools/javac/preview/PreviewAutoSuppress.java | 8 +++++--- .../langtools/tools/javac/records/RecordReading.java | 3 +-- .../tools/javac/recovery/ClassBlockExits.java | 4 ++-- test/langtools/tools/javap/T4975569.java | 3 +-- 11 files changed, 45 insertions(+), 16 deletions(-) diff --git a/test/langtools/lib/combo/tools/javac/combo/CompilationTestCase.java b/test/langtools/lib/combo/tools/javac/combo/CompilationTestCase.java index 0760352e23b7..c3aa92d1f375 100644 --- a/test/langtools/lib/combo/tools/javac/combo/CompilationTestCase.java +++ b/test/langtools/lib/combo/tools/javac/combo/CompilationTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, 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 @@ -112,6 +112,10 @@ protected void assertOKWithWarning(String warning, String... constructs) { assertCompile(expandMarkers(constructs), () -> assertCompileSucceededWithWarning(warning), false); } + protected void assertOKWithWarning(String warning, int numberOfTimes, String... constructs) { + assertCompile(expandMarkers(constructs), () -> assertCompileSucceededWithWarning(warning, numberOfTimes), false); + } + protected void assertFail(String expectedDiag, String... constructs) { assertCompile(expandMarkers(constructs), () -> assertCompileFailed(expectedDiag), false); } diff --git a/test/langtools/lib/combo/tools/javac/combo/Diagnostics.java b/test/langtools/lib/combo/tools/javac/combo/Diagnostics.java index 47f496b58917..87ee54b375ae 100644 --- a/test/langtools/lib/combo/tools/javac/combo/Diagnostics.java +++ b/test/langtools/lib/combo/tools/javac/combo/Diagnostics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -84,6 +84,12 @@ public boolean containsWarningKey(String key) { .anyMatch(d -> d.getCode().equals(key)); } + public boolean containsWarningKey(String key, int numberOfWarnings) { + return diags.stream() + .filter(d -> d.getKind() == Diagnostic.Kind.WARNING || d.getKind() == Diagnostic.Kind.MANDATORY_WARNING) + .filter(d -> d.getCode().equals(key)).count() == numberOfWarnings; + } + /** Get the error keys */ public List errorKeys() { return diags.stream() diff --git a/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java b/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java index ed5936210a8d..3b4b67e7968e 100644 --- a/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java +++ b/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java @@ -154,6 +154,14 @@ protected void assertCompileSucceededWithWarning(String warning) { } } + protected void assertCompileSucceededWithWarning(String warning, int numberOfWarnings) { + if (diags.errorsFound()) + fail("Expected successful compilation"); + if (!diags.containsWarningKey(warning, numberOfWarnings)) { + fail(String.format("Expected compilation warning with %s, found %s", warning, diags.keys())); + } + } + /** * If the provided boolean is true, assert all previous compiles succeeded, * otherwise assert that a compile failed. diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/BridgeShouldHaveNoInteriorAnnotationsTest.java b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/BridgeShouldHaveNoInteriorAnnotationsTest.java index 0ec1a790d72e..3426bfedd301 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/BridgeShouldHaveNoInteriorAnnotationsTest.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/BridgeShouldHaveNoInteriorAnnotationsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -71,7 +71,7 @@ public void remove() { }; - // Expected output can't be directly encoded into NestedLambdasCastedTest !!! + // Expected output can't be directly encoded into BridgeShouldHaveNoInteriorAnnotationsTest !!! static class OutputExpectedOnceHolder { public String[] outputs = { "0: #120(): CAST, offset=1, type_index=0, location=[TYPE_ARGUMENT(0)]", diff --git a/test/langtools/tools/javac/diags/CheckExamples.java b/test/langtools/tools/javac/diags/CheckExamples.java index 502f5fa8b88a..7a188614080a 100644 --- a/test/langtools/tools/javac/diags/CheckExamples.java +++ b/test/langtools/tools/javac/diags/CheckExamples.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,12 +45,22 @@ /** * Check invariants for a set of examples. + * + * READ THIS IF THIS TEST FAILS AFTER ADDING A NEW KEY TO 'compiler.properties': + * The 'examples' subdirectory contains a number of examples which provoke + * the reporting of most of the compiler message keys. + * * -- each example should exactly declare the keys that will be generated when * it is run. + * -- this is done by the "// key:"-comment in each fine. * -- together, the examples should cover the set of resource keys in the * compiler.properties bundle. A list of exceptions may be given in the * not-yet.txt file. Entries on the not-yet.txt list should not be * covered by examples. + * -- some keys are only reported by the compiler when specific options are + * supplied. For the purposes of this test, this can be specified by a + * comment e.g. like this: "// options: -Xlint:empty" + * * When new keys are added to the resource bundle, it is strongly recommended * that corresponding new examples be added here, if at all practical, instead * of simply and lazily being added to the not-yet.txt list. diff --git a/test/langtools/tools/javac/launcher/SourceLauncherTest.java b/test/langtools/tools/javac/launcher/SourceLauncherTest.java index c3c8fb19a1b8..d0cf11a03270 100644 --- a/test/langtools/tools/javac/launcher/SourceLauncherTest.java +++ b/test/langtools/tools/javac/launcher/SourceLauncherTest.java @@ -670,6 +670,7 @@ public void testNoOptionsWarnings(Path base) throws IOException { tb.writeJavaFiles(base, "public class Main { public static void main(String... args) {}}"); String log = new JavaTask(tb) .vmOptions("--source", "21") + .includeStandardOptions(false) // Do not inherit --enable-preview .className(base.resolve("Main.java").toString()) .run(Task.Expect.SUCCESS) .getOutput(Task.OutputKind.STDERR); diff --git a/test/langtools/tools/javac/patterns/BreakAndLoops.java b/test/langtools/tools/javac/patterns/BreakAndLoops.java index 31ad8908f53a..777d5ae4f426 100644 --- a/test/langtools/tools/javac/patterns/BreakAndLoops.java +++ b/test/langtools/tools/javac/patterns/BreakAndLoops.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 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 @@ -237,4 +237,4 @@ public String expand(String optParameter) { return code; } } -} \ No newline at end of file +} diff --git a/test/langtools/tools/javac/preview/PreviewAutoSuppress.java b/test/langtools/tools/javac/preview/PreviewAutoSuppress.java index 058ccdf0a2a9..f2096aff648b 100644 --- a/test/langtools/tools/javac/preview/PreviewAutoSuppress.java +++ b/test/langtools/tools/javac/preview/PreviewAutoSuppress.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,6 +44,8 @@ import java.util.List; public class PreviewAutoSuppress extends TestRunner { + // Major version number (e.g. '27'). + private static final String FEATURE_VERSION = String.valueOf(Runtime.version().feature()); protected ToolBox tb; @@ -83,7 +85,7 @@ public class Use { List log = new JavacTask(tb, Task.Mode.CMDLINE) .outdir(classes) .options("--enable-preview", - "-source", String.valueOf(Runtime.version().feature()), + "-source", FEATURE_VERSION, "-Xlint:preview", "-XDforcePreview", "-XDrawDiagnostics") @@ -182,7 +184,7 @@ public static class C extends Outer {} "--add-exports", "java.base/preview.api=ALL-UNNAMED", "--enable-preview", "-Xlint:preview", - "-source", String.valueOf(Runtime.version().feature()), + "-source", FEATURE_VERSION, "-XDrawDiagnostics") .files(tb.findJavaFiles(testSrc)) .run() diff --git a/test/langtools/tools/javac/records/RecordReading.java b/test/langtools/tools/javac/records/RecordReading.java index 6133a04fca93..e57a7f77ceb2 100644 --- a/test/langtools/tools/javac/records/RecordReading.java +++ b/test/langtools/tools/javac/records/RecordReading.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 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 @@ -31,7 +31,6 @@ * @run main RecordReading */ - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; diff --git a/test/langtools/tools/javac/recovery/ClassBlockExits.java b/test/langtools/tools/javac/recovery/ClassBlockExits.java index b1c54b456aeb..3ac04cbe991c 100644 --- a/test/langtools/tools/javac/recovery/ClassBlockExits.java +++ b/test/langtools/tools/javac/recovery/ClassBlockExits.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 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 @@ -138,4 +138,4 @@ public String expand(String optParameter) { return code; } } -} \ No newline at end of file +} diff --git a/test/langtools/tools/javap/T4975569.java b/test/langtools/tools/javap/T4975569.java index c43598b41dbf..ce75ba845860 100644 --- a/test/langtools/tools/javap/T4975569.java +++ b/test/langtools/tools/javap/T4975569.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -105,4 +105,3 @@ static class S extends T4975569 { protected class Prot { } private class Priv { int i; } } - From 618a3feea9d46b38822a1d52b93ba2d12b34e000 Mon Sep 17 00:00:00 2001 From: Patricio Chilano Mateo Date: Thu, 12 Mar 2026 18:22:32 +0000 Subject: [PATCH 23/97] 8379795: Test SuspendResume3.java can timeout due to deadlock Reviewed-by: dholmes, sspitsyn --- .../SuspendResume3/SuspendResume3.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume3/SuspendResume3.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume3/SuspendResume3.java index fbc9ff2d5b3a..ae49ce29ed3a 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume3/SuspendResume3.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume3/SuspendResume3.java @@ -35,6 +35,11 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.Phaser; +import java.lang.management.LockInfo; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; + import jvmti.JVMTIUtils; public class SuspendResume3 { @@ -88,7 +93,12 @@ private void runTest() throws Exception { w1Sync1.arriveAndAwaitAdvance(); // Let worker2 block on monitor w2Sync1.arriveAndAwaitAdvance(); - await(worker2, Thread.State.BLOCKED); + // Wait until worker2 blocks trying to acquire lock. We can't just check + // for a BLOCKED state because method arriveAndAwaitAdvance might involve + // extra class loading/initialization where worker2 could be seen as BLOCKED + // and thus be suspended below. If the main thread then tries to access those + // same classes before resuming worker2 the test would deadlock. + awaitBlockedOnLock(worker2); // Suspend worker2 JVMTIUtils.suspendThread(worker2); @@ -144,6 +154,21 @@ private void await(Thread thread, Thread.State expectedState) throws Interrupted } } + /** + * Waits for the given thread to block trying to acquire lock's monitor. + */ + private void awaitBlockedOnLock(Thread thread) throws InterruptedException { + while (true) { + ThreadInfo threadInfo = ManagementFactory.getThreadMXBean().getThreadInfo(thread.threadId()); + assertTrue(threadInfo != null, "getThreadInfo() failed"); + LockInfo lockInfo = threadInfo.getLockInfo(); + if (lockInfo != null && lockInfo.getIdentityHashCode() == System.identityHashCode(lock)) { + break; + } + Thread.sleep(10); + } + } + private static void assertTrue(boolean condition, String msg) { if (!condition) { throw new RuntimeException(msg); From e25969121e7da25f648473ebf169538b92e6e6b5 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 12 Mar 2026 19:54:23 +0000 Subject: [PATCH 24/97] 8379937: NullActiveWindowOnFocusLost.java fails intermittently. Reviewed-by: azvegint --- .../NullActiveWindowOnFocusLost.java | 83 ------------------- 1 file changed, 83 deletions(-) delete mode 100644 test/jdk/java/awt/Focus/NullActiveWindowOnFocusLost/NullActiveWindowOnFocusLost.java diff --git a/test/jdk/java/awt/Focus/NullActiveWindowOnFocusLost/NullActiveWindowOnFocusLost.java b/test/jdk/java/awt/Focus/NullActiveWindowOnFocusLost/NullActiveWindowOnFocusLost.java deleted file mode 100644 index 0924413bec41..000000000000 --- a/test/jdk/java/awt/Focus/NullActiveWindowOnFocusLost/NullActiveWindowOnFocusLost.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.awt.Button; -import java.awt.Frame; -import java.util.concurrent.TimeUnit; - -import sun.awt.SunToolkit; - -/** - * @test - * @key headful - * @bug 8211435 - * @modules java.desktop/sun.awt - */ -public final class NullActiveWindowOnFocusLost { - - private static volatile long endtime; - private static Throwable failed; - - public static void main(final String[] args) throws Exception { - // Will run the test no more than 30 seconds - endtime = System.nanoTime() + TimeUnit.SECONDS.toNanos(30); - Thread.setDefaultUncaughtExceptionHandler((t, e) -> failed = e); - - final Thread[] threads = new Thread[20]; - for (int i = 0; i < threads.length; i++) { - threads[i] = testThread(i); - } - for (final Thread thread : threads) { - thread.start(); - } - for (final Thread thread : threads) { - thread.join(); - } - if (failed != null) { - failed.printStackTrace(); - throw new RuntimeException(failed); - } - } - - private static Thread testThread(int index) { - return new Thread(new ThreadGroup("TG " + index), () -> { - SunToolkit.createNewAppContext(); - while (!isComplete()) { - final Frame frame = new Frame(); - frame.setSize(300, 300); - frame.add(new Button("Button")); - frame.setLocationRelativeTo(null); - frame.setVisible(true); - try { - Thread.sleep(index); // increase probability of the failure - } catch (InterruptedException ignored) { - } - frame.dispose(); - } - }); - } - - private static boolean isComplete() { - return endtime - System.nanoTime() < 0 || failed != null; - } -} From d190c445c53537368b921af75494a1f38bf24f2c Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Thu, 12 Mar 2026 20:37:41 +0000 Subject: [PATCH 25/97] 8379558: Test java/util/ResourceBundle/modules/basic/BasicTest.java times out on windows-x64 in Xcomp mode Reviewed-by: iris --- .../java/util/ResourceBundle/modules/basic/BasicTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/jdk/java/util/ResourceBundle/modules/basic/BasicTest.java b/test/jdk/java/util/ResourceBundle/modules/basic/BasicTest.java index c17c4622ecbd..2dda740aaf9b 100644 --- a/test/jdk/java/util/ResourceBundle/modules/basic/BasicTest.java +++ b/test/jdk/java/util/ResourceBundle/modules/basic/BasicTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,7 +43,7 @@ * jdk.test.lib.compiler.CompilerUtils * jdk.test.lib.process.ProcessTools * ModuleTestUtil - * @run junit BasicTest + * @run junit/timeout=200 BasicTest */ import java.nio.file.Path; @@ -206,4 +206,4 @@ private static void jarModLocal(Path srcPath, Path jarPath) throws Throwable { assertEquals(exitCode, 0, "Create extra_modlocal.jar failed. " + "Unexpected exit code: " + exitCode); } -} \ No newline at end of file +} From 579a3dfc7617f0b06defe9d55f5ae3c92819b0ba Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Thu, 12 Mar 2026 22:43:12 +0000 Subject: [PATCH 26/97] 8379938: [macos] jpackage SigningPackageTest test doesn't create .pkg and .dmg bundles Reviewed-by: almatvee --- test/jdk/tools/jpackage/macosx/SigningPackageTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java index d1c17fb61cd1..cc2e7026d72a 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java @@ -65,7 +65,7 @@ * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=SigningPackageTest.test * --jpt-space-subst=* - * --jpt-include=({--mac-signing-key-user-name:*CODESIGN},*{--mac-signing-key-user-name:*PKG},*MAC_DMG+MAC_PKG) + * --jpt-include=({KEY_USER_NAME:*CODESIGN},*{KEY_USER_NAME:*PKG},*MAC_DMG+MAC_PKG) * --jpt-before-run=SigningBase.verifySignTestEnvReady */ From 30be9c1cb25e119f3abfe1ab0be824a3320f7287 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Thu, 12 Mar 2026 23:46:46 +0000 Subject: [PATCH 27/97] 8378885: Add missing @Override annotations in "javax.print.attribute.standard" package part 2 Reviewed-by: prr --- .../classes/javax/print/attribute/standard/Media.java | 5 ++++- .../javax/print/attribute/standard/MediaName.java | 4 +++- .../print/attribute/standard/MediaPrintableArea.java | 7 ++++++- .../javax/print/attribute/standard/MediaSize.java | 5 ++++- .../javax/print/attribute/standard/MediaSizeName.java | 4 +++- .../javax/print/attribute/standard/MediaTray.java | 4 +++- .../attribute/standard/MultipleDocumentHandling.java | 6 +++++- .../print/attribute/standard/NumberOfDocuments.java | 5 ++++- .../attribute/standard/NumberOfInterveningJobs.java | 5 ++++- .../javax/print/attribute/standard/NumberUp.java | 5 ++++- .../print/attribute/standard/NumberUpSupported.java | 5 ++++- .../print/attribute/standard/OrientationRequested.java | 7 ++++++- .../print/attribute/standard/OutputDeviceAssigned.java | 5 ++++- .../print/attribute/standard/PDLOverrideSupported.java | 6 +++++- .../javax/print/attribute/standard/PageRanges.java | 5 ++++- .../javax/print/attribute/standard/PagesPerMinute.java | 5 ++++- .../print/attribute/standard/PagesPerMinuteColor.java | 5 ++++- .../attribute/standard/PresentationDirection.java | 6 +++++- .../javax/print/attribute/standard/PrintQuality.java | 7 ++++++- .../javax/print/attribute/standard/PrinterInfo.java | 5 ++++- .../attribute/standard/PrinterIsAcceptingJobs.java | 6 +++++- .../print/attribute/standard/PrinterLocation.java | 5 ++++- .../print/attribute/standard/PrinterMakeAndModel.java | 5 ++++- .../attribute/standard/PrinterMessageFromOperator.java | 5 ++++- .../print/attribute/standard/PrinterMoreInfo.java | 5 ++++- .../standard/PrinterMoreInfoManufacturer.java | 5 ++++- .../javax/print/attribute/standard/PrinterName.java | 5 ++++- .../print/attribute/standard/PrinterResolution.java | 5 ++++- .../javax/print/attribute/standard/PrinterState.java | 6 +++++- .../print/attribute/standard/PrinterStateReason.java | 6 +++++- .../print/attribute/standard/PrinterStateReasons.java | 10 +++++++++- .../javax/print/attribute/standard/PrinterURI.java | 5 ++++- .../javax/print/attribute/standard/QueuedJobCount.java | 5 ++++- .../standard/ReferenceUriSchemesSupported.java | 6 +++++- .../print/attribute/standard/RequestingUserName.java | 5 ++++- .../javax/print/attribute/standard/Severity.java | 6 +++++- .../javax/print/attribute/standard/SheetCollate.java | 6 +++++- .../classes/javax/print/attribute/standard/Sides.java | 6 +++++- 38 files changed, 170 insertions(+), 38 deletions(-) diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Media.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Media.java index 6b62f1dbecdc..0cb9a4c30c59 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Media.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Media.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -89,6 +89,7 @@ protected Media(int value) { * @return {@code true} if {@code object} is equivalent to this media * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return object instanceof Media other && object.getClass() == this.getClass() && @@ -105,6 +106,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Media.class; } @@ -118,6 +120,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "media"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaName.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaName.java index 9c1b4060a34f..4adb28397596 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaName.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -107,6 +107,7 @@ protected MediaName(int value) { * * @return the string table */ + @Override protected String[] getStringTable() { return myStringTable.clone(); @@ -117,6 +118,7 @@ protected String[] getStringTable() * * @return the enumeration value table */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java index 72a58f2b6979..a9cc2bba1958 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaPrintableArea.java @@ -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 @@ -241,6 +241,7 @@ public float getHeight(int units) { * @return {@code true} if {@code object} is equivalent to this media * margins attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { boolean ret = false; if (object instanceof MediaPrintableArea) { @@ -262,6 +263,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return MediaPrintableArea.class; } @@ -277,6 +279,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "media-printable-area"; } @@ -304,6 +307,7 @@ public String toString(int units, String unitsName) { /** * Returns a string version of this rectangular size attribute in mm. */ + @Override public String toString() { return(toString(MM, "mm")); } @@ -311,6 +315,7 @@ public String toString() { /** * Returns a hash code value for this attribute. */ + @Override public int hashCode() { return x + 37*y + 43*w + 47*h; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java index 6a8db3b94c2e..57c0d3058097 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSize.java @@ -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 @@ -243,6 +243,7 @@ public static MediaSizeName findMedia(float x, float y, int units) { * @return {@code true} if {@code object} is equivalent to this media size * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof MediaSize); } @@ -257,6 +258,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return MediaSize.class; } @@ -270,6 +272,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "media-size"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSizeName.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSizeName.java index f9dde4956105..52b60b046bf1 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSizeName.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaSizeName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -608,6 +608,7 @@ protected MediaSizeName(int value) { /** * Returns the string table for class {@code MediaSizeName}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); @@ -616,6 +617,7 @@ protected String[] getStringTable() /** * Returns the enumeration value table for class {@code MediaSizeName}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaTray.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaTray.java index 138aae6248a7..bc20cbb8901a 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MediaTray.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MediaTray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -132,6 +132,7 @@ protected MediaTray(int value) { /** * Returns the string table for class {@code MediaTray}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); @@ -140,6 +141,7 @@ protected String[] getStringTable() /** * Returns the enumeration value table for class {@code MediaTray}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/MultipleDocumentHandling.java b/src/java.desktop/share/classes/javax/print/attribute/standard/MultipleDocumentHandling.java index 6d12597c88a5..4937f3796843 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/MultipleDocumentHandling.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/MultipleDocumentHandling.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -219,6 +219,7 @@ protected MultipleDocumentHandling(int value) { /** * Returns the string table for class {@code MultipleDocumentHandling}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -227,6 +228,7 @@ protected String[] getStringTable() { * Returns the enumeration value table for class * {@code MultipleDocumentHandling}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -242,6 +244,7 @@ protected EnumSyntax[] getEnumValueTable() { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return MultipleDocumentHandling.class; } @@ -255,6 +258,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "multiple-document-handling"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfDocuments.java b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfDocuments.java index 18b8144fb898..901907620f9b 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfDocuments.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfDocuments.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -77,6 +77,7 @@ public NumberOfDocuments(int value) { * @return {@code true} if {@code object} is equivalent to this number of * documents attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof NumberOfDocuments); @@ -92,6 +93,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return NumberOfDocuments.class; } @@ -105,6 +107,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "number-of-documents"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfInterveningJobs.java b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfInterveningJobs.java index 54cb4b85e7a5..480de3b1b47b 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfInterveningJobs.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberOfInterveningJobs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -78,6 +78,7 @@ public NumberOfInterveningJobs(int value) { * @return {@code true} if {@code object} is equivalent to this number of * intervening jobs attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof NumberOfInterveningJobs); @@ -93,6 +94,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return NumberOfInterveningJobs.class; } @@ -106,6 +108,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "number-of-intervening-jobs"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUp.java b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUp.java index b3aabbc6cfdd..0b019346c8e1 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUp.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -145,6 +145,7 @@ public NumberUp(int value) { * @return {@code true} if {@code object} is equivalent to this number up * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof NumberUp); } @@ -159,6 +160,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return NumberUp.class; } @@ -171,6 +173,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "number-up"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUpSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUpSupported.java index dfb79835e0fd..cef65ed17a37 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUpSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/NumberUpSupported.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -136,6 +136,7 @@ public NumberUpSupported(int lowerBound, int upperBound) { * @return {@code true} if {@code object} is equivalent to this number up * supported attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof NumberUpSupported); @@ -151,6 +152,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return NumberUpSupported.class; } @@ -164,6 +166,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "number-up-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java b/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java index 253adfe1ff18..00e3de4a1de9 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/OrientationRequested.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -150,6 +150,7 @@ protected OrientationRequested(int value) { /** * Returns the string table for class {@code OrientationRequested}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -158,6 +159,7 @@ protected String[] getStringTable() { * Returns the enumeration value table for class * {@code OrientationRequested}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -166,6 +168,7 @@ protected EnumSyntax[] getEnumValueTable() { * Returns the lowest integer value used by class * {@code OrientationRequested}. */ + @Override protected int getOffset() { return 3; } @@ -180,6 +183,7 @@ protected int getOffset() { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return OrientationRequested.class; } @@ -193,6 +197,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "orientation-requested"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/OutputDeviceAssigned.java b/src/java.desktop/share/classes/javax/print/attribute/standard/OutputDeviceAssigned.java index c94a105e92dc..bea5cd35060d 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/OutputDeviceAssigned.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/OutputDeviceAssigned.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -92,6 +92,7 @@ public OutputDeviceAssigned(String deviceName, Locale locale) { * @return {@code true} if {@code object} is equivalent to this output * device assigned attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof OutputDeviceAssigned); @@ -107,6 +108,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return OutputDeviceAssigned.class; } @@ -120,6 +122,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "output-device-assigned"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PDLOverrideSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PDLOverrideSupported.java index 46b9e8493d31..d9862ff633bb 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PDLOverrideSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PDLOverrideSupported.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -97,6 +97,7 @@ protected PDLOverrideSupported(int value) { /** * Returns the string table for class {@code PDLOverrideSupported}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -105,6 +106,7 @@ protected String[] getStringTable() { * Returns the enumeration value table for class * {@code PDLOverrideSupported}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -119,6 +121,7 @@ protected EnumSyntax[] getEnumValueTable() { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PDLOverrideSupported.class; } @@ -132,6 +135,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "pdl-override-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PageRanges.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PageRanges.java index 3ba0fa8e07e7..74325a3eb2ff 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PageRanges.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PageRanges.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -209,6 +209,7 @@ public PageRanges(int lowerBound, int upperBound) { * @return {@code true} if {@code object} is equivalent to this page ranges * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PageRanges); } @@ -223,6 +224,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PageRanges.class; } @@ -235,6 +237,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "page-ranges"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinute.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinute.java index 184339fb9788..4d05663d1db0 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinute.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinute.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -78,6 +78,7 @@ public PagesPerMinute(int value) { * @return {@code true} if {@code object} is equivalent to this pages per * minute attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof PagesPerMinute); @@ -93,6 +94,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PagesPerMinute.class; } @@ -106,6 +108,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "pages-per-minute"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinuteColor.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinuteColor.java index 32f15a4f1651..0dc3d0c9420e 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinuteColor.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PagesPerMinuteColor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -91,6 +91,7 @@ public PagesPerMinuteColor(int value) { * @return {@code true} if {@code object} is equivalent to this pages per * minute color attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PagesPerMinuteColor); @@ -106,6 +107,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PagesPerMinuteColor.class; } @@ -119,6 +121,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "pages-per-minute-color"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PresentationDirection.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PresentationDirection.java index d4ff8581a2d9..c40b6f3390e5 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PresentationDirection.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PresentationDirection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -157,6 +157,7 @@ private PresentationDirection(int value) { /** * Returns the string table for class {@code PresentationDirection}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -165,6 +166,7 @@ protected String[] getStringTable() { * Returns the enumeration value table for class * {@code PresentationDirection}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -179,6 +181,7 @@ protected EnumSyntax[] getEnumValueTable() { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PresentationDirection.class; } @@ -192,6 +195,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "presentation-direction"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrintQuality.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrintQuality.java index d00637fe23c5..2e64522da7cd 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrintQuality.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrintQuality.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -100,6 +100,7 @@ protected PrintQuality(int value) { /** * Returns the string table for class {@code PrintQuality}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -107,6 +108,7 @@ protected String[] getStringTable() { /** * Returns the enumeration value table for class {@code PrintQuality}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -114,6 +116,7 @@ protected EnumSyntax[] getEnumValueTable() { /** * Returns the lowest integer value used by class {@code PrintQuality}. */ + @Override protected int getOffset() { return 3; } @@ -128,6 +131,7 @@ protected int getOffset() { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrintQuality.class; } @@ -141,6 +145,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "print-quality"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterInfo.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterInfo.java index 4d8d2cf06014..39b8759d34d2 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterInfo.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -87,6 +87,7 @@ public PrinterInfo(String info, Locale locale) { * @return {@code true} if {@code object} is equivalent to this printer info * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterInfo); } @@ -101,6 +102,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterInfo.class; } @@ -114,6 +116,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "printer-info"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterIsAcceptingJobs.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterIsAcceptingJobs.java index 92e0d1fcfcd5..f8a99d4db46a 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterIsAcceptingJobs.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterIsAcceptingJobs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -100,6 +100,7 @@ protected PrinterIsAcceptingJobs(int value) { /** * Returns the string table for class {@code PrinterIsAcceptingJobs}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -108,6 +109,7 @@ protected String[] getStringTable() { * Returns the enumeration value table for class * {@code PrinterIsAcceptingJobs}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -122,6 +124,7 @@ protected EnumSyntax[] getEnumValueTable() { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterIsAcceptingJobs.class; } @@ -135,6 +138,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "printer-is-accepting-jobs"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterLocation.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterLocation.java index a0cfed406ce7..c23112c79d24 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterLocation.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterLocation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -83,6 +83,7 @@ public PrinterLocation(String location, Locale locale) { * @return {@code true} if {@code object} is equivalent to this printer * location attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterLocation); } @@ -97,6 +98,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterLocation.class; } @@ -110,6 +112,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "printer-location"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMakeAndModel.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMakeAndModel.java index b5fe4a6ee958..eff34c8bd59a 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMakeAndModel.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMakeAndModel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -82,6 +82,7 @@ public PrinterMakeAndModel(String makeAndModel, Locale locale) { * @return {@code true} if {@code object} is equivalent to this printer make * and model attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterMakeAndModel); @@ -97,6 +98,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterMakeAndModel.class; } @@ -110,6 +112,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "printer-make-and-model"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMessageFromOperator.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMessageFromOperator.java index 9ce0a098b805..e125d4955b36 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMessageFromOperator.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMessageFromOperator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -95,6 +95,7 @@ public PrinterMessageFromOperator(String message, Locale locale) { * @return {@code true} if {@code object} is equivalent to this printer * message from operator attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterMessageFromOperator); @@ -110,6 +111,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterMessageFromOperator.class; } @@ -123,6 +125,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "printer-message-from-operator"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfo.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfo.java index 501ffdb6cfb9..7e94507cf8ce 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfo.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -88,6 +88,7 @@ public PrinterMoreInfo(URI uri) { * @return {@code true} if {@code object} is equivalent to this printer more * info attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterMoreInfo); @@ -103,6 +104,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterMoreInfo.class; } @@ -116,6 +118,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "printer-more-info"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfoManufacturer.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfoManufacturer.java index b6f8b92fc837..be6952fa00bb 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfoManufacturer.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterMoreInfoManufacturer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -88,6 +88,7 @@ public PrinterMoreInfoManufacturer(URI uri) { * @return {@code true} if {@code object} is equivalent to this printer more * info manufacturer attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterMoreInfoManufacturer); @@ -103,6 +104,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterMoreInfoManufacturer.class; } @@ -116,6 +118,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "printer-more-info-manufacturer"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterName.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterName.java index 9a066c5d0051..019ef3c89112 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterName.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -85,6 +85,7 @@ public PrinterName(String printerName, Locale locale) { * @return {@code true} if {@code object} is equivalent to this printer name * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterName); } @@ -99,6 +100,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterName.class; } @@ -112,6 +114,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "printer-name"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterResolution.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterResolution.java index 3bba7feb7512..a47d57209cd5 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterResolution.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterResolution.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -108,6 +108,7 @@ public PrinterResolution(int crossFeedResolution, int feedResolution, * @return {@code true} if {@code object} is equivalent to this printer * resolution attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof PrinterResolution); @@ -123,6 +124,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterResolution.class; } @@ -136,6 +138,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "printer-resolution"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterState.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterState.java index 9cbb3da9473c..f2c921360a4a 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterState.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -114,6 +114,7 @@ protected PrinterState(int value) { /** * Returns the string table for class {@code PrinterState}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -121,6 +122,7 @@ protected String[] getStringTable() { /** * Returns the enumeration value table for class {@code PrinterState}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -135,6 +137,7 @@ protected EnumSyntax[] getEnumValueTable() { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterState.class; } @@ -148,6 +151,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "printer-state"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReason.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReason.java index 5c3b98ee127c..8586713ed657 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReason.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReason.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -403,6 +403,7 @@ protected PrinterStateReason(int value) { /** * Returns the string table for class {@code PrinterStateReason}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -410,6 +411,7 @@ protected String[] getStringTable() { /** * Returns the enumeration value table for class {@code PrinterStateReason}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -424,6 +426,7 @@ protected EnumSyntax[] getEnumValueTable() { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterStateReason.class; } @@ -437,6 +440,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "printer-state-reason"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReasons.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReasons.java index 8e46b1456512..fca59ca4bba0 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReasons.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReasons.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -165,6 +165,7 @@ public PrinterStateReasons(Map map) { * {@link Severity Severity} * @since 1.5 */ + @Override public Severity put(PrinterStateReason reason, Severity severity) { if (reason == null) { throw new NullPointerException("reason is null"); @@ -185,6 +186,7 @@ public Severity put(PrinterStateReason reason, Severity severity) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterStateReasons.class; } @@ -198,6 +200,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "printer-state-reasons"; } @@ -240,6 +243,7 @@ public PrinterStateReasonSet(Severity severity, myEntrySet = entrySet; } + @Override public int size() { int result = 0; for (PrinterStateReason ignored : this) { @@ -248,6 +252,7 @@ public int size() { return result; } + @Override public Iterator iterator() { return new PrinterStateReasonSetIterator(mySeverity, myEntrySet.iterator()); @@ -276,10 +281,12 @@ private void goToNext() { } } + @Override public boolean hasNext() { return myEntry != null; } + @Override public PrinterStateReason next() { if (myEntry == null) { throw new NoSuchElementException(); @@ -289,6 +296,7 @@ public PrinterStateReason next() { return result; } + @Override public void remove() { throw new UnsupportedOperationException(); } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterURI.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterURI.java index dfe0bc5cc1f4..d6934aae1725 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterURI.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterURI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -79,6 +79,7 @@ public PrinterURI(URI uri) { * @return {@code true} if {@code object} is equivalent to this * {@code PrinterURI} attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof PrinterURI); } @@ -93,6 +94,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return PrinterURI.class; } @@ -106,6 +108,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "printer-uri"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/QueuedJobCount.java b/src/java.desktop/share/classes/javax/print/attribute/standard/QueuedJobCount.java index c56eeb35a83b..e44ead9b7cfb 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/QueuedJobCount.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/QueuedJobCount.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -76,6 +76,7 @@ public QueuedJobCount(int value) { * @return {@code true} if {@code object} is equivalent to this queued job * count attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof QueuedJobCount); @@ -91,6 +92,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return QueuedJobCount.class; } @@ -104,6 +106,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "queued-job-count"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/ReferenceUriSchemesSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/ReferenceUriSchemesSupported.java index 9784742030bc..426fe4db3c4a 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/ReferenceUriSchemesSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/ReferenceUriSchemesSupported.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -154,6 +154,7 @@ protected ReferenceUriSchemesSupported(int value) { /** * Returns the string table for class {@code ReferenceUriSchemesSupported}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -162,6 +163,7 @@ protected String[] getStringTable() { * Returns the enumeration value table for class * {@code ReferenceUriSchemesSupported}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -177,6 +179,7 @@ protected EnumSyntax[] getEnumValueTable() { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return ReferenceUriSchemesSupported.class; } @@ -191,6 +194,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "reference-uri-schemes-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/RequestingUserName.java b/src/java.desktop/share/classes/javax/print/attribute/standard/RequestingUserName.java index 1d233eb76026..c2efb2da1d64 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/RequestingUserName.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/RequestingUserName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -92,6 +92,7 @@ public RequestingUserName(String userName, Locale locale) { * @return {@code true} if {@code object} is equivalent to this requesting * user name attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof RequestingUserName); @@ -107,6 +108,7 @@ public boolean equals(Object object) { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return RequestingUserName.class; } @@ -120,6 +122,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "requesting-user-name"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Severity.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Severity.java index 7543d4503a94..c217f12292f4 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Severity.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Severity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -124,6 +124,7 @@ protected Severity(int value) { /** * Returns the string table for class {@code Severity}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -131,6 +132,7 @@ protected String[] getStringTable() { /** * Returns the enumeration value table for class {@code Severity}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -145,6 +147,7 @@ protected EnumSyntax[] getEnumValueTable() { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Severity.class; } @@ -157,6 +160,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "severity"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/SheetCollate.java b/src/java.desktop/share/classes/javax/print/attribute/standard/SheetCollate.java index 45ec5f5374b0..1754a27c86f7 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/SheetCollate.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/SheetCollate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -191,6 +191,7 @@ protected SheetCollate(int value) { /** * Returns the string table for class {@code SheetCollate}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -198,6 +199,7 @@ protected String[] getStringTable() { /** * Returns the enumeration value table for class {@code SheetCollate}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -212,6 +214,7 @@ protected EnumSyntax[] getEnumValueTable() { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return SheetCollate.class; } @@ -225,6 +228,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "sheet-collate"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Sides.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Sides.java index 86d421045b03..0321cd876de2 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Sides.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Sides.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -183,6 +183,7 @@ protected Sides(int value) { /** * Returns the string table for class {@code Sides}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -190,6 +191,7 @@ protected String[] getStringTable() { /** * Returns the enumeration value table for class {@code Sides}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -203,6 +205,7 @@ protected EnumSyntax[] getEnumValueTable() { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Sides.class; } @@ -215,6 +218,7 @@ public final Class getCategory() { * * @return attribute category name */ + @Override public final String getName() { return "sides"; } From 6ff9545cbf5d4f2ce4983ff26469a7913d8ff8bb Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Fri, 13 Mar 2026 01:19:08 +0000 Subject: [PATCH 28/97] 8379027: Convert utilities/exceptions to use Atomic Reviewed-by: dholmes, fbredberg --- .../linux_arm/atomicAccess_linux_arm.hpp | 4 +- .../linux_arm/orderAccess_linux_arm.hpp | 3 +- .../linux_s390/atomicAccess_linux_s390.hpp | 6 +-- .../atomicAccess_windows_aarch64.hpp | 6 +-- .../windows_x86/atomicAccess_windows_x86.hpp | 4 +- .../share/interpreter/interpreterRuntime.cpp | 6 +-- src/hotspot/share/runtime/sharedRuntime.cpp | 3 +- src/hotspot/share/utilities/exceptions.cpp | 54 +++++++++++-------- src/hotspot/share/utilities/exceptions.hpp | 22 ++++---- 9 files changed, 56 insertions(+), 52 deletions(-) diff --git a/src/hotspot/os_cpu/linux_arm/atomicAccess_linux_arm.hpp b/src/hotspot/os_cpu/linux_arm/atomicAccess_linux_arm.hpp index 390207f9e5e2..c03f5ed1c8b3 100644 --- a/src/hotspot/os_cpu/linux_arm/atomicAccess_linux_arm.hpp +++ b/src/hotspot/os_cpu/linux_arm/atomicAccess_linux_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,8 +26,6 @@ #define OS_CPU_LINUX_ARM_ATOMICACCESS_LINUX_ARM_HPP #include "memory/allStatic.hpp" -#include "runtime/os.hpp" -#include "runtime/vm_version.hpp" // Implementation of class AtomicAccess diff --git a/src/hotspot/os_cpu/linux_arm/orderAccess_linux_arm.hpp b/src/hotspot/os_cpu/linux_arm/orderAccess_linux_arm.hpp index 3bb357704fbc..49c6942b8e0f 100644 --- a/src/hotspot/os_cpu/linux_arm/orderAccess_linux_arm.hpp +++ b/src/hotspot/os_cpu/linux_arm/orderAccess_linux_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ // Included in orderAccess.hpp header file. -#include "runtime/os.hpp" #include "runtime/vm_version.hpp" // Implementation of class OrderAccess. diff --git a/src/hotspot/os_cpu/linux_s390/atomicAccess_linux_s390.hpp b/src/hotspot/os_cpu/linux_s390/atomicAccess_linux_s390.hpp index f3c1e8f1a2ca..492ccf73bdfc 100644 --- a/src/hotspot/os_cpu/linux_s390/atomicAccess_linux_s390.hpp +++ b/src/hotspot/os_cpu/linux_s390/atomicAccess_linux_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,10 +26,6 @@ #ifndef OS_CPU_LINUX_S390_ATOMICACCESS_LINUX_S390_HPP #define OS_CPU_LINUX_S390_ATOMICACCESS_LINUX_S390_HPP -#include "runtime/atomicAccess.hpp" -#include "runtime/os.hpp" -#include "runtime/vm_version.hpp" - // Note that the compare-and-swap instructions on System z perform // a serialization function before the storage operand is fetched // and again after the operation is completed. diff --git a/src/hotspot/os_cpu/windows_aarch64/atomicAccess_windows_aarch64.hpp b/src/hotspot/os_cpu/windows_aarch64/atomicAccess_windows_aarch64.hpp index f8119654c50b..9238043f7a41 100644 --- a/src/hotspot/os_cpu/windows_aarch64/atomicAccess_windows_aarch64.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/atomicAccess_windows_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Microsoft Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,9 +27,7 @@ #define OS_CPU_WINDOWS_AARCH64_ATOMICACCESS_WINDOWS_AARCH64_HPP #include -#include "runtime/os.hpp" -#include "runtime/vm_version.hpp" - +#include // As per atomicAccess.hpp all read-modify-write operations have to provide two-way // barriers semantics. The memory_order parameter is ignored - we always provide diff --git a/src/hotspot/os_cpu/windows_x86/atomicAccess_windows_x86.hpp b/src/hotspot/os_cpu/windows_x86/atomicAccess_windows_x86.hpp index aa78a401235e..252411f62bc6 100644 --- a/src/hotspot/os_cpu/windows_x86/atomicAccess_windows_x86.hpp +++ b/src/hotspot/os_cpu/windows_x86/atomicAccess_windows_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ #define OS_CPU_WINDOWS_X86_ATOMICACCESS_WINDOWS_X86_HPP #include -#include "runtime/os.hpp" +#include // Note that in MSVC, volatile memory accesses are explicitly // guaranteed to have acquire release semantics (w.r.t. compiler diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index e3cf5d589c29..59d5f29023ca 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -55,7 +55,6 @@ #include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "prims/nativeLookup.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/continuation.hpp" #include "runtime/deoptimization.hpp" #include "runtime/fieldDescriptor.inline.hpp" @@ -75,6 +74,7 @@ #include "utilities/checkedCast.hpp" #include "utilities/copy.hpp" #include "utilities/events.hpp" +#include "utilities/exceptions.hpp" #if INCLUDE_JFR #include "jfr/jfr.inline.hpp" #endif @@ -352,7 +352,7 @@ JRT_ENTRY(void, InterpreterRuntime::throw_StackOverflowError(JavaThread* current vmClasses::StackOverflowError_klass(), CHECK); // Increment counter for hs_err file reporting - AtomicAccess::inc(&Exceptions::_stack_overflow_errors); + Exceptions::increment_stack_overflow_errors(); // Remove the ScopedValue bindings in case we got a StackOverflowError // while we were trying to manipulate ScopedValue bindings. current->clear_scopedValueBindings(); @@ -366,7 +366,7 @@ JRT_ENTRY(void, InterpreterRuntime::throw_delayed_StackOverflowError(JavaThread* java_lang_Throwable::set_message(exception(), Universe::delayed_stack_overflow_error_message()); // Increment counter for hs_err file reporting - AtomicAccess::inc(&Exceptions::_stack_overflow_errors); + Exceptions::increment_stack_overflow_errors(); // Remove the ScopedValue bindings in case we got a StackOverflowError // while we were trying to manipulate ScopedValue bindings. current->clear_scopedValueBindings(); diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index ae3835dd3448..917ff5b4545e 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -82,6 +82,7 @@ #include "utilities/copy.hpp" #include "utilities/dtrace.hpp" #include "utilities/events.hpp" +#include "utilities/exceptions.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/hashTable.hpp" #include "utilities/macros.hpp" @@ -931,7 +932,7 @@ void SharedRuntime::throw_StackOverflowError_common(JavaThread* current, bool de // bindings. current->clear_scopedValueBindings(); // Increment counter for hs_err file reporting - AtomicAccess::inc(&Exceptions::_stack_overflow_errors); + Exceptions::increment_stack_overflow_errors(); throw_and_post_jvmti_exception(current, exception); } diff --git a/src/hotspot/share/utilities/exceptions.cpp b/src/hotspot/share/utilities/exceptions.cpp index b54474ea6d6d..4455ab801cba 100644 --- a/src/hotspot/share/utilities/exceptions.cpp +++ b/src/hotspot/share/utilities/exceptions.cpp @@ -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 @@ -32,7 +32,6 @@ #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/handles.inline.hpp" #include "runtime/init.hpp" #include "runtime/java.hpp" @@ -203,7 +202,7 @@ void Exceptions::_throw(JavaThread* thread, const char* file, int line, Handle h } if (h_exception->is_a(vmClasses::LinkageError_klass())) { - AtomicAccess::inc(&_linkage_errors, memory_order_relaxed); + _linkage_errors.add_then_fetch(1, memory_order_relaxed); } assert(h_exception->is_a(vmClasses::Throwable_klass()), "exception is not a subclass of java/lang/Throwable"); @@ -268,6 +267,10 @@ void Exceptions::_throw_cause(JavaThread* thread, const char* file, int line, Sy } +void Exceptions::increment_stack_overflow_errors() { + Exceptions::_stack_overflow_errors.add_then_fetch(1, memory_order_relaxed); +} + void Exceptions::throw_stack_overflow_exception(JavaThread* THREAD, const char* file, int line, const methodHandle& method) { Handle exception; if (!THREAD->has_pending_exception()) { @@ -279,7 +282,7 @@ void Exceptions::throw_stack_overflow_exception(JavaThread* THREAD, const char* java_lang_Throwable::fill_in_stack_trace(exception, method); } // Increment counter for hs_err file reporting - AtomicAccess::inc(&Exceptions::_stack_overflow_errors, memory_order_relaxed); + increment_stack_overflow_errors(); } else { // if prior exception, throw that one instead exception = Handle(THREAD, THREAD->pending_exception()); @@ -518,20 +521,20 @@ void Exceptions::wrap_dynamic_exception(bool is_indy, JavaThread* THREAD) { } // Exception counting for hs_err file -volatile int Exceptions::_stack_overflow_errors = 0; -volatile int Exceptions::_linkage_errors = 0; -volatile int Exceptions::_out_of_memory_error_java_heap_errors = 0; -volatile int Exceptions::_out_of_memory_error_metaspace_errors = 0; -volatile int Exceptions::_out_of_memory_error_class_metaspace_errors = 0; +Atomic Exceptions::_stack_overflow_errors{0}; +Atomic Exceptions::_linkage_errors{0}; +Atomic Exceptions::_out_of_memory_error_java_heap_errors{0}; +Atomic Exceptions::_out_of_memory_error_metaspace_errors{0}; +Atomic Exceptions::_out_of_memory_error_class_metaspace_errors{0}; void Exceptions::count_out_of_memory_exceptions(Handle exception) { if (Universe::is_out_of_memory_error_metaspace(exception())) { - AtomicAccess::inc(&_out_of_memory_error_metaspace_errors, memory_order_relaxed); + _out_of_memory_error_metaspace_errors.add_then_fetch(1, memory_order_relaxed); } else if (Universe::is_out_of_memory_error_class_metaspace(exception())) { - AtomicAccess::inc(&_out_of_memory_error_class_metaspace_errors, memory_order_relaxed); + _out_of_memory_error_class_metaspace_errors.add_then_fetch(1, memory_order_relaxed); } else { - // everything else reported as java heap OOM - AtomicAccess::inc(&_out_of_memory_error_java_heap_errors, memory_order_relaxed); + // everything else reported as java heap OOM + _out_of_memory_error_java_heap_errors.add_then_fetch(1, memory_order_relaxed); } } @@ -542,19 +545,24 @@ static void print_oom_count(outputStream* st, const char *err, int count) { } bool Exceptions::has_exception_counts() { - return (_stack_overflow_errors + _out_of_memory_error_java_heap_errors + - _out_of_memory_error_metaspace_errors + _out_of_memory_error_class_metaspace_errors) > 0; + return (_stack_overflow_errors.load_relaxed() + + _out_of_memory_error_java_heap_errors.load_relaxed() + + _out_of_memory_error_metaspace_errors.load_relaxed() + + _out_of_memory_error_class_metaspace_errors.load_relaxed()) > 0; } void Exceptions::print_exception_counts_on_error(outputStream* st) { - print_oom_count(st, "java_heap_errors", _out_of_memory_error_java_heap_errors); - print_oom_count(st, "metaspace_errors", _out_of_memory_error_metaspace_errors); - print_oom_count(st, "class_metaspace_errors", _out_of_memory_error_class_metaspace_errors); - if (_stack_overflow_errors > 0) { - st->print_cr("StackOverflowErrors=%d", _stack_overflow_errors); - } - if (_linkage_errors > 0) { - st->print_cr("LinkageErrors=%d", _linkage_errors); + print_oom_count(st, "java_heap_errors", + _out_of_memory_error_java_heap_errors.load_relaxed()); + print_oom_count(st, "metaspace_errors", + _out_of_memory_error_metaspace_errors.load_relaxed()); + print_oom_count(st, "class_metaspace_errors", + _out_of_memory_error_class_metaspace_errors.load_relaxed()); + if (_stack_overflow_errors.load_relaxed() > 0) { + st->print_cr("StackOverflowErrors=%d", _stack_overflow_errors.load_relaxed()); + } + if (_linkage_errors.load_relaxed() > 0) { + st->print_cr("LinkageErrors=%d", _linkage_errors.load_relaxed()); } } diff --git a/src/hotspot/share/utilities/exceptions.hpp b/src/hotspot/share/utilities/exceptions.hpp index e76a0041d200..0299dedf2e69 100644 --- a/src/hotspot/share/utilities/exceptions.hpp +++ b/src/hotspot/share/utilities/exceptions.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "memory/allocation.hpp" #include "oops/oopsHierarchy.hpp" +#include "runtime/atomic.hpp" #include "utilities/ostream.hpp" #include "utilities/sizes.hpp" @@ -113,12 +114,16 @@ class Exceptions { static bool special_exception(JavaThread* thread, const char* file, int line, Handle exception, Symbol* name = nullptr, const char* message = nullptr); // Count out of memory errors that are interesting in error diagnosis - static volatile int _out_of_memory_error_java_heap_errors; - static volatile int _out_of_memory_error_metaspace_errors; - static volatile int _out_of_memory_error_class_metaspace_errors; + static Atomic _out_of_memory_error_java_heap_errors; + static Atomic _out_of_memory_error_metaspace_errors; + static Atomic _out_of_memory_error_class_metaspace_errors; // Count linkage errors - static volatile int _linkage_errors; + static Atomic _linkage_errors; + + // Count stack overflow errors. + static Atomic _stack_overflow_errors; + public: // this enum is defined to indicate whether it is safe to // ignore the encoding scheme of the original message string. @@ -179,10 +184,9 @@ class Exceptions { static void wrap_dynamic_exception(bool is_indy, JavaThread* thread); - // Exception counting for error files of interesting exceptions that may have - // caused a problem for the jvm - static volatile int _stack_overflow_errors; - + // Exception counting of interesting exceptions that may have caused a + // problem for the JVM, for reporting in the hs_err file. + static void increment_stack_overflow_errors(); static bool has_exception_counts(); static void count_out_of_memory_exceptions(Handle exception); static void print_exception_counts_on_error(outputStream* st); From c0f4b264079b497cadb47af92b6eca6e9ac4f625 Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Fri, 13 Mar 2026 01:30:49 +0000 Subject: [PATCH 29/97] 8379710: Enable vector if-conversion IR matching tests for RISC-V Reviewed-by: fyang --- .../compiler/vectorization/TestVectorAlgorithms.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java b/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java index f11955bef863..2667ac594718 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java @@ -556,7 +556,7 @@ public Object filterI_VectorAPI_v1(int[] a, int[] r, int threshold) { IRNode.VECTOR_MASK_CMP, "> 0", IRNode.VECTOR_TEST, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfCPUFeatureOr = {"asimd", "true"}) + applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public Object filterI_VectorAPI_v2_l2(int[] a, int[] r, int threshold) { return VectorAlgorithmsImpl.filterI_VectorAPI_v2_l2(a, r, threshold); } @@ -566,7 +566,7 @@ public Object filterI_VectorAPI_v2_l2(int[] a, int[] r, int threshold) { IRNode.VECTOR_MASK_CMP, "> 0", IRNode.VECTOR_TEST, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) public Object filterI_VectorAPI_v2_l4(int[] a, int[] r, int threshold) { return VectorAlgorithmsImpl.filterI_VectorAPI_v2_l4(a, r, threshold); } @@ -647,7 +647,7 @@ public Object conditionalSumB_VectorAPI_v1(byte[] a) { @Test @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", IRNode.ADD_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) public Object conditionalSumB_VectorAPI_v2(byte[] a) { return VectorAlgorithmsImpl.conditionalSumB_VectorAPI_v2(a); } @@ -663,7 +663,7 @@ public Object pieceWise2FunctionF_loop(float[] a, float[] r) { @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", IRNode.MUL_VF, "> 0", IRNode.SQRT_VF, "> 0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public Object pieceWise2FunctionF_VectorAPI_v1(float[] a, float[] r) { return VectorAlgorithmsImpl.pieceWise2FunctionF_VectorAPI_v1(a, r); } @@ -672,7 +672,7 @@ public Object pieceWise2FunctionF_VectorAPI_v1(float[] a, float[] r) { @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", IRNode.MUL_VF, "> 0", IRNode.SQRT_VF, "> 0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public Object pieceWise2FunctionF_VectorAPI_v2(float[] a, float[] r) { return VectorAlgorithmsImpl.pieceWise2FunctionF_VectorAPI_v2(a, r); } From baf29eb3e51c7d0e4b391f97e26ef66508764318 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Fri, 13 Mar 2026 02:39:21 +0000 Subject: [PATCH 30/97] 8379024: Convert utilities/vmError to use Atomic Reviewed-by: dholmes, fbredberg --- src/hotspot/share/utilities/vmError.cpp | 76 ++++++++++++------------- src/hotspot/share/utilities/vmError.hpp | 19 ++++--- 2 files changed, 47 insertions(+), 48 deletions(-) diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 6088eafbda48..e79c9cb694e5 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -43,7 +43,6 @@ #include "oops/compressedOops.hpp" #include "prims/whitebox.hpp" #include "runtime/arguments.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/flags/jvmFlag.hpp" #include "runtime/frame.inline.hpp" #include "runtime/init.hpp" @@ -86,12 +85,12 @@ bool VMError::coredump_status; char VMError::coredump_message[O_BUFLEN]; int VMError::_current_step; const char* VMError::_current_step_info; -volatile jlong VMError::_reporting_start_time = -1; -volatile bool VMError::_reporting_did_timeout = false; -volatile jlong VMError::_step_start_time = -1; -volatile bool VMError::_step_did_timeout = false; -volatile bool VMError::_step_did_succeed = false; -volatile intptr_t VMError::_first_error_tid = -1; +Atomic VMError::_reporting_start_time{-1}; +Atomic VMError::_reporting_did_timeout{false}; +Atomic VMError::_step_start_time{-1}; +Atomic VMError::_step_did_timeout{false}; +Atomic VMError::_step_did_succeed{false}; +Atomic VMError::_first_error_tid{-1}; int VMError::_id; const char* VMError::_message; char VMError::_detail_msg[1024]; @@ -105,8 +104,8 @@ int VMError::_lineno; size_t VMError::_size; const size_t VMError::_reattempt_required_stack_headroom = 64 * K; const intptr_t VMError::segfault_address = pd_segfault_address; -Thread* volatile VMError::_handshake_timed_out_thread = nullptr; -Thread* volatile VMError::_safepoint_timed_out_thread = nullptr; +Atomic VMError::_handshake_timed_out_thread{}; +Atomic VMError::_safepoint_timed_out_thread{}; // List of environment variables that should be reported in error log file. static const char* env_list[] = { @@ -248,7 +247,7 @@ bool VMError::can_reattempt_step(const char* &stop_reason) { return false; } - if (_step_did_timeout) { + if (_step_did_timeout.load_relaxed()) { stop_reason = "Step time limit reached"; return false; } @@ -543,12 +542,12 @@ static void report_vm_version(outputStream* st, char* buf, int buflen) { // Returns true if at least one thread reported a fatal error and fatal error handling is in process. bool VMError::is_error_reported() { - return _first_error_tid != -1; + return _first_error_tid.load_relaxed() != -1; } // Returns true if the current thread reported a fatal error. bool VMError::is_error_reported_in_current_thread() { - return _first_error_tid == os::current_thread_id(); + return _first_error_tid.load_relaxed() == os::current_thread_id(); } // Helper, return current timestamp for timeout handling. @@ -560,24 +559,24 @@ jlong VMError::get_current_timestamp() { void VMError::record_reporting_start_time() { const jlong now = get_current_timestamp(); - AtomicAccess::store(&_reporting_start_time, now); + _reporting_start_time.store_relaxed(now); } jlong VMError::get_reporting_start_time() { - return AtomicAccess::load(&_reporting_start_time); + return _reporting_start_time.load_relaxed(); } void VMError::record_step_start_time() { const jlong now = get_current_timestamp(); - AtomicAccess::store(&_step_start_time, now); + _step_start_time.store_relaxed(now); } jlong VMError::get_step_start_time() { - return AtomicAccess::load(&_step_start_time); + return _step_start_time.load_relaxed(); } void VMError::clear_step_start_time() { - return AtomicAccess::store(&_step_start_time, (jlong)0); + return _step_start_time.store_relaxed(0); } // This is the main function to report a fatal error. Only one thread can @@ -612,31 +611,31 @@ void VMError::report(outputStream* st, bool _verbose) { const char* stop_reattempt_reason = nullptr; # define BEGIN \ if (_current_step == 0) { \ - _step_did_succeed = false; \ + _step_did_succeed.store_relaxed(false); \ _current_step = __LINE__; \ { // [Begin logic] # define STEP_IF(s, cond) \ } \ - _step_did_succeed = true; \ + _step_did_succeed.store_relaxed(true); \ } \ if (_current_step < __LINE__) { \ - _step_did_succeed = false; \ + _step_did_succeed.store_relaxed(false); \ _current_step = __LINE__; \ _current_step_info = s; \ if ((cond)) { \ record_step_start_time(); \ - _step_did_timeout = false; + _step_did_timeout.store_relaxed(false); // [Step logic] # define STEP(s) STEP_IF(s, true) # define REATTEMPT_STEP_IF(s, cond) \ } \ - _step_did_succeed = true; \ + _step_did_succeed.store_relaxed(true); \ } \ - if (_current_step < __LINE__ && !_step_did_succeed) { \ + if (_current_step < __LINE__ && !_step_did_succeed.load_relaxed()) { \ _current_step = __LINE__; \ _current_step_info = s; \ const bool cond_value = (cond); \ @@ -650,7 +649,7 @@ void VMError::report(outputStream* st, bool _verbose) { # define END \ } \ - _step_did_succeed = true; \ + _step_did_succeed.store_relaxed(true); \ clear_step_start_time(); \ } @@ -1359,21 +1358,21 @@ void VMError::report(outputStream* st, bool _verbose) { void VMError::set_handshake_timed_out_thread(Thread* thread) { // Only preserve the first thread to time-out this way. The atomic operation ensures // visibility to the target thread. - AtomicAccess::replace_if_null(&_handshake_timed_out_thread, thread); + _handshake_timed_out_thread.compare_exchange(nullptr, thread); } void VMError::set_safepoint_timed_out_thread(Thread* thread) { // Only preserve the first thread to time-out this way. The atomic operation ensures // visibility to the target thread. - AtomicAccess::replace_if_null(&_safepoint_timed_out_thread, thread); + _safepoint_timed_out_thread.compare_exchange(nullptr, thread); } Thread* VMError::get_handshake_timed_out_thread() { - return AtomicAccess::load(&_handshake_timed_out_thread); + return _handshake_timed_out_thread.load_relaxed(); } Thread* VMError::get_safepoint_timed_out_thread() { - return AtomicAccess::load(&_safepoint_timed_out_thread); + return _safepoint_timed_out_thread.load_relaxed(); } // Report for the vm_info_cmd. This prints out the information above omitting @@ -1708,8 +1707,7 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt static bool log_done = false; // done saving error log intptr_t mytid = os::current_thread_id(); - if (_first_error_tid == -1 && - AtomicAccess::cmpxchg(&_first_error_tid, (intptr_t)-1, mytid) == -1) { + if (_first_error_tid.compare_set(-1, mytid)) { if (SuppressFatalErrorMessage) { os::abort(CreateCoredumpOnCrash); @@ -1756,7 +1754,7 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt } else { // This is not the first error, see if it happened in a different thread // or in the same thread during error reporting. - if (_first_error_tid != mytid) { + if (_first_error_tid.load_relaxed() != mytid) { if (!SuppressFatalErrorMessage) { char msgbuf[64]; jio_snprintf(msgbuf, sizeof(msgbuf), @@ -1788,19 +1786,19 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt st->cr(); // Timeout handling. - if (_step_did_timeout) { + if (_step_did_timeout.load_relaxed()) { // The current step had a timeout. Lets continue reporting with the next step. st->print_raw("[timeout occurred during error reporting in step \""); st->print_raw(_current_step_info); st->print_cr("\"] after " INT64_FORMAT " s.", (int64_t) - ((get_current_timestamp() - _step_start_time) / TIMESTAMP_TO_SECONDS_FACTOR)); - } else if (_reporting_did_timeout) { + ((get_current_timestamp() - get_step_start_time()) / TIMESTAMP_TO_SECONDS_FACTOR)); + } else if (_reporting_did_timeout.load_relaxed()) { // We hit ErrorLogTimeout. Reporting will stop altogether. Let's wrap things // up, the process is about to be stopped by the WatcherThread. st->print_cr("------ Timeout during error reporting after " INT64_FORMAT " s. ------", (int64_t) - ((get_current_timestamp() - _reporting_start_time) / TIMESTAMP_TO_SECONDS_FACTOR)); + ((get_current_timestamp() - get_reporting_start_time()) / TIMESTAMP_TO_SECONDS_FACTOR)); st->flush(); // Watcherthread is about to call os::die. Lets just wait. os::infinite_sleep(); @@ -2100,10 +2098,10 @@ bool VMError::check_timeout() { // Timestamp is stored in nanos. if (reporting_start_time > 0) { const jlong end = reporting_start_time + (jlong)ErrorLogTimeout * TIMESTAMP_TO_SECONDS_FACTOR; - if (end <= now && !_reporting_did_timeout) { + if (end <= now && !_reporting_did_timeout.load_relaxed()) { // We hit ErrorLogTimeout and we haven't interrupted the reporting // thread yet. - _reporting_did_timeout = true; + _reporting_did_timeout.store_relaxed(true); interrupt_reporting_thread(); return true; // global timeout } @@ -2119,10 +2117,10 @@ bool VMError::check_timeout() { const int max_step_timeout_secs = 5; const jlong timeout_duration = MAX2((jlong)max_step_timeout_secs, (jlong)ErrorLogTimeout * TIMESTAMP_TO_SECONDS_FACTOR / 4); const jlong end = step_start_time + timeout_duration; - if (end <= now && !_step_did_timeout) { + if (end <= now && !_step_did_timeout.load_relaxed()) { // The step timed out and we haven't interrupted the reporting // thread yet. - _step_did_timeout = true; + _step_did_timeout.store_relaxed(true); interrupt_reporting_thread(); return false; // (Not a global timeout) } diff --git a/src/hotspot/share/utilities/vmError.hpp b/src/hotspot/share/utilities/vmError.hpp index 04cea6de47c0..b46ba2087884 100644 --- a/src/hotspot/share/utilities/vmError.hpp +++ b/src/hotspot/share/utilities/vmError.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,6 +27,7 @@ #define SHARE_UTILITIES_VMERROR_HPP #include "memory/allStatic.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" @@ -73,7 +74,7 @@ class VMError : public AllStatic { // Thread id of the first error. We must be able to handle native thread, // so use thread id instead of Thread* to identify thread. - static volatile intptr_t _first_error_tid; + static Atomic _first_error_tid; // Core dump status, false if we have been unable to write a core/minidump for some reason static bool coredump_status; @@ -85,16 +86,16 @@ class VMError : public AllStatic { // Timeout handling: // Timestamp at which error reporting started; -1 if no error reporting in progress. - static volatile jlong _reporting_start_time; + static Atomic _reporting_start_time; // Whether or not error reporting did timeout. - static volatile bool _reporting_did_timeout; + static Atomic _reporting_did_timeout; // Timestamp at which the last error reporting step started; -1 if no error reporting // in progress. - static volatile jlong _step_start_time; + static Atomic _step_start_time; // Whether or not the last error reporting step did timeout. - static volatile bool _step_did_timeout; + static Atomic _step_did_timeout; // Whether or not the last error reporting step did succeed. - static volatile bool _step_did_succeed; + static Atomic _step_did_succeed; // Install secondary signal handler to handle secondary faults during error reporting // (see VMError::crash_handler) @@ -143,8 +144,8 @@ class VMError : public AllStatic { static void clear_step_start_time(); // Handshake/safepoint timed out threads - static Thread* volatile _handshake_timed_out_thread; - static Thread* volatile _safepoint_timed_out_thread; + static Atomic _handshake_timed_out_thread; + static Atomic _safepoint_timed_out_thread; WINDOWS_ONLY([[noreturn]] static void raise_fail_fast(const void* exrecord, const void* context);) From b5d1af1f026ada0d83f6b402b83fc6b4238ce4aa Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Fri, 13 Mar 2026 02:43:09 +0000 Subject: [PATCH 31/97] 8379026: Convert utilities/events to use Atomic Reviewed-by: dholmes, fbredberg --- src/hotspot/share/utilities/events.cpp | 17 +++++++++-------- src/hotspot/share/utilities/events.hpp | 5 +---- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/hotspot/share/utilities/events.cpp b/src/hotspot/share/utilities/events.cpp index 6adb5311cb59..d2b8e7ba5dab 100644 --- a/src/hotspot/share/utilities/events.cpp +++ b/src/hotspot/share/utilities/events.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,14 +26,15 @@ #include "memory/allocation.inline.hpp" #include "oops/instanceKlass.hpp" #include "oops/symbol.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/osThread.hpp" #include "runtime/timer.hpp" #include "utilities/events.hpp" -EventLog* Events::_logs = nullptr; +static Atomic event_logs_list{}; + StringEventLog* Events::_messages = nullptr; StringEventLog* Events::_memprotect_messages = nullptr; StringEventLog* Events::_nmethod_flush_messages = nullptr; @@ -51,15 +52,15 @@ EventLog::EventLog() { // but use lock free add because there are some events that are created later. EventLog* old_head; do { - old_head = AtomicAccess::load(&Events::_logs); + old_head = event_logs_list.load_relaxed(); _next = old_head; - } while (AtomicAccess::cmpxchg(&Events::_logs, old_head, this) != old_head); + } while (!event_logs_list.compare_set(old_head, this)); } // For each registered event logger, print out the current contents of // the buffer. void Events::print_all(outputStream* out, int max) { - EventLog* log = AtomicAccess::load(&Events::_logs); + EventLog* log = event_logs_list.load_relaxed(); while (log != nullptr) { log->print_log_on(out, max); log = log->next(); @@ -68,7 +69,7 @@ void Events::print_all(outputStream* out, int max) { // Print a single event log specified by name. void Events::print_one(outputStream* out, const char* log_name, int max) { - EventLog* log = AtomicAccess::load(&Events::_logs); + EventLog* log = event_logs_list.load_relaxed(); int num_printed = 0; while (log != nullptr) { if (log->matches_name_or_handle(log_name)) { @@ -81,7 +82,7 @@ void Events::print_one(outputStream* out, const char* log_name, int max) { if (num_printed == 0) { out->print_cr("The name \"%s\" did not match any known event log. " "Valid event log names are:", log_name); - EventLog* log = AtomicAccess::load(&Events::_logs); + EventLog* log = event_logs_list.load_relaxed(); while (log != nullptr) { log->print_names(out); out->cr(); diff --git a/src/hotspot/share/utilities/events.hpp b/src/hotspot/share/utilities/events.hpp index cbbed7232fb1..0d1c08548ff7 100644 --- a/src/hotspot/share/utilities/events.hpp +++ b/src/hotspot/share/utilities/events.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -214,10 +214,7 @@ class ExceptionsEventLog : public ExtendedStringEventLog { class Events : AllStatic { - friend class EventLog; - private: - static EventLog* _logs; // A log for generic messages that aren't well categorized. static StringEventLog* _messages; From bd73864314eefcdf0a57d8a580e40de711f3cf26 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Fri, 13 Mar 2026 03:30:16 +0000 Subject: [PATCH 32/97] 8279196: Test: jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java timed out Reviewed-by: tschatzl, ayang --- .../gc/stacktrace/TestG1OldAllocationPendingStackTrace.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java index 1346c36c04f3..64389a0e921a 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,7 @@ * * @requires vm.gc == "null" | vm.gc == "G1" * @library /test/lib /test/jdk - * @run main/othervm -XX:MaxNewSize=10M -Xmx128M -XX:+UseG1GC -Xlog:gc* + * @run main/othervm -XX:MaxNewSize=10M -Xmx64M -XX:+UseG1GC -Xlog:gc* * -XX:FlightRecorderOptions:stackdepth=256 * jdk.jfr.event.gc.stacktrace.TestG1OldAllocationPendingStackTrace */ From d93204eaffd5bd79ef444055a4116f6b93a0bb85 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Fri, 13 Mar 2026 08:01:12 +0000 Subject: [PATCH 33/97] 8379025: Convert utilities/debug to use Atomic Reviewed-by: dholmes, fbredberg --- src/hotspot/share/utilities/debug.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index 504b923237ec..e8533e294601 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -40,7 +40,7 @@ #include "nmt/memTracker.hpp" #include "oops/klass.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/flags/flagSetting.hpp" #include "runtime/frame.inline.hpp" #include "runtime/handles.inline.hpp" @@ -80,7 +80,7 @@ static char g_dummy; char* g_assert_poison = &g_dummy; const char* g_assert_poison_read_only = &g_dummy; -static intx g_asserting_thread = 0; +static Atomic g_asserting_thread{0}; #endif // CAN_SHOW_REGISTERS_ON_ASSERT int DebuggingContext::_enabled = 0; // Initially disabled. @@ -193,7 +193,7 @@ void report_vm_error(const char* file, int line, const char* error_msg, const ch const void* siginfo = nullptr; #ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if (os::current_thread_id() == g_asserting_thread) { + if (os::current_thread_id() == g_asserting_thread.load_relaxed()) { context = os::get_saved_assert_context(&siginfo); } #endif // CAN_SHOW_REGISTERS_ON_ASSERT @@ -220,7 +220,7 @@ void report_fatal(VMErrorType error_type, const char* file, int line, const char const void* siginfo = nullptr; #ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if (os::current_thread_id() == g_asserting_thread) { + if (os::current_thread_id() == g_asserting_thread.load_relaxed()) { context = os::get_saved_assert_context(&siginfo); } #endif // CAN_SHOW_REGISTERS_ON_ASSERT @@ -265,15 +265,15 @@ void report_untested(const char* file, int line, const char* message) { } void report_java_out_of_memory(const char* message) { - static int out_of_memory_reported = 0; + static Atomic out_of_memory_reported{false}; JFR_ONLY(Jfr::on_report_java_out_of_memory();) // A number of threads may attempt to report OutOfMemoryError at around the // same time. To avoid dumping the heap or executing the data collection - // commands multiple times we just do it once when the first threads reports + // commands multiple times we just do it once when the first thread that reports // the error. - if (AtomicAccess::cmpxchg(&out_of_memory_reported, 0, 1) == 0) { + if (out_of_memory_reported.compare_set(false, true)) { // create heap dump before OnOutOfMemoryError commands are executed if (HeapDumpOnOutOfMemoryError) { tty->print_cr("java.lang.OutOfMemoryError: %s", message); @@ -813,7 +813,7 @@ bool handle_assert_poison_fault(const void* ucVoid) { if (ucVoid != nullptr) { // Save context. const intx my_tid = os::current_thread_id(); - if (AtomicAccess::cmpxchg(&g_asserting_thread, (intx)0, my_tid) == 0) { + if (g_asserting_thread.compare_set(0, my_tid)) { os::save_assert_context(ucVoid); } } From 8c4d27381cd06511e8efc572fffa02cf8a98c75b Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Fri, 13 Mar 2026 08:05:20 +0000 Subject: [PATCH 34/97] 8379273: Convert miscellaneous utilities to use Atomic Reviewed-by: dholmes, fbredberg --- src/hotspot/share/utilities/filterQueue.hpp | 8 ++++---- src/hotspot/share/utilities/filterQueue.inline.hpp | 6 +++--- src/hotspot/share/utilities/tableStatistics.cpp | 11 +++++------ src/hotspot/share/utilities/tableStatistics.hpp | 7 ++++--- src/hotspot/share/utilities/zipLibrary.cpp | 9 +++++---- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/hotspot/share/utilities/filterQueue.hpp b/src/hotspot/share/utilities/filterQueue.hpp index 141c40f09c84..ea47f07b7b83 100644 --- a/src/hotspot/share/utilities/filterQueue.hpp +++ b/src/hotspot/share/utilities/filterQueue.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 @@ -26,7 +26,7 @@ #define SHARE_UTILITIES_FILTERQUEUE_HPP #include "memory/allocation.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" // The FilterQueue is FIFO with the ability to skip over queued items. // The skipping is controlled by using a filter when popping. @@ -42,9 +42,9 @@ class FilterQueue { E _data; }; - Node* _first; + Atomic _first; Node* load_first() { - return AtomicAccess::load_acquire(&_first); + return _first.load_acquire(); } static bool match_all(E d) { return true; } diff --git a/src/hotspot/share/utilities/filterQueue.inline.hpp b/src/hotspot/share/utilities/filterQueue.inline.hpp index 18b40b81c6c7..7fa1bc94b7b7 100644 --- a/src/hotspot/share/utilities/filterQueue.inline.hpp +++ b/src/hotspot/share/utilities/filterQueue.inline.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 @@ -37,7 +37,7 @@ void FilterQueue::push(E data) { while (true){ head = load_first(); insnode->_next = head; - if (AtomicAccess::cmpxchg(&_first, head, insnode) == head) { + if (_first.compare_set(head, insnode)) { break; } yield.wait(); @@ -91,7 +91,7 @@ E FilterQueue::pop(MATCH_FUNC& match_func) { if (match_prev == nullptr) { // Working on first - if (AtomicAccess::cmpxchg(&_first, match, match->_next) == match) { + if (_first.compare_set(match, match->_next)) { E ret = match->_data; delete match; return ret; diff --git a/src/hotspot/share/utilities/tableStatistics.cpp b/src/hotspot/share/utilities/tableStatistics.cpp index 331652becd57..34d0969969aa 100644 --- a/src/hotspot/share/utilities/tableStatistics.cpp +++ b/src/hotspot/share/utilities/tableStatistics.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,6 @@ * */ -#include "runtime/atomicAccess.hpp" #include "runtime/os.hpp" #include "utilities/debug.hpp" #include "utilities/macros.hpp" @@ -42,7 +41,7 @@ TableRateStatistics::~TableRateStatistics() { }; void TableRateStatistics::add() { #if INCLUDE_JFR if (Jfr::is_recording()) { - AtomicAccess::inc(&_added_items); + _added_items.add_then_fetch(1u); } #endif } @@ -50,7 +49,7 @@ void TableRateStatistics::add() { void TableRateStatistics::remove() { #if INCLUDE_JFR if (Jfr::is_recording()) { - AtomicAccess::inc(&_removed_items); + _removed_items.add_then_fetch(1u); } #endif } @@ -61,8 +60,8 @@ void TableRateStatistics::stamp() { _added_items_stamp_prev = _added_items_stamp; _removed_items_stamp_prev = _removed_items_stamp; - _added_items_stamp = _added_items; - _removed_items_stamp = _removed_items; + _added_items_stamp = _added_items.load_relaxed(); + _removed_items_stamp = _removed_items.load_relaxed(); if (_time_stamp == 0) { _time_stamp = now - 1000000000; diff --git a/src/hotspot/share/utilities/tableStatistics.hpp b/src/hotspot/share/utilities/tableStatistics.hpp index d4fd33029224..95856114833e 100644 --- a/src/hotspot/share/utilities/tableStatistics.hpp +++ b/src/hotspot/share/utilities/tableStatistics.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, 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 @@ #define SHARE_UTILITIES_TABLE_STATISTICS_HPP #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/numberSeq.hpp" @@ -35,8 +36,8 @@ class TableRateStatistics : public CHeapObj { friend class TableStatistics; private: - volatile size_t _added_items; - volatile size_t _removed_items; + Atomic _added_items; + Atomic _removed_items; jlong _time_stamp; double _seconds_stamp; diff --git a/src/hotspot/share/utilities/zipLibrary.cpp b/src/hotspot/share/utilities/zipLibrary.cpp index 54875516a0fa..ad6bb82d43f8 100644 --- a/src/hotspot/share/utilities/zipLibrary.cpp +++ b/src/hotspot/share/utilities/zipLibrary.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,7 @@ #include "jvm_io.h" #include "runtime/arguments.hpp" +#include "runtime/atomic.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/os.inline.hpp" #include "runtime/semaphore.inline.hpp" @@ -50,10 +51,10 @@ static ZIP_GZip_InitParams_t ZIP_GZip_InitParams = nullptr; static ZIP_GZip_Fully_t ZIP_GZip_Fully = nullptr; static void* _zip_handle = nullptr; -static bool _loaded = false; +static Atomic _loaded{false}; static inline bool is_loaded() { - return AtomicAccess::load_acquire(&_loaded); + return _loaded.load_acquire(); } static inline bool not_loaded() { @@ -111,7 +112,7 @@ static void load_zip_library(bool vm_exit_on_failure) { } store_function_pointers(&path[0], vm_exit_on_failure); - AtomicAccess::release_store(&_loaded, true); + _loaded.release_store(true); assert(is_loaded(), "invariant"); } From c9b78274643df4fdee315951365816ba860313c1 Mon Sep 17 00:00:00 2001 From: Anton Artemov Date: Fri, 13 Mar 2026 08:45:15 +0000 Subject: [PATCH 35/97] 8294152: AArch64: frame::id() and frame::is_older() broken for interpreted frames with large max_stack Co-authored-by: Richard Reingruber Reviewed-by: rrich, fbredberg, mdoerr, aboldtch, amitkumar, pchilanomate --- src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp | 9 ++++++--- src/hotspot/cpu/ppc/frame_ppc.inline.hpp | 9 ++++++--- src/hotspot/cpu/riscv/frame_riscv.inline.hpp | 9 ++++++--- src/hotspot/cpu/s390/frame_s390.hpp | 4 ++-- src/hotspot/cpu/s390/frame_s390.inline.hpp | 6 +++--- src/hotspot/cpu/x86/frame_x86.inline.hpp | 9 ++++++--- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp index cb53d8663ad1..748ab0e0e2bb 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -245,8 +245,8 @@ inline bool frame::equal(frame other) const { // Return unique id for this frame. The id must have a value where we can distinguish // identity and younger/older relationship. null represents an invalid (incomparable) -// frame. -inline intptr_t* frame::id(void) const { return unextended_sp(); } +// frame. Should not be called for heap frames. +inline intptr_t* frame::id(void) const { return real_fp(); } // Return true if the frame is older (less recent activation) than the frame represented by id inline bool frame::is_older(intptr_t* id) const { assert(this->id() != nullptr && id != nullptr, "null frame id"); @@ -412,6 +412,9 @@ inline frame frame::sender(RegisterMap* map) const { StackWatermarkSet::on_iteration(map->thread(), result); } + // Calling frame::id() is currently not supported for heap frames. + assert(result._on_heap || this->_on_heap || result.is_older(this->id()), "Must be"); + return result; } diff --git a/src/hotspot/cpu/ppc/frame_ppc.inline.hpp b/src/hotspot/cpu/ppc/frame_ppc.inline.hpp index bb711f2d0533..3c05f950d0c8 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/frame_ppc.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -137,10 +137,10 @@ inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address // Return unique id for this frame. The id must have a value where we // can distinguish identity and younger/older relationship. null -// represents an invalid (incomparable) frame. +// represents an invalid (incomparable) frame. Should not be called for heap frames. inline intptr_t* frame::id(void) const { // Use _fp. _sp or _unextended_sp wouldn't be correct due to resizing. - return _fp; + return real_fp(); } // Return true if this frame is older (less recent activation) than @@ -319,6 +319,9 @@ inline frame frame::sender(RegisterMap* map) const { StackWatermarkSet::on_iteration(map->thread(), result); } + // Calling frame::id() is currently not supported for heap frames. + assert(result._on_heap || this->_on_heap || result.is_older(this->id()), "Must be"); + return result; } diff --git a/src/hotspot/cpu/riscv/frame_riscv.inline.hpp b/src/hotspot/cpu/riscv/frame_riscv.inline.hpp index 51a203c548ce..d1841a347e91 100644 --- a/src/hotspot/cpu/riscv/frame_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/frame_riscv.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -236,8 +236,8 @@ inline bool frame::equal(frame other) const { // Return unique id for this frame. The id must have a value where we can distinguish // identity and younger/older relationship. null represents an invalid (incomparable) -// frame. -inline intptr_t* frame::id(void) const { return unextended_sp(); } +// frame. Should not be called for heap frames. +inline intptr_t* frame::id(void) const { return real_fp(); } // Return true if the frame is older (less recent activation) than the frame represented by id inline bool frame::is_older(intptr_t* id) const { assert(this->id() != nullptr && id != nullptr, "null frame id"); @@ -398,6 +398,9 @@ frame frame::sender(RegisterMap* map) const { StackWatermarkSet::on_iteration(map->thread(), result); } + // Calling frame::id() is currently not supported for heap frames. + assert(result._on_heap || this->_on_heap || result.is_older(this->id()), "Must be"); + return result; } diff --git a/src/hotspot/cpu/s390/frame_s390.hpp b/src/hotspot/cpu/s390/frame_s390.hpp index ad754706367d..bcdeec43e1a2 100644 --- a/src/hotspot/cpu/s390/frame_s390.hpp +++ b/src/hotspot/cpu/s390/frame_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -463,7 +463,7 @@ // Accessors - inline intptr_t* fp() const { return _fp; } + inline intptr_t* fp() const { assert_absolute(); return _fp; } private: diff --git a/src/hotspot/cpu/s390/frame_s390.inline.hpp b/src/hotspot/cpu/s390/frame_s390.inline.hpp index dea0e72581f6..6fcd36c57d17 100644 --- a/src/hotspot/cpu/s390/frame_s390.inline.hpp +++ b/src/hotspot/cpu/s390/frame_s390.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -133,10 +133,10 @@ inline void frame::interpreter_frame_set_monitors(BasicObjectLock* monitors) { // Return unique id for this frame. The id must have a value where we // can distinguish identity and younger/older relationship. null -// represents an invalid (incomparable) frame. +// represents an invalid (incomparable) frame. Should not be called for heap frames. inline intptr_t* frame::id(void) const { // Use _fp. _sp or _unextended_sp wouldn't be correct due to resizing. - return _fp; + return real_fp(); } // Return true if this frame is older (less recent activation) than diff --git a/src/hotspot/cpu/x86/frame_x86.inline.hpp b/src/hotspot/cpu/x86/frame_x86.inline.hpp index dcd766545d3e..3f3b951edc88 100644 --- a/src/hotspot/cpu/x86/frame_x86.inline.hpp +++ b/src/hotspot/cpu/x86/frame_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -231,8 +231,8 @@ inline bool frame::equal(frame other) const { // Return unique id for this frame. The id must have a value where we can distinguish // identity and younger/older relationship. null represents an invalid (incomparable) -// frame. -inline intptr_t* frame::id(void) const { return unextended_sp(); } +// frame. Should not be called for heap frames. +inline intptr_t* frame::id(void) const { return real_fp(); } // Return true if the frame is older (less recent activation) than the frame represented by id inline bool frame::is_older(intptr_t* id) const { assert(this->id() != nullptr && id != nullptr, "null frame id"); @@ -397,6 +397,9 @@ inline frame frame::sender(RegisterMap* map) const { StackWatermarkSet::on_iteration(map->thread(), result); } + // Calling frame::id() is currently not supported for heap frames. + assert(result._on_heap || this->_on_heap || result.is_older(this->id()), "Must be"); + return result; } From 38e8a465421726540365366bf6f13c48bfe0719f Mon Sep 17 00:00:00 2001 From: Stefan Johansson Date: Fri, 13 Mar 2026 08:53:59 +0000 Subject: [PATCH 36/97] 8378331: G1: WeakProcessor IsAlive and KeepAlive closures not handling humongous candidates correctly 8378336: G1: j.l.ref.Reference processor always keeps alive all humongous object referents Reviewed-by: tschatzl, kbarrett, iwalulya --- .../share/gc/g1/g1CollectedHeap.inline.hpp | 6 +- src/hotspot/share/gc/g1/g1YoungCollector.cpp | 12 +--- .../g1/TestEagerReclaimHumongousRegions.java | 62 ++++++++++++------- .../humongousObjects/TestObjectCollected.java | 30 +++++++-- 4 files changed, 70 insertions(+), 40 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index 8782b65b6f96..90e87607b872 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -47,9 +47,9 @@ #include "utilities/bitMap.inline.hpp" inline bool G1STWIsAliveClosure::do_object_b(oop p) { - // An object is reachable if it is outside the collection set, - // or is inside and copied. - return !_g1h->is_in_cset(p) || p->is_forwarded(); + // An object is reachable if it is outside the collection set and not a + // humongous candidate, or is inside and copied. + return !_g1h->is_in_cset_or_humongous_candidate(p) || p->is_forwarded(); } inline JavaThread* const* G1JavaThreadsListClaimer::claim(uint& count) { diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index a9db9a7c269c..a9a81f846af5 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -896,17 +896,10 @@ class G1KeepAliveClosure: public OopClosure { assert(obj != nullptr, "the caller should have filtered out null values"); const G1HeapRegionAttr region_attr =_g1h->region_attr(obj); - if (!region_attr.is_in_cset_or_humongous_candidate()) { - return; - } + assert(!region_attr.is_humongous_candidate(), "Humongous candidates should never be considered alive"); if (region_attr.is_in_cset()) { assert(obj->is_forwarded(), "invariant" ); *p = obj->forwardee(); - } else { - assert(!obj->is_forwarded(), "invariant" ); - assert(region_attr.is_humongous_candidate(), - "Only allowed G1HeapRegionAttr state is IsHumongous, but is %d", region_attr.type()); - _g1h->set_humongous_is_live(obj); } } }; @@ -932,7 +925,8 @@ class G1CopyingKeepAliveClosure: public OopClosure { template void do_oop_work(T* p) { oop obj = RawAccess<>::oop_load(p); - if (_g1h->is_in_cset_or_humongous_candidate(obj)) { + assert(!_g1h->region_attr(obj).is_humongous_candidate(), "Humongous candidates should never be considered alive"); + if (_g1h->is_in_cset(obj)) { // If the referent object has been forwarded (either copied // to a new location or to itself in the event of an // evacuation failure) then we need to update the reference diff --git a/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java b/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java index fd1924b9644d..5637e578e8f4 100644 --- a/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java +++ b/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java @@ -84,26 +84,19 @@ enum ExpectedState { * @param phase The phase during concurrent mark to reach before triggering a young garbage collection. * @return Returns the stdout of the VM. */ - private static String runHelperVM(ObjectType type, ReferencePolicy refPolicy, AllocationTiming timing, String phase) throws Exception { + private static String runHelperVM(List args, ObjectType type, ReferencePolicy refPolicy, AllocationTiming timing, String phase) throws Exception { boolean useTypeArray = (type == ObjectType.TYPE_ARRAY); boolean keepReference = (refPolicy == ReferencePolicy.KEEP); boolean allocateAfter = (timing == AllocationTiming.AFTER_MARK_START); - OutputAnalyzer output = ProcessTools.executeLimitedTestJava("-XX:+UseG1GC", - "-Xmx20M", - "-Xms20m", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+VerifyAfterGC", - "-Xbootclasspath/a:.", - "-Xlog:gc=debug,gc+humongous=debug", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+WhiteBoxAPI", - TestEagerReclaimHumongousRegionsClearMarkBitsRunner.class.getName(), - String.valueOf(useTypeArray), - String.valueOf(keepReference), - String.valueOf(allocateAfter), - phase); + args.add(TestEagerReclaimHumongousRegionsClearMarkBitsRunner.class.getName()); + args.add(String.valueOf(useTypeArray)); + args.add(String.valueOf(keepReference)); + args.add(String.valueOf(allocateAfter)); + args.add(phase); + + OutputAnalyzer output = ProcessTools.executeLimitedTestJava(args); String log = output.getStdout(); System.out.println(log); @@ -111,19 +104,25 @@ private static String runHelperVM(ObjectType type, ReferencePolicy refPolicy, Al return log; } + private static List testArgs() throws Exception { + return List.of("-XX:+UseG1GC", + "-Xmx20M", + "-Xms20m", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+VerifyAfterGC", + "-Xbootclasspath/a:.", + "-Xlog:gc=debug,gc+humongous=debug", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI"); + } + private static String boolToInt(boolean value) { return value ? "1" : "0"; } - private static void runTest(ObjectType type, - ReferencePolicy refPolicy, - AllocationTiming timing, - String phase, - ExpectedState expected) throws Exception { - String log = runHelperVM(type, refPolicy, timing, phase); - + private static void verifyLog(String log, AllocationTiming timing, ExpectedState expected) { // Find the log output indicating that the humongous object has been reclaimed, and marked and verify for the expected results. -// [0.351s][debug][gc,humongous] GC(3) Humongous region 2 (object size 4194320 @ 0x00000000fee00000) remset 0 code roots 0 marked 1 pinned count 0 reclaim candidate 1 type array 1 + // [0.351s][debug][gc,humongous] GC(3) Humongous region 2 (object size 4194320 @ 0x00000000fee00000) remset 0 code roots 0 marked 1 pinned count 0 reclaim candidate 1 type array 1 // Now check the result of the reclaim attempt. We are interested in the last such message (as mentioned above, we might get two). String patternString = "gc,humongous.* marked (\\d) pin.*candidate (\\d)"; @@ -152,6 +151,23 @@ private static void runTest(ObjectType type, Asserts.assertTrue(expected.reclaimed == reclaimed, "Wrong log output reclaiming humongous region"); } + private static void runTest(ObjectType type, + ReferencePolicy refPolicy, + AllocationTiming timing, + String phase, + ExpectedState expected) throws Exception { + List vmArgs = testArgs(); + + ArrayList args = new ArrayList(vmArgs); + String log = runHelperVM(args, type, refPolicy, timing, phase); + verifyLog(log, timing, expected); + + ArrayList jfrArgs = new ArrayList(vmArgs); + jfrArgs.addLast("-XX:StartFlightRecording=settings=profile"); + String jfrLog = runHelperVM(jfrArgs, type, refPolicy, timing, phase); + verifyLog(jfrLog, timing, expected); + } + public static void main(String[] args) throws Exception { System.out.println("Tests checking eager reclaim for when the object is allocated before mark start."); runTest(ObjectType.TYPE_ARRAY, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.MARKED_CANDIDATE_RECLAIMED); diff --git a/test/hotspot/jtreg/gc/g1/humongousObjects/TestObjectCollected.java b/test/hotspot/jtreg/gc/g1/humongousObjects/TestObjectCollected.java index b1ddecd72902..b3a30d8f2f4c 100644 --- a/test/hotspot/jtreg/gc/g1/humongousObjects/TestObjectCollected.java +++ b/test/hotspot/jtreg/gc/g1/humongousObjects/TestObjectCollected.java @@ -44,8 +44,8 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * * @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+WhiteBoxAPI -Xbootclasspath/a:. -Xms200m -Xmx200m -Xlog:gc - * -XX:InitiatingHeapOccupancyPercent=100 -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectCollected.gc.log + * -XX:+WhiteBoxAPI -Xbootclasspath/a:. -Xms200m -Xmx200m -Xlog:gc,gc+humongous=debug + * -XX:InitiatingHeapOccupancyPercent=100 -XX:G1HeapRegionSize=1M -Xlog:gc,gc+humongous=debug:file=TestObjectCollected.gc.log * gc.g1.humongousObjects.TestObjectCollected */ @@ -103,12 +103,22 @@ private enum REF_FACTORY { public Reference create() { return new WeakReference<>(new byte[ALLOCATION_SIZE], referenceQueqe); } + + @Override + public boolean expectCleared() { + return true; + } }, SOFT { @Override public Reference create() { return new SoftReference<>(new byte[ALLOCATION_SIZE], referenceQueqe); } + + @Override + public boolean expectCleared() { + return false; + } }; private static final ReferenceQueue referenceQueqe = new ReferenceQueue<>(); @@ -120,6 +130,11 @@ public Reference create() { * @return weak/soft reference on byte array of ALLOCATION_SIZE */ public abstract Reference create(); + + /** + * For WeakReferences we expect the referent to always be cleared after GC. + */ + public abstract boolean expectCleared(); } @@ -162,8 +177,13 @@ public static void doTesting(GC gc, REF_FACTORY ref) { isRegionFree = WHITE_BOX.g1BelongsToFreeRegion(adr); boolean isObjCollected = isRegionFree || !isRegionHumongous; - - Asserts.assertEquals(isRefNulled, isObjCollected, + if (ref.expectCleared()) { + Asserts.assertEquals(isRefNulled, true, + "Reference.get() did not return null for a reference where it is expected"); + Asserts.assertEquals(isObjCollected, true, + "The humonogus object was not collected as expected"); + } else { + Asserts.assertEquals(isRefNulled, isObjCollected, String.format("There is an inconsistensy between reference and white box " + "method behavior - one considers object referenced with " + "%s type collected and another doesn't!\n" @@ -172,7 +192,7 @@ public static void doTesting(GC gc, REF_FACTORY ref) { reference.getClass().getSimpleName(), (isRefNulled ? "" : "not "), (isObjCollected ? "" : " not"))); - + } System.out.println("Passed"); } From 2dd7a20bb39d31b819fab85673cd3b3ed5194e82 Mon Sep 17 00:00:00 2001 From: Casper Norrbin Date: Fri, 13 Mar 2026 09:30:49 +0000 Subject: [PATCH 37/97] 8369503: [Linux] Move machine-specific queries to the OSContainer layer Reviewed-by: sgehwolf, phubner --- .../os/linux/cgroupSubsystem_linux.cpp | 10 ++++-- .../os/linux/cgroupSubsystem_linux.hpp | 4 ++- src/hotspot/os/linux/cgroupUtil_linux.cpp | 33 +++++++++---------- src/hotspot/os/linux/cgroupUtil_linux.hpp | 4 +-- .../os/linux/cgroupV1Subsystem_linux.cpp | 2 -- .../os/linux/cgroupV2Subsystem_linux.cpp | 2 -- src/hotspot/os/linux/osContainer_linux.cpp | 7 +++- .../runtime/test_cgroupSubsystem_linux.cpp | 8 ++--- 8 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index e49d070890e1..13a005591fb8 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -28,7 +28,6 @@ #include "cgroupV2Subsystem_linux.hpp" #include "logging/log.hpp" #include "memory/allocation.hpp" -#include "os_linux.hpp" #include "runtime/globals.hpp" #include "runtime/os.hpp" #include "utilities/globalDefinitions.hpp" @@ -605,6 +604,11 @@ void CgroupSubsystemFactory::cleanup(CgroupInfo* cg_infos) { } } +void CgroupSubsystem::adjust_controllers(physical_memory_size_type upper_mem_bound, int upper_cpu_bound) { + CgroupUtil::adjust_controller(memory_controller()->controller(), upper_mem_bound); + CgroupUtil::adjust_controller(cpu_controller()->controller(), upper_cpu_bound); +} + /* active_processor_count * * Calculate an appropriate number of active processors for the @@ -631,7 +635,7 @@ void CgroupSubsystemFactory::cleanup(CgroupInfo* cg_infos) { * return: * true if there were no errors. false otherwise. */ -bool CgroupSubsystem::active_processor_count(double& value) { +bool CgroupSubsystem::active_processor_count(int (*cpu_bound_func)(), double& value) { // We use a cache with a timeout to avoid performing expensive // computations in the event this function is called frequently. // [See 8227006]. @@ -643,7 +647,7 @@ bool CgroupSubsystem::active_processor_count(double& value) { return true; } - int cpu_count = os::Linux::active_processor_count(); + int cpu_count = cpu_bound_func(); double result = -1; if (!CgroupUtil::processor_count(contrl->controller(), cpu_count, result)) { return false; diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index d083a9985c29..adde37e1c772 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -278,7 +278,7 @@ class CgroupMemoryController: public CHeapObj { class CgroupSubsystem: public CHeapObj { public: bool memory_limit_in_bytes(physical_memory_size_type upper_bound, physical_memory_size_type& value); - bool active_processor_count(double& value); + bool active_processor_count(int (*cpu_bound_func)(), double& value); virtual bool pids_max(uint64_t& value) = 0; virtual bool pids_current(uint64_t& value) = 0; @@ -291,6 +291,8 @@ class CgroupSubsystem: public CHeapObj { virtual CachingCgroupController* cpu_controller() = 0; virtual CgroupCpuacctController* cpuacct_controller() = 0; + void adjust_controllers(physical_memory_size_type upper_mem_bound, int upper_cpu_bound); + bool cpu_quota(int& value); bool cpu_period(int& value); bool cpu_shares(int& value); diff --git a/src/hotspot/os/linux/cgroupUtil_linux.cpp b/src/hotspot/os/linux/cgroupUtil_linux.cpp index 570b335940bf..f166f6cd5e47 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.cpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.cpp @@ -24,7 +24,6 @@ */ #include "cgroupUtil_linux.hpp" -#include "os_linux.hpp" bool CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int upper_bound, double& value) { assert(upper_bound > 0, "upper bound of cpus must be positive"); @@ -82,7 +81,7 @@ double CgroupUtil::get_updated_cpu_limit(CgroupCpuController* cpu, return lowest; } -void CgroupUtil::adjust_controller(CgroupMemoryController* mem) { +void CgroupUtil::adjust_controller(CgroupMemoryController* mem, physical_memory_size_type upper_bound) { assert(mem->cgroup_path() != nullptr, "invariant"); if (strstr(mem->cgroup_path(), "../") != nullptr) { log_warning(os, container)("Cgroup memory controller path at '%s' seems to have moved " @@ -100,17 +99,16 @@ void CgroupUtil::adjust_controller(CgroupMemoryController* mem) { char* cg_path = os::strdup(orig); char* last_slash; assert(cg_path[0] == '/', "cgroup path must start with '/'"); - physical_memory_size_type phys_mem = os::Linux::physical_memory(); char* limit_cg_path = nullptr; physical_memory_size_type limit = value_unlimited; - physical_memory_size_type lowest_limit = phys_mem; - lowest_limit = get_updated_mem_limit(mem, lowest_limit, phys_mem); - physical_memory_size_type orig_limit = lowest_limit != phys_mem ? lowest_limit : phys_mem; + physical_memory_size_type lowest_limit = upper_bound; + lowest_limit = get_updated_mem_limit(mem, lowest_limit, upper_bound); + physical_memory_size_type orig_limit = lowest_limit != upper_bound ? lowest_limit : upper_bound; while ((last_slash = strrchr(cg_path, '/')) != cg_path) { *last_slash = '\0'; // strip path // update to shortened path and try again mem->set_subsystem_path(cg_path); - limit = get_updated_mem_limit(mem, lowest_limit, phys_mem); + limit = get_updated_mem_limit(mem, lowest_limit, upper_bound); if (limit < lowest_limit) { lowest_limit = limit; os::free(limit_cg_path); // handles nullptr @@ -119,13 +117,13 @@ void CgroupUtil::adjust_controller(CgroupMemoryController* mem) { } // need to check limit at mount point mem->set_subsystem_path("/"); - limit = get_updated_mem_limit(mem, lowest_limit, phys_mem); + limit = get_updated_mem_limit(mem, lowest_limit, upper_bound); if (limit < lowest_limit) { lowest_limit = limit; os::free(limit_cg_path); // handles nullptr limit_cg_path = os::strdup("/"); } - assert(lowest_limit <= phys_mem, "limit must not exceed host memory"); + assert(lowest_limit <= upper_bound, "limit must not exceed upper bound"); if (lowest_limit != orig_limit) { // we've found a lower limit anywhere in the hierarchy, // set the path to the limit path @@ -147,7 +145,7 @@ void CgroupUtil::adjust_controller(CgroupMemoryController* mem) { os::free(limit_cg_path); } -void CgroupUtil::adjust_controller(CgroupCpuController* cpu) { +void CgroupUtil::adjust_controller(CgroupCpuController* cpu, int upper_bound) { assert(cpu->cgroup_path() != nullptr, "invariant"); if (strstr(cpu->cgroup_path(), "../") != nullptr) { log_warning(os, container)("Cgroup cpu controller path at '%s' seems to have moved " @@ -165,17 +163,16 @@ void CgroupUtil::adjust_controller(CgroupCpuController* cpu) { char* cg_path = os::strdup(orig); char* last_slash; assert(cg_path[0] == '/', "cgroup path must start with '/'"); - int host_cpus = os::Linux::active_processor_count(); - int lowest_limit = host_cpus; - double cpus = get_updated_cpu_limit(cpu, lowest_limit, host_cpus); - int orig_limit = lowest_limit != host_cpus ? lowest_limit : host_cpus; + int lowest_limit = upper_bound; + double cpus = get_updated_cpu_limit(cpu, lowest_limit, upper_bound); + int orig_limit = lowest_limit != upper_bound ? lowest_limit : upper_bound; char* limit_cg_path = nullptr; while ((last_slash = strrchr(cg_path, '/')) != cg_path) { *last_slash = '\0'; // strip path // update to shortened path and try again cpu->set_subsystem_path(cg_path); - cpus = get_updated_cpu_limit(cpu, lowest_limit, host_cpus); - if (cpus != host_cpus && cpus < lowest_limit) { + cpus = get_updated_cpu_limit(cpu, lowest_limit, upper_bound); + if (cpus != upper_bound && cpus < lowest_limit) { lowest_limit = cpus; os::free(limit_cg_path); // handles nullptr limit_cg_path = os::strdup(cg_path); @@ -183,8 +180,8 @@ void CgroupUtil::adjust_controller(CgroupCpuController* cpu) { } // need to check limit at mount point cpu->set_subsystem_path("/"); - cpus = get_updated_cpu_limit(cpu, lowest_limit, host_cpus); - if (cpus != host_cpus && cpus < lowest_limit) { + cpus = get_updated_cpu_limit(cpu, lowest_limit, upper_bound); + if (cpus != upper_bound && cpus < lowest_limit) { lowest_limit = cpus; os::free(limit_cg_path); // handles nullptr limit_cg_path = os::strdup(cg_path); diff --git a/src/hotspot/os/linux/cgroupUtil_linux.hpp b/src/hotspot/os/linux/cgroupUtil_linux.hpp index 1fd2a7d872be..68585c22c2db 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.hpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.hpp @@ -35,10 +35,10 @@ class CgroupUtil: AllStatic { static bool processor_count(CgroupCpuController* cpu, int upper_bound, double& value); // Given a memory controller, adjust its path to a point in the hierarchy // that represents the closest memory limit. - static void adjust_controller(CgroupMemoryController* m); + static void adjust_controller(CgroupMemoryController* m, physical_memory_size_type upper_bound); // Given a cpu controller, adjust its path to a point in the hierarchy // that represents the closest cpu limit. - static void adjust_controller(CgroupCpuController* c); + static void adjust_controller(CgroupCpuController* c, int upper_bound); private: static physical_memory_size_type get_updated_mem_limit(CgroupMemoryController* m, physical_memory_size_type lowest, diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index c8f5a290c994..e42b7a133919 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -326,8 +326,6 @@ CgroupV1Subsystem::CgroupV1Subsystem(CgroupV1Controller* cpuset, _cpuset(cpuset), _cpuacct(cpuacct), _pids(pids) { - CgroupUtil::adjust_controller(memory); - CgroupUtil::adjust_controller(cpu); _memory = new CachingCgroupController(memory); _cpu = new CachingCgroupController(cpu); } diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 30e1affc646d..edd80bb7427d 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -154,8 +154,6 @@ CgroupV2Subsystem::CgroupV2Subsystem(CgroupV2MemoryController * memory, CgroupV2CpuacctController* cpuacct, CgroupV2Controller unified) : _unified(unified) { - CgroupUtil::adjust_controller(memory); - CgroupUtil::adjust_controller(cpu); _memory = new CachingCgroupController(memory); _cpu = new CachingCgroupController(cpu); _cpuacct = cpuacct; diff --git a/src/hotspot/os/linux/osContainer_linux.cpp b/src/hotspot/os/linux/osContainer_linux.cpp index b46263efd99e..da2cbf381e6c 100644 --- a/src/hotspot/os/linux/osContainer_linux.cpp +++ b/src/hotspot/os/linux/osContainer_linux.cpp @@ -59,6 +59,11 @@ void OSContainer::init() { if (cgroup_subsystem == nullptr) { return; // Required subsystem files not found or other error } + // Adjust controller paths once subsystem is initialized + physical_memory_size_type phys_mem = os::Linux::physical_memory(); + int host_cpus = os::Linux::active_processor_count(); + cgroup_subsystem->adjust_controllers(phys_mem, host_cpus); + /* * In order to avoid a false positive on is_containerized() on * Linux systems outside a container *and* to ensure compatibility @@ -252,7 +257,7 @@ char * OSContainer::cpu_cpuset_memory_nodes() { bool OSContainer::active_processor_count(double& value) { assert(cgroup_subsystem != nullptr, "cgroup subsystem not available"); - return cgroup_subsystem->active_processor_count(value); + return cgroup_subsystem->active_processor_count(&os::Linux::active_processor_count, value); } bool OSContainer::cpu_quota(int& value) { diff --git a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp index 7a4f7bcb99ee..69d04ae8883f 100644 --- a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp +++ b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp @@ -479,7 +479,7 @@ TEST(cgroupTest, set_cgroupv1_subsystem_path_adjusted) { ccc->set_subsystem_path((char*)cpu.cgroup_path); EXPECT_TRUE(ccc->needs_hierarchy_adjustment()); - CgroupUtil::adjust_controller(ccc); + CgroupUtil::adjust_controller(ccc, 1); ASSERT_STREQ(cpu.expected_path, ccc->subsystem_path()); EXPECT_FALSE(ccc->needs_hierarchy_adjustment()); @@ -489,7 +489,7 @@ TEST(cgroupTest, set_cgroupv1_subsystem_path_adjusted) { cmc->set_subsystem_path((char*)memory.cgroup_path); EXPECT_TRUE(cmc->needs_hierarchy_adjustment()); - CgroupUtil::adjust_controller(cmc); + CgroupUtil::adjust_controller(cmc, (physical_memory_size_type)1024); ASSERT_STREQ(memory.expected_path, cmc->subsystem_path()); EXPECT_FALSE(cmc->needs_hierarchy_adjustment()); } @@ -512,7 +512,7 @@ TEST(cgroupTest, set_cgroupv2_subsystem_path_adjusted) { true /* read-only mount */)); EXPECT_TRUE(ccc->needs_hierarchy_adjustment()); - CgroupUtil::adjust_controller(ccc); + CgroupUtil::adjust_controller(ccc, 1); ASSERT_STREQ(cpu.expected_path, ccc->subsystem_path()); EXPECT_FALSE(ccc->needs_hierarchy_adjustment()); @@ -521,7 +521,7 @@ TEST(cgroupTest, set_cgroupv2_subsystem_path_adjusted) { true /* read-only mount */)); EXPECT_TRUE(cmc->needs_hierarchy_adjustment()); - CgroupUtil::adjust_controller(cmc); + CgroupUtil::adjust_controller(cmc, (physical_memory_size_type)1024); ASSERT_STREQ(memory.expected_path, cmc->subsystem_path()); EXPECT_FALSE(cmc->needs_hierarchy_adjustment()); } From fec7229bc23cc404508848d666ad94a0554da619 Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Fri, 13 Mar 2026 09:52:52 +0000 Subject: [PATCH 38/97] 8379625: PPC: cleanup C2 OptoAssembly Reviewed-by: mdoerr, dbriemann --- src/hotspot/cpu/ppc/ppc.ad | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 057015d3c395..21fc12742efc 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -10336,7 +10336,7 @@ instruct cmovI_bso_stackSlotL(iRegIdst dst, flagsRegSrc crx, stackSlotL src) %{ ins_variable_size_depending_on_alignment(true); - format %{ "cmovI $crx, $dst, $src" %} + format %{ "CMOVI $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -10349,7 +10349,7 @@ instruct cmovI_bso_reg(iRegIdst dst, flagsRegSrc crx, regD src) %{ ins_variable_size_depending_on_alignment(true); - format %{ "cmovI $crx, $dst, $src" %} + format %{ "CMOVI $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_reg(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -10361,7 +10361,7 @@ instruct cmovI_bso_reg_conLvalue0_Ex(iRegIdst dst, flagsRegSrc crx, regD src) %{ effect(DEF dst, USE crx, USE src); predicate(false); - format %{ "CmovI $dst, $crx, $src \t// postalloc expanded" %} + format %{ "CMOVI $dst, $crx, $src \t// postalloc expanded" %} postalloc_expand %{ // // replaces @@ -10511,7 +10511,7 @@ instruct cmovL_bso_stackSlotL(iRegLdst dst, flagsRegSrc crx, stackSlotL src) %{ ins_variable_size_depending_on_alignment(true); - format %{ "cmovL $crx, $dst, $src" %} + format %{ "CMOVL $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -10524,7 +10524,7 @@ instruct cmovL_bso_reg(iRegLdst dst, flagsRegSrc crx, regD src) %{ ins_variable_size_depending_on_alignment(true); - format %{ "cmovL $crx, $dst, $src" %} + format %{ "CMOVL $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_reg(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -10536,7 +10536,7 @@ instruct cmovL_bso_reg_conLvalue0_Ex(iRegLdst dst, flagsRegSrc crx, regD src) %{ effect(DEF dst, USE crx, USE src); predicate(false); - format %{ "CmovL $dst, $crx, $src \t// postalloc expanded" %} + format %{ "CMOVL $dst, $crx, $src \t// postalloc expanded" %} postalloc_expand %{ // // replaces @@ -10737,9 +10737,9 @@ instruct convF2HF_reg_reg(iRegIdst dst, regF src, regF tmp) %{ effect(TEMP tmp); ins_cost(3 * DEFAULT_COST); size(12); - format %{ "xscvdphp $tmp, $src\t# convert to half precision\n\t" - "mffprd $dst, $tmp\t# move result from $tmp to $dst\n\t" - "extsh $dst, $dst\t# make it a proper short" + format %{ "XSCVDPHP $tmp, $src\t# convert to half precision\n\t" + "MFFPRD $dst, $tmp\t# move result from $tmp to $dst\n\t" + "EXTSH $dst, $dst\t# make it a proper short" %} ins_encode %{ __ f2hf($dst$$Register, $src$$FloatRegister, $tmp$$FloatRegister); @@ -10751,8 +10751,8 @@ instruct convHF2F_reg_reg(regF dst, iRegIsrc src) %{ match(Set dst (ConvHF2F src)); ins_cost(2 * DEFAULT_COST); size(8); - format %{ "mtfprd $dst, $src\t# move source from $src to $dst\n\t" - "xscvhpdp $dst, $dst\t# convert from half precision" + format %{ "MTFPRD $dst, $src\t# move source from $src to $dst\n\t" + "XSCVHPDP $dst, $dst\t# convert from half precision" %} ins_encode %{ __ hf2f($dst$$FloatRegister, $src$$Register); @@ -11150,7 +11150,7 @@ instruct cmov_bns_less(flagsReg crx) %{ ins_variable_size_depending_on_alignment(true); - format %{ "cmov $crx" %} + format %{ "CMOV $crx" %} size(12); ins_encode %{ Label done; @@ -11178,7 +11178,7 @@ instruct cmpF_reg_reg_Ex(flagsReg crx, regF src1, regF src2) %{ match(Set crx (CmpF src1 src2)); ins_cost(DEFAULT_COST+BRANCH_COST); - format %{ "CmpF $crx, $src1, $src2 \t// postalloc expanded" %} + format %{ "CMPF $crx, $src1, $src2 \t// postalloc expanded" %} postalloc_expand %{ // // replaces @@ -12336,7 +12336,7 @@ instruct minF(regF dst, regF src1, regF src2) %{ predicate(PowerArchitecturePPC64 >= 9); ins_cost(DEFAULT_COST); - format %{ "MinF $dst, $src1, $src2" %} + format %{ "XSMINJDP $dst, $src1, $src2\t// MinF" %} size(4); ins_encode %{ __ xsminjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr()); @@ -12349,7 +12349,7 @@ instruct minD(regD dst, regD src1, regD src2) %{ predicate(PowerArchitecturePPC64 >= 9); ins_cost(DEFAULT_COST); - format %{ "MinD $dst, $src1, $src2" %} + format %{ "XSMINJDP $dst, $src1, $src2\t// MinD" %} size(4); ins_encode %{ __ xsminjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr()); @@ -12362,7 +12362,7 @@ instruct maxF(regF dst, regF src1, regF src2) %{ predicate(PowerArchitecturePPC64 >= 9); ins_cost(DEFAULT_COST); - format %{ "MaxF $dst, $src1, $src2" %} + format %{ "XSMAXJDP $dst, $src1, $src2\t// MaxF" %} size(4); ins_encode %{ __ xsmaxjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr()); @@ -12375,7 +12375,7 @@ instruct maxD(regD dst, regD src1, regD src2) %{ predicate(PowerArchitecturePPC64 >= 9); ins_cost(DEFAULT_COST); - format %{ "MaxD $dst, $src1, $src2" %} + format %{ "XSMAXJDP $dst, $src1, $src2\t// MaxD" %} size(4); ins_encode %{ __ xsmaxjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr()); @@ -13905,7 +13905,7 @@ instruct vfma2D_neg2(vecX dst, vecX src1, vecX src2) %{ instruct overflowAddL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{ match(Set cr0 (OverflowAddL op1 op2)); - format %{ "add_ $op1, $op2\t# overflow check long" %} + format %{ "ADD_ $op1, $op2\t# overflow check long" %} size(12); ins_encode %{ __ li(R0, 0); @@ -13918,7 +13918,7 @@ instruct overflowAddL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{ instruct overflowSubL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{ match(Set cr0 (OverflowSubL op1 op2)); - format %{ "subfo_ R0, $op2, $op1\t# overflow check long" %} + format %{ "SUBFO_ R0, $op2, $op1\t# overflow check long" %} size(12); ins_encode %{ __ li(R0, 0); @@ -13931,7 +13931,7 @@ instruct overflowSubL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{ instruct overflowNegL_reg(flagsRegCR0 cr0, immL_0 zero, iRegLsrc op2) %{ match(Set cr0 (OverflowSubL zero op2)); - format %{ "nego_ R0, $op2\t# overflow check long" %} + format %{ "NEGO_ R0, $op2\t# overflow check long" %} size(12); ins_encode %{ __ li(R0, 0); @@ -13944,7 +13944,7 @@ instruct overflowNegL_reg(flagsRegCR0 cr0, immL_0 zero, iRegLsrc op2) %{ instruct overflowMulL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{ match(Set cr0 (OverflowMulL op1 op2)); - format %{ "mulldo_ R0, $op1, $op2\t# overflow check long" %} + format %{ "MULLDO_ R0, $op1, $op2\t# overflow check long" %} size(12); ins_encode %{ __ li(R0, 0); @@ -14321,7 +14321,7 @@ instruct ForwardExceptionjmp() match(ForwardException); ins_cost(CALL_COST); - format %{ "Jmp forward_exception_stub" %} + format %{ "JMP forward_exception_stub" %} ins_encode %{ __ set_inst_mark(); __ b64_patchable(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); @@ -14349,7 +14349,7 @@ instruct RethrowException() %{ match(Rethrow); ins_cost(CALL_COST); - format %{ "Jmp rethrow_stub" %} + format %{ "JMP rethrow_stub" %} ins_encode %{ __ set_inst_mark(); __ b64_patchable((address)OptoRuntime::rethrow_stub(), relocInfo::runtime_call_type); From 713664fa9315c07caef65aecf6eb6a9e266f1bf0 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Fri, 13 Mar 2026 10:28:25 +0000 Subject: [PATCH 39/97] 8379804: Refactor jdk/com/sun tests to use JUnit Reviewed-by: dfuchs --- .../provider/Cipher/KeyWrap/NISTWrapKAT.java | 24 ++++++------ .../KeyFactory/PBEKeyDestroyTest.java | 36 ++++++++++-------- .../sun/jndi/ldap/LdapPoolTimeoutTest.java | 4 +- .../com/sun/jndi/ldap/LdapTimeoutTest.java | 38 +++++++++---------- .../jndi/ldap/NamingExceptionMessageTest.java | 21 +++++----- 5 files changed, 64 insertions(+), 59 deletions(-) diff --git a/test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/NISTWrapKAT.java b/test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/NISTWrapKAT.java index a79f48196d13..09fd0c440a95 100644 --- a/test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/NISTWrapKAT.java +++ b/test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/NISTWrapKAT.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,20 +24,21 @@ /* * @test * @bug 5008156 8248268 - * @run testng NISTWrapKAT + * @run junit NISTWrapKAT * @summary Verify that the AES-Key-Wrap and AES-Key-Wrap-Pad ciphers * work as expected using NIST test vectors. * @author Valerie Peng */ import java.security.Key; import java.security.AlgorithmParameters; -import javax.crypto.*; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; import java.util.Arrays; import java.math.BigInteger; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.testng.Assert; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class NISTWrapKAT { @@ -101,8 +102,7 @@ private static byte[] toBytes(String hex, int hexLen) { } } - @DataProvider - public Object[][] testData() { + static Object[][] testData() { return new Object[][] { { "AESWrap", KEK, 16, DATA, 16, KW_AES128_128 }, { "AESWrap", KEK, 24, DATA, 16, KW_AES192_128 }, @@ -249,7 +249,8 @@ public Object[][] testData() { }; } - @Test(dataProvider = "testData") + @ParameterizedTest + @MethodSource("testData") public void testKeyWrap(String algo, String key, int keyLen, String data, int dataLen, String expected) throws Exception { System.out.println("Testing " + algo + " Cipher with wrapping " + @@ -311,7 +312,8 @@ public void testKeyWrap(String algo, String key, int keyLen, } } - @Test(dataProvider = "testData") + @ParameterizedTest + @MethodSource("testData") public void testEnc(String algo, String key, int keyLen, String data, int dataLen, String expected) throws Exception { System.out.println("Testing " + algo + " Cipher with enc " + diff --git a/test/jdk/com/sun/crypto/provider/KeyFactory/PBEKeyDestroyTest.java b/test/jdk/com/sun/crypto/provider/KeyFactory/PBEKeyDestroyTest.java index da266c147fb2..41a86ccec644 100644 --- a/test/jdk/com/sun/crypto/provider/KeyFactory/PBEKeyDestroyTest.java +++ b/test/jdk/com/sun/crypto/provider/KeyFactory/PBEKeyDestroyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,13 +26,17 @@ * @bug 8312306 * @summary Check the destroy()/isDestroyed() of the PBEKey impl from SunJCE * @library /test/lib - * @run testng/othervm PBEKeyDestroyTest + * @run junit/othervm PBEKeyDestroyTest */ -import javax.crypto.*; -import javax.crypto.spec.*; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; import java.nio.charset.StandardCharsets; -import org.testng.Assert; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; public class PBEKeyDestroyTest { @@ -48,22 +52,22 @@ public void test() throws Exception { SecretKey key2 = skf.generateSecret(keySpec); // should be equal - Assert.assertFalse(key1.isDestroyed()); - Assert.assertFalse(key2.isDestroyed()); - Assert.assertTrue(key1.equals(key2)); - Assert.assertTrue(key2.equals(key1)); + assertFalse(key1.isDestroyed()); + assertFalse(key2.isDestroyed()); + assertEquals(key1, key2); + assertEquals(key2, key1); // destroy key1 key1.destroy(); - Assert.assertTrue(key1.isDestroyed()); - Assert.assertFalse(key1.equals(key2)); - Assert.assertFalse(key2.equals(key1)); + assertTrue(key1.isDestroyed()); + assertNotEquals(key1, key2); + assertNotEquals(key2, key1); // also destroy key2 key2.destroy(); - Assert.assertTrue(key2.isDestroyed()); - Assert.assertFalse(key1.equals(key2)); - Assert.assertFalse(key2.equals(key1)); + assertTrue(key2.isDestroyed()); + assertNotEquals(key1, key2); + assertNotEquals(key2, key1); // call destroy again to make sure no unexpected exceptions key2.destroy(); diff --git a/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java b/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java index b139f12da25b..7d65298faa6f 100644 --- a/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java +++ b/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java @@ -27,10 +27,10 @@ * @summary Multi-threaded client timeout tests for ldap pool * @library /test/lib * lib/ - * @run testng/othervm/timeout=480 LdapPoolTimeoutTest + * @run junit/othervm/timeout=480 LdapPoolTimeoutTest */ -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import javax.naming.Context; import javax.naming.NamingException; diff --git a/test/jdk/com/sun/jndi/ldap/LdapTimeoutTest.java b/test/jdk/com/sun/jndi/ldap/LdapTimeoutTest.java index 39bf24b9dbf0..a4b032d0974a 100644 --- a/test/jdk/com/sun/jndi/ldap/LdapTimeoutTest.java +++ b/test/jdk/com/sun/jndi/ldap/LdapTimeoutTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2019, 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 @@ -25,14 +25,17 @@ * @test * @library /test/lib * lib/ - * @run testng/othervm LdapTimeoutTest + * @run junit/othervm LdapTimeoutTest * @bug 7094377 8000487 6176036 7056489 8151678 * @summary Timeout tests for ldap */ -import org.testng.Assert; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; import javax.naming.Context; import javax.naming.NamingException; @@ -60,8 +63,6 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static jdk.test.lib.Utils.adjustTimeout; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.expectThrows; public class LdapTimeoutTest { @@ -90,7 +91,7 @@ public class LdapTimeoutTest { assert (2 * CONNECT_MILLIS + READ_MILLIS + TOLERANCE < INFINITY_MILLIS); } - @BeforeTest + @BeforeEach public void beforeTest() { startAuxiliaryDiagnosticOutput(); } @@ -98,11 +99,6 @@ public void beforeTest() { /* * These are timeout tests and they are run in parallel to reduce the total * amount of run time. - * - * Currently it doesn't seem possible to instruct JTREG to run TestNG test - * methods in parallel. That said, this JTREG test is still - * a "TestNG-flavored" test for the sake of having org.testng.Assert - * capability. */ @Test public void test() throws Exception { @@ -190,11 +186,11 @@ static void test4() throws Exception { env.put(Context.PROVIDER_URL, urlTo(server)); server.start(); server.starting().join(); - Assert.ThrowingRunnable completion = + Executable completion = () -> assertCompletion(CONNECT_MILLIS, 2 * CONNECT_MILLIS + TOLERANCE, () -> new InitialDirContext(env)); - NamingException e = expectThrows(NamingException.class, completion); + NamingException e = assertThrows(NamingException.class, completion); String msg = e.getMessage(); assertTrue(msg != null && msg.contains("timeout") && msg.contains(String.valueOf(CONNECT_MILLIS)), @@ -214,11 +210,11 @@ static void test5() throws Exception { InitialDirContext ctx = new InitialDirContext(env); SearchControls scl = new SearchControls(); scl.setSearchScope(SearchControls.SUBTREE_SCOPE); - Assert.ThrowingRunnable completion = + Executable completion = () -> assertCompletion(READ_MILLIS, READ_MILLIS + TOLERANCE, () -> ctx.search("ou=People,o=JNDITutorial", "(objectClass=*)", scl)); - NamingException e = expectThrows(NamingException.class, completion); + NamingException e = assertThrows(NamingException.class, completion); String msg = e.getMessage(); assertTrue(msg != null && msg.contains("timeout") && msg.contains(String.valueOf(READ_MILLIS)), @@ -235,11 +231,11 @@ static void test6() throws Exception { env.put(Context.PROVIDER_URL, urlTo(server)); server.start(); server.starting().join(); - Assert.ThrowingRunnable completion = + Executable completion = () -> assertCompletion(CONNECT_MILLIS, 2 * CONNECT_MILLIS + TOLERANCE, () -> new InitialDirContext(env)); - NamingException e = expectThrows(NamingException.class, completion); + NamingException e = assertThrows(NamingException.class, completion); String msg = e.getMessage(); assertTrue(msg != null && msg.contains("timeout") && msg.contains(String.valueOf(CONNECT_MILLIS)), @@ -261,11 +257,11 @@ static void test7() throws Exception { env.put(Context.PROVIDER_URL, urlTo(server)); server.start(); server.starting().join(); - Assert.ThrowingRunnable completion = + Executable completion = () -> assertCompletion(CONNECT_MILLIS, 2 * CONNECT_MILLIS + TOLERANCE, () -> new InitialDirContext(env)); - NamingException e = expectThrows(NamingException.class, completion); + NamingException e = assertThrows(NamingException.class, completion); String msg = e.getMessage(); assertTrue(msg != null && msg.contains("timeout") && msg.contains(String.valueOf(CONNECT_MILLIS)), diff --git a/test/jdk/com/sun/jndi/ldap/NamingExceptionMessageTest.java b/test/jdk/com/sun/jndi/ldap/NamingExceptionMessageTest.java index c165593ad5e5..1250548b60a1 100644 --- a/test/jdk/com/sun/jndi/ldap/NamingExceptionMessageTest.java +++ b/test/jdk/com/sun/jndi/ldap/NamingExceptionMessageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,7 @@ * @summary Test that CommunicationException is thrown when connection is timed out or closed/cancelled, * and it's text matches the failure reason. * @library /test/lib lib - * @run testng NamingExceptionMessageTest + * @run junit NamingExceptionMessageTest */ import javax.naming.CommunicationException; @@ -45,8 +45,11 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import org.testng.annotations.Test; -import org.testng.Assert; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import jdk.test.lib.net.URIBuilder; public class NamingExceptionMessageTest { @@ -58,9 +61,9 @@ public void timeoutMessageTest() throws Exception { ldapServer.awaitStartup(); var env = ldapServer.getInitialLdapCtxEnvironment(TIMEOUT_VALUE); var communicationException = - Assert.expectThrows(CommunicationException.class, () -> new InitialDirContext(env)); + assertThrows(CommunicationException.class, () -> new InitialDirContext(env)); System.out.println("Got CommunicationException:" + communicationException); - Assert.assertEquals(communicationException.getMessage(), EXPECTED_TIMEOUT_MESSAGE); + assertEquals(EXPECTED_TIMEOUT_MESSAGE, communicationException.getMessage()); } } @@ -70,7 +73,7 @@ public void connectionClosureMessageTest() throws Exception { ldapServer.start(); ldapServer.awaitStartup(); var env = ldapServer.getInitialLdapCtxEnvironment(0); - var namingException = Assert.expectThrows(NamingException.class, () -> new InitialDirContext(env)); + var namingException = assertThrows(NamingException.class, () -> new InitialDirContext(env)); if (namingException instanceof ServiceUnavailableException) { // If naming exception is ServiceUnavailableException it could mean // that the connection was closed on test server-side before LDAP client starts @@ -78,11 +81,11 @@ public void connectionClosureMessageTest() throws Exception { System.out.println("Got ServiceUnavailableException: Test PASSED"); } else { // If exception is not ServiceUnavailableException, CommunicationException is expected - Assert.assertTrue(namingException instanceof CommunicationException); + assertInstanceOf(CommunicationException.class, namingException); var communicationException = (CommunicationException) namingException; System.out.println("Got CommunicationException:" + communicationException); // Check exception message - Assert.assertEquals(communicationException.getMessage(), EXPECTED_CLOSURE_MESSAGE); + assertEquals(EXPECTED_CLOSURE_MESSAGE, communicationException.getMessage()); } } } From 1ea8ef92bcfac4f5c032b6d8bd5a5ba99a6abe2f Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Fri, 13 Mar 2026 10:57:14 +0000 Subject: [PATCH 40/97] 8379781: G1: Full GC does not print partial array task stats Reviewed-by: iwalulya, kbarrett --- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 4 ++-- src/hotspot/share/gc/g1/g1FullCollector.cpp | 8 +++++++- src/hotspot/share/gc/g1/g1FullGCMarker.hpp | 1 + src/hotspot/share/gc/g1/g1ParScanThreadState.cpp | 2 +- src/hotspot/share/gc/g1/g1YoungCollector.cpp | 2 +- src/hotspot/share/gc/parallel/psCompactionManager.cpp | 4 ++-- src/hotspot/share/gc/parallel/psPromotionManager.cpp | 4 ++-- src/hotspot/share/gc/shared/partialArrayTaskStats.cpp | 2 +- 8 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index a2114d8d6b64..33b21d8348d8 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -682,14 +682,14 @@ void G1ConcurrentMark::set_concurrency_and_phase(uint active_tasks, bool concurr #if TASKQUEUE_STATS void G1ConcurrentMark::print_and_reset_taskqueue_stats() { - _task_queues->print_and_reset_taskqueue_stats("G1ConcurrentMark Oop Queue"); + _task_queues->print_and_reset_taskqueue_stats("Concurrent Mark"); auto get_pa_stats = [&](uint i) { return _tasks[i]->partial_array_task_stats(); }; PartialArrayTaskStats::log_set(_max_num_tasks, get_pa_stats, - "G1ConcurrentMark Partial Array Task Stats"); + "Concurrent Mark Partial Array"); for (uint i = 0; i < _max_num_tasks; ++i) { get_pa_stats(i)->reset(); diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index e8498250f85f..c835dd159a69 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -353,7 +353,13 @@ void G1FullCollector::phase1_mark_live_objects() { scope()->tracer()->report_object_count_after_gc(&_is_alive, _heap->workers()); } #if TASKQUEUE_STATS - marking_task_queues()->print_and_reset_taskqueue_stats("Marking Task Queue"); + marking_task_queues()->print_and_reset_taskqueue_stats("Full GC"); + + auto get_stats = [&](uint i) { + return marker(i)->partial_array_splitter().stats(); + }; + PartialArrayTaskStats::log_set(_num_workers, get_stats, + "Full GC Partial Array"); #endif } diff --git a/src/hotspot/share/gc/g1/g1FullGCMarker.hpp b/src/hotspot/share/gc/g1/g1FullGCMarker.hpp index 5973cc841c56..82fe36553190 100644 --- a/src/hotspot/share/gc/g1/g1FullGCMarker.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCMarker.hpp @@ -89,6 +89,7 @@ class G1FullGCMarker : public CHeapObj { ~G1FullGCMarker(); G1MarkTasksQueue* task_queue() { return &_task_queue; } + PartialArraySplitter& partial_array_splitter() { return _partial_array_splitter; } // Marking entry points template inline void mark_and_push(T* p); diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index 75a8ef1a336a..cb857dc6eabd 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -741,7 +741,7 @@ void G1ParScanThreadStateSet::print_partial_array_task_stats() { return state_for_worker(i)->partial_array_task_stats(); }; PartialArrayTaskStats::log_set(_num_workers, get_stats, - "Partial Array Task Stats"); + "Young GC Partial Array"); } #endif // TASKQUEUE_STATS diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index a9a81f846af5..71a76a2e48b6 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -1064,6 +1064,7 @@ void G1YoungCollector::post_evacuate_collection_set(G1EvacInfo* evacuation_info, allocator()->release_gc_alloc_regions(evacuation_info); #if TASKQUEUE_STATS + _g1h->task_queues()->print_and_reset_taskqueue_stats("Young GC"); // Logging uses thread states, which are deleted by cleanup, so this must // be done before cleanup. per_thread_states->print_partial_array_task_stats(); @@ -1183,5 +1184,4 @@ void G1YoungCollector::collect() { policy()->record_young_collection_end(_concurrent_operation_is_full_mark, evacuation_alloc_failed(), _allocation_word_size); } - TASKQUEUE_STATS_ONLY(_g1h->task_queues()->print_and_reset_taskqueue_stats("Oop Queue");) } diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.cpp b/src/hotspot/share/gc/parallel/psCompactionManager.cpp index b8ea47eeb099..0108f1a97629 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.cpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.cpp @@ -203,13 +203,13 @@ void ParCompactionManager::remove_all_shadow_regions() { #if TASKQUEUE_STATS void ParCompactionManager::print_and_reset_taskqueue_stats() { - marking_stacks()->print_and_reset_taskqueue_stats("Marking Stacks"); + marking_stacks()->print_and_reset_taskqueue_stats("Full GC"); auto get_pa_stats = [&](uint i) { return _manager_array[i]->partial_array_task_stats(); }; PartialArrayTaskStats::log_set(ParallelGCThreads, get_pa_stats, - "Partial Array Task Stats"); + "Full GC Partial Array"); uint parallel_gc_threads = ParallelScavengeHeap::heap()->workers().max_workers(); for (uint i = 0; i < parallel_gc_threads; ++i) { get_pa_stats(i)->reset(); diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.cpp b/src/hotspot/share/gc/parallel/psPromotionManager.cpp index d6208755374b..39fcc5556c62 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.cpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.cpp @@ -138,13 +138,13 @@ bool PSPromotionManager::post_scavenge(YoungGCTracer& gc_tracer) { #if TASKQUEUE_STATS void PSPromotionManager::print_and_reset_taskqueue_stats() { - stack_array_depth()->print_and_reset_taskqueue_stats("Oop Queue"); + stack_array_depth()->print_and_reset_taskqueue_stats("Young GC"); auto get_pa_stats = [&](uint i) { return manager_array(i)->partial_array_task_stats(); }; PartialArrayTaskStats::log_set(ParallelGCThreads, get_pa_stats, - "Partial Array Task Stats"); + "Young GC Partial Array"); for (uint i = 0; i < ParallelGCThreads; ++i) { get_pa_stats(i)->reset(); } diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStats.cpp b/src/hotspot/share/gc/shared/partialArrayTaskStats.cpp index ac8a380ec9ad..090430963c62 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStats.cpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStats.cpp @@ -64,7 +64,7 @@ static const char* const stats_hdr[] = { }; void PartialArrayTaskStats::print_header(outputStream* s, const char* title) { - s->print_cr("%s:", title); + s->print_cr("GC Task Stats %s", title); for (uint i = 0; i < ARRAY_SIZE(stats_hdr); ++i) { s->print_cr("%s", stats_hdr[i]); } From 28830e2155106a328382d1796c2071d2efee236e Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Fri, 13 Mar 2026 13:29:11 +0000 Subject: [PATCH 41/97] 8379917: JFR: Method timing event can have incorrect minimum value Reviewed-by: mgronlun --- .../share/classes/jdk/jfr/internal/tracing/TimedMethod.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/TimedMethod.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/TimedMethod.java index bd4f22624447..2651f5b473af 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/TimedMethod.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/TimedMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,10 +46,10 @@ public void updateMinMax(long duration) { while (true) { long max = maximum.get(); if (duration <= max) { - return; + break; } if (maximum.weakCompareAndSetVolatile(max, duration)) { - return; + break; } } } From a211b0442dc292c4aa6c98d4da03ceb19528dd2d Mon Sep 17 00:00:00 2001 From: Guanqiang Han Date: Fri, 13 Mar 2026 14:59:03 +0000 Subject: [PATCH 42/97] 8378742: C2: constant folding for ModFloatingNode should be done in Value method Reviewed-by: roland, vlivanov, mchevalier --- src/hotspot/share/opto/divnode.cpp | 54 +++++++----------- src/hotspot/share/opto/divnode.hpp | 4 +- .../compiler/c2/irTests/ModDNodeTests.java | 54 +++++++++++++----- .../compiler/c2/irTests/ModFNodeTests.java | 56 +++++++++++++------ 4 files changed, 99 insertions(+), 69 deletions(-) diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index ed72d8a11cff..c38e784a7af6 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1592,41 +1592,25 @@ const Type* ModDNode::get_result_if_constant(const Type* dividend, const Type* d return TypeD::make(jdouble_cast(xr)); } -Node* ModFloatingNode::Ideal(PhaseGVN* phase, bool can_reshape) { - if (can_reshape) { - PhaseIterGVN* igvn = phase->is_IterGVN(); - - // Either input is TOP ==> the result is TOP - const Type* dividend_type = phase->type(dividend()); - const Type* divisor_type = phase->type(divisor()); - if (dividend_type == Type::TOP || divisor_type == Type::TOP) { - return phase->C->top(); - } - const Type* constant_result = get_result_if_constant(dividend_type, divisor_type); - if (constant_result != nullptr) { - return make_tuple_of_input_state_and_constant_result(igvn, constant_result); - } +const Type* ModFloatingNode::Value(PhaseGVN* phase) const { + const Type* t = CallLeafPureNode::Value(phase); + if (t == Type::TOP) { return Type::TOP; } + const Type* dividend_type = phase->type(dividend()); + const Type* divisor_type = phase->type(divisor()); + if (dividend_type == Type::TOP || divisor_type == Type::TOP) { + return Type::TOP; } - - return CallLeafPureNode::Ideal(phase, can_reshape); -} - -/* Give a tuple node for ::Ideal to return, made of the input state (control to return addr) - * and the given constant result. Idealization of projections will make sure to transparently - * propagate the input state and replace the result by the said constant. - */ -TupleNode* ModFloatingNode::make_tuple_of_input_state_and_constant_result(PhaseIterGVN* phase, const Type* con) const { - Node* con_node = phase->makecon(con); - TupleNode* tuple = TupleNode::make( - tf()->range(), - in(TypeFunc::Control), - in(TypeFunc::I_O), - in(TypeFunc::Memory), - in(TypeFunc::FramePtr), - in(TypeFunc::ReturnAdr), - con_node); - - return tuple; + const Type* constant_result = get_result_if_constant(dividend_type, divisor_type); + if (constant_result != nullptr) { + const TypeTuple* tt = t->is_tuple(); + uint cnt = tt->cnt(); + uint param_cnt = cnt - TypeFunc::Parms; + const Type** fields = TypeTuple::fields(param_cnt); + fields[TypeFunc::Parms] = constant_result; + if (param_cnt > 1) { fields[TypeFunc::Parms + 1] = Type::HALF; } + return TypeTuple::make(cnt, fields); + } + return t; } //============================================================================= diff --git a/src/hotspot/share/opto/divnode.hpp b/src/hotspot/share/opto/divnode.hpp index 43432b271a42..2598429716fd 100644 --- a/src/hotspot/share/opto/divnode.hpp +++ b/src/hotspot/share/opto/divnode.hpp @@ -175,8 +175,6 @@ class ModLNode : public DivModIntegerNode { // Base class for float and double modulus class ModFloatingNode : public CallLeafPureNode { - TupleNode* make_tuple_of_input_state_and_constant_result(PhaseIterGVN* phase, const Type* con) const; - protected: virtual Node* dividend() const = 0; virtual Node* divisor() const = 0; @@ -184,7 +182,7 @@ class ModFloatingNode : public CallLeafPureNode { public: ModFloatingNode(Compile* C, const TypeFunc* tf, address addr, const char* name); - Node* Ideal(PhaseGVN* phase, bool can_reshape) override; + const Type* Value(PhaseGVN* phase) const override; }; // Float Modulus diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ModDNodeTests.java b/test/hotspot/jtreg/compiler/c2/irTests/ModDNodeTests.java index 6b20b7829233..ac427ea3831a 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/ModDNodeTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/ModDNodeTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,7 @@ /* * @test - * @bug 8345766 + * @bug 8345766 8378742 * @key randomness * @summary Test that Ideal transformations of ModDNode are being performed as expected. * @library /test/lib / @@ -37,6 +37,7 @@ */ public class ModDNodeTests { public static final double q = Utils.getRandomInstance().nextDouble() * 100.0d; + public static volatile int volatileField; public static void main(String[] args) { TestFramework.run(); @@ -48,6 +49,7 @@ public static void main(String[] args) { "unusedResultAfterLoopOpt1", "unusedResultAfterLoopOpt2", "unusedResultAfterLoopOpt3", + "constantFoldInCCP" }) public void runMethod() { Asserts.assertEQ(constant(), q % 72.0d % 30.0d); @@ -61,6 +63,7 @@ public void runMethod() { Asserts.assertEQ(unusedResultAfterLoopOpt1(1.1d, 2.2d), 0.d); Asserts.assertEQ(unusedResultAfterLoopOpt2(1.1d, 2.2d), 0.d); Asserts.assertEQ(unusedResultAfterLoopOpt3(1.1d, 2.2d), 0.d); + Asserts.assertEQ(constantFoldInCCP(), 4.0d); } // Note: we used to check for ConD nodes in the IR. But that is a bit brittle: @@ -72,7 +75,7 @@ public void runMethod() { @Test @IR(counts = {IRNode.MOD_D, "2"}, phase = CompilePhase.AFTER_PARSING) - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public double constant() { // All constants available during parsing @@ -84,7 +87,7 @@ public double constant() { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_D, "1"}, phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public double alsoConstant() { // Make sure value is only available after second loop opts round @@ -102,7 +105,7 @@ public double alsoConstant() { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_D, "2"}, phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public double nanLeftConstant() { // Make sure value is only available after second loop opts round @@ -120,7 +123,7 @@ public double nanLeftConstant() { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_D, "2"}, phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public double nanRightConstant() { // Make sure value is only available after second loop opts round @@ -156,7 +159,7 @@ public double veryNotConstant(double x, double y) { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_D, "0"}, phase = CompilePhase.ITER_GVN1) // IGVN removes unused nodes - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public void unusedResult(double x, double y) { double unused = x % y; @@ -165,9 +168,9 @@ public void unusedResult(double x, double y) { @Test @IR(counts = {IRNode.MOD_D, "1"}, phase = CompilePhase.AFTER_PARSING) - @IR(counts = {IRNode.MOD_D, "0"}, + @IR(failOn = {IRNode.MOD_D}, phase = CompilePhase.ITER_GVN1) // IGVN removes unused nodes - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public void repeatedlyUnused(double x, double y) { double unused = 1.d; @@ -185,9 +188,9 @@ public void repeatedlyUnused(double x, double y) { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_D, "1"}, phase = CompilePhase.ITER_GVN2) - @IR(counts = {IRNode.MOD_D, "0"}, + @IR(failOn = {IRNode.MOD_D}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public double unusedResultAfterLoopOpt1(double x, double y) { double unused = x % y; @@ -210,9 +213,9 @@ public double unusedResultAfterLoopOpt1(double x, double y) { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_D, "1"}, phase = CompilePhase.AFTER_CLOOPS) - @IR(counts = {IRNode.MOD_D, "0"}, + @IR(failOn = {IRNode.MOD_D}, phase = CompilePhase.PHASEIDEALLOOP1) - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public double unusedResultAfterLoopOpt2(double x, double y) { int a = 77; @@ -235,9 +238,9 @@ public double unusedResultAfterLoopOpt2(double x, double y) { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_D, "2"}, phase = CompilePhase.AFTER_CLOOPS) // drop the useless one - @IR(counts = {IRNode.MOD_D, "0"}, + @IR(failOn = {IRNode.MOD_D}, phase = CompilePhase.PHASEIDEALLOOP1) // drop the rest - @IR(counts = {".*CallLeaf.*drem.*", "0"}, + @IR(failOn = {".*CallLeaf.*drem.*"}, phase = CompilePhase.BEFORE_MATCHING) public double unusedResultAfterLoopOpt3(double x, double y) { double unused = x % y; @@ -252,4 +255,25 @@ public double unusedResultAfterLoopOpt3(double x, double y) { int other = (b - 77) * (int)(x % y % 1.d); return (double)other; } + + @Test + @IR(failOn = {IRNode.CMP_D}, + phase = CompilePhase.CCP1) + @IR(failOn = {IRNode.MOD_D}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION) + public double constantFoldInCCP(){ + int i; + for (i = 2; i < 4; i *= 2) { + } + int j; + for (j = 2; j < 4; j *= 2) { + } + volatileField = 42; + double v1 = (double) i / 2; + double v2 = j; + double v = v1 % v2; + for (; v < v2; v *= 2) { + } + return v; + } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ModFNodeTests.java b/test/hotspot/jtreg/compiler/c2/irTests/ModFNodeTests.java index 2f69578c2f03..f703df3d04e1 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/ModFNodeTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/ModFNodeTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,7 @@ /* * @test - * @bug 8345766 + * @bug 8345766 8378742 * @key randomness * @summary Test that Ideal transformations of ModFNode are being performed as expected. * @library /test/lib / @@ -37,6 +37,7 @@ */ public class ModFNodeTests { public static final float q = Utils.getRandomInstance().nextFloat() * 100.0f; + public static volatile int volatileField; public static void main(String[] args) { TestFramework.run(); @@ -48,6 +49,7 @@ public static void main(String[] args) { "unusedResultAfterLoopOpt1", "unusedResultAfterLoopOpt2", "unusedResultAfterLoopOpt3", + "constantFoldInCCP" }) public void runMethod() { Asserts.assertEQ(constant(), q % 72.0f % 30.0f); @@ -61,6 +63,7 @@ public void runMethod() { Asserts.assertEQ(unusedResultAfterLoopOpt1(1.1f, 2.2f), 0.f); Asserts.assertEQ(unusedResultAfterLoopOpt2(1.1f, 2.2f), 0.f); Asserts.assertEQ(unusedResultAfterLoopOpt3(1.1f, 2.2f), 0.f); + Asserts.assertEQ(constantFoldInCCP(), 4.0f); } // Note: we used to check for ConF nodes in the IR. But that is a bit brittle: @@ -72,7 +75,7 @@ public void runMethod() { @Test @IR(counts = {IRNode.MOD_F, "2"}, phase = CompilePhase.AFTER_PARSING) - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public float constant() { // All constants available during parsing @@ -84,7 +87,7 @@ public float constant() { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_F, "1"}, phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public float alsoConstant() { // Make sure value is only available after second loop opts round @@ -102,7 +105,7 @@ public float alsoConstant() { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_F, "2"}, phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public float nanLeftConstant() { // Make sure value is only available after second loop opts round @@ -120,7 +123,7 @@ public float nanLeftConstant() { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_F, "2"}, phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public float nanRightConstant() { // Make sure value is only available after second loop opts round @@ -154,9 +157,9 @@ public float veryNotConstant(float x, float y) { @Test @IR(counts = {IRNode.MOD_F, "1"}, phase = CompilePhase.AFTER_PARSING) - @IR(counts = {IRNode.MOD_F, "0"}, + @IR(failOn = {IRNode.MOD_F}, phase = CompilePhase.ITER_GVN1) // IGVN removes unused nodes - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public void unusedResult(float x, float y) { float unused = x % y; @@ -165,9 +168,9 @@ public void unusedResult(float x, float y) { @Test @IR(counts = {IRNode.MOD_F, "1"}, phase = CompilePhase.AFTER_PARSING) - @IR(counts = {IRNode.MOD_F, "0"}, + @IR(failOn = {IRNode.MOD_F}, phase = CompilePhase.ITER_GVN1) // IGVN removes unused nodes - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public void repeatedlyUnused(float x, float y) { float unused = 1.f; @@ -185,9 +188,9 @@ public void repeatedlyUnused(float x, float y) { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_F, "1"}, phase = CompilePhase.ITER_GVN2) - @IR(counts = {IRNode.MOD_F, "0"}, + @IR(failOn = {IRNode.MOD_F}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public float unusedResultAfterLoopOpt1(float x, float y) { float unused = x % y; @@ -210,9 +213,9 @@ public float unusedResultAfterLoopOpt1(float x, float y) { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_F, "1"}, phase = CompilePhase.AFTER_CLOOPS) - @IR(counts = {IRNode.MOD_F, "0"}, + @IR(failOn = {IRNode.MOD_F}, phase = CompilePhase.PHASEIDEALLOOP1) - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public float unusedResultAfterLoopOpt2(float x, float y) { int a = 77; @@ -235,9 +238,9 @@ public float unusedResultAfterLoopOpt2(float x, float y) { phase = CompilePhase.AFTER_PARSING) @IR(counts = {IRNode.MOD_F, "2"}, phase = CompilePhase.AFTER_CLOOPS) // drop the useless one - @IR(counts = {IRNode.MOD_F, "0"}, + @IR(failOn = {IRNode.MOD_F}, phase = CompilePhase.PHASEIDEALLOOP1) // drop the rest - @IR(counts = {".*CallLeaf.*frem.*", "0"}, + @IR(failOn = {".*CallLeaf.*frem.*"}, phase = CompilePhase.BEFORE_MATCHING) public float unusedResultAfterLoopOpt3(float x, float y) { float unused = x % y; @@ -252,4 +255,25 @@ public float unusedResultAfterLoopOpt3(float x, float y) { int other = (b - 77) * (int)(x % y % 1.f); return (float)other; } + + @Test + @IR(failOn = {IRNode.CMP_F}, + phase = CompilePhase.CCP1) + @IR(failOn = {IRNode.MOD_F}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION) + public float constantFoldInCCP(){ + int i; + for (i = 2; i < 4; i *= 2) { + } + int j; + for (j = 2; j < 4; j *= 2) { + } + volatileField = 42; + float v1 = (float) i / 2; + float v2 = j; + float v = v1 % v2; + for (; v < v2; v *= 2) { + } + return v; + } } From e9446e15546aa37e7550dac293a3ed6131f2b500 Mon Sep 17 00:00:00 2001 From: Ben Perez Date: Fri, 13 Mar 2026 15:02:02 +0000 Subject: [PATCH 43/97] 8368841: X25519 implementation differs from the specification in RFC 7748 Reviewed-by: ascarpino, semery --- .../sun/security/ec/XDHPublicKeyImpl.java | 9 +++- .../sun/security/ec/XECOperations.java | 8 +++- test/jdk/sun/security/ec/xec/TestXDH.java | 45 ++++++++++++++---- test/jdk/sun/security/ec/xec/TestXECOps.java | 46 ++++++++++++++++--- test/lib/jdk/test/lib/Convert.java | 22 +-------- 5 files changed, 88 insertions(+), 42 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ec/XDHPublicKeyImpl.java b/src/java.base/share/classes/sun/security/ec/XDHPublicKeyImpl.java index e6f8961f412e..e161880f8832 100644 --- a/src/java.base/share/classes/sun/security/ec/XDHPublicKeyImpl.java +++ b/src/java.base/share/classes/sun/security/ec/XDHPublicKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,7 +54,11 @@ public final class XDHPublicKeyImpl extends X509Key implements XECPublicKey { this.paramSpec = new NamedParameterSpec(params.getName()); this.algid = new AlgorithmId(params.getOid()); - this.u = u.mod(params.getP()); + + // RFC 7748 Section 5 requires the MSB of `u` to be zeroed for X25519 + this.u = (params == XECParameters.X448) ? + u.mod(params.getP()) : + u.clearBit(255).mod(params.getP()); byte[] u_arr = this.u.toByteArray(); reverse(u_arr); @@ -72,6 +76,7 @@ public final class XDHPublicKeyImpl extends X509Key implements XECPublicKey { XECParameters params = XECParameters.get(InvalidKeyException::new, algid); this.paramSpec = new NamedParameterSpec(params.getName()); + // construct the BigInteger representation byte[] u_arr = getKey().toByteArray(); reverse(u_arr); diff --git a/src/java.base/share/classes/sun/security/ec/XECOperations.java b/src/java.base/share/classes/sun/security/ec/XECOperations.java index dd8aa482cc39..0ea39a10d40a 100644 --- a/src/java.base/share/classes/sun/security/ec/XECOperations.java +++ b/src/java.base/share/classes/sun/security/ec/XECOperations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -89,7 +89,11 @@ public BigInteger computePublic(byte[] k) { */ public byte[] encodedPointMultiply(byte[] k, BigInteger u) { pruneK(k); - ImmutableIntegerModuloP elemU = field.getElement(u); + + ImmutableIntegerModuloP elemU = (params == XECParameters.X448) ? + field.getElement(u) : + field.getElement(u.clearBit(255)); + return pointMultiply(k, elemU).asByteArray(params.getBytes()); } diff --git a/test/jdk/sun/security/ec/xec/TestXDH.java b/test/jdk/sun/security/ec/xec/TestXDH.java index a32da875fa58..30c3b4334064 100644 --- a/test/jdk/sun/security/ec/xec/TestXDH.java +++ b/test/jdk/sun/security/ec/xec/TestXDH.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,20 +23,32 @@ /* * @test - * @bug 8171277 8206915 + * @bug 8171277 8206915 8368841 * @summary Test XDH key agreement * @library /test/lib - * @build jdk.test.lib.Convert * @run main TestXDH */ -import java.security.*; -import java.security.spec.*; -import javax.crypto.*; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; +import java.security.PrivateKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.NamedParameterSpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.security.spec.XECPublicKeySpec; +import java.security.spec.XECPrivateKeySpec; +import javax.crypto.KeyAgreement; import java.util.Arrays; import java.util.HexFormat; -import jdk.test.lib.Convert; import jdk.test.lib.hexdump.ASN1Formatter; import jdk.test.lib.hexdump.HexPrinter; @@ -354,7 +366,6 @@ private static void runDiffieHellmanTest(String a_pri, throw new RuntimeException("fail: expected=" + result + ", actual=" + HexFormat.of().withUpperCase().formatHex(sharedSecret)); } - } private static void runDiffieHellmanTest(String curveName, String a_pri, @@ -365,9 +376,8 @@ private static void runDiffieHellmanTest(String curveName, String a_pri, KeySpec privateSpec = new XECPrivateKeySpec(paramSpec, HexFormat.of().parseHex(a_pri)); PrivateKey privateKey = kf.generatePrivate(privateSpec); - boolean clearHighBit = curveName.equals("X25519"); KeySpec publicSpec = new XECPublicKeySpec(paramSpec, - Convert.hexStringToBigInteger(clearHighBit, b_pub)); + hexStringToBigInteger(b_pub)); PublicKey publicKey = kf.generatePublic(publicSpec); byte[] encodedPrivateKey = privateKey.getEncoded(); @@ -394,6 +404,21 @@ private static void runDiffieHellmanTest(String curveName, String a_pri, } } + /* + * Converts a hexidecimal string to the corresponding little-endian + * number as a BigInteger + */ + private static BigInteger hexStringToBigInteger(String str) { + BigInteger result = BigInteger.ZERO; + for (int i = 0; i < str.length() / 2; i++) { + int curVal = Character.digit(str.charAt(2 * i), 16); + curVal <<= 4; + curVal += Character.digit(str.charAt(2 * i + 1), 16); + result = result.add(BigInteger.valueOf(curVal).shiftLeft(8 * i)); + } + return result; + } + /* * Ensure that SunEC rejects parameters/points for the wrong curve * when the algorithm ID for a specific curve is specified. diff --git a/test/jdk/sun/security/ec/xec/TestXECOps.java b/test/jdk/sun/security/ec/xec/TestXECOps.java index cdbcf95fc0ba..a7533b89c02c 100644 --- a/test/jdk/sun/security/ec/xec/TestXECOps.java +++ b/test/jdk/sun/security/ec/xec/TestXECOps.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,8 +31,9 @@ * @run main TestXECOps */ -import sun.security.ec.*; - +import sun.security.ec.XECOperations; +import sun.security.ec.XECParameters; +import java.math.BigInteger; import java.security.spec.NamedParameterSpec; import java.util.*; import jdk.test.lib.Convert; @@ -93,6 +94,7 @@ private void runDiffieHellmanTest(String opName, String a_str, XECParameters.get(RuntimeException::new, paramSpec); XECOperations ops = new XECOperations(settings); + // Test encodedPointMultiply(byte[] k, byte[] u) byte[] basePoint = Convert.byteToByteArray(settings.getBasePoint(), settings.getBytes()); byte[] a = HexFormat.of().parseHex(a_str); @@ -101,18 +103,29 @@ private void runDiffieHellmanTest(String opName, String a_str, byte[] a_copy = Arrays.copyOf(a, a.length); byte[] b_copy = Arrays.copyOf(b, b.length); - byte[] basePoint_copy = Arrays.copyOf(basePoint, basePoint.length); byte[] resultA = ops.encodedPointMultiply(b, ops.encodedPointMultiply(a, basePoint)); byte[] resultB = ops.encodedPointMultiply(a_copy, - ops.encodedPointMultiply(b_copy, basePoint_copy)); + ops.encodedPointMultiply(b_copy, basePoint)); if (!Arrays.equals(resultA, expectedResult)) { throw new RuntimeException("fail"); } if (!Arrays.equals(resultB, expectedResult)) { throw new RuntimeException("fail"); } + + // Test encodedPointMultiply(byte[] k, BigInteger u) + reverse(basePoint); + BigInteger bp = new BigInteger(1, basePoint); + byte[] c = HexFormat.of().parseHex(a_str); + byte[] d = HexFormat.of().parseHex(b_str); + + byte[] res0 = ops.encodedPointMultiply(d, + ops.encodedPointMultiply(c, bp)); + if (!Arrays.equals(res0, expectedResult)) { + throw new RuntimeException("bigint fail"); + } } private void runTest(String opName, String k_in_str, @@ -125,12 +138,31 @@ private void runTest(String opName, String k_in_str, NamedParameterSpec paramSpec = new NamedParameterSpec(opName); XECParameters settings = XECParameters.get(RuntimeException::new, paramSpec); + + // Test encodedPointMultiply(byte[] k, byte[] u) XECOperations ops = new XECOperations(settings); - byte[] u_out = ops.encodedPointMultiply(k_in, u_in); + byte[] res0 = ops.encodedPointMultiply(k_in, u_in); + + if (!Arrays.equals(res0, u_out_expected)) { + throw new RuntimeException("fail"); + } + + // Test encodedPointMultiply(byte[] k, BigInteger u) + reverse(u_in); + BigInteger u = new BigInteger(1, u_in); + byte[] res1 = ops.encodedPointMultiply(k_in, u); - if (!Arrays.equals(u_out, u_out_expected)) { + if (!Arrays.equals(res1, u_out_expected)) { throw new RuntimeException("fail"); } } + + private static void reverse(byte[] array) { + for (int i = 0; i < array.length / 2; i++) { + byte temp = array[i]; + array[i] = array[array.length - i - 1]; + array[array.length - i - 1] = temp; + } + } } diff --git a/test/lib/jdk/test/lib/Convert.java b/test/lib/jdk/test/lib/Convert.java index 7ed1112c94d5..2738e04c2f08 100644 --- a/test/lib/jdk/test/lib/Convert.java +++ b/test/lib/jdk/test/lib/Convert.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,26 +41,6 @@ public static byte[] byteToByteArray(byte v, int length) { return result; } - /* - * Convert a hexadecimal string to the corresponding little-ending number - * as a BigInteger. The clearHighBit argument determines whether the most - * significant bit of the highest byte should be set to 0 in the result. - */ - public static - BigInteger hexStringToBigInteger(boolean clearHighBit, String str) { - BigInteger result = BigInteger.ZERO; - for (int i = 0; i < str.length() / 2; i++) { - int curVal = Character.digit(str.charAt(2 * i), 16); - curVal <<= 4; - curVal += Character.digit(str.charAt(2 * i + 1), 16); - if (clearHighBit && i == str.length() / 2 - 1) { - curVal &= 0x7F; - } - result = result.add(BigInteger.valueOf(curVal).shiftLeft(8 * i)); - } - return result; - } - private static EdECPoint byteArrayToEdPoint(byte[] arr) { byte msb = arr[arr.length - 1]; boolean xOdd = (msb & 0x80) != 0; From 92fafa4ae3a02bb35d69915342432a948b643c4b Mon Sep 17 00:00:00 2001 From: Anton Artemov Date: Fri, 13 Mar 2026 15:28:20 +0000 Subject: [PATCH 44/97] 8377603: Review worst-case testing of inverse hyperbolic methods Reviewed-by: darcy --- test/jdk/java/lang/Math/HyperbolicTests.java | 2 +- .../java/lang/StrictMath/HyperbolicTests.java | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/test/jdk/java/lang/Math/HyperbolicTests.java b/test/jdk/java/lang/Math/HyperbolicTests.java index ac5526c19532..f45f0d25a485 100644 --- a/test/jdk/java/lang/Math/HyperbolicTests.java +++ b/test/jdk/java/lang/Math/HyperbolicTests.java @@ -2233,7 +2233,7 @@ static int testAtanh() { } /** - * Test accuracy of {Math, StrictMath}.tanh using quad precision + * Test accuracy of {Math, StrictMath}.atanh using quad precision * tanh implementation as the reference. There are additional tests. * The specified accuracy is 2.5 ulps. * diff --git a/test/jdk/java/lang/StrictMath/HyperbolicTests.java b/test/jdk/java/lang/StrictMath/HyperbolicTests.java index 4be1b350c1d5..9eaed0a081a4 100644 --- a/test/jdk/java/lang/StrictMath/HyperbolicTests.java +++ b/test/jdk/java/lang/StrictMath/HyperbolicTests.java @@ -595,6 +595,13 @@ private static int testAsinh() { {0x1.fffffffffff92p+1, 0x1.0c1f8a6e80edp+1}, {0x1.0108b83c4bbc8p-1, 0x1.ee9c256f3947ep-2}, {-0x1.c41e527b70f43p-3, -0x1.c0863c7dece22p-3}, + + // Julia worst case + {-0x1.02657ff36d5f3p-2, -0x1.ff75bb69b0bf6p-3}, + + // Empirical worst-case points in other libraries with + // larger worst-case errors than FDLIBM + {0x1.0ab3fc30267c2p-1, 0x1.ffd39fc024fbp-2}, }; for (double[] testCase: testCases) { @@ -643,6 +650,15 @@ private static int testAcosh() { {0x1.ff66e0de4dc6fp+1023, 0x1.633cc2ae1c934p+9}, {0x1.f97ccb0aef314p+11, 0x1.1ff088806d82ep+3}, {0x1.fdf28623ef923p+1021, 0x1.628af341989dap+9}, + + // Julia worst case + {0x1.0001ff6afc4bap+0, 0x1.ffb5238940116p-8}, + + // Empirical worst-case points in other libraries with + // larger worst-case errors than FDLIBM + {0x1.0007fd4307b75p+0, 0x1.ffa704d280935p-7}, + {0x1.071334daf83adp+0, 0x1.e063ca7176ffdp-3}, + {0x1.1d7bc19163966p+0, 0x1.e6db4d68a00dcp-2}, }; for (double[] testCase: testCases) { @@ -709,6 +725,15 @@ private static int testAtanh() { {0x1.ffffffffffffep-2, 0x1.193ea7aad0309p-1}, {0x1.ffffffffffffbp-1, 0x1.1e9067763b478p+4}, {0x1.ffffffffffffep-1, 0x1.25e4f7b2737fap+4}, + + // Julia worst case + {-0x1.f97fabc0650c4p-4, -0x1.fc16bb2fd3672p-4}, + + // Empirical worst-case points in other libraries with + // larger worst-case errors than FDLIBM + {0x1.ffd834a270fp-10, 0x1.ffd85f432fed2p-10}, + {-0x1.ffbe8dd88527fp-9, -0x1.ffbf38422c2dbp-9}, + {-0x1.e7c1f36602014p-4, -0x1.ea153d6c96817p-4}, }; for (double[] testCase: testCases) From 1b81d0ac1d2b28e5822665b179b3d7986f077e80 Mon Sep 17 00:00:00 2001 From: Ozan Cetin Date: Fri, 13 Mar 2026 15:46:41 +0000 Subject: [PATCH 45/97] 8379544: C2: URShiftI Ideal optimization opportunity for - 3rd level notification required Reviewed-by: qamai, dfenacci --- src/hotspot/share/opto/phaseX.cpp | 13 ++++++ .../c2/gvn/MissedURShiftIAddILShiftIdeal.java | 43 +++++++++++++++++-- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 082746f80b73..29961e152b3b 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2588,6 +2588,19 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ return u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL; }); } + // If changed LShiftI/LShiftL inputs, check AddI/AddL users for their + // URShiftI/URShiftL users for "((x << z) + y) >>> z" optimization opportunity + // (see URShiftINode::Ideal). Handles the case where the LShift input changes. + if (use_op == Op_LShiftI || use_op == Op_LShiftL) { + for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { + Node* add = use->fast_out(i2); + if (add->Opcode() == Op_AddI || add->Opcode() == Op_AddL) { + add_users_to_worklist_if(worklist, add, [](Node* u) { + return u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL; + }); + } + } + } // If changed AndI/AndL inputs, check RShift/URShift users for "(x & mask) >> shift" optimization opportunity if (use_op == Op_AndI || use_op == Op_AndL) { add_users_to_worklist_if(worklist, use, [](Node* u) { diff --git a/test/hotspot/jtreg/compiler/c2/gvn/MissedURShiftIAddILShiftIdeal.java b/test/hotspot/jtreg/compiler/c2/gvn/MissedURShiftIAddILShiftIdeal.java index dd446d8bb253..a953ea3bccaf 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/MissedURShiftIAddILShiftIdeal.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/MissedURShiftIAddILShiftIdeal.java @@ -30,7 +30,7 @@ /* * @test - * @bug 8378413 + * @bug 8378413 8379544 * @key randomness * @summary Verify that URShift{I,L}Node::Ideal optimizes ((x << C) + y) >>> C * regardless of Add input order, i.e. it is commutative w.r.t. the addition. @@ -50,8 +50,8 @@ public static void main(String[] args) { framework.start(); } - @Run(test = {"testI", "testICommuted", "testIComputedY", - "testL", "testLCommuted", "testLComputedY"}) + @Run(test = {"testI", "testICommuted", "testIComputedY", "testIComputedX", + "testL", "testLCommuted", "testLComputedY", "testLComputedX"}) public void runMethod() { int xi = RANDOM.nextInt(); int yi = RANDOM.nextInt(); @@ -71,6 +71,7 @@ public void assertResultI(int x, int y, int a, int b) { Asserts.assertEQ(((x << 3) + y) >>> 3, testI(x, y)); Asserts.assertEQ((y + (x << 5)) >>> 5, testICommuted(x, y)); Asserts.assertEQ(((x << 7) + (a ^ b)) >>> 7, testIComputedY(x, a, b)); + Asserts.assertEQ((((a ^ b) << 19) + y) >>> 19, testIComputedX(a, b, y)); } @DontCompile @@ -78,6 +79,7 @@ public void assertResultL(long x, long y, long a, long b) { Asserts.assertEQ(((x << 9) + y) >>> 9, testL(x, y)); Asserts.assertEQ((y + (x << 11)) >>> 11, testLCommuted(x, y)); Asserts.assertEQ(((x << 13) + (a ^ b)) >>> 13, testLComputedY(x, a, b)); + Asserts.assertEQ((((a ^ b) << 19) + y) >>> 19, testLComputedX(a, b, y)); } @Test @@ -135,4 +137,39 @@ static long testLCommuted(long x, long y) { static long testLComputedY(long x, long a, long b) { return ((x << 13) + (a ^ b)) >>> 13; } + + @Test + // (((a ^ b) << v) + y) >>> 19 => ((a ^ b) + (y >>> 19)) & mask + // v is only known to be 19 after loop optimization, so LShiftI's shift count + // changes mid-IGVN. URShiftI must be notified through AddI (Case 2). + @IR(counts = {IRNode.LSHIFT_I, "0", + IRNode.URSHIFT_I, "1", + IRNode.AND_I, "1"}) + static int testIComputedX(int a, int b, int y) { + int u = 19; + int v = 0; + do { + u--; + v++; + } while (u > 0); + return (((a ^ b) << v) + y) >>> 19; + } + + @Test + // (((a ^ b) << v) + y) >>> 19 => ((a ^ b) + (y >>> 19)) & mask + // v is only known to be 19 after loop optimization, so LShiftL's shift count + // changes mid-IGVN. URShiftL must be notified through AddL (Case 2). + @IR(counts = {IRNode.LSHIFT_L, "0", + IRNode.URSHIFT_L, "1", + IRNode.AND_L, "1"}) + static long testLComputedX(long a, long b, long y) { + int u = 19; + int v = 0; + do { + u--; + v++; + } while (u > 0); + return (((a ^ b) << v) + y) >>> 19; + } + } From 06ea3858b1d031413e73b97e6e6edaa4c62142a6 Mon Sep 17 00:00:00 2001 From: Mahendra Chhipa Date: Fri, 13 Mar 2026 15:55:19 +0000 Subject: [PATCH 46/97] 8379528: Add @required in open/test/jdk/:jdk_sctp test, as these are applicable only on linux platforms Reviewed-by: mbaesken, syan --- test/jdk/com/sun/nio/sctp/SctpChannel/Bind.java | 3 ++- test/jdk/com/sun/nio/sctp/SctpChannel/CloseDescriptors.java | 2 +- test/jdk/com/sun/nio/sctp/SctpChannel/CommUp.java | 3 ++- test/jdk/com/sun/nio/sctp/SctpChannel/Connect.java | 3 ++- test/jdk/com/sun/nio/sctp/SctpChannel/Receive.java | 3 ++- test/jdk/com/sun/nio/sctp/SctpChannel/ReceiveIntoDirect.java | 3 ++- test/jdk/com/sun/nio/sctp/SctpChannel/Send.java | 3 ++- test/jdk/com/sun/nio/sctp/SctpChannel/Shutdown.java | 3 ++- test/jdk/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java | 3 ++- test/jdk/com/sun/nio/sctp/SctpMultiChannel/Branch.java | 3 ++- .../com/sun/nio/sctp/SctpMultiChannel/CloseDescriptors.java | 2 +- test/jdk/com/sun/nio/sctp/SctpMultiChannel/Send.java | 3 ++- test/jdk/com/sun/nio/sctp/SctpMultiChannel/SendFailed.java | 3 ++- .../com/sun/nio/sctp/SctpMultiChannel/SocketOptionTests.java | 3 ++- test/jdk/com/sun/nio/sctp/SctpServerChannel/Accept.java | 3 ++- .../com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java | 3 ++- 16 files changed, 30 insertions(+), 16 deletions(-) diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/Bind.java b/test/jdk/com/sun/nio/sctp/SctpChannel/Bind.java index ff5ca00846c7..a1eb2233b02e 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/Bind.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/Bind.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/CloseDescriptors.java b/test/jdk/com/sun/nio/sctp/SctpChannel/CloseDescriptors.java index 5d62b88f23e2..bf5872b06d3f 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/CloseDescriptors.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/CloseDescriptors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/CommUp.java b/test/jdk/com/sun/nio/sctp/SctpChannel/CommUp.java index 320e556f800c..67a946c15d05 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/CommUp.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/CommUp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* @test * @bug 6863110 + * @requires (os.family == "linux") * @library /test/lib * @summary Newly connected/accepted SctpChannel should fire OP_READ if registered with a Selector * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/Connect.java b/test/jdk/com/sun/nio/sctp/SctpChannel/Connect.java index 79b9453a5950..699ef3d09716 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/Connect.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/Connect.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/Receive.java b/test/jdk/com/sun/nio/sctp/SctpChannel/Receive.java index 48eb413a9ca7..896c339dcc08 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/Receive.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/Receive.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/ReceiveIntoDirect.java b/test/jdk/com/sun/nio/sctp/SctpChannel/ReceiveIntoDirect.java index 48d89c34ca4a..1c52f83b972f 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/ReceiveIntoDirect.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/ReceiveIntoDirect.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 @@ -23,6 +23,7 @@ /* @test * @bug 8034181 + * @requires (os.family == "linux") * @library /test/lib * @summary SIGBUS in SctpChannelImpl receive * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/Send.java b/test/jdk/com/sun/nio/sctp/SctpChannel/Send.java index 35a28f4eb38a..a04205a9463a 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/Send.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/Send.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/Shutdown.java b/test/jdk/com/sun/nio/sctp/SctpChannel/Shutdown.java index 0891851fe86f..75f0fa9a724f 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/Shutdown.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/Shutdown.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java b/test/jdk/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java index 857efbb63f5c..2e6192a0d0ed 100644 --- a/test/jdk/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java +++ b/test/jdk/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Branch.java b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Branch.java index 9beb8c74c3ed..199cbfc0fc19 100644 --- a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Branch.java +++ b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Branch.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/CloseDescriptors.java b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/CloseDescriptors.java index fdafec807458..f6fffe700c56 100644 --- a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/CloseDescriptors.java +++ b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/CloseDescriptors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Send.java b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Send.java index 8fb551dd8f47..6f1b05e9db99 100644 --- a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Send.java +++ b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/Send.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SendFailed.java b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SendFailed.java index a64d4d582703..85db90279c03 100644 --- a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SendFailed.java +++ b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SendFailed.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* @test * @bug 8067846 + * @requires (os.family == "linux") * @library /test/lib * @summary Test for send failed notification */ diff --git a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SocketOptionTests.java b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SocketOptionTests.java index 3a681884b9a7..5d782fcd4e74 100644 --- a/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SocketOptionTests.java +++ b/test/jdk/com/sun/nio/sctp/SctpMultiChannel/SocketOptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpServerChannel/Accept.java b/test/jdk/com/sun/nio/sctp/SctpServerChannel/Accept.java index c68bfd289450..84c010aaaf08 100644 --- a/test/jdk/com/sun/nio/sctp/SctpServerChannel/Accept.java +++ b/test/jdk/com/sun/nio/sctp/SctpServerChannel/Accept.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar diff --git a/test/jdk/com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java b/test/jdk/com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java index abf35bf779db..3c358e0a86d9 100644 --- a/test/jdk/com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java +++ b/test/jdk/com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* @test * @bug 4927640 + * @requires (os.family == "linux") * @library /test/lib * @summary Tests the SCTP protocol implementation * @author chegar From fdb129ad94b1125340d62766fa36bee4739917f0 Mon Sep 17 00:00:00 2001 From: Shawn Emery Date: Fri, 13 Mar 2026 16:34:30 +0000 Subject: [PATCH 47/97] 8378893: X25519 should utilize a larger limb size Co-authored-by: Ferenc Rakoczi Reviewed-by: ascarpino, bperez --- .../classes/build/tools/intpoly/FieldGen.java | 35 +- .../math/intpoly/IntegerPolynomial25519.java | 531 ++++++++++++++++++ .../util/math/TestIntegerModuloP.java | 4 +- 3 files changed, 535 insertions(+), 35 deletions(-) create mode 100644 src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial25519.java diff --git a/make/jdk/src/classes/build/tools/intpoly/FieldGen.java b/make/jdk/src/classes/build/tools/intpoly/FieldGen.java index fcc45db02196..c29a315f368b 100644 --- a/make/jdk/src/classes/build/tools/intpoly/FieldGen.java +++ b/make/jdk/src/classes/build/tools/intpoly/FieldGen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,36 +34,6 @@ public class FieldGen { - static FieldParams Curve25519 = new FieldParams( - "IntegerPolynomial25519", 26, 10, 1, 255, - Arrays.asList( - new Term(0, -19) - ), - Curve25519CrSequence(), simpleSmallCrSequence(10) - ); - - private static List Curve25519CrSequence() { - List result = new ArrayList(); - - // reduce(7,2) - result.add(new Reduce(17)); - result.add(new Reduce(18)); - - // carry(8,2) - result.add(new Carry(8)); - result.add(new Carry(9)); - - // reduce(0,7) - for (int i = 10; i < 17; i++) { - result.add(new Reduce(i)); - } - - // carry(0,9) - result.addAll(fullCarry(10)); - - return result; - } - static FieldParams Curve448 = new FieldParams( "IntegerPolynomial448", 28, 16, 1, 448, Arrays.asList( @@ -224,8 +194,7 @@ private static List orderFieldSmallCrSequence(int numLimbs) { } static final FieldParams[] ALL_FIELDS = { - Curve25519, Curve448, - P256, P384, P521, O256, O384, O521, O25519, O448 + Curve448, P256, P384, P521, O256, O384, O521, O25519, O448 }; public static class Term { diff --git a/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial25519.java b/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial25519.java new file mode 100644 index 000000000000..c8f23da417e9 --- /dev/null +++ b/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial25519.java @@ -0,0 +1,531 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util.math.intpoly; + +import java.math.BigInteger; + +public final class IntegerPolynomial25519 extends IntegerPolynomial { + private static final int BITS_PER_LIMB = 51; + private static final int NUM_LIMBS = 5; + private static final int MAX_ADDS = 1; + public static final BigInteger MODULUS = evaluateModulus(); + private static final long CARRY_ADD = 1L << (BITS_PER_LIMB - 1); + private static final long LIMB_MASK = -1L >>> (64 - BITS_PER_LIMB); + + public static final IntegerPolynomial25519 ONE = + new IntegerPolynomial25519(); + + private IntegerPolynomial25519() { + super(BITS_PER_LIMB, NUM_LIMBS, MAX_ADDS, MODULUS); + } + + private static BigInteger evaluateModulus() { + BigInteger result = BigInteger.valueOf(2).pow(255); + result = result.subtract(BigInteger.valueOf(19)); + + return result; + } + + /** + * Carry from a range of limb positions. + * Override for performance (unnesting). + * + * @param limbs [in|out] the limbs for carry operation. + * @param start [in] the starting position of carry. + * @param end [in] the ending position of carry. + */ + @Override + protected void carry(long[] limbs, int start, int end) { + long carry; + + for (int i = start; i < end; i++) { + carry = (limbs[i] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[i] -= (carry << BITS_PER_LIMB); + limbs[i + 1] += carry; + } + } + + /** + * Carry operation for all limb positions. + * Override for performance (unroll and unnesting). + * + * @param limbs [in|out] the limbs for carry operation. + */ + @Override + protected void carry(long[] limbs) { + long carry = (limbs[0] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[0] -= carry << BITS_PER_LIMB; + limbs[1] += carry; + + carry = (limbs[1] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[1] -= carry << BITS_PER_LIMB; + limbs[2] += carry; + + carry = (limbs[2] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[2] -= carry << BITS_PER_LIMB; + limbs[3] += carry; + + carry = (limbs[3] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[3] -= carry << BITS_PER_LIMB; + limbs[4] += carry; + } + + /** + * Multiply limbs by scalar value. + * Superclass assumes that limb primitive radix > (bits per limb * 2) + * + * @param a [in|out] the limbs to multiply a carry operation. 'a' is + * assumed to be reduced. + * @param b [in] the scalar value to be muliplied with the limbs. + */ + @Override + protected void multByInt(long[] a, long b) { + long aa0 = a[0]; + long aa1 = a[1]; + long aa2 = a[2]; + long aa3 = a[3]; + long aa4 = a[4]; + + long bb0 = b; + + final long shift1 = 64 - BITS_PER_LIMB; + final long shift2 = BITS_PER_LIMB; + + long d0; // low digit from multiplication + long dd0; // high digit from multiplication + // multiplication result digits for each column + long c0, c1, c2, c3, c4, c5; + + // Row 0 - multiply by aa0 + d0 = aa0 * bb0; + dd0 = Math.multiplyHigh(aa0, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + c0 = d0; + c1 = dd0; + + // Row 1 - multiply by aa1 + d0 = aa1 * bb0; + dd0 = Math.multiplyHigh(aa1, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + c1 += d0; + c2 = dd0; + + // Row 2 - multiply by aa2 + d0 = aa2 * bb0; + dd0 = Math.multiplyHigh(aa2, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + c2 += d0; + c3 = dd0; + + // Row 3 - multiply by aa3 + d0 = aa3 * bb0; + dd0 = Math.multiplyHigh(aa3, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + c3 += d0; + c4 = dd0; + + // Row 4 - multiply by aa4 + d0 = aa4 * bb0; + dd0 = Math.multiplyHigh(aa4, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + c4 += d0; + c5 = dd0; + + // Perform pseudo-Mersenne reduction + a[0] = c0 + (19 * c5); + + a[1] = c1; + a[2] = c2; + a[3] = c3; + a[4] = c4; + + reduce(a); + } + + /** + * Carry in all positions and reduce high order limb. + * + * @param limbs [in|out] the limbs to carry and reduce. + */ + protected void reduce(long[] limbs) { + long carry = (limbs[3] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[3] -= carry << BITS_PER_LIMB; + limbs[4] += carry; + + carry = (limbs[4] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[4] -= carry << BITS_PER_LIMB; + + limbs[0] += 19 * carry; + + carry = (limbs[0] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[0] -= carry << BITS_PER_LIMB; + limbs[1] += carry; + + carry = (limbs[1] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[1] -= carry << BITS_PER_LIMB; + limbs[2] += carry; + + carry = (limbs[2] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[2] -= carry << BITS_PER_LIMB; + limbs[3] += carry; + + carry = (limbs[3] + CARRY_ADD) >> BITS_PER_LIMB; + limbs[3] -= carry << BITS_PER_LIMB; + limbs[4] += carry; + } + + /** + * Reduces digit 'v' at limb position 'i' to a lower limb. + * + * @param limbs [in|out] the limbs to reduce in. + * @param v [in] the digit to reduce to the lower limb. + * @param i [in] the limbs to reduce from. + */ + protected void reduceIn(long[] limbs, long v, int i) { + limbs[i - NUM_LIMBS] += 19 * v; + } + + /** + * Carry from high order limb and reduce to the lower order limb. Assumed + * to be called two times to propagate the carries. + * + * @param limbs [in|out] the limbs to fully carry and reduce. + */ + protected void finalCarryReduceLast(long[] limbs) { + long carry = limbs[4] >> BITS_PER_LIMB; + + limbs[4] -= carry << BITS_PER_LIMB; + limbs[0] += 19 * carry; + } + + /** + * Multiply two limbs using a high/low digit technique that allows for + * larger limb sizes. It is assumed that both limbs have already been + * reduced. + * + * @param a [in] the limb operand to multiply. + * @param b [in] the limb operand to multiply. + * @param r [out] the product of the limbs operands that is fully reduced. + */ + protected void mult(long[] a, long[] b, long[] r) { + long aa0 = a[0]; + long aa1 = a[1]; + long aa2 = a[2]; + long aa3 = a[3]; + long aa4 = a[4]; + + long bb0 = b[0]; + long bb1 = b[1]; + long bb2 = b[2]; + long bb3 = b[3]; + long bb4 = b[4]; + + final long shift1 = 64 - BITS_PER_LIMB; + final long shift2 = BITS_PER_LIMB; + + long d0, d1, d2, d3, d4; // low digits from multiplication + long dd0, dd1, dd2, dd3, dd4; // high digits from multiplication + // multiplication result digits for each column + long c0, c1, c2, c3, c4, c5, c6, c7, c8, c9; + + // Row 0 - multiply by aa0 + d0 = aa0 * bb0; + dd0 = Math.multiplyHigh(aa0, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa0 * bb1; + dd1 = Math.multiplyHigh(aa0, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa0 * bb2; + dd2 = Math.multiplyHigh(aa0, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa0 * bb3; + dd3 = Math.multiplyHigh(aa0, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa0 * bb4; + dd4 = Math.multiplyHigh(aa0, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c0 = d0; + c1 = d1 + dd0; + c2 = d2 + dd1; + c3 = d3 + dd2; + c4 = d4 + dd3; + c5 = dd4; + + // Row 1 - multiply by aa1 + d0 = aa1 * bb0; + dd0 = Math.multiplyHigh(aa1, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa1 * bb1; + dd1 = Math.multiplyHigh(aa1, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa1 * bb2; + dd2 = Math.multiplyHigh(aa1, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa1 * bb3; + dd3 = Math.multiplyHigh(aa1, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa1 * bb4; + dd4 = Math.multiplyHigh(aa1, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c1 += d0; + c2 += d1 + dd0; + c3 += d2 + dd1; + c4 += d3 + dd2; + c5 += d4 + dd3; + c6 = dd4; + + // Row 2 - multiply by aa2 + d0 = aa2 * bb0; + dd0 = Math.multiplyHigh(aa2, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa2 * bb1; + dd1 = Math.multiplyHigh(aa2, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa2 * bb2; + dd2 = Math.multiplyHigh(aa2, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa2 * bb3; + dd3 = Math.multiplyHigh(aa2, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa2 * bb4; + dd4 = Math.multiplyHigh(aa2, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c2 += d0; + c3 += d1 + dd0; + c4 += d2 + dd1; + c5 += d3 + dd2; + c6 += d4 + dd3; + c7 = dd4; + + // Row 3 - multiply by aa3 + d0 = aa3 * bb0; + dd0 = Math.multiplyHigh(aa3, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa3 * bb1; + dd1 = Math.multiplyHigh(aa3, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa3 * bb2; + dd2 = Math.multiplyHigh(aa3, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa3 * bb3; + dd3 = Math.multiplyHigh(aa3, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa3 * bb4; + dd4 = Math.multiplyHigh(aa3, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c3 += d0; + c4 += d1 + dd0; + c5 += d2 + dd1; + c6 += d3 + dd2; + c7 += d4 + dd3; + c8 = dd4; + + // Row 4 - multiply by aa4 + d0 = aa4 * bb0; + dd0 = Math.multiplyHigh(aa4, bb0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa4 * bb1; + dd1 = Math.multiplyHigh(aa4, bb1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa4 * bb2; + dd2 = Math.multiplyHigh(aa4, bb2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa4 * bb3; + dd3 = Math.multiplyHigh(aa4, bb3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa4 * bb4; + dd4 = Math.multiplyHigh(aa4, bb4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c4 += d0; + c5 += d1 + dd0; + c6 += d2 + dd1; + c7 += d3 + dd2; + c8 += d4 + dd3; + c9 = dd4; + + // Perform pseudo-Mersenne reduction + r[0] = c0 + (19 * c5); + r[1] = c1 + (19 * c6); + r[2] = c2 + (19 * c7); + r[3] = c3 + (19 * c8); + r[4] = c4 + (19 * c9); + + reduce(r); + } + + /** + * Takes a single limb and squares it using a high/low digit technique that + * allows for larger limb sizes. It is assumed that the limb input has + * already been reduced. + * + * @param a [in] the limb operand to square. + * @param r [out] the resulting square of the limb which is fully reduced. + */ + protected void square(long[] a, long[] r) { + long aa0 = a[0]; + long aa1 = a[1]; + long aa2 = a[2]; + long aa3 = a[3]; + long aa4 = a[4]; + + final long shift1 = 64 - BITS_PER_LIMB; + final long shift2 = BITS_PER_LIMB; + + long d0, d1, d2, d3, d4; // low digits from multiplication + long dd0, dd1, dd2, dd3, dd4; // high digits from multiplication + // multiplication result digits for each column + long c0, c1, c2, c3, c4, c5, c6, c7, c8, c9; + + // Row 0 - multiply by aa0 + d0 = aa0 * aa0; + dd0 = Math.multiplyHigh(aa0, aa0) << shift1 | (d0 >>> shift2); + d0 &= LIMB_MASK; + + d1 = aa0 * aa1; + dd1 = Math.multiplyHigh(aa0, aa1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa0 * aa2; + dd2 = Math.multiplyHigh(aa0, aa2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa0 * aa3; + dd3 = Math.multiplyHigh(aa0, aa3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa0 * aa4; + dd4 = Math.multiplyHigh(aa0, aa4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c0 = d0; + c1 = (d1 << 1) + dd0; + c2 = (d2 + dd1) << 1; + c3 = (d3 + dd2) << 1; + c4 = (d4 + dd3) << 1; + c5 = dd4 << 1; + + // Row 1 - multiply by aa1 + d1 = aa1 * aa1; + dd1 = Math.multiplyHigh(aa1, aa1) << shift1 | (d1 >>> shift2); + d1 &= LIMB_MASK; + + d2 = aa1 * aa2; + dd2 = Math.multiplyHigh(aa1, aa2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa1 * aa3; + dd3 = Math.multiplyHigh(aa1, aa3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa1 * aa4; + dd4 = Math.multiplyHigh(aa1, aa4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c2 += d1; + c3 += (d2 << 1) + dd1; + c4 += (d3 + dd2) << 1; + c5 += (d4 + dd3) << 1; + c6 = dd4 << 1; + + // Row 2 - multiply by aa2 + d2 = aa2 * aa2; + dd2 = Math.multiplyHigh(aa2, aa2) << shift1 | (d2 >>> shift2); + d2 &= LIMB_MASK; + + d3 = aa2 * aa3; + dd3 = Math.multiplyHigh(aa2, aa3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa2 * aa4; + dd4 = Math.multiplyHigh(aa2, aa4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c4 += d2; + c5 += (d3 << 1) + dd2; + c6 += (d4 + dd3) << 1; + c7 = dd4 << 1; + + // Row 3 - multiply by aa3 + d3 = aa3 * aa3; + dd3 = Math.multiplyHigh(aa3, aa3) << shift1 | (d3 >>> shift2); + d3 &= LIMB_MASK; + + d4 = aa3 * aa4; + dd4 = Math.multiplyHigh(aa3, aa4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c6 += d3; + c7 += (d4 << 1) + dd3; + c8 = dd4 << 1; + + // Row 4 - multiply by aa4 + d4 = aa4 * aa4; + dd4 = Math.multiplyHigh(aa4, aa4) << shift1 | (d4 >>> shift2); + d4 &= LIMB_MASK; + + c8 += d4; + c9 = dd4; + + // Perform pseudo-Mersenne reduction + r[0] = c0 + (19 * c5); + r[1] = c1 + (19 * c6); + r[2] = c2 + (19 * c7); + r[3] = c3 + (19 * c8); + r[4] = c4 + (19 * c9); + + reduce(r); + } +} diff --git a/test/jdk/sun/security/util/math/TestIntegerModuloP.java b/test/jdk/sun/security/util/math/TestIntegerModuloP.java index e1c023654249..f2b73223efc1 100644 --- a/test/jdk/sun/security/util/math/TestIntegerModuloP.java +++ b/test/jdk/sun/security/util/math/TestIntegerModuloP.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,7 @@ * @summary Test proper operation of integer field arithmetic * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly * @build BigIntegerModuloP - * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomial25519 32 0 + * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomial25519 31 0 */ /* From 9f2591d8cffd1b3496c956659e0ea12364696691 Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Fri, 13 Mar 2026 18:37:04 +0000 Subject: [PATCH 48/97] 8379828: Remove ResourceBundle module tests with legacy ISO code property Reviewed-by: jlu, iris --- .../modules/ModuleTestUtil.java | 32 ++----------------- .../modules/basic/BasicTest.java | 1 - 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/test/jdk/java/util/ResourceBundle/modules/ModuleTestUtil.java b/test/jdk/java/util/ResourceBundle/modules/ModuleTestUtil.java index 24c6e181ccc0..ec4c2355d180 100644 --- a/test/jdk/java/util/ResourceBundle/modules/ModuleTestUtil.java +++ b/test/jdk/java/util/ResourceBundle/modules/ModuleTestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,8 +28,6 @@ import java.util.List; import java.util.stream.Stream; -import jdk.test.lib.JDKToolLauncher; -import jdk.test.lib.Utils; import jdk.test.lib.compiler.CompilerUtils; import jdk.test.lib.process.ProcessTools; @@ -185,30 +183,4 @@ public static void runModuleWithCp(String cp, String mp, String mn, } } } - - /** - * Run the module test with "useOldISOCodes=true". - * - * @param mp module path - * @param mn module name - * @param localeList locale list - */ - public static void runModuleWithLegacyCode(String mp, String mn, List localeList) - throws Throwable { - List args = List.of( - "-ea", "-esa", - "-Djava.locale.useOldISOCodes=true", - "-p", mp, - "-m", mn); - // Build process (with VM flags) - ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( - Stream.concat(args.stream(), localeList.stream()).toList()); - // Evaluate process status - int exitCode = ProcessTools.executeCommand(pb).getExitValue(); - - if (exitCode != 0) { - throw new RuntimeException("Execution of the test failed. " - + "Unexpected exit code: " + exitCode); - } - } -} \ No newline at end of file +} diff --git a/test/jdk/java/util/ResourceBundle/modules/basic/BasicTest.java b/test/jdk/java/util/ResourceBundle/modules/basic/BasicTest.java index 2dda740aaf9b..f3432a733b43 100644 --- a/test/jdk/java/util/ResourceBundle/modules/basic/BasicTest.java +++ b/test/jdk/java/util/ResourceBundle/modules/basic/BasicTest.java @@ -132,7 +132,6 @@ public void runBasicTest(String src, String mod, List moduleList, moduleList.forEach(mn -> ModuleTestUtil.prepareModule(srcPath, modPath, mn, resFormat)); ModuleTestUtil.runModule(modPath.toString(), MAIN, localeList); - ModuleTestUtil.runModuleWithLegacyCode(modPath.toString(), MAIN, localeList); } @Test From 98520c029d27bd5e8b9e2113ac93f4adac164873 Mon Sep 17 00:00:00 2001 From: Frederic Thevenet Date: Sat, 14 Mar 2026 11:21:12 +0000 Subject: [PATCH 49/97] 8378657: OutputAnalyzer should have a quiet mode Reviewed-by: rriggs, stuefe --- .../jdk/test/lib/process/OutputAnalyzer.java | 31 +++++++++++++++++-- .../jdk/test/lib/process/OutputBuffer.java | 20 +++++++++--- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/test/lib/jdk/test/lib/process/OutputAnalyzer.java b/test/lib/jdk/test/lib/process/OutputAnalyzer.java index d8b3f4702607..553e13b28ff4 100644 --- a/test/lib/jdk/test/lib/process/OutputAnalyzer.java +++ b/test/lib/jdk/test/lib/process/OutputAnalyzer.java @@ -56,8 +56,23 @@ public final class OutputAnalyzer { * @throws IOException If an I/O error occurs. */ public OutputAnalyzer(Process process, Charset cs) throws IOException { - buffer = OutputBuffer.of(process, cs); + this(process, cs, true); } + + /** + * Create an OutputAnalyzer, a utility class for verifying output and exit + * value from a Process, with a configurable verbosity level. + * + * @param process Process to analyze + * @param cs The charset used to convert stdout/stderr from bytes to chars + * or null for the default charset. + * @param verbose Set to false to limit logging to stdout. + * @throws IOException If an I/O error occurs. + */ + public OutputAnalyzer(Process process, Charset cs, boolean verbose) throws IOException { + buffer = OutputBuffer.of(process, cs, verbose); + } + /** * Create an OutputAnalyzer, a utility class for verifying output and exit * value from a Process @@ -66,7 +81,19 @@ public OutputAnalyzer(Process process, Charset cs) throws IOException { * @throws IOException If an I/O error occurs. */ public OutputAnalyzer(Process process) throws IOException { - buffer = OutputBuffer.of(process); + this(process, true); + } + + /** + * Create an OutputAnalyzer, a utility class for verifying output and exit + * value from a Process, with a configurable verbosity level. + * + * @param process Process to analyze + * @param verbose Set to false to limit logging to stdout. + * @throws IOException If an I/O error occurs. + */ + public OutputAnalyzer(Process process, boolean verbose) throws IOException { + buffer = OutputBuffer.of(process, verbose); } /** diff --git a/test/lib/jdk/test/lib/process/OutputBuffer.java b/test/lib/jdk/test/lib/process/OutputBuffer.java index 0390535bf89a..57e00aa73c7b 100644 --- a/test/lib/jdk/test/lib/process/OutputBuffer.java +++ b/test/lib/jdk/test/lib/process/OutputBuffer.java @@ -89,12 +89,20 @@ default public List getStdoutAsList() { */ public long pid(); + public static OutputBuffer of(Process p, boolean quiet) { + return of(p, null, quiet); + } + public static OutputBuffer of(Process p, Charset cs) { - return new LazyOutputBuffer(p, cs); + return of(p, cs, false); } public static OutputBuffer of(Process p) { - return new LazyOutputBuffer(p, null); + return of(p, null, false); + } + + public static OutputBuffer of(Process p, Charset cs, boolean quiet) { + return new LazyOutputBuffer(p, cs, quiet); } public static OutputBuffer of(String stdout, String stderr, int exitValue) { @@ -130,19 +138,23 @@ public String get() { } } + private final boolean verbose; private final StreamTask outTask; private final StreamTask errTask; private final Process p; private volatile Integer exitValue; // null implies we don't yet know private final void logProgress(String state) { + if (verbose) { System.out.println("[" + Instant.now().toString() + "] " + state - + " for process " + p.pid()); + + " for process " + p.pid()); System.out.flush(); + } } - private LazyOutputBuffer(Process p, Charset cs) { + private LazyOutputBuffer(Process p, Charset cs, boolean verbose) { this.p = p; + this.verbose = verbose; logProgress("Gathering output"); outTask = new StreamTask(p.getInputStream(), cs); errTask = new StreamTask(p.getErrorStream(), cs); From 1606564eddfd17f74015e219cd5b754ec35bf0c8 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Sat, 14 Mar 2026 14:06:38 +0000 Subject: [PATCH 50/97] 8379802: [AIX] unify DL_info struct and put it into a single header Reviewed-by: mdoerr, clanger, erikj --- make/common/modules/LauncherCommon.gmk | 8 ++- make/hotspot/lib/JvmFlags.gmk | 7 ++- .../modules/java.desktop/lib/AwtLibraries.gmk | 14 +++--- src/hotspot/os/aix/porting_aix.hpp | 24 ++------- src/java.base/aix/native/include/dl_info.h | 49 +++++++++++++++++++ src/java.base/aix/native/libjli/java_md_aix.h | 9 +--- .../aix/native/libawt/porting_aix.c | 11 ++--- .../aix/native/libawt/porting_aix.h | 25 +--------- 8 files changed, 83 insertions(+), 64 deletions(-) create mode 100644 src/java.base/aix/native/include/dl_info.h diff --git a/make/common/modules/LauncherCommon.gmk b/make/common/modules/LauncherCommon.gmk index 7682ffbb95ca..859494861b23 100644 --- a/make/common/modules/LauncherCommon.gmk +++ b/make/common/modules/LauncherCommon.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -35,8 +35,14 @@ include ProcessMarkdown.gmk 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 \ # diff --git a/make/hotspot/lib/JvmFlags.gmk b/make/hotspot/lib/JvmFlags.gmk index 57b632ee5323..27a96cc48653 100644 --- a/make/hotspot/lib/JvmFlags.gmk +++ b/make/hotspot/lib/JvmFlags.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -43,10 +43,15 @@ JVM_SRC_DIRS += $(call uniq, $(wildcard $(foreach d, $(JVM_SRC_ROOTS), \ $(JVM_VARIANT_OUTPUTDIR)/gensrc # +ifeq ($(call isTargetOs, aix), true) + ADD_PLATFORM_INCLUDE_DIR := -I$(TOPDIR)/src/java.base/aix/native/include +endif + JVM_CFLAGS_INCLUDES += \ $(patsubst %,-I%,$(JVM_SRC_DIRS)) \ -I$(TOPDIR)/src/hotspot/share/include \ -I$(TOPDIR)/src/hotspot/os/$(HOTSPOT_TARGET_OS_TYPE)/include \ + $(ADD_PLATFORM_INCLUDE_DIR) \ -I$(SUPPORT_OUTPUTDIR)/modules_include/java.base \ -I$(SUPPORT_OUTPUTDIR)/modules_include/java.base/$(OPENJDK_TARGET_OS_INCLUDE_SUBDIR) \ -I$(TOPDIR)/src/java.base/share/native/libjimage \ diff --git a/make/modules/java.desktop/lib/AwtLibraries.gmk b/make/modules/java.desktop/lib/AwtLibraries.gmk index 1aa2578d2e2e..887dfab01dff 100644 --- a/make/modules/java.desktop/lib/AwtLibraries.gmk +++ b/make/modules/java.desktop/lib/AwtLibraries.gmk @@ -99,14 +99,16 @@ ifeq ($(call isTargetOs, windows), true) $(TOPDIR)/src/$(MODULE)/windows/native/libawt/windows/awt.rc endif -# This is the object file to provide the dladdr API, which is not -# part of AIX. It occurs several times in the jdk code base. -# Do not include it. When statically linking the java -# launcher with all JDK and VM static libraries, we use the -# --whole-archive linker option. The duplicate objects in different -# static libraries cause linking errors due to duplicate symbols. ifeq ($(call isTargetOs, aix), true) + # This is the object file to provide the dladdr API, which is not + # part of AIX. It occurs several times in the jdk code base. + # Do not include it. When statically linking the java + # launcher with all JDK and VM static libraries, we use the + # --whole-archive linker option. The duplicate objects in different + # static libraries cause linking errors due to duplicate symbols. LIBAWT_STATIC_EXCLUDE_OBJS := porting_aix.o + + LIBAWT_CFLAGS += -I$(TOPDIR)/src/java.base/aix/native/include endif # -fgcse-after-reload improves performance of MaskFill in Java2D by 20% for diff --git a/src/hotspot/os/aix/porting_aix.hpp b/src/hotspot/os/aix/porting_aix.hpp index a1a22d81471b..0bd71079d0ac 100644 --- a/src/hotspot/os/aix/porting_aix.hpp +++ b/src/hotspot/os/aix/porting_aix.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024 SAP SE. 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 @@ -37,25 +37,9 @@ // (see http://linux.die.net/man/3/dladdr) // dladdr(3) is not POSIX but a GNU extension, and is not available on AIX. // -// Differences between AIX dladdr and Linux dladdr: -// -// 1) Dl_info.dli_fbase: can never work, is disabled. -// A loaded image on AIX is divided in multiple segments, at least two -// (text and data) but potentially also far more. This is because the loader may -// load each member into an own segment, as for instance happens with the libC.a -// 2) Dl_info.dli_sname: This only works for code symbols (functions); for data, a -// zero-length string is returned (""). -// 3) Dl_info.dli_saddr: For code, this will return the entry point of the function, -// not the function descriptor. - -typedef struct { - const char *dli_fname; // file path of loaded library - // void *dli_fbase; - const char *dli_sname; // symbol name; "" if not known - void *dli_saddr; // address of *entry* of function; not function descriptor; -} Dl_info; - -// Note: we export this to use it inside J2se too + +#include "dl_info.h" + #ifdef __cplusplus extern "C" #endif diff --git a/src/java.base/aix/native/include/dl_info.h b/src/java.base/aix/native/include/dl_info.h new file mode 100644 index 000000000000..0ea10149300b --- /dev/null +++ b/src/java.base/aix/native/include/dl_info.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1996, 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. 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. + */ + +#ifndef AIX_INCLUDE_DL_INFO_H +#define AIX_INCLUDE_DL_INFO_H + +/* struct for dladdr + * Differences between AIX dladdr and Linux dladdr: + * + * 1) Dl_info.dli_fbase: can never work, is not included in our struct + * A loaded image on AIX is divided in multiple segments, at least two + * (text and data) but potentially also far more. This is because the loader may + * load each member into an own segment, as for instance happens with the libC.a + * 2) Dl_info.dli_sname: This only works for code symbols (functions); for data, a + * zero-length string is returned (""). + * 3) Dl_info.dli_saddr: For code, this will return the entry point of the function, + * not the function descriptor. + */ + +typedef struct { + const char *dli_fname; /* file path of loaded library */ + const char *dli_sname; /* symbol name; "" if not known */ + void *dli_saddr; /* address of *entry* of function; not function descriptor; */ +} Dl_info; + +#endif diff --git a/src/java.base/aix/native/libjli/java_md_aix.h b/src/java.base/aix/native/libjli/java_md_aix.h index d319a1d63531..d63030fc65e0 100644 --- a/src/java.base/aix/native/libjli/java_md_aix.h +++ b/src/java.base/aix/native/libjli/java_md_aix.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018 SAP SE. All rights reserved. + * Copyright (c) 2016, 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 @@ -37,12 +37,7 @@ * in the hotspot implementation which is not available at this place, though. */ -typedef struct { - const char *dli_fname; /* file path of loaded library */ - void *dli_fbase; /* unsupported */ - const char *dli_sname; /* unsupported */ - void *dli_saddr; /* unsupported */ -} Dl_info; +#include "dl_info.h" int dladdr(void *addr, Dl_info *info); diff --git a/src/java.desktop/aix/native/libawt/porting_aix.c b/src/java.desktop/aix/native/libawt/porting_aix.c index b506ef5a44b1..d8688c212d7d 100644 --- a/src/java.desktop/aix/native/libawt/porting_aix.c +++ b/src/java.desktop/aix/native/libawt/porting_aix.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023 SAP SE. 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 @@ -48,15 +48,14 @@ static int dladdr_dont_reload(void* addr, Dl_info* info) { if (addr >= p->ldinfo_textorg && (char*)addr < (char*)(p->ldinfo_textorg) + p->ldinfo_textsize) { info->dli_fname = p->ldinfo_filename; - info->dli_fbase = p->ldinfo_textorg; - return 1; /* [sic] */ + return 1; } if (!p->ldinfo_next) { break; } p = (struct ld_info*)(((char*)p) + p->ldinfo_next); } - return 0; /* [sic] */ + return 0; } #ifdef __cplusplus @@ -69,14 +68,14 @@ int dladdr(void *addr, Dl_info *info) { loaded = 1; } if (!addr) { - return 0; /* [sic] */ + 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) { /* [sic] */ + if (rc == 0) { fill_dll_info(); /* refill, maybe loadquery info is outdated */ rc = dladdr_dont_reload(addr, info); if (rc == 0) { diff --git a/src/java.desktop/aix/native/libawt/porting_aix.h b/src/java.desktop/aix/native/libawt/porting_aix.h index 719bbaf224e8..04d115909153 100644 --- a/src/java.desktop/aix/native/libawt/porting_aix.h +++ b/src/java.desktop/aix/native/libawt/porting_aix.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018 SAP SE. 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 @@ -24,36 +24,15 @@ * */ -/* - * Header file to contain porting-relevant code which does not have a - * home anywhere else. - * This is initially based on hotspot/src/os/aix/vm/{loadlib,porting}_aix.{hpp,cpp} - */ - /* * 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. * - * Differences between AIX dladdr and Linux dladdr: - * - * 1) Dl_info.dli_fbase: can never work, is disabled. - * A loaded image on AIX is divided in multiple segments, at least two - * (text and data) but potentially also far more. This is because the loader may - * load each member into an own segment, as for instance happens with the libC.a - * 2) Dl_info.dli_sname: This only works for code symbols (functions); for data, a - * zero-length string is returned (""). - * 3) Dl_info.dli_saddr: For code, this will return the entry point of the function, - * not the function descriptor. */ -typedef struct { - const char *dli_fname; /* file path of loaded library */ - void *dli_fbase; /* doesn't make sense on AIX */ - const char *dli_sname; /* symbol name; "" if not known */ - void *dli_saddr; /* address of *entry* of function; not function descriptor; */ -} Dl_info; +#include "dl_info.h" #ifdef __cplusplus extern "C" From 7484701e18d801914b85bf95fe020e85ebae40cc Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Mon, 16 Mar 2026 02:12:25 +0000 Subject: [PATCH 51/97] 8309041: Swingutilities2 methods requesting focus can have a Cause Reviewed-by: tr, dnguyen --- .../share/classes/sun/swing/SwingUtilities2.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java index d552bc5c9f23..28b93ed1e2a0 100644 --- a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java +++ b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java @@ -44,6 +44,7 @@ import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Toolkit; +import java.awt.event.FocusEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; @@ -795,7 +796,7 @@ public static boolean shouldIgnore(MouseEvent me, JComponent c) { */ public static void adjustFocus(JComponent c) { if (!c.hasFocus() && c.isRequestFocusEnabled()) { - c.requestFocus(); + c.requestFocus(FocusEvent.Cause.MOUSE_EVENT); } } @@ -1646,7 +1647,7 @@ public static Component compositeRequestFocus(Component component) { FocusTraversalPolicy policy = container.getFocusTraversalPolicy(); Component comp = policy.getDefaultComponent(container); if (comp!=null) { - comp.requestFocus(); + comp.requestFocus(FocusEvent.Cause.TRAVERSAL); return comp; } } @@ -1656,13 +1657,13 @@ public static Component compositeRequestFocus(Component component) { Component comp = policy.getComponentAfter(rootAncestor, container); if (comp!=null && SwingUtilities.isDescendingFrom(comp, container)) { - comp.requestFocus(); + comp.requestFocus(FocusEvent.Cause.TRAVERSAL); return comp; } } } if (component.isFocusable()) { - component.requestFocus(); + component.requestFocus(FocusEvent.Cause.TRAVERSAL); return component; } return null; From ef0235825a20c33d8d0c83a21d6981606b881d57 Mon Sep 17 00:00:00 2001 From: Tejesh R Date: Mon, 16 Mar 2026 05:45:42 +0000 Subject: [PATCH 52/97] 8354901: javax/swing/JTable/8236907/LastVisibleRow.java fails on macosx-aarch64 due to difference in before and after image comparison Reviewed-by: serb, honkar --- .../swing/JTable/8236907/LastVisibleRow.java | 53 ++++++++----------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/test/jdk/javax/swing/JTable/8236907/LastVisibleRow.java b/test/jdk/javax/swing/JTable/8236907/LastVisibleRow.java index 8b60bf8beec7..ebd563624cdf 100644 --- a/test/jdk/javax/swing/JTable/8236907/LastVisibleRow.java +++ b/test/jdk/javax/swing/JTable/8236907/LastVisibleRow.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,6 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + /* * @test * @key headful @@ -59,11 +60,9 @@ public static void main(String[] args) throws Exception { Point clkPoint; try { testRobot = new Robot(); - SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - createAndShowGUI(); - } - }); + testRobot.setAutoWaitForIdle(true); + testRobot.setAutoDelay(30); + SwingUtilities.invokeAndWait(LastVisibleRow::createAndShowGUI); testRobot.delay(1000); testRobot.waitForIdle(); BufferedImage bufferedImageBefore = testRobot.createScreenCapture(getCaptureRect()); @@ -73,7 +72,7 @@ public void run() { mouseEvents(clkPoint); testRobot.waitForIdle(); clearSelect(); - testRobot.delay(1000); + resetMousePos(); testRobot.waitForIdle(); BufferedImage bufferedImageAfter = testRobot.createScreenCapture(getCaptureRect()); @@ -137,43 +136,37 @@ private static void mouseEvents(Point clkPnt) { testRobot.delay(50); } + private static void resetMousePos() { + testRobot.mouseMove(50, 50); + testRobot.delay(50); + } + // getMousePosition Actions for last row click private static Point getMousePosition() throws Exception { final Point[] clickPoint = new Point[1]; - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - clickPoint[0] = getCellClickPoint(2, 0); - } - }); + SwingUtilities.invokeAndWait((Runnable) () -> clickPoint[0] = getCellClickPoint(2, 0)); return clickPoint[0]; } // Clears the selected table row private static void clearSelect() throws Exception { - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - table.getSelectionModel().clearSelection(); - table.setFocusable(false); - } + SwingUtilities.invokeAndWait((Runnable) () -> { + table.getSelectionModel().clearSelection(); + table.setFocusable(false); }); } // getCaptureRect Method - To Compute the Rectangle Area of last row private static Rectangle getCaptureRect() throws InterruptedException, InvocationTargetException { final Rectangle[] captureRect = new Rectangle[1]; - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - Rectangle cellRect = table.getCellRect(2, 0, true); - Point point = new Point(cellRect.x, cellRect.y); - SwingUtilities.convertPointToScreen(point, table); - - captureRect[0] = new Rectangle(point.x+5, point.y+2, - table.getColumnCount() * cellRect.width - 10, - cellRect.height-2); - } + SwingUtilities.invokeAndWait((Runnable) () -> { + Rectangle cellRect = table.getCellRect(2, 0, true); + Point point = new Point(cellRect.x, cellRect.y); + SwingUtilities.convertPointToScreen(point, table); + + captureRect[0] = new Rectangle(point.x + 5, point.y + 2, + table.getColumnCount() * cellRect.width - 10, + cellRect.height - 2); }); return captureRect[0]; } From f430f1d865cf069d407e9f41320a7cbd9470fd3a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 16 Mar 2026 07:39:21 +0000 Subject: [PATCH 53/97] 8378968: C2 VectorAVX AVX2: wrong result, bad optimization in VectorLongToMaskNode::Ideal Reviewed-by: jbhateja, vlivanov, kvn --- src/hotspot/share/opto/vectornode.cpp | 27 +- src/hotspot/share/opto/vectornode.hpp | 14 +- .../compiler/lib/ir_framework/IRNode.java | 30 +- .../TestVectorLongToMaskNodeIdealization.java | 424 ++++++++++++++++++ 4 files changed, 465 insertions(+), 30 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/vectorapi/TestVectorLongToMaskNodeIdealization.java diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index d332bf440f67..6012bdef86ee 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -2020,26 +2020,21 @@ Node* VectorLongToMaskNode::Ideal(PhaseGVN* phase, bool can_reshape) { uint vlen = dst_type->length(); const TypeVectMask* is_mask = dst_type->isa_vectmask(); + // Pattern: (VectorLongToMask (AndL (VectorMaskToLong src) mask)) + // Replace with: (VectorMaskCast src) + // The cast is needed if there are different mask types, and can be folded otherwise. + // The mask has exactly the vlen first bits on: mask = (2 << vlen - 1) if (in(1)->Opcode() == Op_AndL && in(1)->in(1)->Opcode() == Op_VectorMaskToLong && in(1)->in(2)->bottom_type()->isa_long() && in(1)->in(2)->bottom_type()->is_long()->is_con() && - in(1)->in(2)->bottom_type()->is_long()->get_con() == ((1L << vlen) - 1)) { - // Different src/dst mask length represents a re-interpretation operation, - // we can however generate a mask casting operation if length matches. - Node* src = in(1)->in(1)->in(1); - if (is_mask == nullptr) { - if (src->Opcode() != Op_VectorStoreMask) { - return nullptr; - } - src = src->in(1); - } - const TypeVect* src_type = src->bottom_type()->is_vect(); - if (src_type->length() == vlen && - ((src_type->isa_vectmask() == nullptr && is_mask == nullptr) || - (src_type->isa_vectmask() && is_mask))) { - return new VectorMaskCastNode(src, dst_type); - } + in(1)->in(2)->bottom_type()->is_long()->get_con() == ((1LL << vlen) - 1)) { + Node* src = in(1)->in(1)->in(1); + const TypeVect* src_type = src->bottom_type()->is_vect(); + if (src_type->length() == vlen && + ((src_type->isa_vectmask() == nullptr) == (is_mask == nullptr))) { + return new VectorMaskCastNode(src, dst_type); + } } // VectorLongToMask(-1/0) => MaskAll(-1/0) diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index f0d010ee7350..dce43f929056 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -1798,13 +1798,23 @@ class VectorStoreMaskNode : public VectorNode { static VectorStoreMaskNode* make(PhaseGVN& gvn, Node* in, BasicType in_type, uint num_elem); }; -// Lane-wise type cast a vector mask to the given vector type. The vector length -// of the input and output must be the same. +// Lane-wise type cast a vector mask to the given vector type. +// The vector length of the input and output must be the same. +// We can only cast between: +// - BVectMask and BVectMask (0x00/0x01) +// - NVectMask and NVectMask (0x0..0/0xF..F of different bit lengths) +// - PVectMask and PVectMask (specialized predicate/mask registers) +// Casting N/PVectMask <-> BVectMask needs to be done by +// VectorStoreMask and VectorLoadMask. class VectorMaskCastNode : public VectorNode { public: VectorMaskCastNode(Node* in, const TypeVect* vt) : VectorNode(in, vt) { const TypeVect* in_vt = in->bottom_type()->is_vect(); assert(in_vt->length() == vt->length(), "vector length must match"); + assert((in_vt->element_basic_type() == T_BOOLEAN) == (vt->element_basic_type() == T_BOOLEAN), + "Cast from/to BVectMask not allowed, use VectorLoadMask/VectorStoreMask instead"); + assert((in_vt->isa_vectmask() == nullptr) == (vt->isa_vectmask() == nullptr), + "Both BVectMask, or both NVectMask, or both PVectMask"); } Node* Identity(PhaseGVN* phase); virtual int Opcode() const; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 3508c06ad0a6..1c59a03b4b06 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -117,13 +117,14 @@ public class IRNode { public static final String VECTOR_SIZE_32 = VECTOR_SIZE + "32"; public static final String VECTOR_SIZE_64 = VECTOR_SIZE + "64"; - private static final String TYPE_BYTE = "B"; - private static final String TYPE_CHAR = "C"; - private static final String TYPE_SHORT = "S"; - private static final String TYPE_INT = "I"; - private static final String TYPE_LONG = "J"; - private static final String TYPE_FLOAT = "F"; - private static final String TYPE_DOUBLE = "D"; + private static final String TYPE_BYTE = "B"; + private static final String TYPE_CHAR = "C"; + private static final String TYPE_SHORT = "S"; + private static final String TYPE_INT = "I"; + private static final String TYPE_LONG = "J"; + private static final String TYPE_FLOAT = "F"; + private static final String TYPE_DOUBLE = "D"; + private static final String TYPE_BOOLEAN = "Z"; /** * IR placeholder string to regex-for-compile-phase map. @@ -1110,6 +1111,11 @@ public class IRNode { vectorNode(LOAD_VECTOR_D, "LoadVector", TYPE_DOUBLE); } + public static final String LOAD_VECTOR_Z = VECTOR_PREFIX + "LOAD_VECTOR_Z" + POSTFIX; + static { + vectorNode(LOAD_VECTOR_Z, "LoadVector", TYPE_BOOLEAN); + } + public static final String LOAD_VECTOR_GATHER = PREFIX + "LOAD_VECTOR_GATHER" + POSTFIX; static { beforeMatchingNameRegex(LOAD_VECTOR_GATHER, "LoadVectorGather"); @@ -3512,11 +3518,11 @@ public static long getMaxElementsForTypeOnX86(String typeString, VMInfo vmInfo) */ public static int getTypeSizeInBytes(String typeString) { return switch (typeString) { - case TYPE_BYTE -> 1; - case TYPE_CHAR, TYPE_SHORT -> 2; - case TYPE_INT, TYPE_FLOAT -> 4; - case TYPE_LONG, TYPE_DOUBLE -> 8; - default -> 0; + case TYPE_BYTE, TYPE_BOOLEAN -> 1; + case TYPE_CHAR, TYPE_SHORT -> 2; + case TYPE_INT, TYPE_FLOAT -> 4; + case TYPE_LONG, TYPE_DOUBLE -> 8; + default -> 0; }; } diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorLongToMaskNodeIdealization.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorLongToMaskNodeIdealization.java new file mode 100644 index 000000000000..706e21554bb5 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorLongToMaskNodeIdealization.java @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.vectorapi; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.Random; + +import jdk.test.lib.Utils; + +import compiler.lib.ir_framework.*; +import compiler.lib.verify.Verify; +import jdk.incubator.vector.*; +import compiler.lib.compile_framework.CompileFramework; + +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateToken; +import compiler.lib.template_framework.library.TestFrameworkClass; +import compiler.lib.template_framework.library.PrimitiveType; +import compiler.lib.template_framework.library.CodeGenerationDataNameType; +import static compiler.lib.template_framework.Template.scope; +import static compiler.lib.template_framework.Template.let; + +/* + * @test + * @bug 8277997 8378968 + * @key randomness + * @summary Testing some optimizations in VectorLongToMaskNode::Ideal + * For now: VectorMask.fromLong(.., mask.toLong()) + * @modules jdk.incubator.vector + * @library /test/lib / + * @compile ../../compiler/lib/ir_framework/TestFramework.java + * @compile ../../compiler/lib/generators/Generators.java + * @compile ../../compiler/lib/verify/Verify.java + * @run driver ${test.main.class} + */ +public class TestVectorLongToMaskNodeIdealization { + private static final Random RANDOM = Utils.getRandomInstance(); + + static final long[] ONES_L = new long[64]; + static { Arrays.fill(ONES_L, 1); } + + static final boolean[] TRUES = new boolean[64]; + static { Arrays.fill(TRUES, true); } + + public static void main(String[] args) { + // Run some tests directly first. + TestFramework testFramework = new TestFramework(); + testFramework.setDefaultWarmup(10000) + .addFlags("--add-modules=jdk.incubator.vector") + .start(); + + // Then also generate some random examples. + CompileFramework comp = new CompileFramework(); + comp.addJavaSourceCode("compiler.vectorapi.templated.Templated", generate(comp)); + comp.compile("--add-modules=jdk.incubator.vector"); + comp.invoke("compiler.vectorapi.templated.Templated", "main", new Object[] {new String[] { + "--add-modules=jdk.incubator.vector", + "--add-opens", "jdk.incubator.vector/jdk.incubator.vector=ALL-UNNAMED", + "--add-opens", "java.base/java.lang=ALL-UNNAMED" + }}); + } + + // ------------------------------------------------------------------------------------- + @Test + @IR(counts = {IRNode.REPLICATE_L, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.REPLICATE_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_LOAD_MASK, "> 0", // Not yet optimized away + IRNode.VECTOR_STORE_MASK, "> 0", // Not yet optimized away + IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0", // Optimized away + IRNode.VECTOR_MASK_CAST, "> 0", // Not yet optimized away + IRNode.VECTOR_BLEND_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.XOR_VI, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureAnd = {"avx2", "true", "avx512", "false"}) + @IR(counts = {IRNode.REPLICATE_L, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.REPLICATE_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_LOAD_MASK, "= 0", // Optimized away + IRNode.VECTOR_STORE_MASK, "= 0", // Optimized away + IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0", // Optimized away + IRNode.VECTOR_MASK_CAST, "> 0", // Cast I->J + IRNode.VECTOR_BLEND_I, "= 0", // Not needed + IRNode.XOR_VI, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeature = {"avx512", "true"}) + // This is the original reproducer for JDK-8378968, which failed on AVX2 with a wrong result. + public static Object test1() { + // There was a bug here with AVX2: + var ones = LongVector.broadcast(LongVector.SPECIES_256, 1); + var trues_L256 = ones.compare(VectorOperators.NE, 0); + // VectorMaskCmp #vectory -> (L-mask=0x0..0/0xF..F) + + var trues_I128 = VectorMask.fromLong(IntVector.SPECIES_128, trues_L256.toLong()); + // VectorStoreMask(L-mask to 0/1) + // VectorMaskToLong + // AndL(truncate) + // VectorLongToMask -> 0/1 + // + // VectorLongToMaskNode::Ideal transforms this into: + // VectorMaskCmp #vectory -> (L-mask=0x0..0/0xF..F) + // VectorMaskCastNode -> (L-mask=0x0..0/0xF..F to 0/1) + // But VectorMaskCastNode is not made for such mask conversion to boolean mask, + // and so it wrongly produces a 0x00/0xFF byte mask, instead of bytes 0x00/01. + // See: vector_mask_cast + // + // The correct transformation would have been to: + // VectorMaskCmp #vectory -> (L-mask=0x0..0/0xF..F) + // VectorStoreMask(L-mask to 0/1, i.e. 0x00/0x01 bytes) + + var zeros = IntVector.zero(IntVector.SPECIES_128); + var m1s = zeros.lanewise(VectorOperators.NOT, trues_I128); + // The rest of the code is: + // VectorLoadMask (0/1 to I-mask=0x0..0/0xF..F) + // It expects x=0x00/0x01 bytes, and does a subtraction 0-x to get values 0x00/0xFF + // that are then widened to int-length. + // But if it instead gets 0x00/0xFF, the subtraction produces 0x00/0x01 values, which + // are widened to int 0x0..0/0..01 values. + // See: load_vector_mask + // Blend, which expects I-mask (0x0..0/0xF..F) + // It looks at the 7th (uppermost) bit of every byte to determine which byte is taken. + // If it instead gets the 0x0..0/0x0..01 mask, it interprets both as "false". + + int[] out = new int[64]; + m1s.intoArray(out, 0); + return out; + } + + static final Object GOLD_TEST1 = test1(); + + @Check(test = "test1") + public static void check_test1(Object out) { + Verify.checkEQ(GOLD_TEST1, out); + } + // ------------------------------------------------------------------------------------- + @Test + @IR(counts = {IRNode.REPLICATE_L, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.REPLICATE_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.LOAD_VECTOR_L, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_LOAD_MASK, "> 0", // Not yet optimized away + IRNode.VECTOR_STORE_MASK, "> 0", // Not yet optimized away + IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0", // Optimized away + IRNode.VECTOR_MASK_CAST, "> 0", // Not yet optimized away: Cast Z->Z, see JDK-8379866 + IRNode.VECTOR_BLEND_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.XOR_VI, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureAnd = {"avx2", "true", "avx512", "false"}) + @IR(counts = {IRNode.REPLICATE_L, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.REPLICATE_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.LOAD_VECTOR_L, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_LOAD_MASK, "= 0", // Optimized away + IRNode.VECTOR_STORE_MASK, "= 0", // Optimized away + IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0", // Optimized away + IRNode.VECTOR_MASK_CAST, "> 0", // Cast I->J + IRNode.VECTOR_BLEND_I, "= 0", // Not needed + IRNode.XOR_VI, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeature = {"avx512", "true"}) + // The original reproducer test1 could eventually constant-fold the comparison + // with zero and trues. So let's make sure we load the data from a mutable array. + public static Object test1b() { + // load instead of broadcast: + var ones = LongVector.fromArray(LongVector.SPECIES_256, ONES_L, 0); + var trues_L256 = ones.compare(VectorOperators.NE, 0); + + var trues_I128 = VectorMask.fromLong(IntVector.SPECIES_128, trues_L256.toLong()); + + var zeros = IntVector.zero(IntVector.SPECIES_128); + var m1s = zeros.lanewise(VectorOperators.NOT, trues_I128); + + int[] out = new int[64]; + m1s.intoArray(out, 0); + return out; + } + + static final Object GOLD_TEST1B = test1b(); + + @Check(test = "test1b") + public static void check_test1b(Object out) { + Verify.checkEQ(GOLD_TEST1B, out); + } + // ------------------------------------------------------------------------------------- + @Test + @IR(counts = {IRNode.REPLICATE_L, IRNode.VECTOR_SIZE_4, "= 0", + IRNode.REPLICATE_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.LOAD_VECTOR_Z, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.VECTOR_MASK_CMP, "= 0", + IRNode.VECTOR_LOAD_MASK, "> 0", + IRNode.VECTOR_STORE_MASK, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0", // Optimized away + IRNode.VECTOR_MASK_CAST, "> 0", // Not yet optimized Z->Z, see JDK-8379866 + IRNode.VECTOR_BLEND_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.XOR_VI, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureAnd = {"avx2", "true", "avx512", "false"}) + @IR(counts = {IRNode.REPLICATE_L, IRNode.VECTOR_SIZE_4, "= 0", + IRNode.REPLICATE_I, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.LOAD_VECTOR_Z, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.VECTOR_MASK_CMP, "= 0", + IRNode.VECTOR_LOAD_MASK, "> 0", + IRNode.VECTOR_STORE_MASK, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0", // Optimized away + IRNode.VECTOR_MASK_CAST, "> 0", // Cast I->J + IRNode.VECTOR_BLEND_I, "= 0", // Not needed + IRNode.XOR_VI, IRNode.VECTOR_SIZE_4, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeature = {"avx512", "true"}) + // And now let's try a case where we load the mask from boolean array, so we don't + // have the VectorStoreMask before the VectorMaskToLong. + public static Object test1c() { + // Load true mask from array directly. + var trues_L256 = VectorMask.fromArray(LongVector.SPECIES_256, TRUES, 0); + + var trues_I128 = VectorMask.fromLong(IntVector.SPECIES_128, trues_L256.toLong()); + + var zeros = IntVector.zero(IntVector.SPECIES_128); + var m1s = zeros.lanewise(VectorOperators.NOT, trues_I128); + + int[] out = new int[64]; + m1s.intoArray(out, 0); + return out; + } + + static final Object GOLD_TEST1C = test1c(); + + @Check(test = "test1c") + public static void check_test1c(Object out) { + Verify.checkEQ(GOLD_TEST1C, out); + } + // ------------------------------------------------------------------------------------- + + // TODO: we can refactor this away once JDK-8369699 is integrated. + record VectorType(PrimitiveType elementType, int length) { + String typeName() { + return switch(elementType.name()) { + case "byte" -> "ByteVector"; + case "short" -> "ShortVector"; + case "int" -> "IntVector"; + case "long" -> "LongVector"; + case "float" -> "FloatVector"; + case "double" -> "DoubleVector"; + default -> throw new UnsupportedOperationException("Not supported: " + elementType.name()); + }; + } + + String speciesName() { + return typeName() + ".SPECIES_" + bitSize(); + } + + int bitSize() { + return elementType.byteSize() * length() * 8; + } + } + + public static final List VECTOR_TYPES = List.of( + new VectorType(CodeGenerationDataNameType.bytes(), 8), + new VectorType(CodeGenerationDataNameType.bytes(), 16), + new VectorType(CodeGenerationDataNameType.bytes(), 32), + new VectorType(CodeGenerationDataNameType.bytes(), 64), + new VectorType(CodeGenerationDataNameType.shorts(), 4), + new VectorType(CodeGenerationDataNameType.shorts(), 8), + new VectorType(CodeGenerationDataNameType.shorts(), 16), + new VectorType(CodeGenerationDataNameType.shorts(), 32), + new VectorType(CodeGenerationDataNameType.ints(), 2), + new VectorType(CodeGenerationDataNameType.ints(), 4), + new VectorType(CodeGenerationDataNameType.ints(), 8), + new VectorType(CodeGenerationDataNameType.ints(), 16), + new VectorType(CodeGenerationDataNameType.longs(), 1), + new VectorType(CodeGenerationDataNameType.longs(), 2), + new VectorType(CodeGenerationDataNameType.longs(), 4), + new VectorType(CodeGenerationDataNameType.longs(), 8), + new VectorType(CodeGenerationDataNameType.floats(), 2), + new VectorType(CodeGenerationDataNameType.floats(), 4), + new VectorType(CodeGenerationDataNameType.floats(), 8), + new VectorType(CodeGenerationDataNameType.floats(), 16), + new VectorType(CodeGenerationDataNameType.doubles(), 1), + new VectorType(CodeGenerationDataNameType.doubles(), 2), + new VectorType(CodeGenerationDataNameType.doubles(), 4), + new VectorType(CodeGenerationDataNameType.doubles(), 8) + ); + + private static String generate(CompileFramework comp) { + // Create a list to collect all tests. + List tests = new ArrayList<>(); + + var testTemplate = Template.make("t1", "t2", (VectorType t1, VectorType t2) -> scope( + let("e1", t1.elementType()), + let("e2", t2.elementType()), + let("V1", t1.typeName()), + let("V2", t2.typeName()), + let("S1", t1.speciesName()), + let("S2", t2.speciesName()), + """ + // ------------ $test ------------- + @Test + @Warmup(10_000) + """, + // Now let's generate some IR rules. + // General Idea: if the lengths match, we can optimize. If the don't match, it is + // a truncation/zero extension, and we don't optimize. + // + // TODO: length=64 leads the AndL mask to be all-ones, and fold away immediately. + // We could eventually extend the optimization to handle that. See JDK-8379398. + // + // AVX512: expect vectorization in length range [4..64] + // TODO: 2-element masks are currently not properly intrinsified, see JDK-8378589. + (t1.length() >= 4 && t2.length() >= 4) + ?( (t1.length() == t2.length && t1.length() != 64) + ? """ + @IR(counts = {IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0"}, // Optimized away + applyIfCPUFeature = {"avx512", "true"}) + """ + : """ + @IR(counts = {IRNode.VECTOR_LONG_TO_MASK, "> 0", // Cannot optimize + IRNode.VECTOR_MASK_TO_LONG, "> 0"}, // Cannot optimize + applyIfCPUFeature = {"avx512", "true"}) + """) + :( """ + // AVX512: at least one vector length not in range [4..64] -> no IR rule. + """), + // AVX2: expect vectorization if: length >= 4 and bitSize <= 256 + // TODO: 2-element masks are currently not properly intrinsified, see JDK-8378589. + (t1.bitSize() <= 256 && t2.bitSize() <= 256 && t1.length() >= 4 && t2.length() >= 4) + ?( (t1.length() == t2.length) + ? """ + @IR(counts = {IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away + IRNode.VECTOR_MASK_TO_LONG, "= 0"}, // Optimized away + applyIfCPUFeatureAnd = {"avx2", "true", "avx512", "false"}) + """ + : """ + @IR(counts = {IRNode.VECTOR_LONG_TO_MASK, "> 0", // Cannot optimize + IRNode.VECTOR_MASK_TO_LONG, "> 0"}, // Cannot optimize + applyIfCPUFeatureAnd = {"avx2", "true", "avx512", "false"}) + """) + :( """ + // AVX2: at least one vector length not: length >= 4 and bitSize <= 256 -> no IR rule. + """), + """ + public static Object $test() { + var inputs = #V1.fromArray(#S1, $INPUT, 0); + var mask1 = inputs.compare(VectorOperators.GT, 0); + + var mask2 = VectorMask.fromLong(#S2, mask1.toLong()); + + var zeros = #V2.zero(#S2); + var m1s = #V2.broadcast(#S2, -1); + var res = zeros.blend(m1s, mask2); + + #e2[] out = new #e2[64]; + res.intoArray(out, 0); + return out; + } + + public static #e1[] $INPUT = new #e1[64]; + """, + "static { for (int i = 0; i < $INPUT.length; i++) { $INPUT[i] = ", t1.elementType().callLibraryRNG(), "; } }", + """ + public static Object $GOLD = $test(); + + @Check(test = "$test") + public static void $check(Object val) { + Verify.checkEQ($GOLD, val); + } + """ + )); + + tests.add(PrimitiveType.generateLibraryRNG()); + + // It would take a bit long to cover all 20*20=400 combinations, but we can sample some: + for (int i = 0; i < 20; i++) { + VectorType t1 = VECTOR_TYPES.get(RANDOM.nextInt(VECTOR_TYPES.size())); + VectorType t2 = VECTOR_TYPES.get(RANDOM.nextInt(VECTOR_TYPES.size())); + tests.add(testTemplate.asToken(t1, t2)); + } + + // Create the test class, which runs all testTemplateTokens. + return TestFrameworkClass.render( + // package and class name. + "compiler.vectorapi.templated", "Templated", + // List of imports. + Set.of("compiler.lib.verify.*", + "java.util.Random", + "jdk.test.lib.Utils", + "compiler.lib.generators.*", + "jdk.incubator.vector.*"), + // classpath, so the Test VM has access to the compiled class files. + comp.getEscapedClassPathOfCompiledClasses(), + // The list of tests. + tests); + } +} From c04fe765a3bf952fdb6df526ee681395f2015def Mon Sep 17 00:00:00 2001 From: Dusan Balek Date: Mon, 16 Mar 2026 07:47:26 +0000 Subject: [PATCH 54/97] 8378524: Compile-time constant generated through annotation processor is not usable in repeated annotation but fails compilation Reviewed-by: jlahoda, vromero --- .../com/sun/tools/javac/comp/Annotate.java | 1 - .../ComplexGeneratedInRepeating.java | 221 ++++++++++++++++++ 2 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 test/langtools/tools/javac/annotations/repeatingAnnotations/generatedInRepeating/ComplexGeneratedInRepeating.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java index f865afe11fb7..118d761573b9 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java @@ -861,7 +861,6 @@ private T processRepeatedAnnotations(List anno if (!chk.validateAnnotationDeferErrors(annoTree)) log.error(annoTree.pos(), Errors.DuplicateAnnotationInvalidRepeated(origAnnoType)); - c = attributeAnnotation(annoTree, targetContainerType, ctx.env); c.setSynthesized(true); @SuppressWarnings("unchecked") diff --git a/test/langtools/tools/javac/annotations/repeatingAnnotations/generatedInRepeating/ComplexGeneratedInRepeating.java b/test/langtools/tools/javac/annotations/repeatingAnnotations/generatedInRepeating/ComplexGeneratedInRepeating.java new file mode 100644 index 000000000000..2ddb15a64ea3 --- /dev/null +++ b/test/langtools/tools/javac/annotations/repeatingAnnotations/generatedInRepeating/ComplexGeneratedInRepeating.java @@ -0,0 +1,221 @@ +/* + * 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 8378524 + * @summary Check that repeating annotations whose attributes are not-yet-generated classes and their members work. + * @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 ComplexGeneratedInRepeating + */ + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.element.TypeElement; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.ToolBox; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +public class ComplexGeneratedInRepeating { + + Path base; + ToolBox tb = new ToolBox(); + + @Test + void testMember() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + package test; + + import java.lang.annotation.Repeatable; + + @Rep(Constants.C) + @Rep(Constants.C) + public class Test {} + + @Repeatable(Reps.class) + @interface Rep { + int value(); + } + @interface Reps { + Rep[] value(); + } + """) + .processors(new ProcessorImpl()) + .run() + .writeAll(); + } + + @Test + void testUnresolvableMember() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + package test; + + import java.lang.annotation.Repeatable; + + @Rep(Constants.C) + @Rep(Constants.Unknown) + public class Test {} + + @Repeatable(Reps.class) + @interface Rep { + int value(); + } + @interface Reps { + Rep[] value(); + } + """) + .processors(new ProcessorImpl()) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:6:15: compiler.err.cant.resolve.location: kindname.variable, Unknown, , , (compiler.misc.location: kindname.class, test.Constants, null)", + "1 error")); + } + + @Test + void testIncompatibleMember() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + package test; + + import java.lang.annotation.Repeatable; + + @Rep(Constants.C) + @Rep(Constants.S) + public class Test {} + + @Repeatable(Reps.class) + @interface Rep { + int value(); + } + @interface Reps { + Rep[] value(); + } + """) + .processors(new ProcessorImpl()) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:6:15: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int)", + "1 error")); + } + + @Test + void testAnnotation() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + package test; + + import java.lang.annotation.Repeatable; + + @Rep(@Ann(Constants.C)) + @Rep(@Ann(Constants.C)) + public class Test {} + + @Repeatable(Reps.class) + @interface Rep { + Ann value(); + } + @interface Reps { + Rep[] value(); + } + """) + .processors(new ProcessorImpl()) + .run() + .writeAll(); + } + + @SupportedAnnotationTypes("*") + private static class ProcessorImpl extends AbstractProcessor { + + int round = 0; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (round++ == 0) { + try (Writer w = processingEnv.getFiler().createSourceFile("test.Constants").openWriter()) { + w.append(""" + package test; + public class Constants { + public static final int C = 0; + public static final String S = ""; + } + """); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + try (Writer w = processingEnv.getFiler().createSourceFile("test.Ann").openWriter()) { + w.append(""" + package test; + public @interface Ann { + int value(); + } + """); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + return false; + } + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +} From d3be157fd5f94502e73dd2bda35653a4ea95248d Mon Sep 17 00:00:00 2001 From: Fredrik Bredberg Date: Mon, 16 Mar 2026 09:06:58 +0000 Subject: [PATCH 55/97] 8379457: Test EATests.java#id0 ERROR: monitor list errors: error_cnt=1 Reviewed-by: aartemov, aboldtch, dholmes --- src/hotspot/share/runtime/synchronizer.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index 8ea5fe311457..e3293f94eeb8 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -1416,7 +1416,11 @@ void ObjectSynchronizer::chk_in_use_entry(ObjectMonitor* n, outputStream* out, } const markWord mark = obj->mark(); - if (!mark.has_monitor()) { + // Note: When using ObjectMonitorTable we may observe an intermediate state, + // where the monitor is globally visible, but no thread has yet transitioned + // the markWord. To avoid reporting a false positive during this transition, we + // skip the `!mark.has_monitor()` test if we are using the ObjectMonitorTable. + if (!UseObjectMonitorTable && !mark.has_monitor()) { out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's " "object does not think it has a monitor: obj=" INTPTR_FORMAT ", mark=" INTPTR_FORMAT, p2i(n), From 3b8af308d7c403cfc81195562e74cea0fccec3f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20Sikstr=C3=B6m?= Date: Mon, 16 Mar 2026 09:10:07 +0000 Subject: [PATCH 56/97] 8379665: Obsolete AlwaysActAsServerClassMachine and NeverActAsServerClassMachine Reviewed-by: stefank, ayang, aboldtch --- .../cpu/aarch64/c1_globals_aarch64.hpp | 1 - .../cpu/aarch64/c2_globals_aarch64.hpp | 3 - src/hotspot/cpu/arm/c1_globals_arm.hpp | 1 - src/hotspot/cpu/arm/c2_globals_arm.hpp | 3 - src/hotspot/cpu/ppc/c1_globals_ppc.hpp | 1 - src/hotspot/cpu/ppc/c2_globals_ppc.hpp | 3 - src/hotspot/cpu/riscv/c1_globals_riscv.hpp | 1 - src/hotspot/cpu/riscv/c2_globals_riscv.hpp | 3 - src/hotspot/cpu/s390/c1_globals_s390.hpp | 1 - src/hotspot/cpu/s390/c2_globals_s390.hpp | 3 - src/hotspot/cpu/x86/c1_globals_x86.hpp | 1 - src/hotspot/cpu/x86/c2_globals_x86.hpp | 3 - .../share/compiler/compilerDefinitions.cpp | 18 ++--- .../share/compiler/compiler_globals_pd.hpp | 7 -- src/hotspot/share/gc/shared/gc_globals.hpp | 6 -- src/hotspot/share/runtime/arguments.cpp | 4 +- src/hotspot/share/runtime/os.cpp | 10 --- src/java.base/share/man/java.md | 36 +++++----- test/hotspot/gtest/runtime/test_globals.cpp | 2 +- .../gc/arguments/TestSelectDefaultGC.java | 70 ------------------- .../jfr/event/compiler/TestCompilerPhase.java | 1 - .../test/lib/cli/CommandLineOptionTest.java | 20 ------ 22 files changed, 25 insertions(+), 173 deletions(-) delete mode 100644 test/hotspot/jtreg/gc/arguments/TestSelectDefaultGC.java diff --git a/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp b/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp index df013d9d1ee4..bb6b3ce907e2 100644 --- a/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp @@ -51,7 +51,6 @@ define_pd_global(bool, ProfileInterpreter, false); define_pd_global(size_t, CodeCacheExpansionSize, 32*K ); define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -define_pd_global(bool, NeverActAsServerClassMachine, true ); define_pd_global(bool, CICompileOSR, true ); #endif // !COMPILER2 define_pd_global(bool, UseTypeProfile, false); diff --git a/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp index 87ad9caaa454..192461d1a61a 100644 --- a/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp @@ -74,9 +74,6 @@ define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M ); define_pd_global(size_t, CodeCacheMinBlockLength, 6); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -// Ergonomics related flags -define_pd_global(bool, NeverActAsServerClassMachine, false); - define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed. #endif // CPU_AARCH64_C2_GLOBALS_AARCH64_HPP diff --git a/src/hotspot/cpu/arm/c1_globals_arm.hpp b/src/hotspot/cpu/arm/c1_globals_arm.hpp index 992cfd0e4084..9db999e81b3a 100644 --- a/src/hotspot/cpu/arm/c1_globals_arm.hpp +++ b/src/hotspot/cpu/arm/c1_globals_arm.hpp @@ -52,7 +52,6 @@ define_pd_global(bool, ProfileInterpreter, false); define_pd_global(size_t, CodeCacheExpansionSize, 32*K ); define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -define_pd_global(bool, NeverActAsServerClassMachine, true); define_pd_global(bool, CICompileOSR, true ); #endif // COMPILER2 define_pd_global(bool, UseTypeProfile, false); diff --git a/src/hotspot/cpu/arm/c2_globals_arm.hpp b/src/hotspot/cpu/arm/c2_globals_arm.hpp index 84abde5650be..34da47792ae8 100644 --- a/src/hotspot/cpu/arm/c2_globals_arm.hpp +++ b/src/hotspot/cpu/arm/c2_globals_arm.hpp @@ -93,7 +93,4 @@ define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed -// Ergonomics related flags -define_pd_global(bool, NeverActAsServerClassMachine, false); - #endif // CPU_ARM_C2_GLOBALS_ARM_HPP diff --git a/src/hotspot/cpu/ppc/c1_globals_ppc.hpp b/src/hotspot/cpu/ppc/c1_globals_ppc.hpp index 7d2e44a88b6b..c6fe15aac072 100644 --- a/src/hotspot/cpu/ppc/c1_globals_ppc.hpp +++ b/src/hotspot/cpu/ppc/c1_globals_ppc.hpp @@ -51,7 +51,6 @@ define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M ); define_pd_global(size_t, CodeCacheExpansionSize, 32*K); define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -define_pd_global(bool, NeverActAsServerClassMachine, true); define_pd_global(size_t, InitialCodeCacheSize, 160*K); #endif // !COMPILER2 diff --git a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp index eeff8d93dd42..e4942fa1850a 100644 --- a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp +++ b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp @@ -90,7 +90,4 @@ define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, TrapBasedRangeChecks, true); -// Ergonomics related flags -define_pd_global(bool, NeverActAsServerClassMachine, false); - #endif // CPU_PPC_C2_GLOBALS_PPC_HPP diff --git a/src/hotspot/cpu/riscv/c1_globals_riscv.hpp b/src/hotspot/cpu/riscv/c1_globals_riscv.hpp index 00b92a0dec30..b940393f063e 100644 --- a/src/hotspot/cpu/riscv/c1_globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/c1_globals_riscv.hpp @@ -51,7 +51,6 @@ define_pd_global(bool, ProfileInterpreter, false); define_pd_global(size_t, CodeCacheExpansionSize, 32*K ); define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -define_pd_global(bool, NeverActAsServerClassMachine, true ); define_pd_global(bool, CICompileOSR, true ); #endif // !COMPILER2 define_pd_global(bool, UseTypeProfile, false); diff --git a/src/hotspot/cpu/riscv/c2_globals_riscv.hpp b/src/hotspot/cpu/riscv/c2_globals_riscv.hpp index afb4ae8fcdd0..73ef97939ed2 100644 --- a/src/hotspot/cpu/riscv/c2_globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/c2_globals_riscv.hpp @@ -74,9 +74,6 @@ define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M ); define_pd_global(size_t, CodeCacheMinBlockLength, 6); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -// Ergonomics related flags -define_pd_global(bool, NeverActAsServerClassMachine, false); - define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed. #endif // CPU_RISCV_C2_GLOBALS_RISCV_HPP diff --git a/src/hotspot/cpu/s390/c1_globals_s390.hpp b/src/hotspot/cpu/s390/c1_globals_s390.hpp index bd07dd870662..64cc239800ac 100644 --- a/src/hotspot/cpu/s390/c1_globals_s390.hpp +++ b/src/hotspot/cpu/s390/c1_globals_s390.hpp @@ -51,7 +51,6 @@ define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M); define_pd_global(size_t, CodeCacheExpansionSize, 32*K); define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -define_pd_global(bool, NeverActAsServerClassMachine, true); define_pd_global(size_t, InitialCodeCacheSize, 160*K); #endif // !COMPILER2 diff --git a/src/hotspot/cpu/s390/c2_globals_s390.hpp b/src/hotspot/cpu/s390/c2_globals_s390.hpp index 068511be8f32..eee3a8588c3c 100644 --- a/src/hotspot/cpu/s390/c2_globals_s390.hpp +++ b/src/hotspot/cpu/s390/c2_globals_s390.hpp @@ -78,7 +78,4 @@ define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed on z/Architecture. -// Ergonomics related flags -define_pd_global(bool, NeverActAsServerClassMachine, false); - #endif // CPU_S390_C2_GLOBALS_S390_HPP diff --git a/src/hotspot/cpu/x86/c1_globals_x86.hpp b/src/hotspot/cpu/x86/c1_globals_x86.hpp index 063a9185d53f..bb75a31a77c9 100644 --- a/src/hotspot/cpu/x86/c1_globals_x86.hpp +++ b/src/hotspot/cpu/x86/c1_globals_x86.hpp @@ -50,7 +50,6 @@ define_pd_global(bool, ProfileInterpreter, false); define_pd_global(size_t, CodeCacheExpansionSize, 32*K ); define_pd_global(size_t, CodeCacheMinBlockLength, 1 ); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); -define_pd_global(bool, NeverActAsServerClassMachine, true ); define_pd_global(bool, CICompileOSR, true ); #endif // !COMPILER2 define_pd_global(bool, UseTypeProfile, false); diff --git a/src/hotspot/cpu/x86/c2_globals_x86.hpp b/src/hotspot/cpu/x86/c2_globals_x86.hpp index bc119693d32b..11d8c03d0cae 100644 --- a/src/hotspot/cpu/x86/c2_globals_x86.hpp +++ b/src/hotspot/cpu/x86/c2_globals_x86.hpp @@ -73,7 +73,4 @@ define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed on x86. -// Ergonomics related flags -define_pd_global(bool, NeverActAsServerClassMachine, false); - #endif // CPU_X86_C2_GLOBALS_X86_HPP diff --git a/src/hotspot/share/compiler/compilerDefinitions.cpp b/src/hotspot/share/compiler/compilerDefinitions.cpp index b82440368359..5b207334599f 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.cpp +++ b/src/hotspot/share/compiler/compilerDefinitions.cpp @@ -194,9 +194,6 @@ void CompilerConfig::set_client_emulation_mode_flags() { FLAG_SET_ERGO(EnableJVMCI, false); FLAG_SET_ERGO(UseJVMCICompiler, false); #endif - if (FLAG_IS_DEFAULT(NeverActAsServerClassMachine)) { - FLAG_SET_ERGO(NeverActAsServerClassMachine, true); - } if (FLAG_IS_DEFAULT(InitialCodeCacheSize)) { FLAG_SET_ERGO(InitialCodeCacheSize, 160*K); } @@ -548,17 +545,10 @@ bool CompilerConfig::should_set_client_emulation_mode_flags() { return false; #endif - if (has_c1()) { - if (!is_compilation_mode_selected()) { - if (NeverActAsServerClassMachine) { - return true; - } - } else if (!has_c2() && !is_jvmci_compiler()) { - return true; - } - } - - return false; + return has_c1() && + is_compilation_mode_selected() && + !has_c2() && + !is_jvmci_compiler(); } void CompilerConfig::ergo_initialize() { diff --git a/src/hotspot/share/compiler/compiler_globals_pd.hpp b/src/hotspot/share/compiler/compiler_globals_pd.hpp index 537a7f030fb4..8ac4b53d6cd0 100644 --- a/src/hotspot/share/compiler/compiler_globals_pd.hpp +++ b/src/hotspot/share/compiler/compiler_globals_pd.hpp @@ -69,13 +69,6 @@ define_pd_global(size_t, NonNMethodCodeHeapSize, 32*M); define_pd_global(size_t, CodeCacheExpansionSize, 32*K); define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 200*K); -#ifndef ZERO -define_pd_global(bool, NeverActAsServerClassMachine, true); -#else -// Zero runs without compilers. Do not let this code to force -// the GC mode and default heap settings. -define_pd_global(bool, NeverActAsServerClassMachine, false); -#endif #define CI_COMPILER_COUNT 0 #else diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index 2fd062de6481..66ca10f1fb6b 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.hpp @@ -256,12 +256,6 @@ "before pushing a continuation entry") \ range(1, INT_MAX/2) \ \ - product_pd(bool, NeverActAsServerClassMachine, \ - "(Deprecated) Never act like a server-class machine") \ - \ - product(bool, AlwaysActAsServerClassMachine, false, \ - "(Deprecated) Always act like a server-class machine") \ - \ product(bool, AggressiveHeap, false, \ "(Deprecated) Optimize heap options for long-running memory " \ "intensive apps") \ diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 11c4853c3881..449ac8e826ce 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -537,8 +537,6 @@ static SpecialFlag const special_jvm_flags[] = { { "UseCompressedClassPointers", JDK_Version::jdk(25), JDK_Version::jdk(27), JDK_Version::undefined() }, #endif { "AggressiveHeap", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, - { "NeverActAsServerClassMachine", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, - { "AlwaysActAsServerClassMachine", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, // --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in: { "CreateMinidumpOnCrash", JDK_Version::jdk(9), JDK_Version::undefined(), JDK_Version::undefined() }, @@ -554,6 +552,8 @@ static SpecialFlag const special_jvm_flags[] = { { "ParallelRefProcBalancingEnabled", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "MaxRAM", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "NewSizeThreadIncrease", JDK_Version::undefined(), JDK_Version::jdk(27), JDK_Version::jdk(28) }, + { "NeverActAsServerClassMachine", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, + { "AlwaysActAsServerClassMachine", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, #ifdef ASSERT { "DummyObsoleteTestFlag", JDK_Version::undefined(), JDK_Version::jdk(18), JDK_Version::undefined() }, diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 16335f97fdb1..8f7e4d3cb6dc 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -1912,17 +1912,7 @@ void os::trace_page_sizes_for_requested_size(const char* str, // as was done for logical processors here, or replicate and // specialize this method for each platform. (Or fix os to have // some inheritance structure and use subclassing. Sigh.) -// If you want some platform to always or never behave as a server -// class machine, change the setting of AlwaysActAsServerClassMachine -// and NeverActAsServerClassMachine in globals*.hpp. bool os::is_server_class_machine() { - // First check for the early returns - if (NeverActAsServerClassMachine) { - return false; - } - if (AlwaysActAsServerClassMachine) { - return true; - } // Then actually look at the machine bool result = false; const unsigned int server_processors = 2; diff --git a/src/java.base/share/man/java.md b/src/java.base/share/man/java.md index 8cfccff5abeb..18d64b3a4c2b 100644 --- a/src/java.base/share/man/java.md +++ b/src/java.base/share/man/java.md @@ -2944,17 +2944,29 @@ they're used. the configuration of the computer (RAM and CPU). By default, the option is disabled and the heap sizes are configured less aggressively. +## Obsolete Java Options + +These `java` options are still accepted but ignored, and a warning is issued +when they're used. + +[`--illegal-access=`]{#--illegal-access}*parameter* +: Controlled _relaxed strong encapsulation_, as defined in [JEP + 261](https://openjdk.org/jeps/261#Relaxed-strong-encapsulation). + This option was deprecated in JDK 16 by [JEP + 396](https://openjdk.org/jeps/396) and made obsolete in JDK 17 + by [JEP 403](https://openjdk.org/jeps/403). + [`-XX:+NeverActAsServerClassMachine`]{#-XX__NeverActAsServerClassMachine} -: Enable the "Client VM emulation" mode which only uses the C1 JIT compiler, - a 32Mb CodeCache and the Serial GC. The maximum amount of memory that the - JVM may use is set to 1GB by default. The string "emulated-client" is added +: Enabled the "Client VM emulation" mode, which used only the C1 JIT compiler, + a 32Mb CodeCache, and the Serial GC. The maximum amount of memory that the + JVM could use was set to 1GB by default. The string "emulated-client" was added to the JVM version string. - By default the flag is set to `true` only on Windows in 32-bit mode and + By default the flag was set to `true` only on Windows in 32-bit mode and `false` in all other cases. - The "Client VM emulation" mode will not be enabled if any of the following - flags are used on the command line: + The "Client VM emulation" mode was not enabled if any of the following + flags were used on the command line: ``` -XX:{+|-}TieredCompilation @@ -2964,18 +2976,6 @@ they're used. -XX:{+|-}UseJVMCICompiler ``` -## Obsolete Java Options - -These `java` options are still accepted but ignored, and a warning is issued -when they're used. - -[`--illegal-access=`]{#--illegal-access}*parameter* -: Controlled _relaxed strong encapsulation_, as defined in [JEP - 261](https://openjdk.org/jeps/261#Relaxed-strong-encapsulation). - This option was deprecated in JDK 16 by [JEP - 396](https://openjdk.org/jeps/396) and made obsolete in JDK 17 - by [JEP 403](https://openjdk.org/jeps/403). - ## Removed Java Options No documented java options have been removed in JDK @@VERSION_SPECIFICATION@@. diff --git a/test/hotspot/gtest/runtime/test_globals.cpp b/test/hotspot/gtest/runtime/test_globals.cpp index 9ef5bd6a5af7..e88f930ff731 100644 --- a/test/hotspot/gtest/runtime/test_globals.cpp +++ b/test/hotspot/gtest/runtime/test_globals.cpp @@ -42,7 +42,7 @@ } while (0) TEST_VM(FlagGuard, bool_flag) { - TEST_FLAG(AlwaysActAsServerClassMachine, bool, true); + TEST_FLAG(PrintCompilation, bool, true); } TEST_VM(FlagGuard, int_flag) { diff --git a/test/hotspot/jtreg/gc/arguments/TestSelectDefaultGC.java b/test/hotspot/jtreg/gc/arguments/TestSelectDefaultGC.java deleted file mode 100644 index a5e144bcd151..000000000000 --- a/test/hotspot/jtreg/gc/arguments/TestSelectDefaultGC.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package gc.arguments; - -/* - * @test TestSelectDefaultGC - * @summary Test selection of GC when no GC option is specified - * @bug 8068582 - * @library /test/lib - * @library / - * @requires vm.gc.Serial & vm.gc.G1 - * @modules java.base/jdk.internal.misc - * java.management - * @run driver gc.arguments.TestSelectDefaultGC - */ - -import jdk.test.lib.process.OutputAnalyzer; - -public class TestSelectDefaultGC { - public static void assertVMOption(OutputAnalyzer output, String option, boolean value) { - output.shouldMatch(" " + option + " .*=.* " + value + " "); - } - - public static void testDefaultGC(boolean actAsServer) throws Exception { - // Start VM without specifying GC - OutputAnalyzer output = GCArguments.executeTestJava( - "-XX:" + (actAsServer ? "+" : "-") + "AlwaysActAsServerClassMachine", - "-XX:" + (actAsServer ? "-" : "+") + "NeverActAsServerClassMachine", - "-XX:+PrintFlagsFinal", - "-version"); - output.shouldHaveExitValue(0); - - final boolean isServer = actAsServer; - - // Verify GC selection - // G1 is default for server class machines - assertVMOption(output, "UseG1GC", isServer); - // Serial is default for non-server class machines - assertVMOption(output, "UseSerialGC", !isServer); - } - - public static void main(String[] args) throws Exception { - // Test server class machine - testDefaultGC(false); - - // Test non-server class machine - testDefaultGC(true); - } -} diff --git a/test/jdk/jdk/jfr/event/compiler/TestCompilerPhase.java b/test/jdk/jdk/jfr/event/compiler/TestCompilerPhase.java index 33884d448bff..0433e157f3d4 100644 --- a/test/jdk/jdk/jfr/event/compiler/TestCompilerPhase.java +++ b/test/jdk/jdk/jfr/event/compiler/TestCompilerPhase.java @@ -42,7 +42,6 @@ * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -Xbootclasspath/a:. - * -XX:-NeverActAsServerClassMachine * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:CompileOnly=jdk.jfr.event.compiler.TestCompilerPhase::dummyMethod * -XX:+SegmentedCodeCache -Xbootclasspath/a:. diff --git a/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java b/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java index a9ae57bca710..48738d4a0da7 100644 --- a/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java +++ b/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java @@ -201,10 +201,6 @@ public static void verifySameJVMStartup(String expectedMessages[], if (!Platform.isStatic()) { finalOptions.add(CommandLineOptionTest.getVMTypeOption()); } - String extraFlagForEmulated = CommandLineOptionTest.getVMTypeOptionForEmulated(); - if (extraFlagForEmulated != null) { - finalOptions.add(extraFlagForEmulated); - } Collections.addAll(finalOptions, options); CommandLineOptionTest.verifyJVMStartup(expectedMessages, @@ -401,10 +397,6 @@ public static void verifyOptionValueForSameVM(String optionName, if (!Platform.isStatic()) { finalOptions.add(CommandLineOptionTest.getVMTypeOption()); } - String extraFlagForEmulated = CommandLineOptionTest.getVMTypeOptionForEmulated(); - if (extraFlagForEmulated != null) { - finalOptions.add(extraFlagForEmulated); - } Collections.addAll(finalOptions, additionalVMOpts); CommandLineOptionTest.verifyOptionValue(optionName, expectedValue, @@ -512,18 +504,6 @@ private static String getVMTypeOption() { throw new RuntimeException("Unknown VM mode."); } - /** - * @return addtional VMoptions(Emulated related) required to start a new VM with the same type as current. - */ - private static String getVMTypeOptionForEmulated() { - if (Platform.isServer() && !Platform.isEmulatedClient()) { - return "-XX:-NeverActAsServerClassMachine"; - } else if (Platform.isEmulatedClient()) { - return "-XX:+NeverActAsServerClassMachine"; - } - return null; - } - private final BooleanSupplier predicate; /** From caf7e840c330431b329edb06cbc3c4708f1a9822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Mon, 16 Mar 2026 09:29:09 +0000 Subject: [PATCH 57/97] 8377541: C2: Memory Barrier IR nodes not eliminated for stable array element access Reviewed-by: dfenacci, qamai, mchevalier, dlong --- src/hotspot/share/opto/graphKit.cpp | 68 +++++++++++- src/hotspot/share/opto/graphKit.hpp | 25 ++++- src/hotspot/share/opto/library_call.cpp | 41 ------- src/hotspot/share/opto/library_call.hpp | 23 ---- .../gcbarriers/TestZGCBarrierElision.java | 5 +- .../stable/TestStableArrayMembars.java | 105 ++++++++++++++++++ 6 files changed, 196 insertions(+), 71 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/stable/TestStableArrayMembars.java diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index c969abb85bb9..31da6fd0e2dd 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -670,6 +670,48 @@ ciInstance* GraphKit::builtin_throw_exception(Deoptimization::DeoptReason reason } } +GraphKit::SavedState::SavedState(GraphKit* kit) : + _kit(kit), + _sp(kit->sp()), + _jvms(kit->jvms()), + _map(kit->clone_map()), + _discarded(false) +{ + for (DUIterator_Fast imax, i = kit->control()->fast_outs(imax); i < imax; i++) { + Node* out = kit->control()->fast_out(i); + if (out->is_CFG()) { + _ctrl_succ.push(out); + } + } +} + +GraphKit::SavedState::~SavedState() { + if (_discarded) { + _kit->destruct_map_clone(_map); + return; + } + _kit->jvms()->set_map(_map); + _kit->jvms()->set_sp(_sp); + _map->set_jvms(_kit->jvms()); + _kit->set_map(_map); + _kit->set_sp(_sp); + for (DUIterator_Fast imax, i = _kit->control()->fast_outs(imax); i < imax; i++) { + Node* out = _kit->control()->fast_out(i); + if (out->is_CFG() && out->in(0) == _kit->control() && out != _kit->map() && !_ctrl_succ.member(out)) { + _kit->_gvn.hash_delete(out); + out->set_req(0, _kit->C->top()); + _kit->C->record_for_igvn(out); + --i; --imax; + _kit->_gvn.hash_find_insert(out); + } + } +} + +void GraphKit::SavedState::discard() { + _discarded = true; +} + + //----------------------------PreserveJVMState--------------------------------- PreserveJVMState::PreserveJVMState(GraphKit* kit, bool clone_map) { DEBUG_ONLY(kit->verify_map()); @@ -1678,13 +1720,22 @@ Node* GraphKit::access_load_at(Node* obj, // containing obj return top(); // Dead path ? } + SavedState old_state(this); C2AccessValuePtr addr(adr, adr_type); C2ParseAccess access(this, decorators | C2_READ_ACCESS, bt, obj, addr); + Node* load; if (access.is_raw()) { - return _barrier_set->BarrierSetC2::load_at(access, val_type); + load = _barrier_set->BarrierSetC2::load_at(access, val_type); } else { - return _barrier_set->load_at(access, val_type); + load = _barrier_set->load_at(access, val_type); + } + + // Restore the previous state only if the load got folded to a constant + // and we can discard any barriers that might have been added. + if (load == nullptr || !load->is_Con()) { + old_state.discard(); } + return load; } Node* GraphKit::access_load(Node* adr, // actual address to load val at @@ -1695,13 +1746,22 @@ Node* GraphKit::access_load(Node* adr, // actual address to load val at return top(); // Dead path ? } + SavedState old_state(this); C2AccessValuePtr addr(adr, adr->bottom_type()->is_ptr()); C2ParseAccess access(this, decorators | C2_READ_ACCESS, bt, nullptr, addr); + Node* load; if (access.is_raw()) { - return _barrier_set->BarrierSetC2::load_at(access, val_type); + load = _barrier_set->BarrierSetC2::load_at(access, val_type); } else { - return _barrier_set->load_at(access, val_type); + load = _barrier_set->load_at(access, val_type); } + + // Restore the previous state only if the load got folded to a constant + // and we can discard any barriers that might have been added. + if (load == nullptr || !load->is_Con()) { + old_state.discard(); + } + return load; } Node* GraphKit::access_atomic_cmpxchg_val_at(Node* obj, diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index 0537d31ae363..dc691173f710 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -876,6 +876,29 @@ class GraphKit : public Phase { Node* box_vector(Node* in, const TypeInstPtr* vbox_type, BasicType elem_bt, int num_elem, bool deoptimize_on_exception = false); Node* unbox_vector(Node* in, const TypeInstPtr* vbox_type, BasicType elem_bt, int num_elem); Node* vector_shift_count(Node* cnt, int shift_op, BasicType bt, int num_elem); + + // Helper class to support reverting to a previous parsing state. + // When an intrinsic makes changes before bailing out, it's necessary to restore the graph + // as it was. See JDK-8359344 for what can happen wrong. It's also not always possible to + // bailout before making changes because the bailing out decision might depend on new nodes + // (their types, for instance). + // + // So, if an intrinsic might cause this situation, one must start by saving the state in a + // SavedState by constructing it, and the state will be restored on destruction. If the + // intrinsic is not bailing out, one need to call discard to prevent restoring the old state. + class SavedState : public StackObj { + GraphKit* _kit; + int _sp; + JVMState* _jvms; + SafePointNode* _map; + Unique_Node_List _ctrl_succ; + bool _discarded; + + public: + SavedState(GraphKit*); + ~SavedState(); + void discard(); + }; }; // Helper class to support building of control flow branches. Upon diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 8d3c5a98ea00..501e35ae5cb9 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -2393,47 +2393,6 @@ DecoratorSet LibraryCallKit::mo_decorator_for_access_kind(AccessKind kind) { } } -LibraryCallKit::SavedState::SavedState(LibraryCallKit* kit) : - _kit(kit), - _sp(kit->sp()), - _jvms(kit->jvms()), - _map(kit->clone_map()), - _discarded(false) -{ - for (DUIterator_Fast imax, i = kit->control()->fast_outs(imax); i < imax; i++) { - Node* out = kit->control()->fast_out(i); - if (out->is_CFG()) { - _ctrl_succ.push(out); - } - } -} - -LibraryCallKit::SavedState::~SavedState() { - if (_discarded) { - _kit->destruct_map_clone(_map); - return; - } - _kit->jvms()->set_map(_map); - _kit->jvms()->set_sp(_sp); - _map->set_jvms(_kit->jvms()); - _kit->set_map(_map); - _kit->set_sp(_sp); - for (DUIterator_Fast imax, i = _kit->control()->fast_outs(imax); i < imax; i++) { - Node* out = _kit->control()->fast_out(i); - if (out->is_CFG() && out->in(0) == _kit->control() && out != _kit->map() && !_ctrl_succ.member(out)) { - _kit->_gvn.hash_delete(out); - out->set_req(0, _kit->C->top()); - _kit->C->record_for_igvn(out); - --i; --imax; - _kit->_gvn.hash_find_insert(out); - } - } -} - -void LibraryCallKit::SavedState::discard() { - _discarded = true; -} - bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, const AccessKind kind, const bool unaligned) { if (callee()->is_static()) return false; // caller must have the capability! DecoratorSet decorators = C2_UNSAFE_ACCESS; diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 56141be23625..bd4b753d1cbe 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -129,29 +129,6 @@ class LibraryCallKit : public GraphKit { virtual int reexecute_sp() { return _reexecute_sp; } - /* When an intrinsic makes changes before bailing out, it's necessary to restore the graph - * as it was. See JDK-8359344 for what can happen wrong. It's also not always possible to - * bailout before making changes because the bailing out decision might depend on new nodes - * (their types, for instance). - * - * So, if an intrinsic might cause this situation, one must start by saving the state in a - * SavedState by constructing it, and the state will be restored on destruction. If the - * intrinsic is not bailing out, one need to call discard to prevent restoring the old state. - */ - class SavedState { - LibraryCallKit* _kit; - uint _sp; - JVMState* _jvms; - SafePointNode* _map; - Unique_Node_List _ctrl_succ; - bool _discarded; - - public: - SavedState(LibraryCallKit*); - ~SavedState(); - void discard(); - }; - // Helper functions to inline natives Node* generate_guard(Node* test, RegionNode* region, float true_prob); Node* generate_slow_guard(Node* test, RegionNode* region); diff --git a/test/hotspot/jtreg/compiler/gcbarriers/TestZGCBarrierElision.java b/test/hotspot/jtreg/compiler/gcbarriers/TestZGCBarrierElision.java index ff20f750935f..38aaee62045a 100644 --- a/test/hotspot/jtreg/compiler/gcbarriers/TestZGCBarrierElision.java +++ b/test/hotspot/jtreg/compiler/gcbarriers/TestZGCBarrierElision.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 @@ -251,7 +251,8 @@ void runAtomicOperationTests() { class TestZGCEffectiveBarrierElision { @Test - @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) + // C2 does not emit a field load during parsing, so it also does not emit any barriers. + @IR(failOn = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED }, phase = CompilePhase.FINAL_CODE) static void testAllocateThenLoad() { Outer o1 = new Outer(); Common.blackhole(o1); diff --git a/test/hotspot/jtreg/compiler/stable/TestStableArrayMembars.java b/test/hotspot/jtreg/compiler/stable/TestStableArrayMembars.java new file mode 100644 index 000000000000..9d7babddfb99 --- /dev/null +++ b/test/hotspot/jtreg/compiler/stable/TestStableArrayMembars.java @@ -0,0 +1,105 @@ +/* + * 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 8377541 + * @summary Test that membars are eliminated when loading from a stable array. + * @library /test/lib / + * @modules java.base/jdk.internal.misc + * @modules java.base/jdk.internal.vm.annotation + * @run driver ${test.main.class} + */ + +package compiler.stable; + +import java.util.Objects; + +import jdk.internal.misc.Unsafe; +import jdk.internal.vm.annotation.Stable; + +import compiler.lib.ir_framework.*; + +public class TestStableArrayMembars { + + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + public static void main(String[] args) { + TestFramework tf = new TestFramework(); + tf.addTestClassesToBootClassPath(); + tf.addFlags( "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED"); + tf.start(); + } + + static final class LazyIntArray { + @Stable + private final Integer[] arr; + + LazyIntArray() { + this.arr = new Integer[10]; + } + + @ForceInline + Integer get(int idx) { + Integer i = contentsAcquire(offsetFor(idx)); + return i == null ? slowPath(arr, idx) : i; + } + + @ForceInline + private Integer contentsAcquire(long offset) { + return (Integer) UNSAFE.getReferenceAcquire(arr, offset); + } + + @ForceInline + private static long offsetFor(long index) { + return Unsafe.ARRAY_OBJECT_BASE_OFFSET + Unsafe.ARRAY_OBJECT_INDEX_SCALE * index; + } + + static Integer slowPath(final Integer[] array, final int index) { + final long offset = offsetFor(index); + final Integer t = array[index]; + if (t == null) { + final Integer newValue = Integer.valueOf(42); + Objects.requireNonNull(newValue); + set(array, index, newValue); + + return newValue; + } + return t; + } + + static void set(Integer[] array, int index, Integer newValue) { + if (array[index] == null) { + UNSAFE.putReferenceRelease(array, Unsafe.ARRAY_OBJECT_BASE_OFFSET + Unsafe.ARRAY_OBJECT_INDEX_SCALE * (long) index, newValue); + } + } + } + + static final LazyIntArray la = new LazyIntArray(); + + @Test + @IR(failOn = { IRNode.LOAD, IRNode.MEMBAR }) + static Integer test() { + return la.get(0); + } +} From c0aa411b3456ae71db775b8c7756ae1b8fa6da58 Mon Sep 17 00:00:00 2001 From: Dusan Balek Date: Mon, 16 Mar 2026 12:07:04 +0000 Subject: [PATCH 58/97] 8378251: Extend word delimiters used in jshell Reviewed-by: jlahoda --- .../jshell/tool/ConsoleIOContext.java | 3 +- test/langtools/jdk/jshell/InputUITest.java | 36 +++++++++++++++++-- test/langtools/jdk/jshell/UITesting.java | 6 +++- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java index 57d427c17742..32664504d209 100644 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -181,6 +181,7 @@ public int readBuffered(byte[] b) throws IOException { setupReader.accept(reader); reader.setOpt(Option.DISABLE_EVENT_EXPANSION); + reader.setVariable(LineReader.WORDCHARS, "_$"); reader.setParser((line, cursor, context) -> { if (!ConsoleIOContext.this.allowIncompleteInputs && !repl.isComplete(line)) { diff --git a/test/langtools/jdk/jshell/InputUITest.java b/test/langtools/jdk/jshell/InputUITest.java index 1a420d2c3458..6886e1302a28 100644 --- a/test/langtools/jdk/jshell/InputUITest.java +++ b/test/langtools/jdk/jshell/InputUITest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8356165 8358552 + * @bug 8356165 8358552 8378251 * @summary Check user input works properly * @modules * jdk.compiler/com.sun.tools.javac.api @@ -99,4 +99,34 @@ public void testUserInputWithCtrlDAndMultipleSnippets() throws Exception { waitOutput(out, patternQuote("==> 65")); }, false); } -} \ No newline at end of file + + @Test + public void testAltBackspaceDeletesPreviousWord() throws Exception { + doRunTest((inputSink, out) -> { + inputSink.write("int x = 12 24" + ESC_DEL + "\n"); + waitOutput(out, "int x = 12 24\u001B\\[2D\u001B\\[K\n" + + "\u001B\\[\\?2004lx ==> 12\n" + + "\u001B\\[\\?2004h" + PROMPT); + inputSink.write("System.in" + ESC_DEL + "out.println(x)\n"); + waitOutput(out, "System.in\u001B\\[2D\u001B\\[Kout.println\\(x\\)\u001B\\[3D\u001B\\[3C\n" + + "\u001B\\[\\?2004l12\n" + + "\u001B\\[\\?2004h" + PROMPT); + }, false); + } + + @Test + public void testAltDDeletesNextWord() throws Exception { + doRunTest((inputSink, out) -> { + inputSink.write("int x = 12 24" + ESC_B + ESC_D + "\n"); + waitOutput(out, "int x = 12 24\u001B\\[2D\u001B\\[K\n" + + "\u001B\\[\\?2004lx ==> 12\n" + + "\u001B\\[\\?2004h" + PROMPT); + inputSink.write("System.in.println" + ESC_B + ESC_B + ESC_D + + "out" + ESC_F + ESC_F + "(x)\n"); + waitOutput(out, "System.in.println\u001B\\[7D\u001B\\[3D\u001B\\[2P" + + "\u001B\\[1@o\u001B\\[1@u\u001B\\[1@t\u001B\\[C\u001B\\[7C\\(x\\)\u001B\\[3D\u001B\\[3C\n" + + "\u001B\\[\\?2004l12\n" + + "\u001B\\[\\?2004h" + PROMPT); + }, false); + } +} diff --git a/test/langtools/jdk/jshell/UITesting.java b/test/langtools/jdk/jshell/UITesting.java index a1bd8f35dee7..d63a8460889a 100644 --- a/test/langtools/jdk/jshell/UITesting.java +++ b/test/langtools/jdk/jshell/UITesting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,6 +53,10 @@ public class UITesting { protected static final String UP = "\033[A"; protected static final String DOWN = "\033[B"; protected static final String CTRL_D = "\u0004"; + protected static final String ESC_DEL = "\u001B\u007F"; // ESC + DEL (common Alt+Backspace) + protected static final String ESC_B = "\u001Bb"; // ESC + b (common Alt+b) + protected static final String ESC_F = "\u001Bf"; // ESC + f (common Alt+f) + protected static final String ESC_D = "\u001Bd"; // ESC + d (common Alt+d) private final boolean laxLineEndings; public UITesting() { From 318646a6b4559e93722937c97329a9d710b59479 Mon Sep 17 00:00:00 2001 From: David Beaumont Date: Mon, 16 Mar 2026 13:17:53 +0000 Subject: [PATCH 59/97] 8379626: Refactor jaxp/functional/javax/xml tests to use JUnit Reviewed-by: vyazici, joehw --- .../catalog/CatalogReferCircularityTest.java | 6 +- .../xml/datatype/ptests/DurationTest.java | 298 ++++++++-------- .../ptests/FactoryNewInstanceTest.java | 45 ++- .../ptests/XMLGregorianCalendarTest.java | 172 ++++----- .../xml/parsers/ptests/DBFNamespaceTest.java | 39 +- .../ptests/DocumentBuilderFactoryTest.java | 131 +++---- .../parsers/ptests/DocumentBuilderImpl01.java | 48 +-- .../parsers/ptests/FactoryConfErrorTest.java | 40 ++- .../ptests/SAXFactoryNewInstanceTest.java | 35 +- .../xml/parsers/ptests/SAXParserFactTest.java | 24 +- .../xml/parsers/ptests/SAXParserTest.java | 337 +++++++----------- .../xml/parsers/ptests/SAXParserTest02.java | 76 ++-- .../xml/parsers/ptests/SAXParserTest03.java | 46 ++- .../XMLEventFactoryNewInstanceTest.java | 52 +-- .../XMLInputFactoryNewInstanceTest.java | 52 +-- .../XMLOutputFactoryNewInstanceTest.java | 52 +-- .../xml/transform/ptests/Bug6384418Test.java | 15 +- .../xml/transform/ptests/DOMResultTest.java | 45 +-- .../transform/ptests/ErrorListenerTest.java | 31 +- .../xml/transform/ptests/SAXSourceTest.java | 32 +- .../xml/transform/ptests/SAXTFactoryTest.java | 164 ++++----- .../transform/ptests/StreamResultTest.java | 32 +- .../transform/ptests/TfClearParamTest.java | 37 +- .../xml/transform/ptests/TransformTest.java | 78 ++-- .../transform/ptests/TransformerExcpTest.java | 61 ++-- .../ptests/TransformerFactoryTest.java | 79 ++-- .../xml/transform/ptests/TransformerTest.java | 52 ++- .../transform/ptests/TransformerTest02.java | 31 +- .../transform/ptests/TransformerTest03.java | 33 +- .../xml/transform/ptests/URIResolverTest.java | 122 ++----- .../ptests/othervm/TFCErrorTest.java | 31 +- .../xml/transform/xmlfiles/out/saxtf008GF.out | 30 +- .../validation/ptests/SchemaFactoryTest.java | 255 ++++++------- .../ptests/TypeInfoProviderTest.java | 40 +-- .../ptests/ValidatorHandlerTest.java | 86 +++-- .../xml/validation/ptests/ValidatorTest.java | 112 +++--- .../ptests/XPathEvaluationResultTest.java | 75 +--- .../xml/xpath/ptests/XPathExpressionTest.java | 278 ++++----------- .../xml/xpath/ptests/XPathFactoryTest.java | 109 +++--- .../ptests/XPathFunctionResolverTest.java | 22 +- .../javax/xml/xpath/ptests/XPathTest.java | 289 +++++++-------- .../isolatedjdk/catalog/PropertiesTest.java | 42 +-- .../javax/xml/parsers/ptests/MyCHandler.java | 13 +- .../xml/parsers/ptests/ParserTestConst.java | 21 +- .../ptests/TransformerTestConst.java | 20 +- .../ptests/ValidationTestConst.java | 22 +- .../xml/xpath/ptests/XPathTestConst.java | 10 +- .../libs/jaxp/library/JAXPDataProvider.java | 14 +- 48 files changed, 1681 insertions(+), 2053 deletions(-) diff --git a/test/jaxp/javax/xml/jaxp/functional/catalog/CatalogReferCircularityTest.java b/test/jaxp/javax/xml/jaxp/functional/catalog/CatalogReferCircularityTest.java index c88827c1a39e..612011f8eb61 100644 --- a/test/jaxp/javax/xml/jaxp/functional/catalog/CatalogReferCircularityTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/catalog/CatalogReferCircularityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,6 @@ package catalog; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -31,6 +30,7 @@ import javax.xml.catalog.CatalogResolver; import static catalog.CatalogTestUtils.catalogResolver; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test @@ -55,7 +55,7 @@ public class CatalogReferCircularityTest { "catalogReferCircle-left.xml" }) public void testReferCircularity(String catalogFile) { CatalogResolver resolver = catalogResolver(catalogFile); - Assertions.assertThrows( + assertThrows( CatalogException.class, () -> resolver.resolveEntity(null, "http://remote/dtd/ghost/docGhost.dtd")); } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/DurationTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/DurationTest.java index cb8f5c56048e..83839033b62b 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/DurationTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/DurationTest.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 @@ -23,52 +23,50 @@ package javax.xml.datatype.ptests; -import static javax.xml.datatype.DatatypeConstants.DAYS; -import static javax.xml.datatype.DatatypeConstants.HOURS; -import static javax.xml.datatype.DatatypeConstants.MINUTES; -import static javax.xml.datatype.DatatypeConstants.MONTHS; -import static javax.xml.datatype.DatatypeConstants.SECONDS; -import static javax.xml.datatype.DatatypeConstants.YEARS; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Calendar; -import java.util.function.Function; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.Duration; import javax.xml.namespace.QName; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Calendar; +import java.util.function.Function; -import org.testng.Assert; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static javax.xml.datatype.DatatypeConstants.DAYS; +import static javax.xml.datatype.DatatypeConstants.HOURS; +import static javax.xml.datatype.DatatypeConstants.MINUTES; +import static javax.xml.datatype.DatatypeConstants.MONTHS; +import static javax.xml.datatype.DatatypeConstants.SECONDS; +import static javax.xml.datatype.DatatypeConstants.YEARS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.datatype.ptests.DurationTest + * @run junit/othervm javax.xml.datatype.ptests.DurationTest * @summary Class containing the test cases for Duration. */ public class DurationTest { - private DatatypeFactory datatypeFactory; + private static DatatypeFactory datatypeFactory = null; - /* - * Setup. - */ - @BeforeClass - public void setup() throws DatatypeConfigurationException { + @BeforeAll + public static void setup() throws DatatypeConfigurationException { datatypeFactory = DatatypeFactory.newInstance(); } - @DataProvider(name = "legal-number-duration") - public Object[][] getLegalNumberDuration() { + public static Object[][] getLegalNumberDuration() { return new Object[][] { // is positive, year, month, day, hour, minute, second { true, 1, 1, 1, 1, 1, 1 }, @@ -82,13 +80,13 @@ public Object[][] getLegalNumberDuration() { * Test for constructor Duration(boolean isPositive,int years,int months, * int days,int hours,int minutes,int seconds). */ - @Test(dataProvider = "legal-number-duration") + @ParameterizedTest + @MethodSource("getLegalNumberDuration") public void checkNumberDurationPos(boolean isPositive, int years, int months, int days, int hours, int minutes, int seconds) { datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds); } - @DataProvider(name = "illegal-number-duration") - public Object[][] getIllegalNumberDuration() { + public static Object[][] getIllegalNumberDuration() { return new Object[][] { // is positive, year, month, day, hour, minute, second { true, 1, 1, -1, 1, 1, 1 }, @@ -103,13 +101,15 @@ public Object[][] getIllegalNumberDuration() { * int days,int hours,int minutes,int seconds), if any of the fields is * negative should throw IllegalArgumentException. */ - @Test(expectedExceptions = IllegalArgumentException.class, dataProvider = "illegal-number-duration") + @ParameterizedTest + @MethodSource("getIllegalNumberDuration") public void checkDurationNumberNeg(boolean isPositive, int years, int months, int days, int hours, int minutes, int seconds) { - datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds); + assertThrows( + IllegalArgumentException.class, + () -> datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds)); } - @DataProvider(name = "legal-bigint-duration") - public Object[][] getLegalBigIntegerDuration() { + public static Object[][] getLegalBigIntegerDuration() { return new Object[][] { // is positive, year, month, day, hour, minute, second { true, zero, zero, zero, zero, zero, new BigDecimal(zero) }, @@ -125,14 +125,14 @@ public Object[][] getLegalBigIntegerDuration() { * years,BigInteger months, BigInteger days,BigInteger hours,BigInteger * minutes,BigDecimal seconds). */ - @Test(dataProvider = "legal-bigint-duration") + @ParameterizedTest + @MethodSource("getLegalBigIntegerDuration") public void checkBigIntegerDurationPos(boolean isPositive, BigInteger years, BigInteger months, BigInteger days, BigInteger hours, BigInteger minutes, BigDecimal seconds) { datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds); } - @DataProvider(name = "illegal-bigint-duration") - public Object[][] getIllegalBigIntegerDuration() { + public static Object[][] getIllegalBigIntegerDuration() { return new Object[][] { // is positive, year, month, day, hour, minute, second { true, null, null, null, null, null, null }, @@ -146,79 +146,66 @@ public Object[][] getIllegalBigIntegerDuration() { * minutes,BigDecimal seconds), if all the fields are null should throw * IllegalArgumentException. */ - @Test(expectedExceptions = IllegalArgumentException.class, dataProvider = "illegal-bigint-duration") - public void checkBigIntegerDurationNeg(boolean isPositive, BigInteger years, BigInteger months, BigInteger days, BigInteger hours, BigInteger minutes, - BigDecimal seconds) { - datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds); - } - - @DataProvider(name = "legal-millisec-duration") - public Object[][] getLegalMilliSecondDuration() { - return new Object[][] { { 1000000 }, { 0 }, { Long.MAX_VALUE }, { Long.MIN_VALUE } - - }; + @ParameterizedTest + @MethodSource("getIllegalBigIntegerDuration") + public void checkBigIntegerDurationNeg( + boolean isPositive, BigInteger years, BigInteger months, BigInteger days, BigInteger hours, BigInteger minutes, BigDecimal seconds) { + assertThrows(IllegalArgumentException.class, () -> datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds)); } /* * Test for constructor Duration(long durationInMilliSeconds) */ - @Test(dataProvider = "legal-millisec-duration") + @ParameterizedTest + @ValueSource(longs={ 1000000, 0, Long.MAX_VALUE, Long.MIN_VALUE }) public void checkMilliSecondDuration(long millisec) { datatypeFactory.newDuration(millisec); } - @DataProvider(name = "legal-lexical-duration") - public Object[][] getLegalLexicalDuration() { - return new Object[][] { { "P1Y1M1DT1H1M1S" }, { "-P1Y1M1DT1H1M1S" } }; - } - /* * Test for constructor Duration(java.lang.String lexicalRepresentation) */ - @Test(dataProvider = "legal-lexical-duration") + @ParameterizedTest + @ValueSource(strings={ "P1Y1M1DT1H1M1S", "-P1Y1M1DT1H1M1S" }) public void checkLexicalDurationPos(String lexRepresentation) { datatypeFactory.newDuration(lexRepresentation); } - @DataProvider(name = "illegal-lexical-duration") - public Object[][] getIllegalLexicalDuration() { - return new Object[][] { - { null }, - { "P1Y1M1DT1H1M1S " }, - { " P1Y1M1DT1H1M1S" }, - { "X1Y1M1DT1H1M1S" }, - { "" }, - { "P1Y2MT" } // The designator 'T' shall be absent if all of the time items are absent in "PnYnMnDTnHnMnS" - }; + /* + * Test for constructor Duration(java.lang.String lexicalRepresentation), + * null should throw NullPointerException + */ + @Test + public void checkLexicalDurationNull() { + assertThrows(NullPointerException.class, () -> datatypeFactory.newDuration(null)); } /* * Test for constructor Duration(java.lang.String lexicalRepresentation), - * null should throw NullPointerException, invalid lex should throw - * IllegalArgumentException + * invalid lex should throw IllegalArgumentException */ - @Test(expectedExceptions = { NullPointerException.class, IllegalArgumentException.class }, dataProvider = "illegal-lexical-duration") + @ParameterizedTest + @ValueSource(strings={ "P1Y1M1DT1H1M1S ", " P1Y1M1DT1H1M1S", "X1Y1M1DT1H1M1S", "", "P1Y2MT" }) public void checkLexicalDurationNeg(String lexRepresentation) { - datatypeFactory.newDuration(lexRepresentation); + assertThrows(IllegalArgumentException.class, () -> datatypeFactory.newDuration(lexRepresentation)); } - @DataProvider(name = "equal-duration") - public Object[][] getEqualDurations() { + public static Object[][] getEqualDurations() { return new Object[][] { { "P1Y1M1DT1H1M1S", "P1Y1M1DT1H1M1S" } }; } /* * Test for compare() both durations valid and equal. */ - @Test(dataProvider = "equal-duration") + @ParameterizedTest + @MethodSource("getEqualDurations") public void checkDurationEqual(String lexRepresentation1, String lexRepresentation2) { Duration duration1 = datatypeFactory.newDuration(lexRepresentation1); Duration duration2 = datatypeFactory.newDuration(lexRepresentation2); - assertTrue(duration1.equals(duration2)); + assertEquals(duration1, duration2); } - @DataProvider(name = "greater-duration") - public Object[][] getGreaterDuration() { + public static Object[][] getGreaterDuration() { return new Object[][] { { "P1Y1M1DT1H1M2S", "P1Y1M1DT1H1M1S" }, { "P1Y1M1DT1H1M1S", "-P1Y1M1DT1H1M2S" }, @@ -229,15 +216,15 @@ public Object[][] getGreaterDuration() { /* * Test for compare() both durations valid and lhs > rhs. */ - @Test(dataProvider = "greater-duration") + @ParameterizedTest + @MethodSource("getGreaterDuration") public void checkDurationCompare(String lexRepresentation1, String lexRepresentation2) { Duration duration1 = datatypeFactory.newDuration(lexRepresentation1); Duration duration2 = datatypeFactory.newDuration(lexRepresentation2); - assertTrue(duration1.compare(duration2) == DatatypeConstants.GREATER); + assertEquals(DatatypeConstants.GREATER, duration1.compare(duration2)); } - @DataProvider(name = "not-equal-duration") - public Object[][] getNotEqualDurations() { + public static Object[][] getNotEqualDurations() { return new Object[][] { { "P1Y1M1DT1H1M1S", "-P1Y1M1DT1H1M1S" }, { "P2Y1M1DT1H1M1S", "P1Y1M1DT1H1M1S" } }; @@ -246,15 +233,15 @@ public Object[][] getNotEqualDurations() { /* * Test for equals() both durations valid and lhs not equals rhs. */ - @Test(dataProvider = "not-equal-duration") + @ParameterizedTest + @MethodSource("getNotEqualDurations") public void checkDurationNotEqual(String lexRepresentation1, String lexRepresentation2) { Duration duration1 = datatypeFactory.newDuration(lexRepresentation1); Duration duration2 = datatypeFactory.newDuration(lexRepresentation2); - Assert.assertNotEquals(duration1, duration2); + assertNotEquals(duration1, duration2); } - @DataProvider(name = "duration-sign") - public Object[][] getDurationAndSign() { + public static Object[][] getDurationAndSign() { return new Object[][] { { "P0Y0M0DT0H0M0S", 0 }, { "P1Y0M0DT0H0M0S", 1 }, @@ -264,10 +251,11 @@ public Object[][] getDurationAndSign() { /* * Test for Duration.getSign(). */ - @Test(dataProvider = "duration-sign") - public void checkDurationSign(String lexRepresentation, int sign) { + @ParameterizedTest + @MethodSource("getDurationAndSign") + public void checkDurationSign(String lexRepresentation, int expectedSign) { Duration duration = datatypeFactory.newDuration(lexRepresentation); - assertEquals(duration.getSign(), sign); + assertEquals(expectedSign, duration.getSign()); } /* @@ -278,10 +266,9 @@ public void checkDurationNegate() { Duration durationPos = datatypeFactory.newDuration("P1Y0M0DT0H0M0S"); Duration durationNeg = datatypeFactory.newDuration("-P1Y0M0DT0H0M0S"); - assertEquals(durationPos.negate(), durationNeg); - assertEquals(durationNeg.negate(), durationPos); - assertEquals(durationPos.negate().negate(), durationPos); - + assertEquals(durationNeg, durationPos.negate()); + assertEquals(durationPos, durationNeg.negate()); + assertEquals(durationPos, durationPos.negate().negate()); } /* @@ -342,24 +329,22 @@ public void checkDurationIsSet() { /* * Test Duration.isSet(Field) throws NPE if the field parameter is null. */ - @Test(expectedExceptions = NullPointerException.class) public void checkDurationIsSetNeg() { Duration duration = datatypeFactory.newDuration(true, 0, 0, 0, 0, 0, 0); - duration.isSet(null); + assertThrows(NullPointerException.class, () -> duration.isSet(null)); } /* * Test for -getField(DatatypeConstants.Field) DatatypeConstants.Field is * null - throws NPE. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void checkDurationGetFieldNeg() { - Duration duration67 = datatypeFactory.newDuration("P1Y1M1DT1H1M1S"); - duration67.getField(null); + Duration duration = datatypeFactory.newDuration("P1Y1M1DT1H1M1S"); + assertThrows(NullPointerException.class, () -> duration.getField(null)); } - @DataProvider(name = "duration-fields") - public Object[][] getDurationAndFields() { + public static Object[][] getDurationAndFields() { return new Object[][] { { "P1Y1M1DT1H1M1S", one, one, one, one, one, new BigDecimal(one) }, { "PT1M", null, null, null, null, one, null }, @@ -369,21 +354,21 @@ public Object[][] getDurationAndFields() { /* * Test for Duration.getField(DatatypeConstants.Field). */ - @Test(dataProvider = "duration-fields") + @ParameterizedTest + @MethodSource("getDurationAndFields") public void checkDurationGetField(String lexRepresentation, BigInteger years, BigInteger months, BigInteger days, BigInteger hours, BigInteger minutes, BigDecimal seconds) { Duration duration = datatypeFactory.newDuration(lexRepresentation); - assertEquals(duration.getField(YEARS), years); - assertEquals(duration.getField(MONTHS), months); - assertEquals(duration.getField(DAYS), days); - assertEquals(duration.getField(HOURS), hours); - assertEquals(duration.getField(MINUTES), minutes); - assertEquals(duration.getField(SECONDS), seconds); + assertEquals(years, duration.getField(YEARS)); + assertEquals(months, duration.getField(MONTHS)); + assertEquals(days, duration.getField(DAYS)); + assertEquals(hours, duration.getField(HOURS)); + assertEquals(minutes, duration.getField(MINUTES)); + assertEquals(seconds, duration.getField(SECONDS)); } - @DataProvider(name = "number-string") - public Object[][] getNumberAndString() { + public static Object[][] getNumberAndString() { return new Object[][] { // is positive, year, month, day, hour, minute, second, lexical { true, 1, 1, 1, 1, 1, 1, "P1Y1M1DT1H1M1S" }, @@ -396,22 +381,22 @@ public Object[][] getNumberAndString() { /* * Test for - toString(). */ - @Test(dataProvider = "number-string") + @ParameterizedTest + @MethodSource("getNumberAndString") public void checkDurationToString(boolean isPositive, int years, int months, int days, int hours, int minutes, int seconds, String lexical) { - Duration duration = datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds); - assertEquals(duration.toString(), lexical); + Duration duration = datatypeFactory.newDuration(isPositive, years, months, days, hours, minutes, seconds); + assertEquals(lexical, duration.toString()); - assertEquals(datatypeFactory.newDuration(duration.toString()), duration); + assertEquals(duration, datatypeFactory.newDuration(duration.toString())); } - @DataProvider(name = "duration-field") - public Object[][] getDurationAndField() { - Function getyears = duration -> duration.getYears(); - Function getmonths = duration -> duration.getMonths(); - Function getdays = duration -> duration.getDays(); - Function gethours = duration -> duration.getHours(); - Function getminutes = duration -> duration.getMinutes(); - Function getseconds = duration -> duration.getSeconds(); + public static Object[][] getDurationAndField() { + Function getyears = Duration::getYears; + Function getmonths = Duration::getMonths; + Function getdays = Duration::getDays; + Function gethours = Duration::getHours; + Function getminutes = Duration::getMinutes; + Function getseconds = Duration::getSeconds; return new Object[][] { { "P1Y1M1DT1H1M1S", getyears, 1 }, { "P1M1DT1H1M1S", getyears, 0 }, @@ -431,10 +416,11 @@ public Object[][] getDurationAndField() { /* * Test for Duration.getYears(), getMonths(), etc. */ - @Test(dataProvider = "duration-field") - public void checkDurationGetOneField(String lexRepresentation, Function getter, int value) { + @ParameterizedTest + @MethodSource("getDurationAndField") + public void checkDurationGetOneField(String lexRepresentation, Function getter, int expectedValue) { Duration duration = datatypeFactory.newDuration(lexRepresentation); - assertEquals(getter.apply(duration).intValue(), value); + assertEquals(expectedValue, getter.apply(duration).intValue()); } /* @@ -443,7 +429,7 @@ public void checkDurationGetOneField(String lexRepresentation, Function duration.getTimeInMillis((Calendar) null)); } - @DataProvider(name = "duration-for-hash") - public Object[][] getDurationsForHash() { + public static Object[][] getDurationsForHash() { return new Object[][] { { "P1Y1M1DT1H1M1S", "P1Y1M1DT1H1M1S" }, { "P1D", "PT24H" }, @@ -483,17 +467,17 @@ public Object[][] getDurationsForHash() { * Test for Duration.hashcode(). hashcode() should return same value for * some equal durations. */ - @Test(dataProvider = "duration-for-hash") + @ParameterizedTest + @MethodSource("getDurationsForHash") public void checkDurationHashCode(String lexRepresentation1, String lexRepresentation2) { Duration duration1 = datatypeFactory.newDuration(lexRepresentation1); Duration duration2 = datatypeFactory.newDuration(lexRepresentation2); int hash1 = duration1.hashCode(); int hash2 = duration2.hashCode(); - assertTrue(hash1 == hash2, " generated hash1 : " + hash1 + " generated hash2 : " + hash2); + assertEquals(hash1, hash2, " generated hash1 : " + hash1 + " generated hash2 : " + hash2); } - @DataProvider(name = "duration-for-add") - public Object[][] getDurationsForAdd() { + public static Object[][] getDurationsForAdd() { return new Object[][] { // initVal, addVal, resultVal { "P1Y1M1DT1H1M1S", "P1Y1M1DT1H1M1S", "P2Y2M2DT2H2M2S" }, @@ -504,7 +488,8 @@ public Object[][] getDurationsForAdd() { /* * Test for add(Duration rhs). */ - @Test(dataProvider = "duration-for-add") + @ParameterizedTest + @MethodSource("getDurationsForAdd") public void checkDurationAdd(String initVal, String addVal, String result) { Duration durationInit = datatypeFactory.newDuration(initVal); Duration durationAdd = datatypeFactory.newDuration(addVal); @@ -513,25 +498,32 @@ public void checkDurationAdd(String initVal, String addVal, String result) { assertEquals(durationInit.add(durationAdd), durationResult); } - @DataProvider(name = "duration-for-addneg") - public Object[][] getDurationsForAddNeg() { + public static Object[][] getDurationsForAddNeg() { return new Object[][] { // initVal, addVal - { "P1Y1M1DT1H1M1S", null }, { "P1Y", "-P1D" }, { "-P1Y", "P1D" }, }; } /* - * Test for add(Duration rhs) 'rhs' is null , should throw NPE. "1 year" + - * "-1 day" or "-1 year" + "1 day" should throw IllegalStateException + * Test for add(Duration rhs). + * "1 year" + "-1 day" or "-1 year" + "1 day" should throw IllegalStateException */ - @Test(expectedExceptions = { NullPointerException.class, IllegalStateException.class }, dataProvider = "duration-for-addneg") + @ParameterizedTest + @MethodSource("getDurationsForAddNeg") public void checkDurationAddNeg(String initVal, String addVal) { Duration durationInit = datatypeFactory.newDuration(initVal); Duration durationAdd = addVal == null ? null : datatypeFactory.newDuration(addVal); + assertThrows(IllegalStateException.class, () -> durationInit.add(durationAdd)); + } - durationInit.add(durationAdd); + /* + * Test for add(Duration rhs) 'rhs' is null , should throw NPE. + */ + @Test + public void checkDurationAddNull() { + Duration durationInit = datatypeFactory.newDuration("P1Y1M1DT1H1M1S"); + assertThrows(NullPointerException.class, () -> durationInit.add(null)); } /* @@ -540,15 +532,14 @@ public void checkDurationAddNeg(String initVal, String addVal) { * Bug # 4972785 UnsupportedOperationException is expected * */ - @Test(expectedExceptions = UnsupportedOperationException.class) + @Test public void checkDurationCompareLarge() { String duration1Lex = "P100000000000000000000D"; String duration2Lex = "PT2400000000000000000000H"; Duration duration1 = datatypeFactory.newDuration(duration1Lex); Duration duration2 = datatypeFactory.newDuration(duration2Lex); - duration1.compare(duration2); - + assertThrows(UnsupportedOperationException.class, () -> duration1.compare(duration2)); } /* @@ -562,25 +553,24 @@ public void checkDurationGetXMLSchemaType() { // DURATION Duration duration = datatypeFactory.newDuration("P1Y1M1DT1H1M1S"); QName duration_xmlSchemaType = duration.getXMLSchemaType(); - assertEquals(duration_xmlSchemaType, DatatypeConstants.DURATION, "Expected DatatypeConstants.DURATION, returned " + duration_xmlSchemaType.toString()); + assertEquals(DatatypeConstants.DURATION, duration_xmlSchemaType, "Expected DatatypeConstants.DURATION, returned " + duration_xmlSchemaType.toString()); // DURATION_DAYTIME Duration duration_dayTime = datatypeFactory.newDuration("P1DT1H1M1S"); QName duration_dayTime_xmlSchemaType = duration_dayTime.getXMLSchemaType(); - assertEquals(duration_dayTime_xmlSchemaType, DatatypeConstants.DURATION_DAYTIME, "Expected DatatypeConstants.DURATION_DAYTIME, returned " + assertEquals(DatatypeConstants.DURATION_DAYTIME, duration_dayTime_xmlSchemaType, "Expected DatatypeConstants.DURATION_DAYTIME, returned " + duration_dayTime_xmlSchemaType.toString()); // DURATION_YEARMONTH Duration duration_yearMonth = datatypeFactory.newDuration("P1Y1M"); QName duration_yearMonth_xmlSchemaType = duration_yearMonth.getXMLSchemaType(); - assertEquals(duration_yearMonth_xmlSchemaType, DatatypeConstants.DURATION_YEARMONTH, "Expected DatatypeConstants.DURATION_YEARMONTH, returned " + assertEquals(DatatypeConstants.DURATION_YEARMONTH, duration_yearMonth_xmlSchemaType, "Expected DatatypeConstants.DURATION_YEARMONTH, returned " + duration_yearMonth_xmlSchemaType.toString()); } - private final int undef = DatatypeConstants.FIELD_UNDEFINED; - private final BigInteger zero = BigInteger.ZERO; - private final BigInteger one = BigInteger.ONE; - + private final static int undef = DatatypeConstants.FIELD_UNDEFINED; + private final static BigInteger zero = BigInteger.ZERO; + private final static BigInteger one = BigInteger.ONE; } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/FactoryNewInstanceTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/FactoryNewInstanceTest.java index 7b75562bd878..642dfad6810e 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/FactoryNewInstanceTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/FactoryNewInstanceTest.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 @@ -23,25 +23,27 @@ package javax.xml.datatype.ptests; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertEquals; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.Duration; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.datatype.ptests.FactoryNewInstanceTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.datatype.ptests.FactoryNewInstanceTest * @summary Tests for DatatypeFactory.newInstance(factoryClassName , classLoader) */ public class FactoryNewInstanceTest { @@ -50,9 +52,11 @@ public class FactoryNewInstanceTest { "com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl"; private static final String DATATYPE_FACTORY_CLASSNAME = DEFAULT_IMPL_CLASS; - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { - return new Object[][] { { DATATYPE_FACTORY_CLASSNAME, null }, { DATATYPE_FACTORY_CLASSNAME, this.getClass().getClassLoader() } }; + public static Object[][] getValidateParameters() { + return new Object[][] { + { DATATYPE_FACTORY_CLASSNAME, null }, + { DATATYPE_FACTORY_CLASSNAME, FactoryNewInstanceTest.class.getClassLoader() }, + }; } /** @@ -66,8 +70,8 @@ public void testDefaultInstance() throws Exception { DatatypeFactory dtf2 = DatatypeFactory.newInstance(); assertNotSame(dtf1, dtf2, "same instance returned:"); assertSame(dtf1.getClass(), dtf2.getClass(), - "unexpected class mismatch for newDefaultInstance():"); - assertEquals(dtf1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultInstance():"); + assertEquals(DEFAULT_IMPL_CLASS, dtf1.getClass().getName()); } /* @@ -76,7 +80,8 @@ public void testDefaultInstance() throws Exception { * implementation of javax.xml.datatype.DatatypeFactory , should return * newInstance of DatatypeFactory */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewInstance(String factoryClassName, ClassLoader classLoader) throws DatatypeConfigurationException { DatatypeFactory dtf = DatatypeFactory.newInstance(DATATYPE_FACTORY_CLASSNAME, null); Duration duration = dtf.newDuration(true, 1, 1, 1, 1, 1, 1); @@ -89,9 +94,11 @@ public void testNewInstance(String factoryClassName, ClassLoader classLoader) th * java.lang.ClassLoader classLoader) factoryClassName is null , should * throw DatatypeConfigurationException */ - @Test(expectedExceptions = DatatypeConfigurationException.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewInstanceNeg(String factoryClassName, ClassLoader classLoader) throws DatatypeConfigurationException { - DatatypeFactory.newInstance(factoryClassName, classLoader); + assertThrows( + DatatypeConfigurationException.class, + () -> DatatypeFactory.newInstance(factoryClassName, classLoader)); } - } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/XMLGregorianCalendarTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/XMLGregorianCalendarTest.java index d9df1a2cddd0..8365f2f3ad52 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/XMLGregorianCalendarTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/datatype/ptests/XMLGregorianCalendarTest.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 @@ -23,56 +23,54 @@ package javax.xml.datatype.ptests; -import static java.util.Calendar.HOUR; -import static java.util.Calendar.MINUTE; -import static java.util.Calendar.YEAR; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; - -import java.util.GregorianCalendar; -import java.util.Locale; -import java.util.TimeZone; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.Duration; import javax.xml.datatype.XMLGregorianCalendar; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; -import org.testng.Assert; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static java.util.Calendar.HOUR; +import static java.util.Calendar.MINUTE; +import static java.util.Calendar.YEAR; +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.assertTrue; /* * @test * @bug 5049592 5041845 5048932 5064587 5040542 5049531 5049528 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.datatype.ptests.XMLGregorianCalendarTest + * @run junit/othervm javax.xml.datatype.ptests.XMLGregorianCalendarTest * @summary Class containing the test cases for XMLGregorianCalendar */ public class XMLGregorianCalendarTest { private DatatypeFactory datatypeFactory; - @BeforeClass + @BeforeEach public void setup() throws DatatypeConfigurationException { datatypeFactory = DatatypeFactory.newInstance(); } - @DataProvider(name = "valid-milliseconds") - public Object[][] getValidMilliSeconds() { - return new Object[][] { { 0 }, { 1 }, { 2 }, { 16 }, { 1000 } }; - } - /* * Test DatatypeFactory.newXMLGregorianCalendar(..) with milliseconds > 1. * * Bug # 5049592 - * */ - @Test(dataProvider = "valid-milliseconds") - public void checkNewCalendar(int ms) { + @ParameterizedTest + @ValueSource(ints={ 0, 1, 2, 16, 1000 }) + public void checkNewCalendar(int expectedMillis) { // valid milliseconds XMLGregorianCalendar calendar = datatypeFactory.newXMLGregorianCalendar(2004, // year 6, // month @@ -80,12 +78,12 @@ public void checkNewCalendar(int ms) { 19, // hour 20, // minute 59, // second - ms, // milliseconds + expectedMillis, // milliseconds 840 // timezone ); // expected success - assertEquals(calendar.getMillisecond(), ms); + assertEquals(expectedMillis, calendar.getMillisecond()); } /* @@ -93,23 +91,19 @@ public void checkNewCalendar(int ms) { * * Bug # 5049592 */ - @Test(dataProvider = "valid-milliseconds") - public void checkNewTime(int ms) { + @ParameterizedTest + @ValueSource(ints={ 0, 1, 2, 16, 1000 }) + public void checkNewTime(int expectedMillis) { // valid milliseconds XMLGregorianCalendar calendar2 = datatypeFactory.newXMLGregorianCalendarTime(19, // hour 20, // minute 59, // second - ms, // milliseconds + expectedMillis, // milliseconds 840 // timezone ); // expected success - assertEquals(calendar2.getMillisecond(), ms); - } - - @DataProvider(name = "invalid-milliseconds") - public Object[][] getInvalidMilliSeconds() { - return new Object[][] { { -1 }, { 1001 } }; + assertEquals(expectedMillis, calendar2.getMillisecond()); } /* @@ -119,18 +113,21 @@ public Object[][] getInvalidMilliSeconds() { * 1001. * */ - @Test(expectedExceptions = IllegalArgumentException.class, dataProvider = "invalid-milliseconds") - public void checkNewCalendarNeg(int milliseconds) { + @ParameterizedTest + @ValueSource(ints={ -1, 1001 }) + public void checkNewCalendarNeg(int invalidMilliseconds) { // invalid milliseconds - datatypeFactory.newXMLGregorianCalendar(2004, // year - 6, // month - 2, // day - 19, // hour - 20, // minute - 59, // second - milliseconds, // milliseconds - 840 // timezone - ); + assertThrows( + IllegalArgumentException.class, + () -> datatypeFactory.newXMLGregorianCalendar(2004, // year + 6, // month + 2, // day + 19, // hour + 20, // minute + 59, // second + invalidMilliseconds, // milliseconds + 840 // timezone + )); } /* @@ -140,19 +137,21 @@ public void checkNewCalendarNeg(int milliseconds) { * 1001. * */ - @Test(expectedExceptions = IllegalArgumentException.class, dataProvider = "invalid-milliseconds") - public void checkNewTimeNeg(int milliseconds) { + @ParameterizedTest + @ValueSource(ints={ -1, 1001 }) + public void checkNewTimeNeg(int invalidMilliseconds) { // invalid milliseconds - datatypeFactory.newXMLGregorianCalendarTime(19, // hour - 20, // minute - 59, // second - milliseconds, // milliseconds - 840 // timezone - ); + assertThrows( + IllegalArgumentException.class, + () -> datatypeFactory.newXMLGregorianCalendarTime(19, // hour + 20, // minute + 59, // second + invalidMilliseconds, // milliseconds + 840 // timezone + )); } - @DataProvider(name = "data-for-add") - public Object[][] getDataForAdd() { + public static Object[][] getDataForAdd() { return new Object[][] { //calendar1, calendar2, duration { "1999-12-31T00:00:00Z", "2000-01-01T00:00:00Z", "P1D" }, @@ -173,7 +172,8 @@ public Object[][] getDataForAdd() { * Test XMLGregorianCalendar.add(Duration). * */ - @Test(dataProvider = "data-for-add") + @ParameterizedTest + @MethodSource("getDataForAdd") public void checkAddDays(String cal1, String cal2, String dur) { XMLGregorianCalendar calendar1 = datatypeFactory.newXMLGregorianCalendar(cal1); @@ -184,70 +184,51 @@ public void checkAddDays(String cal1, String cal2, String dur) { XMLGregorianCalendar calendar1Clone = (XMLGregorianCalendar)calendar1.clone(); calendar1Clone.add(duration); - assertEquals(calendar1Clone, calendar2); + assertEquals(calendar2, calendar1Clone); calendar2.add(duration.negate()); - assertEquals(calendar2, calendar1); + assertEquals(calendar1, calendar2); } - @DataProvider(name = "gMonth") - public Object[][] getGMonth() { - return new Object[][] { - { "2000-02" }, - { "2000-03" }, - { "2018-02" }}; - } /* * Test XMLGregorianCalendar#isValid(). for gMonth * * Bug # 5041845 - * */ - @Test(dataProvider = "gMonth") + @ParameterizedTest + @ValueSource(strings={ "2000-02", "2000-03", "2018-02" }) public void checkIsValid(String month) { - XMLGregorianCalendar gMonth = datatypeFactory.newXMLGregorianCalendar(month); gMonth.setYear(null); - Assert.assertTrue(gMonth.isValid(), gMonth.toString() + " should isValid"); - - } - - @DataProvider(name = "lexical01") - public Object[][] getLexicalRepresentForNormalize01() { - return new Object[][] { { "2000-01-16T12:00:00Z" }, { "2000-01-16T12:00:00" } }; + assertTrue(gMonth.isValid(), gMonth + " should isValid"); } /* * Test XMLGregorianCalendar#normalize(...). * * Bug # 5048932 XMLGregorianCalendar.normalize works - * */ - @Test(dataProvider = "lexical01") + @ParameterizedTest + @ValueSource(strings={ "2000-01-16T12:00:00Z", "2000-01-16T12:00:00" }) public void checkNormalize01(String lexical) { XMLGregorianCalendar lhs = datatypeFactory.newXMLGregorianCalendar(lexical); lhs.normalize(); } - @DataProvider(name = "lexical02") - public Object[][] getLexicalRepresentForNormalize02() { - return new Object[][] { { "2000-01-16T00:00:00.01Z" }, { "2000-01-16T00:00:00.01" }, { "13:20:00" } }; - } - /* * Test XMLGregorianCalendar#normalize(...). * * Bug # 5064587 XMLGregorianCalendar.normalize shall not change timezone - * */ - @Test(dataProvider = "lexical02") + @ParameterizedTest + @ValueSource(strings={ "2000-01-16T00:00:00.01Z", "2000-01-16T00:00:00.01", "13:20:00" }) public void checkNormalize02(String lexical) { XMLGregorianCalendar orig = datatypeFactory.newXMLGregorianCalendar(lexical); XMLGregorianCalendar normalized = datatypeFactory.newXMLGregorianCalendar(lexical).normalize(); - assertEquals(normalized.getTimezone(), orig.getTimezone()); - assertEquals(normalized.getMillisecond(), orig.getMillisecond()); + assertEquals(orig.getTimezone(), normalized.getTimezone()); + assertEquals(orig.getMillisecond(), normalized.getMillisecond()); } /* @@ -291,8 +272,6 @@ public void checkToGregorianCalendar01() { int hour = calendar.get(HOUR); assertTrue((year == 2003 && hour == 2 && minute == 3), " expecting year == 2003, hour == 2, minute == 3" + ", result is year == " + year + ", hour == " + hour + ", minute == " + minute); - - } /* @@ -311,8 +290,7 @@ public void checkToGregorianCalendar02() { calendar.toGregorianCalendar(TimeZone.getDefault(), Locale.getDefault(), null); } - @DataProvider(name = "calendar") - public Object[][] getXMLGregorianCalendarData() { + public static Object[][] getXMLGregorianCalendarData() { return new Object[][] { // year, month, day, hour, minute, second { 1970, 1, 1, 0, 0, 0 }, // DATETIME @@ -332,10 +310,12 @@ public Object[][] getXMLGregorianCalendarData() { * Bug # 5049528 * */ - @Test(dataProvider = "calendar") + @ParameterizedTest + @MethodSource("getXMLGregorianCalendarData") public void checkToStringPos(final int year, final int month, final int day, final int hour, final int minute, final int second) { XMLGregorianCalendar calendar = datatypeFactory.newXMLGregorianCalendar(year, month, day, hour, minute, second, undef, undef); - calendar.toString(); + assertNotNull(calendar.toString()); + assertFalse(calendar.toString().isEmpty()); } /* @@ -345,13 +325,13 @@ public void checkToStringPos(final int year, final int month, final int day, fin * if all parameters are undef * */ - @Test(expectedExceptions = IllegalStateException.class) + @Test public void checkToStringNeg() { XMLGregorianCalendar calendar = datatypeFactory.newXMLGregorianCalendar(undef, undef, undef, undef, undef, undef, undef, undef); // expected to fail - calendar.toString(); + assertThrows(IllegalStateException.class, calendar::toString); } - private final int undef = DatatypeConstants.FIELD_UNDEFINED; + private static final int undef = DatatypeConstants.FIELD_UNDEFINED; } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DBFNamespaceTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DBFNamespaceTest.java index 8bf88ff09fc3..6cf6e925a36b 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DBFNamespaceTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DBFNamespaceTest.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 @@ -23,22 +23,21 @@ package javax.xml.parsers.ptests; -import static javax.xml.parsers.ptests.ParserTestConst.GOLDEN_DIR; -import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; - -import java.io.File; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXResult; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.Document; +import static javax.xml.parsers.ptests.ParserTestConst.GOLDEN_DIR; +import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; /** * This tests DocumentBuilderFactory for namespace processing and no-namespace @@ -47,7 +46,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.DBFNamespaceTest + * @run junit/othervm javax.xml.parsers.ptests.DBFNamespaceTest */ public class DBFNamespaceTest { @@ -56,15 +55,14 @@ public class DBFNamespaceTest { * @return a two-dimensional array contains factory, output file name and * golden validate file name. */ - @DataProvider(name = "input-provider") - public Object[][] getInput() { + public static Object[][] getInput() { DocumentBuilderFactory dbf1 = DocumentBuilderFactory.newInstance(); - String outputfile1 = USER_DIR + "dbfnstest01.out"; + String outputfile1 = "dbfnstest01.out"; String goldfile1 = GOLDEN_DIR + "dbfnstest01GF.out"; DocumentBuilderFactory dbf2 = DocumentBuilderFactory.newInstance(); dbf2.setNamespaceAware(true); - String outputfile2 = USER_DIR + "dbfnstest02.out"; + String outputfile2 = "dbfnstest02.out"; String goldfile2 = GOLDEN_DIR + "dbfnstest02GF.out"; return new Object[][] { { dbf1, outputfile1, goldfile1 }, { dbf2, outputfile2, goldfile2 } }; } @@ -77,12 +75,15 @@ public Object[][] getInput() { * @param goldfile golden validate file name. * @throws Exception If any errors occur. */ - @Test(dataProvider = "input-provider") + @ParameterizedTest + @MethodSource("getInput") public void testNamespaceTest(DocumentBuilderFactory dbf, String outputfile, String goldfile) throws Exception { Document doc = dbf.newDocumentBuilder().parse(new File(XML_DIR, "namespace1.xml")); dummyTransform(doc, outputfile); - assertTrue(compareWithGold(goldfile, outputfile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldfile)), + Files.readAllLines(Path.of(outputfile))); } /** @@ -90,8 +91,6 @@ public void testNamespaceTest(DocumentBuilderFactory dbf, String outputfile, * invoke the callbacks through a ContentHandler. If namespace processing is * not chosen, namespaceURI in callbacks should be an empty string otherwise * it should be namespaceURI. - * - * @throws Exception If any errors occur. */ private void dummyTransform(Document document, String fileName) throws Exception { diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderFactoryTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderFactoryTest.java index f21af74330d3..044a1ed5de2d 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderFactoryTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderFactoryTest.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 @@ -23,26 +23,16 @@ package javax.xml.parsers.ptests; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static javax.xml.parsers.ptests.ParserTestConst.GOLDEN_DIR; -import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertNotSame; - -import java.io.BufferedReader; -import java.io.Closeable; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileReader; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -54,17 +44,28 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXResult; +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.nio.file.Files; +import java.nio.file.Path; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.parsers.ptests.ParserTestConst.GOLDEN_DIR; +import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @bug 8080907 8169778 @@ -73,7 +74,8 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.DocumentBuilderFactoryTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.parsers.ptests.DocumentBuilderFactoryTest */ public class DocumentBuilderFactoryTest { @@ -93,9 +95,11 @@ public class DocumentBuilderFactoryTest { * * @return a data provider contains DocumentBuilderFactory instantiation parameters. */ - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { - return new Object[][] { { DOCUMENT_BUILDER_FACTORY_CLASSNAME, null }, { DOCUMENT_BUILDER_FACTORY_CLASSNAME, this.getClass().getClassLoader() } }; + public static Object[][] getValidateParameters() { + return new Object[][] { + { DOCUMENT_BUILDER_FACTORY_CLASSNAME, null }, + { DOCUMENT_BUILDER_FACTORY_CLASSNAME, DocumentBuilderFactoryTest.class.getClassLoader() }, + }; } /** @@ -109,8 +113,8 @@ public void testDefaultInstance() throws Exception { DocumentBuilderFactory dbf2 = DocumentBuilderFactory.newInstance(); assertNotSame(dbf1, dbf2, "same instance returned:"); assertSame(dbf1.getClass(), dbf2.getClass(), - "unexpected class mismatch for newDefaultInstance():"); - assertEquals(dbf1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultInstance():"); + assertEquals(DEFAULT_IMPL_CLASS, dbf1.getClass().getName()); } /** @@ -119,12 +123,9 @@ public void testDefaultInstance() throws Exception { * points to correct implementation of * javax.xml.parsers.DocumentBuilderFactory , should return newInstance of * DocumentBuilderFactory - * - * @param factoryClassName - * @param classLoader - * @throws ParserConfigurationException */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewInstance(String factoryClassName, ClassLoader classLoader) throws ParserConfigurationException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(factoryClassName, classLoader); DocumentBuilder builder = dbf.newDocumentBuilder(); @@ -135,13 +136,13 @@ public void testNewInstance(String factoryClassName, ClassLoader classLoader) th * test for DocumentBuilderFactory.newInstance(java.lang.String * factoryClassName, java.lang.ClassLoader classLoader) factoryClassName is * null , should throw FactoryConfigurationError - * - * @param factoryClassName - * @param classLoader */ - @Test(expectedExceptions = FactoryConfigurationError.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewInstanceNeg(String factoryClassName, ClassLoader classLoader) { - DocumentBuilderFactory.newInstance(factoryClassName, classLoader); + assertThrows( + FactoryConfigurationError.class, + () -> DocumentBuilderFactory.newInstance(factoryClassName, classLoader)); } /** @@ -162,11 +163,11 @@ public void testCheckSchemaSupport1() throws Exception { assertFalse(eh.isErrorOccured()); } - @DataProvider(name = "schema-source") - public Object[][] getSchemaSource() throws FileNotFoundException { + public static Object[][] getSchemaSource() throws FileNotFoundException { return new Object[][] { { new FileInputStream(new File(XML_DIR, "test.xsd")) }, - { new InputSource(filenameToURL(XML_DIR + "test.xsd")) } }; + { new InputSource(Path.of(XML_DIR).resolve("test.xsd").toUri().toASCIIString()) }, + }; } /** @@ -174,7 +175,8 @@ public Object[][] getSchemaSource() throws FileNotFoundException { * this case the schema source property is set. * @throws Exception If any errors occur. */ - @Test(dataProvider = "schema-source") + @ParameterizedTest + @MethodSource("getSchemaSource") public void testCheckSchemaSupport2(Object schemaSource) throws Exception { try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); @@ -201,7 +203,8 @@ public void testCheckSchemaSupport2(Object schemaSource) throws Exception { * this case the schema source property is set. * @throws Exception If any errors occur. */ - @Test(dataProvider = "schema-source") + @ParameterizedTest + @MethodSource("getSchemaSource") public void testCheckSchemaSupport3(Object schemaSource) throws Exception { try { SAXParserFactory spf = SAXParserFactory.newInstance(); @@ -236,7 +239,7 @@ public void testCheckDocumentBuilderFactory02() throws Exception { Document doc = docBuilder.parse(new File(XML_DIR, "DocumentBuilderFactory01.xml")); Element e = (Element) doc.getElementsByTagName("html").item(0); NodeList nl = e.getChildNodes(); - assertEquals(nl.getLength(), 1); + assertEquals(1, nl.getLength()); } /** @@ -312,7 +315,7 @@ public void testCheckDocumentBuilderFactory05() throws Exception { Element e = (Element) doc.getElementsByTagName("title").item(0); NodeList nl = e.getChildNodes(); assertTrue(dbf.isExpandEntityReferences()); - assertEquals(nl.item(0).getNodeValue().trim().charAt(0), 'W'); + assertEquals('W', nl.item(0).getNodeValue().trim().charAt(0)); } } @@ -331,7 +334,7 @@ public void testCheckDocumentBuilderFactory06() throws Exception { MyErrorHandler eh = MyErrorHandler.newInstance(); db.setErrorHandler(eh); Document doc = db.parse(new File(XML_DIR, "DocumentBuilderFactory04.xml")); - assertTrue(doc instanceof Document); + assertInstanceOf(Document.class, doc); assertFalse(eh.isErrorOccured()); } @@ -350,7 +353,7 @@ public void testCheckDocumentBuilderFactory07() throws Exception { Element e = (Element) doc.getElementsByTagName("title").item(0); NodeList nl = e.getChildNodes(); assertTrue(dbf.isExpandEntityReferences()); - assertEquals(nl.item(0).getNodeValue().trim().charAt(0), 'W'); + assertEquals('W', nl.item(0).getNodeValue().trim().charAt(0)); } } @@ -458,12 +461,12 @@ public void testCheckDocumentBuilderFactory13() throws Exception { * throw Sax Exception. * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class) + @Test public void testCheckDocumentBuilderFactory14() throws Exception { // Accesing default working directory. DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = dbf.newDocumentBuilder(); - docBuilder.parse(""); + assertThrows(SAXException.class, () -> docBuilder.parse("")); } /** @@ -472,12 +475,12 @@ public void testCheckDocumentBuilderFactory14() throws Exception { * @throws Exception If any errors occur. * */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testCheckDocumentBuilderFactory15() throws Exception { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = dbf.newDocumentBuilder(); String uri = null; - docBuilder.parse(uri); + assertThrows(IllegalArgumentException.class, () -> docBuilder.parse(uri)); } /** @@ -496,7 +499,7 @@ public void testCheckIgnoringComments() throws Exception { Document doc = docBuilder.parse(fis); Element e = (Element) doc.getElementsByTagName("body").item(0); NodeList nl = e.getChildNodes(); - assertEquals(nl.getLength(), 0); + assertEquals(0, nl.getLength()); } } @@ -531,7 +534,7 @@ public void testCheckIgnoringComments1() throws Exception { @Test public void testCheckElementContentWhitespace() throws Exception { String goldFile = GOLDEN_DIR + "dbfactory02GF.out"; - String outputFile = USER_DIR + "dbfactory02.out"; + String outputFile = "dbfactory02.out"; MyErrorHandler eh = MyErrorHandler.newInstance(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setValidating(true); @@ -549,6 +552,8 @@ public void testCheckElementContentWhitespace() throws Exception { saxResult.setHandler(handler); transformer.transform(domSource, saxResult); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderImpl01.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderImpl01.java index 4e9006f9f5b0..8d265336bbd5 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderImpl01.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/DocumentBuilderImpl01.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 @@ -23,23 +23,20 @@ package javax.xml.parsers.ptests; -import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FilePermission; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.io.FileInputStream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; +import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * This checks for the methods of DocumentBuilder @@ -47,7 +44,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.DocumentBuilderImpl01 + * @run junit/othervm javax.xml.parsers.ptests.DocumentBuilderImpl01 */ public class DocumentBuilderImpl01 implements EntityResolver { /** @@ -57,8 +54,7 @@ public class DocumentBuilderImpl01 implements EntityResolver { * @throws ParserConfigurationException if a DocumentBuilder cannot be * created which satisfies the configuration requested. */ - @DataProvider(name = "builder-provider") - public Object[][] getBuilder() throws ParserConfigurationException { + public static Object[][] getBuilder() throws ParserConfigurationException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = dbf.newDocumentBuilder(); return new Object[][] { { docBuilder } }; @@ -69,7 +65,8 @@ public Object[][] getBuilder() throws ParserConfigurationException { * to return false because not setting the validation. * @param docBuilder document builder instance. */ - @Test(dataProvider = "builder-provider") + @ParameterizedTest + @MethodSource("getBuilder") public void testCheckDocumentBuilderImpl01(DocumentBuilder docBuilder) { assertFalse(docBuilder.isValidating()); } @@ -78,7 +75,8 @@ public void testCheckDocumentBuilderImpl01(DocumentBuilder docBuilder) { * Test the default functionality of isNamespaceAware method. * @param docBuilder document builder instance. */ - @Test(dataProvider = "builder-provider") + @ParameterizedTest + @MethodSource("getBuilder") public void testCheckDocumentBuilderImpl02(DocumentBuilder docBuilder) { assertFalse(docBuilder.isNamespaceAware()); } @@ -88,7 +86,8 @@ public void testCheckDocumentBuilderImpl02(DocumentBuilder docBuilder) { * @param docBuilder document builder instance. * @throws Exception If any errors occur. */ - @Test(dataProvider = "builder-provider") + @ParameterizedTest + @MethodSource("getBuilder") public void testCheckDocumentBuilderImpl04(DocumentBuilder docBuilder) throws Exception { try (FileInputStream fis = new FileInputStream(new File(XML_DIR, @@ -103,7 +102,8 @@ public void testCheckDocumentBuilderImpl04(DocumentBuilder docBuilder) * @param docBuilder document builder instance. * @throws Exception If any errors occur. */ - @Test(dataProvider = "builder-provider") + @ParameterizedTest + @MethodSource("getBuilder") public void testCheckDocumentBuilderImpl05(DocumentBuilder docBuilder) throws Exception { assertNotNull(docBuilder.parse(new File(XML_DIR, @@ -115,13 +115,14 @@ public void testCheckDocumentBuilderImpl05(DocumentBuilder docBuilder) * @param docBuilder document builder instance. * @throws Exception If any errors occur. */ - @Test(dataProvider = "builder-provider") + @ParameterizedTest + @MethodSource("getBuilder") public void testCheckDocumentBuilderImpl06(DocumentBuilder docBuilder) throws Exception { try (FileInputStream fis = new FileInputStream(new File(XML_DIR, "DocumentBuilderImpl02.xml"))) { assertNotNull(docBuilder.parse(fis, new File(XML_DIR).toURI() - .toASCIIString() + FILE_SEP)); + .toASCIIString() + '/')); } } @@ -129,7 +130,8 @@ public void testCheckDocumentBuilderImpl06(DocumentBuilder docBuilder) * Test the setEntityResolver. * @param docBuilder document builder instance. */ - @Test(dataProvider = "builder-provider") + @ParameterizedTest + @MethodSource("getBuilder") public void testCheckDocumentBuilderImpl07(DocumentBuilder docBuilder) { docBuilder.setEntityResolver(this); assertNotNull(resolveEntity("publicId", "http://www.myhost.com/today")); diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/FactoryConfErrorTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/FactoryConfErrorTest.java index 3bcc49d58b26..fe4288fc2b1c 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/FactoryConfErrorTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/FactoryConfErrorTest.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 @@ -23,16 +23,17 @@ package javax.xml.parsers.ptests; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; -import static jaxp.library.JAXPTestUtilities.clearSystemProperty; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.SAXParserFactory; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Class containing the test cases for SAXParserFactory/DocumentBuilderFactory @@ -41,45 +42,46 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.FactoryConfErrorTest + * @run junit/othervm javax.xml.parsers.ptests.FactoryConfErrorTest */ +@Execution(ExecutionMode.SAME_THREAD) public class FactoryConfErrorTest { /** * Set properties DocumentBuilderFactory and SAXParserFactory to invalid * value before any test run. */ - @BeforeTest - public void setup() { - setSystemProperty("javax.xml.parsers.DocumentBuilderFactory", "xx"); - setSystemProperty("javax.xml.parsers.SAXParserFactory", "xx"); + @BeforeAll + public static void setup() { + System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "xx"); + System.setProperty("javax.xml.parsers.SAXParserFactory", "xx"); } /** * Restore properties DocumentBuilderFactory and SAXParserFactory to default * value after all tests run. */ - @AfterTest - public void cleanup() { - clearSystemProperty("javax.xml.parsers.DocumentBuilderFactory"); - clearSystemProperty("javax.xml.parsers.SAXParserFactory"); + @AfterAll + public static void cleanup() { + System.clearProperty("javax.xml.parsers.DocumentBuilderFactory"); + System.clearProperty("javax.xml.parsers.SAXParserFactory"); } /** * To test exception thrown if javax.xml.parsers.SAXParserFactory property * is invalid. */ - @Test(expectedExceptions = FactoryConfigurationError.class) + @Test public void testNewInstance01() { - SAXParserFactory.newInstance(); + assertThrows(FactoryConfigurationError.class, SAXParserFactory::newInstance); } /** * To test exception thrown if javax.xml.parsers.DocumentBuilderFactory is * invalid. */ - @Test(expectedExceptions = FactoryConfigurationError.class) + @Test public void testNewInstance02() { - DocumentBuilderFactory.newInstance(); + assertThrows(FactoryConfigurationError.class, DocumentBuilderFactory::newInstance); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXFactoryNewInstanceTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXFactoryNewInstanceTest.java index 2b47bd6a3da1..22fca5ec207e 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXFactoryNewInstanceTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXFactoryNewInstanceTest.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 @@ -23,32 +23,35 @@ package javax.xml.parsers.ptests; -import static org.testng.Assert.assertNotNull; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.xml.sax.SAXException; import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.xml.sax.SAXException; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.SAXFactoryNewInstanceTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.parsers.ptests.SAXFactoryNewInstanceTest * @summary Tests for SAXParserFactory.newInstance(factoryClassName , classLoader) */ public class SAXFactoryNewInstanceTest { private static final String SAXPARSER_FACTORY_CLASSNAME = "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"; - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { - return new Object[][] { { SAXPARSER_FACTORY_CLASSNAME, null }, { SAXPARSER_FACTORY_CLASSNAME, this.getClass().getClassLoader() } }; + public static Object[][] getValidateParameters() { + return new Object[][] { + { SAXPARSER_FACTORY_CLASSNAME, null }, + { SAXPARSER_FACTORY_CLASSNAME, SAXFactoryNewInstanceTest.class.getClassLoader() }, + }; } /* @@ -57,7 +60,8 @@ public Object[][] getValidateParameters() { * implementation of javax.xml.parsers.SAXParserFactory , should return * newInstance of SAXParserFactory */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewInstance(String factoryClassName, ClassLoader classLoader) throws ParserConfigurationException, SAXException { SAXParserFactory spf = SAXParserFactory.newInstance(factoryClassName, classLoader); SAXParser sp = spf.newSAXParser(); @@ -69,9 +73,12 @@ public void testNewInstance(String factoryClassName, ClassLoader classLoader) th * java.lang.ClassLoader classLoader) factoryClassName is null , should * throw FactoryConfigurationError */ - @Test(expectedExceptions = FactoryConfigurationError.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewInstanceNeg(String factoryClassName, ClassLoader classLoader) { - SAXParserFactory.newInstance(factoryClassName, classLoader); + assertThrows( + FactoryConfigurationError.class, + () -> SAXParserFactory.newInstance(factoryClassName, classLoader)); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserFactTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserFactTest.java index 77134c58ac14..0f21dffcea74 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserFactTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserFactTest.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 @@ -22,15 +22,16 @@ */ package javax.xml.parsers.ptests; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.Test; import javax.xml.parsers.SAXParserFactory; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Class containing the test cases for SAXParserFactory API. @@ -39,7 +40,7 @@ * @test * @bug 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.SAXParserFactTest + * @run junit/othervm javax.xml.parsers.ptests.SAXParserFactTest */ public class SAXParserFactTest { @@ -55,16 +56,15 @@ public class SAXParserFactTest { /** * Test if newDefaultInstance() method returns an instance * of the expected factory. - * @throws Exception If any errors occur. */ @Test - public void testDefaultInstance() throws Exception { + public void testDefaultInstance() { SAXParserFactory spf1 = SAXParserFactory.newDefaultInstance(); SAXParserFactory spf2 = SAXParserFactory.newInstance(); assertNotSame(spf1, spf2, "same instance returned:"); assertSame(spf1.getClass(), spf2.getClass(), - "unexpected class mismatch for newDefaultInstance():"); - assertEquals(spf1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultInstance():"); + assertEquals(DEFAULT_IMPL_CLASS, spf1.getClass().getName()); } /** diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest.java index f828dbeea184..38f2e98e286c 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest.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 @@ -23,37 +23,45 @@ package javax.xml.parsers.ptests; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.PropertyPermission; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.xml.sax.HandlerBase; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertThrows; + /** * Class contains the test cases for SAXParser API */ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.SAXParserTest + * @run junit/othervm javax.xml.parsers.ptests.SAXParserTest */ public class SAXParserTest { + private static final DefaultHandler DEFAULT_HANDLER = new DefaultHandler(); + + @SuppressWarnings("deprecation") + private static final HandlerBase HANDLER_BASE = new HandlerBase(); + /** * Provide SAXParser. * * @return a data provider contains a SAXParser instance. * @throws Exception If any errors occur. */ - @DataProvider(name = "parser-provider") - public Object[][] getParser() throws Exception { + public static Object[][] getParser() throws Exception { SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser saxparser = spf.newSAXParser(); return new Object[][] { { saxparser } }; @@ -62,423 +70,332 @@ public Object[][] getParser() throws Exception { /** * Test case with FileInputStream null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse01(SAXParser saxparser) throws Exception { - FileInputStream instream = null; - saxparser.parse(instream, new HandlerBase()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse01(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((InputStream) null, HANDLER_BASE)); } /** * Test with by setting URI as null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse02(SAXParser saxparser) throws Exception { - String uri = null; - saxparser.parse(uri, new HandlerBase()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse02(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((String) null, HANDLER_BASE)); } /** * Test with non-existence URI, parsing should fail and throw IOException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = { SAXException.class }, - dataProvider = "parser-provider") - public void testParse03(SAXParser saxparser) throws Exception { - saxparser.parse("", new HandlerBase()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse03(SAXParser saxparser) { + assertThrows(SAXException.class, () -> saxparser.parse("", HANDLER_BASE)); } /** * Test with File null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse04(SAXParser saxparser) throws Exception { - File file = null; - saxparser.parse(file, new HandlerBase()); + public void testParse04(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((File) null, HANDLER_BASE)); } /** * Test with empty string as File, parsing should fail and throw * SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, dataProvider = "parser-provider") - public void testParse05(SAXParser saxparser) throws Exception { - saxparser.parse(new File(""), new HandlerBase()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse05(SAXParser saxparser) { + File file = new File(""); + assertThrows(SAXException.class, () -> saxparser.parse(file, HANDLER_BASE)); } /** * Test with input source null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse06(SAXParser saxparser) throws Exception { - InputSource is = null; - saxparser.parse(is, new HandlerBase()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse06(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((InputSource) null, HANDLER_BASE)); } /** * Test with FileInputStream null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse07(SAXParser saxparser) throws Exception { - FileInputStream instream = null; - saxparser.parse(instream, new DefaultHandler()); + public void testParse07(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((InputStream) null, DEFAULT_HANDLER)); } /** * Test with URI null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse08(SAXParser saxparser) throws Exception { - String uri = null; - saxparser.parse(uri, new DefaultHandler()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse08(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((String) null, DEFAULT_HANDLER)); } /** * Test with non-existence URI, parsing should fail and throw SAXException * or IOException. - * - * @param saxparser - * a SAXParser instance. - * @throws Exception - * If any errors occur. */ - @Test(expectedExceptions = { SAXException.class, IOException.class }, dataProvider = "parser-provider") - public void testParse09(SAXParser saxparser) throws Exception { - saxparser.parse("no-such-file", new DefaultHandler()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse09(SAXParser saxparser) { + assertThrows(IOException.class, () -> saxparser.parse("no-such-file", DEFAULT_HANDLER)); } /** * Test with empty string as File, parsing should fail and throw * SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, dataProvider = "parser-provider") - public void testParse10(SAXParser saxparser) throws Exception { + @ParameterizedTest + @MethodSource("getParser") + public void testParse10(SAXParser saxparser) { File file = new File(""); - saxparser.parse(file, new DefaultHandler()); + assertThrows(SAXException.class, () -> saxparser.parse(file, DEFAULT_HANDLER)); } /** * Test with File null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse11(SAXParser saxparser) throws Exception { - saxparser.parse((File) null, new DefaultHandler()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse11(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((File) null, DEFAULT_HANDLER)); } /** * Test with input source null, parsing should fail and throw * IllegalArgumentException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class, - dataProvider = "parser-provider") - public void testParse12(SAXParser saxparser) throws Exception { - InputSource is = null; - saxparser.parse(is, new DefaultHandler()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse12(SAXParser saxparser) { + assertThrows(IllegalArgumentException.class, () -> saxparser.parse((InputSource) null, DEFAULT_HANDLER)); } /** * Test with an error in XML file, parsing should fail and throw * SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, - dataProvider = "parser-provider") public void testParse13(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream(new File( XML_DIR, "invalid.xml"))) { - saxparser.parse(instream, new HandlerBase()); + assertThrows(SAXException.class, () -> saxparser.parse(instream, HANDLER_BASE)); } } /** * Test with a valid in XML file, parser should parse the XML document. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse14(SAXParser saxparser) throws Exception { - saxparser.parse(new File(XML_DIR, "parsertest.xml"), - new HandlerBase()); + saxparser.parse(new File(XML_DIR, "parsertest.xml"), HANDLER_BASE); } /** * Test with valid input stream, parser should parse the XML document * successfully. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse15(SAXParser saxparser) throws Exception { - try (FileInputStream instream = new FileInputStream(new File(XML_DIR, - "correct.xml"))) { - saxparser.parse(instream, new HandlerBase()); + try (FileInputStream instream = new FileInputStream(new File(XML_DIR, "correct.xml"))) { + saxparser.parse(instream, HANDLER_BASE); } } /** * Test with valid input source, parser should parse the XML document * successfully. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse16(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream( new File(XML_DIR, "parsertest.xml"))) { - saxparser.parse(instream, new HandlerBase(), + saxparser.parse(instream, HANDLER_BASE, new File(XML_DIR).toURI().toASCIIString()); } } /** * Test with proper URI, parser should parse successfully. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse17(SAXParser saxparser) throws Exception { File file = new File(XML_DIR, "correct.xml"); - saxparser.parse(file.toURI().toASCIIString(), new HandlerBase()); + saxparser.parse(file.toURI().toASCIIString(), HANDLER_BASE); } /** * Test with XML file that has errors parsing should fail and throw * SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, - dataProvider = "parser-provider") - public void testParse18(SAXParser saxparser) throws Exception { - saxparser.parse(new File(XML_DIR, "valid.xml"), new HandlerBase()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse18(SAXParser saxparser) { + File file = new File(XML_DIR, "valid.xml"); + assertThrows(SAXException.class, () -> saxparser.parse(file, HANDLER_BASE)); } /** * Test with XML file that has no errors Parser should successfully * parse the XML document. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse19(SAXParser saxparser) throws Exception { - saxparser.parse(new File(XML_DIR, "correct.xml"), new HandlerBase()); + saxparser.parse(new File(XML_DIR, "correct.xml"), HANDLER_BASE); } /** * Test with input source attached an invalid XML, parsing should fail * and throw SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, - dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse20(SAXParser saxparser) throws Exception { try(FileInputStream instream = new FileInputStream(new File(XML_DIR, "invalid.xml"))) { - saxparser.parse(new InputSource(instream), new HandlerBase()); + InputSource is = new InputSource(instream); + assertThrows(SAXException.class, () -> saxparser.parse(is, HANDLER_BASE)); } } /** * Test with input source attached an valid XML, parser should * successfully parse the XML document. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse21(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream(new File(XML_DIR, "correct.xml"))) { - saxparser.parse(new InputSource(instream), new HandlerBase()); + saxparser.parse(new InputSource(instream), HANDLER_BASE); } } /** * Test with an error in xml file, parsing should fail and throw * SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, - dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse22(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream( new File(XML_DIR, "invalid.xml"))) { - saxparser.parse(instream, new DefaultHandler()); + assertThrows(SAXException.class, () -> saxparser.parse(instream, DEFAULT_HANDLER)); } } /** * Test with valid input stream, parser should parse the XML document * successfully. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse23(SAXParser saxparser) throws Exception { - DefaultHandler handler = new DefaultHandler(); - saxparser.parse(new File(XML_DIR, "parsertest.xml"), handler); + saxparser.parse(new File(XML_DIR, "parsertest.xml"), DEFAULT_HANDLER); } /** * Test with valid input stream, parser should parse the XML document * successfully. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse24(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream(new File(XML_DIR, "correct.xml"))) { - DefaultHandler handler = new DefaultHandler(); - saxparser.parse(instream, handler); + saxparser.parse(instream, DEFAULT_HANDLER); } } /** * Test with valid input source, parser should parse the XML document * successfully. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse25(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream( new File(XML_DIR, "parsertest.xml"))) { - saxparser.parse(instream, new DefaultHandler(), - new File(XML_DIR).toURI().toASCIIString()); + saxparser.parse(instream, DEFAULT_HANDLER, + new File(XML_DIR).toURI().toASCIIString()); } } /** * Test with proper URI, parser should parse successfully. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse26(SAXParser saxparser) throws Exception { File file = new File(XML_DIR, "correct.xml"); - saxparser.parse(file.toURI().toASCIIString(), new DefaultHandler()); + saxparser.parse(file.toURI().toASCIIString(), DEFAULT_HANDLER); } /** * Test with XML file that has errors, parsing should fail and throw * SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, - dataProvider = "parser-provider") - public void testParse27(SAXParser saxparser) throws Exception { - saxparser.parse(new File(XML_DIR, "valid.xml"), new DefaultHandler()); + @ParameterizedTest + @MethodSource("getParser") + public void testParse27(SAXParser saxparser) { + File file = new File(XML_DIR, "valid.xml"); + assertThrows(SAXException.class, () -> saxparser.parse(file, DEFAULT_HANDLER)); } /** * Test with XML file that has no errors, parser should successfully * parse the XML document. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse28(SAXParser saxparser) throws Exception { - saxparser.parse(new File(XML_DIR, "correct.xml"), new DefaultHandler()); + saxparser.parse(new File(XML_DIR, "correct.xml"), DEFAULT_HANDLER); } /** * Test with an invalid XML file, parser should throw SAXException. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class, - dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse29(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream( new File(XML_DIR, "invalid.xml"))) { - saxparser.parse(new InputSource(instream), new DefaultHandler()); + InputSource is = new InputSource(instream); + assertThrows(SAXException.class, () -> saxparser.parse(is, DEFAULT_HANDLER)); } } /** * Test case to parse an XML file that not use namespaces. - * - * @param saxparser a SAXParser instance. - * @throws Exception If any errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParse30(SAXParser saxparser) throws Exception { try (FileInputStream instream = new FileInputStream( new File(XML_DIR, "correct.xml"))) { - saxparser.parse(new InputSource(instream), new DefaultHandler()); + saxparser.parse(new InputSource(instream), DEFAULT_HANDLER); } } /** * Test case to parse an XML file that uses namespaces. - * - * @throws Exception If any errors occur. */ @Test public void testParse31() throws Exception { @@ -486,7 +403,7 @@ public void testParse31() throws Exception { new File(XML_DIR, "ns4.xml"))) { SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(true); - spf.newSAXParser().parse(instream, new HandlerBase()); + spf.newSAXParser().parse(instream, HANDLER_BASE); } } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest02.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest02.java index db02034e22ca..2597bd975a51 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest02.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest02.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 @@ -23,28 +23,31 @@ package javax.xml.parsers.ptests; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.xml.sax.SAXException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.ext.DeclHandler; import org.xml.sax.ext.LexicalHandler; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + /** * Class contains the test cases for SAXParser API */ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.SAXParserTest02 + * @run junit/othervm javax.xml.parsers.ptests.SAXParserTest02 */ public class SAXParserTest02 { private static final String DOM_NODE = "http://xml.org/sax/properties/dom-node"; @@ -58,8 +61,7 @@ public class SAXParserTest02 { * @return a data provider contains a SAXParser instance. * @throws Exception If any errors occur. */ - @DataProvider(name = "parser-provider") - public Object[][] getParser() throws Exception { + public static Object[][] getParser() throws Exception { SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser saxparser = spf.newSAXParser(); return new Object[][] { { saxparser } }; @@ -70,7 +72,8 @@ public Object[][] getParser() throws Exception { * * @param saxparser a SAXParser instance. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testValidate01(SAXParser saxparser) { assertFalse(saxparser.isValidating()); } @@ -78,8 +81,6 @@ public void testValidate01(SAXParser saxparser) { /** * Test to test the functionality of setValidating and isValidating * methods. - * - * @throws Exception If any errors occur. */ @Test public void testValidate02() throws Exception { @@ -95,7 +96,8 @@ public void testValidate02() throws Exception { * * @param saxparser a SAXParser instance. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testNamespace01(SAXParser saxparser) { assertFalse(saxparser.isNamespaceAware()); } @@ -119,7 +121,8 @@ public void testNamespace02() throws Exception { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testParser01(SAXParser saxparser) throws SAXException { assertNotNull(saxparser.getParser()); } @@ -131,7 +134,8 @@ public void testParser01(SAXParser saxparser) throws SAXException { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testXmlReader01(SAXParser saxparser) throws SAXException { assertNotNull(saxparser.getXMLReader()); } @@ -142,10 +146,10 @@ public void testXmlReader01(SAXParser saxparser) throws SAXException { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(expectedExceptions = SAXNotSupportedException.class, - dataProvider = "parser-provider") - public void testProperty01(SAXParser saxparser) throws SAXException { - saxparser.getProperty(XML_STRING); + @ParameterizedTest + @MethodSource("getParser") + public void testProperty01(SAXParser saxparser) { + assertThrows(SAXNotSupportedException.class, () -> saxparser.getProperty(XML_STRING)); } /** @@ -154,10 +158,10 @@ public void testProperty01(SAXParser saxparser) throws SAXException { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(expectedExceptions = SAXNotSupportedException.class, - dataProvider = "parser-provider") - public void testProperty02(SAXParser saxparser) throws SAXException { - saxparser.getProperty(DOM_NODE); + @ParameterizedTest + @MethodSource("getParser") + public void testProperty02(SAXParser saxparser) { + assertThrows(SAXNotSupportedException.class, () -> saxparser.getProperty(DOM_NODE)); } /** @@ -166,7 +170,8 @@ public void testProperty02(SAXParser saxparser) throws SAXException { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testProperty03(SAXParser saxparser) throws SAXException { assertNull(saxparser.getProperty(LEXICAL_HANDLER)); } @@ -177,7 +182,8 @@ public void testProperty03(SAXParser saxparser) throws SAXException { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testProperty04(SAXParser saxparser) throws SAXException { assertNull(saxparser.getProperty(DECL_HANDLER)); } @@ -188,11 +194,12 @@ public void testProperty04(SAXParser saxparser) throws SAXException { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testProperty05(SAXParser saxparser) throws SAXException { MyLexicalHandler myLexicalHandler = new MyLexicalHandler(); saxparser.setProperty(LEXICAL_HANDLER, myLexicalHandler); - assertTrue(saxparser.getProperty(LEXICAL_HANDLER) instanceof LexicalHandler); + assertInstanceOf(LexicalHandler.class, saxparser.getProperty(LEXICAL_HANDLER)); } /** @@ -201,11 +208,12 @@ public void testProperty05(SAXParser saxparser) throws SAXException { * @param saxparser a SAXParser instance. * @throws SAXException If any parse errors occur. */ - @Test(dataProvider = "parser-provider") + @ParameterizedTest + @MethodSource("getParser") public void testProperty06(SAXParser saxparser) throws SAXException { MyDeclHandler myDeclHandler = new MyDeclHandler(); saxparser.setProperty(DECL_HANDLER, myDeclHandler); - assertTrue(saxparser.getProperty(DECL_HANDLER) instanceof DeclHandler); + assertInstanceOf(DeclHandler.class, saxparser.getProperty(DECL_HANDLER)); } /** diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest03.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest03.java index 47d202da7b8e..61a270b67d5a 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest03.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/parsers/ptests/SAXParserTest03.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 @@ -23,19 +23,18 @@ package javax.xml.parsers.ptests; -import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - -import java.io.File; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.xml.sax.SAXException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import java.io.File; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.xml.sax.SAXException; +import static javax.xml.parsers.ptests.ParserTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Class contains the test cases for SAXParser API @@ -43,7 +42,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.parsers.ptests.SAXParserTest03 + * @run junit/othervm javax.xml.parsers.ptests.SAXParserTest03 */ public class SAXParserTest03 { @@ -52,8 +51,7 @@ public class SAXParserTest03 { * * @return a dimensional contains. */ - @DataProvider(name = "input-provider") - public Object[][] getFactory() { + public static Object[][] getFactory() { SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setValidating(true); return new Object[][] { { spf, MyErrorHandler.newInstance() } }; @@ -67,7 +65,8 @@ public Object[][] getFactory() { * @param handler an error handler for capturing events. * @throws Exception If any errors occur. */ - @Test(dataProvider = "input-provider") + @ParameterizedTest + @MethodSource("getFactory") public void testParseValidate01(SAXParserFactory spf, MyErrorHandler handler) throws Exception { spf.newSAXParser().parse(new File(XML_DIR, "parsertest.xml"), handler); @@ -82,7 +81,8 @@ public void testParseValidate01(SAXParserFactory spf, MyErrorHandler handler) * @param handler an error handler for capturing events. * @throws Exception If any errors occur. */ - @Test(dataProvider = "input-provider") + @ParameterizedTest + @MethodSource("getFactory") public void testParseValidate02(SAXParserFactory spf, MyErrorHandler handler) throws Exception { spf.setNamespaceAware(true); @@ -99,17 +99,13 @@ public void testParseValidate02(SAXParserFactory spf, MyErrorHandler handler) * @param handler an error handler for capturing events. * @throws Exception If any errors occur. */ - @Test(dataProvider = "input-provider") + @ParameterizedTest + @MethodSource("getFactory") public void testParseValidate03(SAXParserFactory spf, MyErrorHandler handler) throws Exception { - try { - spf.setNamespaceAware(true); - SAXParser saxparser = spf.newSAXParser(); - saxparser.parse(new File(XML_DIR, "invalidns.xml"), handler); - fail("Expecting SAXException here"); - } catch (SAXException e) { - assertTrue(handler.isErrorOccured()); - } + spf.setNamespaceAware(true); + SAXParser saxparser = spf.newSAXParser(); + assertThrows(SAXException.class, () -> saxparser.parse(new File(XML_DIR, "invalidns.xml"), handler)); + assertTrue(handler.isErrorOccured()); } - } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLEventFactoryNewInstanceTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLEventFactoryNewInstanceTest.java index b846524e0bf4..36bf2d01c365 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLEventFactoryNewInstanceTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLEventFactoryNewInstanceTest.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 @@ -23,26 +23,25 @@ package javax.xml.stream.ptests; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; -import static jaxp.library.JAXPTestUtilities.clearSystemProperty; - -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertEquals; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import javax.xml.stream.XMLEventFactory; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.stream.ptests.XMLEventFactoryNewInstanceTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.stream.ptests.XMLEventFactoryNewInstanceTest * @summary Tests for XMLEventFactory.newFactory(factoryId , classLoader) */ public class XMLEventFactoryNewInstanceTest { @@ -52,26 +51,25 @@ public class XMLEventFactoryNewInstanceTest { private static final String XMLEVENT_FACTORY_CLASSNAME = DEFAULT_IMPL_CLASS; private static final String XMLEVENT_FACTORY_ID = "javax.xml.stream.XMLEventFactory"; - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { + public static Object[][] getValidateParameters() { return new Object[][] { - { XMLEVENT_FACTORY_ID, null }, - { XMLEVENT_FACTORY_ID, this.getClass().getClassLoader() } }; + { XMLEVENT_FACTORY_ID, null }, + { XMLEVENT_FACTORY_ID, XMLEventFactoryNewInstanceTest.class.getClassLoader() }, + }; } /** * Test if newDefaultFactory() method returns an instance * of the expected factory. - * @throws Exception If any errors occur. */ @Test - public void testDefaultInstance() throws Exception { + public void testDefaultInstance() { XMLEventFactory ef1 = XMLEventFactory.newDefaultFactory(); XMLEventFactory ef2 = XMLEventFactory.newFactory(); assertNotSame(ef1, ef2, "same instance returned:"); assertSame(ef1.getClass(), ef2.getClass(), - "unexpected class mismatch for newDefaultFactory():"); - assertEquals(ef1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultFactory():"); + assertEquals(DEFAULT_IMPL_CLASS, ef1.getClass().getName()); } /* @@ -80,14 +78,15 @@ public void testDefaultInstance() throws Exception { * implementation of javax.xml.stream.XMLEventFactory , should return * newInstance of XMLEventFactory */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewFactory(String factoryId, ClassLoader classLoader) { - setSystemProperty(XMLEVENT_FACTORY_ID, XMLEVENT_FACTORY_CLASSNAME); + System.setProperty(XMLEVENT_FACTORY_ID, XMLEVENT_FACTORY_CLASSNAME); try { XMLEventFactory xef = XMLEventFactory.newFactory(factoryId, classLoader); assertNotNull(xef); } finally { - clearSystemProperty(XMLEVENT_FACTORY_ID); + System.clearProperty(XMLEVENT_FACTORY_ID); } } @@ -96,9 +95,10 @@ public void testNewFactory(String factoryId, ClassLoader classLoader) { * java.lang.ClassLoader classLoader) factoryClassName is null , should * throw NullPointerException */ - @Test(expectedExceptions = NullPointerException.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewFactoryNeg(String factoryId, ClassLoader classLoader) { - XMLEventFactory.newFactory(null, null); + assertThrows(NullPointerException.class, () -> XMLEventFactory.newFactory(factoryId, classLoader)); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLInputFactoryNewInstanceTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLInputFactoryNewInstanceTest.java index ac6b009410cd..fe1ef8d746ee 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLInputFactoryNewInstanceTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLInputFactoryNewInstanceTest.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 @@ -23,26 +23,25 @@ package javax.xml.stream.ptests; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; -import static jaxp.library.JAXPTestUtilities.clearSystemProperty; - -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertEquals; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import javax.xml.stream.XMLInputFactory; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.stream.ptests.XMLInputFactoryNewInstanceTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.stream.ptests.XMLInputFactoryNewInstanceTest * @summary Tests for XMLInputFactory.newFactory(factoryId , classLoader) */ public class XMLInputFactoryNewInstanceTest { @@ -52,26 +51,25 @@ public class XMLInputFactoryNewInstanceTest { private static final String XMLINPUT_FACTORY_CLASSNAME = DEFAULT_IMPL_CLASS; private static final String XMLINPUT_FACTORY_ID = "javax.xml.stream.XMLInputFactory"; - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { + public static Object[][] getValidateParameters() { return new Object[][] { - { XMLINPUT_FACTORY_ID, null }, - { XMLINPUT_FACTORY_ID, this.getClass().getClassLoader() } }; + { XMLINPUT_FACTORY_ID, null }, + { XMLINPUT_FACTORY_ID, XMLInputFactoryNewInstanceTest.class.getClassLoader() }, + }; } /** * Test if newDefaultFactory() method returns an instance * of the expected factory. - * @throws Exception If any errors occur. */ @Test - public void testDefaultInstance() throws Exception { + public void testDefaultInstance() { XMLInputFactory if1 = XMLInputFactory.newDefaultFactory(); XMLInputFactory if2 = XMLInputFactory.newFactory(); assertNotSame(if1, if2, "same instance returned:"); assertSame(if1.getClass(), if2.getClass(), - "unexpected class mismatch for newDefaultFactory():"); - assertEquals(if1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultFactory():"); + assertEquals(DEFAULT_IMPL_CLASS, if1.getClass().getName()); } /* @@ -80,14 +78,15 @@ public void testDefaultInstance() throws Exception { * implementation of javax.xml.stream.XMLInputFactory , should return * newInstance of XMLInputFactory */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewFactory(String factoryId, ClassLoader classLoader) { - setSystemProperty(XMLINPUT_FACTORY_ID, XMLINPUT_FACTORY_CLASSNAME); + System.setProperty(XMLINPUT_FACTORY_ID, XMLINPUT_FACTORY_CLASSNAME); try { XMLInputFactory xif = XMLInputFactory.newFactory(factoryId, classLoader); assertNotNull(xif); } finally { - clearSystemProperty(XMLINPUT_FACTORY_ID); + System.clearProperty(XMLINPUT_FACTORY_ID); } } @@ -96,9 +95,10 @@ public void testNewFactory(String factoryId, ClassLoader classLoader) { * java.lang.ClassLoader classLoader) factoryClassName is null , should * throw NullPointerException */ - @Test(expectedExceptions = NullPointerException.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewFactoryNeg(String factoryId, ClassLoader classLoader) { - XMLInputFactory.newFactory(factoryId, classLoader); + assertThrows(NullPointerException.class, () -> XMLInputFactory.newFactory(factoryId, classLoader)); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLOutputFactoryNewInstanceTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLOutputFactoryNewInstanceTest.java index fa4060248345..b531759f0f79 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLOutputFactoryNewInstanceTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/stream/ptests/XMLOutputFactoryNewInstanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,26 +23,25 @@ package javax.xml.stream.ptests; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; -import static jaxp.library.JAXPTestUtilities.clearSystemProperty; - -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertEquals; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import javax.xml.stream.XMLOutputFactory; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.stream.ptests.XMLOutputFactoryNewInstanceTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.stream.ptests.XMLOutputFactoryNewInstanceTest * @summary Tests for XMLOutputFactory.newFactory(factoryId , classLoader) */ public class XMLOutputFactoryNewInstanceTest { @@ -52,26 +51,25 @@ public class XMLOutputFactoryNewInstanceTest { private static final String XMLOUTPUT_FACTORY_CLASSNAME = DEFAULT_IMPL_CLASS; private static final String XMLOUTPUT_FACTORY_ID = "javax.xml.stream.XMLOutputFactory"; - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { + public static Object[][] getValidateParameters() { return new Object[][] { - { XMLOUTPUT_FACTORY_ID, null }, - { XMLOUTPUT_FACTORY_ID, this.getClass().getClassLoader() } }; + { XMLOUTPUT_FACTORY_ID, null }, + { XMLOUTPUT_FACTORY_ID, XMLOutputFactoryNewInstanceTest.class.getClassLoader() }, + }; } /** * Test if newDefaultFactory() method returns an instance * of the expected factory. - * @throws Exception If any errors occur. */ @Test - public void testDefaultInstance() throws Exception { + public void testDefaultInstance() { XMLOutputFactory of1 = XMLOutputFactory.newDefaultFactory(); XMLOutputFactory of2 = XMLOutputFactory.newFactory(); assertNotSame(of1, of2, "same instance returned:"); assertSame(of1.getClass(), of2.getClass(), - "unexpected class mismatch for newDefaultFactory():"); - assertEquals(of1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultFactory():"); + assertEquals(DEFAULT_IMPL_CLASS, of1.getClass().getName()); } /* @@ -80,14 +78,15 @@ public void testDefaultInstance() throws Exception { * implementation of javax.xml.stream.XMLOutputFactory , should return * newInstance of XMLOutputFactory */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewFactory(String factoryId, ClassLoader classLoader) { - setSystemProperty(XMLOUTPUT_FACTORY_ID, XMLOUTPUT_FACTORY_CLASSNAME); + System.setProperty(XMLOUTPUT_FACTORY_ID, XMLOUTPUT_FACTORY_CLASSNAME); try { XMLOutputFactory xif = XMLOutputFactory.newFactory(factoryId, classLoader); assertNotNull(xif); } finally { - clearSystemProperty(XMLOUTPUT_FACTORY_ID); + System.clearProperty(XMLOUTPUT_FACTORY_ID); } } @@ -96,9 +95,10 @@ public void testNewFactory(String factoryId, ClassLoader classLoader) { * java.lang.ClassLoader classLoader) factoryClassName is null , should * throw NullPointerException */ - @Test(expectedExceptions = NullPointerException.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewFactoryNeg(String factoryId, ClassLoader classLoader) { - XMLOutputFactory.newFactory(factoryId, classLoader); + assertThrows(NullPointerException.class, () -> XMLOutputFactory.newFactory(factoryId, classLoader)); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/Bug6384418Test.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/Bug6384418Test.java index cbe4cc592098..5023d6750935 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/Bug6384418Test.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/Bug6384418Test.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 @@ -23,10 +23,8 @@ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; - -import java.io.ByteArrayOutputStream; -import java.io.File; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -35,15 +33,16 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.ByteArrayOutputStream; +import java.io.File; -import org.testng.annotations.Test; -import org.w3c.dom.Document; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; /* * @test * @bug 6384418 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.Bug6384418Test + * @run junit/othervm javax.xml.transform.ptests.Bug6384418Test * @summary verify the transforming won't throw any exception */ public class Bug6384418Test { diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/DOMResultTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/DOMResultTest.java index b26537d22bf6..9229c3feb10b 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/DOMResultTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/DOMResultTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,23 +23,7 @@ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; - -import java.io.BufferedWriter; -import java.io.FileWriter; -import java.io.IOException; - -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMResult; -import javax.xml.transform.sax.SAXSource; -import javax.xml.transform.sax.SAXTransformerFactory; -import javax.xml.transform.sax.TransformerHandler; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Attr; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; @@ -48,6 +32,21 @@ import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.TransformerHandler; +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; + /** * DOM parse on test file to be compared with golden output file. No Exception * is expected. @@ -55,7 +54,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.DOMResultTest + * @run junit/othervm javax.xml.transform.ptests.DOMResultTest */ public class DOMResultTest { /** @@ -64,8 +63,8 @@ public class DOMResultTest { */ @Test public void testcase01() throws Exception { - String resultFile = USER_DIR + "domresult01.out"; - String goldFile = GOLDEN_DIR + "domresult01GF.out"; + String resultFile = "domresult01.out"; + String goldFile = GOLDEN_DIR + "domresult01GF.out"; String xsltFile = XML_DIR + "cities.xsl"; String xmlFile = XML_DIR + "cities.xml"; @@ -86,7 +85,9 @@ public void testcase01() throws Exception { try (BufferedWriter writer = new BufferedWriter(new FileWriter(resultFile))) { writeNodes(node, writer); } - assertTrue(compareWithGold(goldFile, resultFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(resultFile))); } /** diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/ErrorListenerTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/ErrorListenerTest.java index c591c18fb459..bc067cbcf33d 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/ErrorListenerTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/ErrorListenerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,19 +23,18 @@ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; - -import java.io.File; +import org.junit.jupiter.api.Test; import javax.xml.transform.ErrorListener; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamSource; +import java.io.File; -import org.testng.annotations.Test; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Class containing the test cases for ErrorListener interface @@ -43,7 +42,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.ErrorListenerTest + * @run junit/othervm javax.xml.transform.ptests.ErrorListenerTest */ public class ErrorListenerTest implements ErrorListener { /** @@ -63,15 +62,13 @@ private static enum ListenerStatus{NOT_INVOKED, ERROR, WARNING, FATAL}; @Test public void errorListener01() { ErrorListenerTest listener = new ErrorListenerTest(); - try { - TransformerFactory tfactory = TransformerFactory.newInstance(); - tfactory.setErrorListener (listener); - tfactory.newTransformer(new StreamSource( - new File(XML_DIR + "invalid.xsl"))); - fail("Expect TransformerConfigurationException here"); - } catch (TransformerConfigurationException ex) { - assertEquals(listener.status, ListenerStatus.FATAL); - } + TransformerFactory tfactory = TransformerFactory.newInstance(); + tfactory.setErrorListener(listener); + StreamSource source = new StreamSource(new File(XML_DIR + "invalid.xsl")); + assertThrows( + TransformerConfigurationException.class, + () -> tfactory.newTransformer(source)); + assertEquals(ListenerStatus.FATAL, listener.status); } /** diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXSourceTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXSourceTest.java index 3e50e8bb79e1..851ed676f3a1 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXSourceTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXSourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,22 +23,21 @@ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; +import org.junit.jupiter.api.Test; +import org.xml.sax.InputSource; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; -import org.testng.annotations.Test; -import org.xml.sax.InputSource; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; /** @@ -47,7 +46,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.SAXSourceTest + * @run junit/othervm javax.xml.transform.ptests.SAXSourceTest */ public class SAXSourceTest { /** @@ -57,8 +56,6 @@ public class SAXSourceTest { /** * Test obtaining a SAX InputSource object from a Source object. - * - * @throws IOException reading file error. */ @Test public void source2inputsource01() throws IOException { @@ -72,8 +69,6 @@ public void source2inputsource01() throws IOException { * This test case tries to get InputSource from DOMSource using * sourceToInputSource method. It is not possible and hence null is * expected. This is a negative test case, - * - * @throws Exception If any errors occur. */ @Test public void source2inputsource02() throws Exception { @@ -87,8 +82,6 @@ public void source2inputsource02() throws Exception { * This test case tries to get InputSource from SAXSource using * sourceToInputSource method. This will also check if the systemId * remained the same. This is a positive test case. - * - * @throws IOException reading file error. */ @Test public void source2inputsource03() throws IOException { @@ -97,8 +90,7 @@ public void source2inputsource03() throws IOException { SAXSource saxSource = new SAXSource(new InputSource(fis)); saxSource.setSystemId(SYSTEM_ID); - assertEquals(SAXSource.sourceToInputSource(saxSource).getSystemId(), - SYSTEM_ID); + assertEquals(SYSTEM_ID, SAXSource.sourceToInputSource(saxSource).getSystemId()); } } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXTFactoryTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXTFactoryTest.java index 0e1eabfa8fec..9c12cb9f1195 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXTFactoryTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/SAXTFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,15 +23,14 @@ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLFilter; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -45,14 +44,17 @@ import javax.xml.transform.sax.TransformerHandler; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.xml.sax.InputSource; -import org.xml.sax.XMLFilter; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.XMLReaderFactory; +import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Test newTransformerhandler() method which takes StreamSource as argument can @@ -61,7 +63,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.SAXTFactoryTest + * @run junit/othervm javax.xml.transform.ptests.SAXTFactoryTest */ public class SAXTFactoryTest { /** @@ -84,16 +86,14 @@ public class SAXTFactoryTest { * argument can be set to XMLReader. SAXSource has input XML file as its * input source. XMLReader has a transformer handler which write out the * result to output file. Test verifies output file is same as golden file. - * - * @throws Exception If any errors occur. */ @Test public void testcase01() throws Exception { - String outputFile = USER_DIR + "saxtf001.out"; + String outputFile = "saxtf001.out"; String goldFile = GOLDEN_DIR + "saxtf001GF.out"; try (FileOutputStream fos = new FileOutputStream(outputFile)) { - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory) TransformerFactory.newInstance(); TransformerHandler handler = saxTFactory.newTransformerHandler(new StreamSource(XSLT_FILE)); @@ -102,7 +102,7 @@ public void testcase01() throws Exception { reader.setContentHandler(handler); reader.parse(XML_FILE); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** @@ -110,17 +110,15 @@ public void testcase01() throws Exception { * argument can be set to XMLReader. SAXSource has input XML file as its * input source. XMLReader has a content handler which write out the result * to output file. Test verifies output file is same as golden file. - * - * @throws Exception If any errors occur. */ @Test public void testcase02() throws Exception { - String outputFile = USER_DIR + "saxtf002.out"; + String outputFile = "saxtf002.out"; String goldFile = GOLDEN_DIR + "saxtf002GF.out"; try (FileOutputStream fos = new FileOutputStream(outputFile); - FileInputStream fis = new FileInputStream(XSLT_FILE)) { - XMLReader reader = XMLReaderFactory.createXMLReader(); + FileInputStream fis = new FileInputStream(XSLT_FILE)) { + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory) TransformerFactory.newInstance(); SAXSource ss = new SAXSource(); @@ -132,18 +130,16 @@ public void testcase02() throws Exception { reader.setContentHandler(handler); reader.parse(XML_FILE); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Unit test for newTransformerhandler(Source). DcoumentBuilderFactory is * namespace awareness, DocumentBuilder parse xslt file as DOMSource. - * - * @throws Exception If any errors occur. */ @Test public void testcase03() throws Exception { - String outputFile = USER_DIR + "saxtf003.out"; + String outputFile = "saxtf003.out"; String goldFile = GOLDEN_DIR + "saxtf003GF.out"; try (FileOutputStream fos = new FileOutputStream(outputFile)) { @@ -151,10 +147,9 @@ public void testcase03() throws Exception { dbf.setNamespaceAware(true); DocumentBuilder docBuilder = dbf.newDocumentBuilder(); Document document = docBuilder.parse(new File(XSLT_FILE)); - Node node = (Node)document; - DOMSource domSource= new DOMSource(node); + DOMSource domSource = new DOMSource(document); - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory)TransformerFactory.newInstance(); TransformerHandler handler = @@ -164,15 +159,13 @@ public void testcase03() throws Exception { reader.setContentHandler(handler); reader.parse(XML_FILE); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Negative test for newTransformerHandler when relative URI is in XML file. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = TransformerConfigurationException.class) + @Test public void transformerHandlerTest04() throws Exception { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); @@ -180,31 +173,28 @@ public void transformerHandlerTest04() throws Exception { Document document = docBuilder.parse(new File(XSLT_INCL_FILE)); DOMSource domSource= new DOMSource(document); SAXTransformerFactory saxTFactory - = (SAXTransformerFactory)TransformerFactory.newInstance(); - saxTFactory.newTransformerHandler(domSource); + = (SAXTransformerFactory) TransformerFactory.newInstance(); + assertThrows(TransformerConfigurationException.class, () -> saxTFactory.newTransformerHandler(domSource)); } /** * Unit test for XMLReader parsing when relative URI is used in xsl file and * SystemId was set. - * - * @throws Exception If any errors occur. */ @Test public void testcase05() throws Exception { - String outputFile = USER_DIR + "saxtf005.out"; + String outputFile = "saxtf005.out"; String goldFile = GOLDEN_DIR + "saxtf005GF.out"; try (FileOutputStream fos = new FileOutputStream(outputFile)) { - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory)TransformerFactory.newInstance(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder docBuilder = dbf.newDocumentBuilder(); Document document = docBuilder.parse(new File(XSLT_INCL_FILE)); - Node node = (Node)document; - DOMSource domSource= new DOMSource(node); + DOMSource domSource = new DOMSource(document); domSource.setSystemId("file:///" + XML_DIR); @@ -216,28 +206,26 @@ public void testcase05() throws Exception { reader.setContentHandler(handler); reader.parse(XML_FILE); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Unit test newTransformerHandler with a DOMSource. - * - * @throws Exception If any errors occur. */ @Test public void testcase06() throws Exception { - String outputFile = USER_DIR + "saxtf006.out"; + String outputFile = "saxtf006.out"; String goldFile = GOLDEN_DIR + "saxtf006GF.out"; try (FileOutputStream fos = new FileOutputStream(outputFile)) { - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory)TransformerFactory.newInstance(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder docBuilder = dbf.newDocumentBuilder(); - Node node = (Node)docBuilder.parse(new File(XSLT_INCL_FILE)); + Node node = docBuilder.parse(new File(XSLT_INCL_FILE)); DOMSource domSource = new DOMSource(node, "file:///" + XML_DIR); TransformerHandler handler = @@ -248,20 +236,19 @@ public void testcase06() throws Exception { reader.setContentHandler(handler); reader.parse(XML_FILE); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Test newTransformerHandler with a Template Handler. - * - * @throws Exception If any errors occur. */ + @Test public void testcase08() throws Exception { - String outputFile = USER_DIR + "saxtf008.out"; + String outputFile = "saxtf008.out"; String goldFile = GOLDEN_DIR + "saxtf008GF.out"; try (FileOutputStream fos = new FileOutputStream(outputFile)) { - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory)TransformerFactory.newInstance(); @@ -277,22 +264,20 @@ public void testcase08() throws Exception { reader.setContentHandler(tfhandler); reader.parse(XML_FILE); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Test newTransformerHandler with a Template Handler along with a relative * URI in the style-sheet file. - * - * @throws Exception If any errors occur. */ @Test public void testcase09() throws Exception { - String outputFile = USER_DIR + "saxtf009.out"; + String outputFile = "saxtf009.out"; String goldFile = GOLDEN_DIR + "saxtf009GF.out"; try (FileOutputStream fos = new FileOutputStream(outputFile)) { - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory)TransformerFactory.newInstance(); @@ -307,21 +292,19 @@ public void testcase09() throws Exception { reader.setContentHandler(tfhandler); reader.parse(XML_FILE); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Unit test for contentHandler setter/getter along reader as handler's * parent. - * - * @throws Exception If any errors occur. */ @Test public void testcase10() throws Exception { - String outputFile = USER_DIR + "saxtf010.out"; + String outputFile = "saxtf010.out"; String goldFile = GOLDEN_DIR + "saxtf010GF.out"; // The transformer will use a SAX parser as it's reader. - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory)TransformerFactory.newInstance(); XMLFilter filter = @@ -333,26 +316,23 @@ public void testcase10() throws Exception { // the content handler for the parser object (it's "parent"), and // will then call the parse method on the parser. filter.parse(new InputSource(XML_FILE)); - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Unit test for contentHandler setter/getter with parent. - * - * @throws Exception If any errors occur. */ @Test public void testcase11() throws Exception { - String outputFile = USER_DIR + "saxtf011.out"; + String outputFile = "saxtf011.out"; String goldFile = GOLDEN_DIR + "saxtf011GF.out"; // The transformer will use a SAX parser as it's reader. - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder docBuilder = dbf.newDocumentBuilder(); Document document = docBuilder.parse(new File(XSLT_FILE)); - Node node = (Node)document; - DOMSource domSource= new DOMSource(node); + DOMSource domSource = new DOMSource(document); SAXTransformerFactory saxTFactory = (SAXTransformerFactory)TransformerFactory.newInstance(); @@ -365,20 +345,18 @@ public void testcase11() throws Exception { // the content handler for the parser object (it's "parent"), and // will then call the parse method on the parser. filter.parse(new InputSource(XML_FILE)); - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Unit test for contentHandler setter/getter. - * - * @throws Exception If any errors occur. */ @Test public void testcase12() throws Exception { - String outputFile = USER_DIR + "saxtf012.out"; + String outputFile = "saxtf012.out"; String goldFile = GOLDEN_DIR + "saxtf012GF.out"; // The transformer will use a SAX parser as it's reader. - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); InputSource is = new InputSource(new FileInputStream(XSLT_FILE)); SAXSource saxSource = new SAXSource(); @@ -394,27 +372,26 @@ public void testcase12() throws Exception { // the content handler for the parser object (it's "parent"), and // will then call the parse method on the parser. filter.parse(new InputSource(XML_FILE)); - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); } /** * Unit test for TemplatesHandler setter/getter. - * - * @throws Exception If any errors occur. */ @Test public void testcase13() throws Exception { - String outputFile = USER_DIR + "saxtf013.out"; + String outputFile = "saxtf013.out"; String goldFile = GOLDEN_DIR + "saxtf013GF.out"; try(FileInputStream fis = new FileInputStream(XML_FILE)) { // The transformer will use a SAX parser as it's reader. - XMLReader reader = XMLReaderFactory.createXMLReader(); + XMLReader reader = getXmlReader(); SAXTransformerFactory saxTFactory = (SAXTransformerFactory) TransformerFactory.newInstance(); TemplatesHandler thandler = saxTFactory.newTemplatesHandler(); - // I have put this as it was complaining about systemid - thandler.setSystemId("file:///" + USER_DIR); + // Set the root directory as the ID so the handler can resolve relative paths. + String rootDirUri = Path.of(".").toAbsolutePath().toUri().toASCIIString(); + thandler.setSystemId(rootDirUri); reader.setContentHandler(thandler); reader.parse(XSLT_FILE); @@ -425,6 +402,17 @@ public void testcase13() throws Exception { filter.setContentHandler(new MyContentHandler(outputFile)); filter.parse(new InputSource(fis)); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertFileLines(goldFile, outputFile); + } + + @SuppressWarnings("deprecation") + private static XMLReader getXmlReader() throws SAXException { + return XMLReaderFactory.createXMLReader(); + } + + private static void assertFileLines(String goldenFile, String actualFile) throws IOException { + assertLinesMatch( + Files.readAllLines(Path.of(goldenFile)), + Files.readAllLines(Path.of(actualFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/StreamResultTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/StreamResultTest.java index 6002e3715238..abd15888493e 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/StreamResultTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/StreamResultTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,15 +22,9 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.failUnexpected; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Arrays; -import java.util.Properties; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -41,10 +35,14 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Properties; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.xml.sax.SAXException; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.fail; /** * Test a StreamResult using a file name that contains URL characters that need @@ -53,7 +51,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.StreamResultTest + * @run junit/othervm javax.xml.transform.ptests.StreamResultTest */ public class StreamResultTest { /** @@ -90,15 +88,15 @@ public void testcase01() { DOMSource domSource = new DOMSource(document); StreamSource streamSource = new StreamSource(new FileInputStream(xmlFile)); - File streamResultFile = new File(USER_DIR + file); + File streamResultFile = new File(file); StreamResult streamResult = new StreamResult(streamResultFile); Transformer transformer = TransformerFactory.newInstance().newTransformer(domSource); transformer.setOutputProperties(transformProperties); transformer.transform(streamSource, streamResult); } catch (SAXException | IOException | ParserConfigurationException - | TransformerException ex) { - failUnexpected(ex); + | TransformerException ex) { + fail(ex); } }); } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TfClearParamTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TfClearParamTest.java index f647d4c0bf1d..01dcf3e6b976 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TfClearParamTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TfClearParamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,12 +22,10 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; - -import java.io.File; -import java.io.FileInputStream; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.InputSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -37,11 +35,12 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.xml.sax.InputSource; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; /** * Class containing the test cases for SAXParserFactory API @@ -49,7 +48,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.TfClearParamTest + * @run junit/othervm javax.xml.transform.ptests.TfClearParamTest */ public class TfClearParamTest { /** @@ -82,7 +81,7 @@ public class TfClearParamTest { public void clear01() throws TransformerConfigurationException { Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setParameter(LONG_PARAM_NAME, PARAM_VALUE); - assertEquals(transformer.getParameter(LONG_PARAM_NAME).toString(), PARAM_VALUE); + assertEquals(PARAM_VALUE, transformer.getParameter(LONG_PARAM_NAME).toString()); } /** @@ -124,9 +123,9 @@ public void clear03() throws TransformerConfigurationException { public void clear04() throws TransformerConfigurationException { Transformer transformer = TransformerFactory.newInstance().newTransformer(); - int intObject = 5; - transformer.setParameter(SHORT_PARAM_NAME, intObject); - assertEquals(transformer.getParameter(SHORT_PARAM_NAME), intObject); + int expectedIntValue = 5; + transformer.setParameter(SHORT_PARAM_NAME, expectedIntValue); + assertEquals(expectedIntValue, transformer.getParameter(SHORT_PARAM_NAME)); } /** @@ -141,7 +140,7 @@ public void clear05() throws TransformerConfigurationException { newTransformer(new StreamSource(new File(XSL_FILE))); transformer.setParameter(LONG_PARAM_NAME, PARAM_VALUE); - assertEquals(transformer.getParameter(LONG_PARAM_NAME), PARAM_VALUE); + assertEquals(PARAM_VALUE, transformer.getParameter(LONG_PARAM_NAME)); } /** @@ -172,7 +171,7 @@ public void clear07() throws Exception { Transformer transformer = TransformerFactory.newInstance().newTransformer(saxSource); transformer.setParameter(LONG_PARAM_NAME, PARAM_VALUE); - assertEquals(transformer.getParameter(LONG_PARAM_NAME), PARAM_VALUE); + assertEquals(PARAM_VALUE, transformer.getParameter(LONG_PARAM_NAME)); } } @@ -212,7 +211,7 @@ public void clear09() throws Exception { Transformer transformer = tfactory.newTransformer(domSource); transformer.setParameter(LONG_PARAM_NAME, PARAM_VALUE); - assertEquals(transformer.getParameter(LONG_PARAM_NAME), PARAM_VALUE); + assertEquals(PARAM_VALUE, transformer.getParameter(LONG_PARAM_NAME)); } /** diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformTest.java index 8666306669de..1d9600b330be 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformTest.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 @@ -23,18 +23,19 @@ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; - -import java.io.BufferedWriter; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.function.Supplier; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -56,30 +57,33 @@ import javax.xml.transform.stax.StAXSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.function.Supplier; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.xml.sax.Attributes; -import org.xml.sax.ContentHandler; -import org.xml.sax.InputSource; -import org.xml.sax.Locator; -import org.xml.sax.SAXException; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.TransformTest + * @run junit/othervm javax.xml.transform.ptests.TransformTest * @summary Tests for variable combination of Transformer.transform(Source, Result) */ -@Test(singleThreaded = true) +@TestInstance(Lifecycle.PER_CLASS) +@Execution(ExecutionMode.SAME_THREAD) public class TransformTest { /* * Initialize the share objects. */ - @BeforeClass + @BeforeAll public void setup() throws Exception { ifac = XMLInputFactory.newInstance(); ofac = XMLOutputFactory.newInstance(); @@ -95,7 +99,6 @@ public void setup() throws Exception { xmlDoc = db.parse(xmlInputStream()); } - @DataProvider(name = "input-provider") public Object[][] prepareTestCombination() throws Exception { Supplier staxStreamSource = () -> new StAXSource(getXMLStreamReader()); @@ -160,7 +163,8 @@ public Object[][] prepareTestCombination() throws Exception { /* * run Transformer.transform(Source, Result) */ - @Test(dataProvider = "input-provider") + @ParameterizedTest + @MethodSource("prepareTestCombination") public void testTransform(Supplier src, Supplier res, Transformer transformer) throws Throwable { try { transformer.transform(src.get(), res.get()); @@ -257,7 +261,7 @@ public MyContentHandler(OutputStream os) { public void setDocumentLocator(Locator locator) { } - public void startDocument() throws SAXException { + public void startDocument() { String str = "startDocument"; try { bWriter.write(str, 0, str.length()); @@ -267,7 +271,7 @@ public void startDocument() throws SAXException { } } - public void endDocument() throws SAXException { + public void endDocument() { String str = "endDocument"; try { bWriter.write(str, 0, str.length()); @@ -279,7 +283,7 @@ public void endDocument() throws SAXException { } } - public void startPrefixMapping(String prefix, String uri) throws SAXException { + public void startPrefixMapping(String prefix, String uri) { String str = "startPrefixMapping: " + prefix + ", " + uri; try { bWriter.write(str, 0, str.length()); @@ -289,7 +293,7 @@ public void startPrefixMapping(String prefix, String uri) throws SAXException { } } - public void endPrefixMapping(String prefix) throws SAXException { + public void endPrefixMapping(String prefix) { String str = "endPrefixMapping: " + prefix; try { bWriter.write(str, 0, str.length()); @@ -299,7 +303,7 @@ public void endPrefixMapping(String prefix) throws SAXException { } } - public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { + public void startElement(String namespaceURI, String localName, String qName, Attributes atts) { StringBuilder str = new StringBuilder("startElement: ").append(namespaceURI).append(", ").append(namespaceURI).append(", ").append(qName).append(" : "); int n = atts.getLength(); for (int i = 0; i < n; i++) { @@ -314,7 +318,7 @@ public void startElement(String namespaceURI, String localName, String qName, At } } - public void endElement(String namespaceURI, String localName, String qName) throws SAXException { + public void endElement(String namespaceURI, String localName, String qName) { String str = "endElement: " + namespaceURI + ", " + namespaceURI + ", " + qName; try { bWriter.write(str, 0, str.length()); @@ -325,7 +329,7 @@ public void endElement(String namespaceURI, String localName, String qName) thro } - public void characters(char ch[], int start, int length) throws SAXException { + public void characters(char ch[], int start, int length) { String str = new String(ch, start, length); try { bWriter.write(str, 0, str.length()); @@ -335,7 +339,7 @@ public void characters(char ch[], int start, int length) throws SAXException { } } - public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { + public void ignorableWhitespace(char ch[], int start, int length) { String str = "ignorableWhitespace"; try { bWriter.write(str, 0, str.length()); @@ -345,7 +349,7 @@ public void ignorableWhitespace(char ch[], int start, int length) throws SAXExce } } - public void processingInstruction(String target, String data) throws SAXException { + public void processingInstruction(String target, String data) { String str = "processingInstruction: " + target + ", " + target; try { bWriter.write(str, 0, str.length()); @@ -355,7 +359,7 @@ public void processingInstruction(String target, String data) throws SAXExceptio } } - public void skippedEntity(String name) throws SAXException { + public void skippedEntity(String name) { String str = "skippedEntity: " + name; try { bWriter.write(str, 0, str.length()); diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerExcpTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerExcpTest.java index 8907b7afd558..25b715864ddf 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerExcpTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerExcpTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,21 +22,21 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.fail; - -import java.io.File; +import org.junit.jupiter.api.Test; import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.sax.SAXResult; import javax.xml.transform.stream.StreamSource; +import java.io.File; -import org.testng.annotations.Test; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Basic test for TransformerException specification. @@ -44,31 +44,28 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.TransformerExcpTest + * @run junit/othervm javax.xml.transform.ptests.TransformerExcpTest */ public class TransformerExcpTest { /** * Transform an unformatted style-sheet file. TransformerException is thrown. */ @Test - public void tfexception() { - try { - // invalid.xsl has well-formedness error. Therefore transform throws - // TransformerException - StreamSource streamSource - = new StreamSource(new File(XML_DIR + "invalid.xsl")); - TransformerFactory tFactory = TransformerFactory.newInstance(); - Transformer transformer = tFactory.newTransformer(streamSource); - transformer.transform( - new StreamSource(new File(XML_DIR + "cities.xml")), - new SAXResult()); - fail("TransformerException is not thrown as expected"); - } catch (TransformerException e) { - assertNotNull(e.getCause()); - assertNotNull(e.getException()); - assertNull(e.getLocationAsString()); - assertEquals(e.getMessageAndLocation(),e.getMessage()); - } + public void tfexception() throws TransformerConfigurationException { + // invalid.xsl has well-formedness error. Therefore transform throws + // TransformerException + StreamSource streamSource + = new StreamSource(new File(XML_DIR + "invalid.xsl")); + TransformerFactory tFactory = TransformerFactory.newInstance(); + + TransformerException e = assertThrows( + TransformerException.class, + () -> tFactory.newTransformer(streamSource)); + + assertNotNull(e.getCause()); + assertNotNull(e.getException()); + assertNull(e.getLocationAsString()); + assertEquals(e.getMessageAndLocation(), e.getMessage()); } @@ -77,20 +74,20 @@ public void tfexception() { * TransformerException(Throwable), initCause should throw * IllegalStateException */ - @Test(expectedExceptions = IllegalStateException.class) + @Test public void tfexception06() { TransformerException te = new TransformerException(new Throwable()); - te.initCause(null); + assertThrows(IllegalStateException.class, () -> te.initCause(null)); } /** * Spec says, "if the throwable was created with TransformerException(String, * Throwable), initCause should throw IllegalStateException */ - @Test(expectedExceptions = IllegalStateException.class) + @Test public void tfexception07() { TransformerException te = new TransformerException("MyMessage", new Throwable()); - te.initCause(null); + assertThrows(IllegalStateException.class, () -> te.initCause(null)); } /** diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerFactoryTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerFactoryTest.java index 8223319aa0a9..b53fe2980051 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerFactoryTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,18 +22,11 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertEquals; - -import java.io.FileInputStream; -import java.io.FileOutputStream; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -44,12 +37,19 @@ import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.nio.file.Files; +import java.nio.file.Path; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.Document; +import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Class containing the test cases for TransformerFactory API's @@ -59,7 +59,8 @@ * @test * @bug 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.TransformerFactoryTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.transform.ptests.TransformerFactoryTest */ public class TransformerFactoryTest { /** @@ -78,24 +79,25 @@ public class TransformerFactoryTest { * * @return a data provider contains TransformerFactory instantiation parameters. */ - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { - return new Object[][] { { TRANSFORMER_FACTORY_CLASSNAME, null }, { TRANSFORMER_FACTORY_CLASSNAME, this.getClass().getClassLoader() } }; + public static Object[][] getValidateParameters() { + return new Object[][] { + { TRANSFORMER_FACTORY_CLASSNAME, null }, + { TRANSFORMER_FACTORY_CLASSNAME, TransformerFactoryTest.class.getClassLoader() }, + }; } /** * Test if newDefaultInstance() method returns an instance * of the expected factory. - * @throws Exception If any errors occur. */ @Test - public void testDefaultInstance() throws Exception { + public void testDefaultInstance() { TransformerFactory tf1 = TransformerFactory.newDefaultInstance(); TransformerFactory tf2 = TransformerFactory.newInstance(); assertNotSame(tf1, tf2, "same instance returned:"); assertSame(tf1.getClass(), tf2.getClass(), - "unexpected class mismatch for newDefaultInstance():"); - assertEquals(tf1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultInstance():"); + assertEquals(DEFAULT_IMPL_CLASS, tf1.getClass().getName()); } /** @@ -104,12 +106,9 @@ public void testDefaultInstance() throws Exception { * points to correct implementation of * javax.xml.transform.TransformerFactory , should return newInstance of * TransformerFactory - * - * @param factoryClassName - * @param classLoader - * @throws TransformerConfigurationException */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewInstance(String factoryClassName, ClassLoader classLoader) throws TransformerConfigurationException { TransformerFactory tf = TransformerFactory.newInstance(factoryClassName, classLoader); Transformer transformer = tf.newTransformer(); @@ -120,13 +119,13 @@ public void testNewInstance(String factoryClassName, ClassLoader classLoader) th * Test for TransformerFactory.newInstance(java.lang.String * factoryClassName, java.lang.ClassLoader classLoader) factoryClassName is * null , should throw TransformerFactoryConfigurationError - * - * @param factoryClassName - * @param classLoader */ - @Test(expectedExceptions = TransformerFactoryConfigurationError.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewInstanceNeg(String factoryClassName, ClassLoader classLoader) { - TransformerFactory.newInstance(factoryClassName, classLoader); + assertThrows( + TransformerFactoryConfigurationError.class, + () -> TransformerFactory.newInstance(factoryClassName, classLoader)); } /** @@ -134,12 +133,10 @@ public void testNewInstanceNeg(String factoryClassName, ClassLoader classLoader) * of TransformerFactory. * The style sheet returned is then copied to an tfactory01.out * It will then be verified to see if it matches the golden files. - * - * @throws Exception If any errors occur. */ @Test public void tfactory01() throws Exception { - String outputFile = USER_DIR + "tfactory01.out"; + String outputFile = "tfactory01.out"; String goldFile = GOLDEN_DIR + "tfactory01GF.out"; String xmlFile = XML_DIR + "TransformerFactoryTest.xml"; String xmlURI = "file:///" + XML_DIR; @@ -160,6 +157,8 @@ public void tfactory01() throws Exception { Transformer t = tFactory.newTransformer(); t.transform(s, streamResult); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest.java index f940f268ff42..6a779cc05e0d 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,14 +22,9 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; - -import java.io.File; -import java.io.FileInputStream; -import java.util.Properties; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -41,10 +36,14 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; +import java.util.Properties; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.xml.sax.InputSource; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * Basic test cases for Transformer API @@ -52,7 +51,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.TransformerTest + * @run junit/othervm javax.xml.transform.ptests.TransformerTest */ public class TransformerTest { /** @@ -90,8 +89,6 @@ public void transformer02() throws Exception { /** * This tests if newTransformer(DOMSource) method returns Transformer. - * - * @throws Exception If any errors occur. */ @Test public void transformer03() throws Exception { @@ -109,8 +106,6 @@ public void transformer03() throws Exception { /** * This tests set/get ErrorListener methods of Transformer. - * - * @throws Exception If any errors occur. */ @Test public void transformer04() throws Exception { @@ -124,13 +119,11 @@ public void transformer04() throws Exception { .newTransformer(domSource); transformer.setErrorListener(new MyErrorListener()); assertNotNull(transformer.getErrorListener()); - assertTrue(transformer.getErrorListener() instanceof MyErrorListener); + assertInstanceOf(MyErrorListener.class, transformer.getErrorListener()); } /** * This tests getOutputProperties() method of Transformer. - * - * @throws Exception If any errors occur. */ @Test public void transformer05() throws Exception { @@ -144,18 +137,16 @@ public void transformer05() throws Exception { newTransformer(domSource); Properties prop = transformer.getOutputProperties(); - assertEquals(prop.getProperty("indent"), "yes"); - assertEquals(prop.getProperty("method"), "xml"); - assertEquals(prop.getProperty("encoding"), "UTF-8"); - assertEquals(prop.getProperty("standalone"), "no"); - assertEquals(prop.getProperty("version"), "1.0"); - assertEquals(prop.getProperty("omit-xml-declaration"), "no"); + assertEquals("yes", prop.getProperty("indent")); + assertEquals("xml", prop.getProperty("method")); + assertEquals("UTF-8", prop.getProperty("encoding")); + assertEquals("no", prop.getProperty("standalone")); + assertEquals("1.0", prop.getProperty("version")); + assertEquals("no", prop.getProperty("omit-xml-declaration")); } /** * This tests getOutputProperty() method of Transformer. - * - * @throws Exception If any errors occur. */ @Test public void transformer06() throws Exception { @@ -168,7 +159,7 @@ public void transformer06() throws Exception { DOMSource domSource = new DOMSource(document); Transformer transformer = tfactory.newTransformer(domSource); - assertEquals(transformer.getOutputProperty("method"), "xml"); + assertEquals("xml", transformer.getOutputProperty("method")); } } @@ -199,8 +190,7 @@ public void warning (TransformerException e) { * @param e exception of a fatal error. */ @Override - public void fatalError (TransformerException e) throws - TransformerException { + public void fatalError(TransformerException e) { System.out.println(" In fatal"); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest02.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest02.java index a9391b02866a..0b7a9a85f095 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest02.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest02.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,15 +22,7 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; +import org.junit.jupiter.api.Test; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; @@ -38,8 +30,15 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.nio.file.Files; +import java.nio.file.Path; -import org.testng.annotations.Test; +import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; /** @@ -49,17 +48,15 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.TransformerTest02 + * @run junit/othervm javax.xml.transform.ptests.TransformerTest02 */ public class TransformerTest02 { /** * Unit test for transform(StreamSource, StreamResult). - * - * @throws Exception If any errors occur. */ @Test public void testcase01() throws Exception { - String outputFile = USER_DIR + "transformer02.out"; + String outputFile = "transformer02.out"; String goldFile = GOLDEN_DIR + "transformer02GF.out"; String xsltFile = XML_DIR + "cities.xsl"; String xmlFile = XML_DIR + "cities.xml"; @@ -79,6 +76,8 @@ public void testcase01() throws Exception { transformer.setOutputProperty("indent", "no"); transformer.transform(streamSource, streamResult); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest03.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest03.java index 4cdd2e606f14..18e5033b0f0f 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest03.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/TransformerTest03.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,16 +22,7 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.util.Properties; +import org.junit.jupiter.api.Test; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; @@ -39,8 +30,16 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Properties; -import org.testng.annotations.Test; +import static javax.xml.transform.ptests.TransformerTestConst.GOLDEN_DIR; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; /** * Here Properties Object is populated with required properties.A transformer @@ -51,17 +50,15 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.TransformerTest03 + * @run junit/othervm javax.xml.transform.ptests.TransformerTest03 */ public class TransformerTest03 { /** * Test for Transformer.setOutputProperties method. - * - * @throws Exception If any errors occur. */ @Test public void testcase01() throws Exception { - String outputFile = USER_DIR + "transformer03.out"; + String outputFile = "transformer03.out"; String goldFile = GOLDEN_DIR + "transformer03GF.out"; String xsltFile = XML_DIR + "cities.xsl"; String xmlFile = XML_DIR + "cities.xml"; @@ -88,6 +85,8 @@ public void testcase01() throws Exception { transformer.setOutputProperties(properties); transformer.transform(new StreamSource(fis), new StreamResult(fos)); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/URIResolverTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/URIResolverTest.java index 0d642b67129c..3db6bec77aef 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/URIResolverTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/URIResolverTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,12 +22,9 @@ */ package javax.xml.transform.ptests; -import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; - -import java.io.File; -import java.io.FileInputStream; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -39,10 +36,12 @@ import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.FileInputStream; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.xml.sax.InputSource; +import static javax.xml.transform.ptests.TransformerTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * URIResolver should be invoked when transform happens. @@ -50,9 +49,9 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.URIResolverTest + * @run junit/othervm javax.xml.transform.ptests.URIResolverTest */ -public class URIResolverTest implements URIResolver { +public class URIResolverTest { /** * System ID constant. */ @@ -73,60 +72,29 @@ public class URIResolverTest implements URIResolver { */ private final static String XSL_TEMP_FILE = "temp/cities.xsl"; - /** - * expected HREF. - */ - private final String validateHref; - - /** - * expected Base URI. - */ - private final String validateBase; - - /** - * Default constructor for testng invocation. - */ - public URIResolverTest(){ - validateHref = null; - validateBase = null; - } - - /** - * Constructor for setting expected Href and expected Base URI. - * @param validateHref expected Href - * @param validateBase expected Base URI - */ - public URIResolverTest(String validateHref, String validateBase){ - this.validateHref = validateHref; - this.validateBase = validateBase; - } - - /** - * Called by the processor when it encounters an xsl:include, xsl:import, - * or document() function. - * @param href An href attribute, which may be relative or absolute. - * @param base The base URI against which the first argument will be made - * absolute if the absolute URI is required. - * @return null always. - */ - @Override - public Source resolve(String href, String base) { - assertEquals(href, validateHref); - assertEquals(base, validateBase); - return null; + record TestResolver(String expectedHref, String expectedBase) implements URIResolver { + /** + * Called by the processor when it encounters an xsl:include, xsl:import, + * or document() function. + */ + @Override + public Source resolve(String href, String base) { + assertEquals(expectedHref, href); + assertEquals(expectedBase, base); + // Return null if the href cannot be resolved. + return null; + } } /** * This is to test the URIResolver.resolve() method when a transformer is * created using StreamSource. style-sheet file has xsl:include in it. - * - * @throws Exception If any errors occur. */ @Test - public static void resolver01() throws Exception { + public void resolver01() throws Exception { try (FileInputStream fis = new FileInputStream(XSL_INCLUDE_FILE)) { TransformerFactory tfactory = TransformerFactory.newInstance(); - URIResolverTest resolver = new URIResolverTest(XSL_TEMP_FILE, SYSTEM_ID); + TestResolver resolver = new TestResolver(XSL_TEMP_FILE, SYSTEM_ID); tfactory.setURIResolver(resolver); StreamSource streamSource = new StreamSource(fis); @@ -138,13 +106,11 @@ public static void resolver01() throws Exception { /** * This is to test the URIResolver.resolve() method when a transformer is * created using DOMSource. style-sheet file has xsl:include in it. - * - * @throws Exception If any errors occur. */ @Test - public static void resolver02() throws Exception { + public void resolver02() throws Exception { TransformerFactory tfactory = TransformerFactory.newInstance(); - URIResolverTest resolver = new URIResolverTest(XSL_TEMP_FILE, SYSTEM_ID); + TestResolver resolver = new TestResolver(XSL_TEMP_FILE, SYSTEM_ID); tfactory.setURIResolver(resolver); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); @@ -159,13 +125,11 @@ public static void resolver02() throws Exception { /** * This is to test the URIResolver.resolve() method when a transformer is * created using SAXSource. style-sheet file has xsl:include in it. - * - * @throws Exception If any errors occur. */ @Test - public static void resolver03() throws Exception { - try (FileInputStream fis = new FileInputStream(XSL_INCLUDE_FILE)){ - URIResolverTest resolver = new URIResolverTest(XSL_TEMP_FILE, SYSTEM_ID); + public void resolver03() throws Exception { + try (FileInputStream fis = new FileInputStream(XSL_INCLUDE_FILE)) { + TestResolver resolver = new TestResolver(XSL_TEMP_FILE, SYSTEM_ID); TransformerFactory tfactory = TransformerFactory.newInstance(); tfactory.setURIResolver(resolver); InputSource is = new InputSource(fis); @@ -178,13 +142,11 @@ public static void resolver03() throws Exception { /** * This is to test the URIResolver.resolve() method when a transformer is * created using StreamSource. style-sheet file has xsl:import in it. - * - * @throws Exception If any errors occur. */ @Test - public static void resolver04() throws Exception { + public void resolver04() throws Exception { try (FileInputStream fis = new FileInputStream(XSL_IMPORT_FILE)) { - URIResolverTest resolver = new URIResolverTest(XSL_TEMP_FILE, SYSTEM_ID); + TestResolver resolver = new TestResolver(XSL_TEMP_FILE, SYSTEM_ID); TransformerFactory tfactory = TransformerFactory.newInstance(); tfactory.setURIResolver(resolver); StreamSource streamSource = new StreamSource(fis); @@ -196,12 +158,10 @@ public static void resolver04() throws Exception { /** * This is to test the URIResolver.resolve() method when a transformer is * created using DOMSource. style-sheet file has xsl:import in it. - * - * @throws Exception If any errors occur. */ @Test - public static void resolver05() throws Exception { - URIResolverTest resolver = new URIResolverTest(XSL_TEMP_FILE, SYSTEM_ID); + public void resolver05() throws Exception { + TestResolver resolver = new TestResolver(XSL_TEMP_FILE, SYSTEM_ID); TransformerFactory tfactory = TransformerFactory.newInstance(); tfactory.setURIResolver(resolver); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); @@ -215,13 +175,11 @@ public static void resolver05() throws Exception { /** * This is to test the URIResolver.resolve() method when a transformer is * created using SAXSource. style-sheet file has xsl:import in it. - * - * @throws Exception If any errors occur. */ @Test - public static void resolver06() throws Exception { - try (FileInputStream fis = new FileInputStream(XSL_IMPORT_FILE)){ - URIResolverTest resolver = new URIResolverTest(XSL_TEMP_FILE, SYSTEM_ID); + public void resolver06() throws Exception { + try (FileInputStream fis = new FileInputStream(XSL_IMPORT_FILE)) { + TestResolver resolver = new TestResolver(XSL_TEMP_FILE, SYSTEM_ID); TransformerFactory tfactory = TransformerFactory.newInstance(); tfactory.setURIResolver(resolver); InputSource is = new InputSource(fis); @@ -234,13 +192,11 @@ public static void resolver06() throws Exception { /** * This is to test the URIResolver.resolve() method when there is an error * in the file. - * - * @throws Exception If any errors occur. */ @Test - public static void docResolver01() throws Exception { + public void docResolver01() throws Exception { try (FileInputStream fis = new FileInputStream(XML_DIR + "doctest.xsl")) { - URIResolverTest resolver = new URIResolverTest("temp/colors.xml", SYSTEM_ID); + TestResolver resolver = new TestResolver("temp/colors.xml", SYSTEM_ID); StreamSource streamSource = new StreamSource(fis); streamSource.setSystemId(SYSTEM_ID); diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/othervm/TFCErrorTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/othervm/TFCErrorTest.java index 41a528ce1d2c..31fa29602504 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/othervm/TFCErrorTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/ptests/othervm/TFCErrorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,14 +22,13 @@ */ package javax.xml.transform.ptests.othervm; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; - -import static org.testng.Assert.fail; +import org.junit.jupiter.api.Test; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Negative test for set invalid TransformerFactory property. @@ -37,17 +36,21 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.transform.ptests.othervm.TFCErrorTest + * @run junit/othervm javax.xml.transform.ptests.othervm.TFCErrorTest */ public class TFCErrorTest { - @Test(expectedExceptions = ClassNotFoundException.class) - public void tfce01() throws Exception { - try{ - setSystemProperty("javax.xml.transform.TransformerFactory","xx"); - TransformerFactory.newInstance(); - fail("Expect TransformerFactoryConfigurationError here"); - } catch (TransformerFactoryConfigurationError expected) { - throw expected.getException(); + private static final String TRANSFORMER_FACTORY = "javax.xml.transform.TransformerFactory"; + + @Test + public void tfce01() { + System.setProperty(TRANSFORMER_FACTORY, "xx"); + try { + TransformerFactoryConfigurationError e = assertThrows( + TransformerFactoryConfigurationError.class, + TransformerFactory::newInstance); + assertInstanceOf(ClassNotFoundException.class, e.getException()); + } finally { + System.clearProperty(TRANSFORMER_FACTORY); } } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/out/saxtf008GF.out b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/out/saxtf008GF.out index 1fc9136a82bc..4e646319c6e2 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/out/saxtf008GF.out +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/out/saxtf008GF.out @@ -1,17 +1,17 @@ - -Paris -Nice -Lyon - - -Roma -Milano -Firenze -Napoli - - -Madrid -Barcelona - + + Paris + Nice + Lyon + + + Roma + Milano + Firenze + Napoli + + + Madrid + Barcelona + diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/SchemaFactoryTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/SchemaFactoryTest.java index d97218838704..dc5acdfe1a36 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/SchemaFactoryTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/SchemaFactoryTest.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 @@ -22,23 +22,22 @@ */ package javax.xml.validation.ptests; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertEquals; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Paths; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.SAXParseException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -52,31 +51,38 @@ import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.xml.sax.ErrorHandler; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.SAXNotRecognizedException; -import org.xml.sax.SAXNotSupportedException; -import org.xml.sax.SAXParseException; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; +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.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @bug 8080907 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.validation.ptests.SchemaFactoryTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.validation.ptests.SchemaFactoryTest * @summary Class containing the test cases for SchemaFactory */ -@Test(singleThreaded = true) +@Execution(ExecutionMode.SAME_THREAD) +@TestInstance(Lifecycle.PER_CLASS) public class SchemaFactoryTest { - @BeforeClass + @BeforeAll public void setup() throws SAXException, IOException, ParserConfigurationException { sf = newSchemaFactory(); assertNotNull(sf); @@ -96,7 +102,6 @@ public void setup() throws SAXException, IOException, ParserConfigurationExcepti } - @DataProvider(name = "parameters") public Object[][] getValidateParameters() { return new Object[][] { { W3C_XML_SCHEMA_NS_URI, SCHEMA_FACTORY_CLASSNAME, null }, { W3C_XML_SCHEMA_NS_URI, SCHEMA_FACTORY_CLASSNAME, this.getClass().getClassLoader() } }; @@ -105,16 +110,15 @@ public Object[][] getValidateParameters() { /** * Test if newDefaultInstance() method returns an instance * of the expected factory. - * @throws Exception If any errors occur. */ @Test - public void testDefaultInstance() throws Exception { + public void testDefaultInstance() { SchemaFactory sf1 = SchemaFactory.newDefaultInstance(); SchemaFactory sf2 = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI); assertNotSame(sf1, sf2, "same instance returned:"); assertSame(sf1.getClass(), sf2.getClass(), - "unexpected class mismatch for newDefaultInstance():"); - assertEquals(sf1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultInstance():"); + assertEquals(DEFAULT_IMPL_CLASS, sf1.getClass().getName()); assertTrue(sf1.isSchemaLanguageSupported(W3C_XML_SCHEMA_NS_URI), "isSchemaLanguageSupported(W3C_XML_SCHEMA_NS_URI):"); assertFalse(sf1.isSchemaLanguageSupported(UNRECOGNIZED_NAME), @@ -128,9 +132,10 @@ public void testDefaultInstance() throws Exception { * javax.xml.validation.SchemaFactory , should return newInstance of * SchemaFactory */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewInstance(String schemaLanguage, String factoryClassName, ClassLoader classLoader) throws SAXException { - SchemaFactory sf = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI, SCHEMA_FACTORY_CLASSNAME, null); + SchemaFactory sf = SchemaFactory.newInstance(schemaLanguage, factoryClassName, classLoader); Schema schema = sf.newSchema(); assertNotNull(schema); } @@ -140,10 +145,12 @@ public void testNewInstance(String schemaLanguage, String factoryClassName, Clas * java.lang.String factoryClassName, java.lang.ClassLoader classLoader) * factoryClassName is null , should throw IllegalArgumentException */ - @Test(expectedExceptions = IllegalArgumentException.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") public void testNewInstanceWithNullFactoryClassName(String factoryClassName, ClassLoader classLoader) { - - SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI, factoryClassName, classLoader); + assertThrows( + IllegalArgumentException.class, + () -> SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI, factoryClassName, classLoader)); } /* @@ -151,9 +158,11 @@ public void testNewInstanceWithNullFactoryClassName(String factoryClassName, Cla * java.lang.String factoryClassName, java.lang.ClassLoader classLoader) * schemaLanguage is null , should throw NPE */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testNewInstanceWithNullSchemaLanguage() { - SchemaFactory.newInstance(null, SCHEMA_FACTORY_CLASSNAME, this.getClass().getClassLoader()); + assertThrows( + NullPointerException.class, + () -> SchemaFactory.newInstance(null, SCHEMA_FACTORY_CLASSNAME, this.getClass().getClassLoader())); } /* @@ -161,15 +170,17 @@ public void testNewInstanceWithNullSchemaLanguage() { * java.lang.String factoryClassName, java.lang.ClassLoader classLoader) * schemaLanguage is empty , should throw IllegalArgumentException */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testNewInstanceWithEmptySchemaLanguage() { - SchemaFactory.newInstance("", SCHEMA_FACTORY_CLASSNAME, this.getClass().getClassLoader()); + assertThrows( + IllegalArgumentException.class, + () -> SchemaFactory.newInstance("", SCHEMA_FACTORY_CLASSNAME, this.getClass().getClassLoader())); } - @Test(expectedExceptions = SAXParseException.class) - public void testNewSchemaDefault() throws SAXException, IOException { - validate(sf.newSchema()); + @Test + public void testNewSchemaDefault() { + assertThrows(SAXParseException.class, () -> validate(sf.newSchema())); } @Test @@ -177,12 +188,11 @@ public void testNewSchemaWithFile() throws SAXException, IOException { validate(sf.newSchema(new File(XML_DIR + "test.xsd"))); } - @Test(expectedExceptions = NullPointerException.class) - public void testNewSchemaWithNullFile() throws SAXException { - sf.newSchema((File) null); + @Test + public void testNewSchemaWithNullFile() { + assertThrows(NullPointerException.class, () -> sf.newSchema((File) null)); } - @DataProvider(name = "valid-source") public Object[][] getValidSource() throws XMLStreamException { return new Object[][] { { streamSource(xsd1) }, @@ -193,43 +203,42 @@ public Object[][] getValidSource() throws XMLStreamException { } - @Test(dataProvider = "valid-source") + @ParameterizedTest + @MethodSource("getValidSource") public void testNewSchemaWithValidSource(Source schema) throws SAXException, IOException { validate(sf.newSchema(schema)); } - @DataProvider(name = "invalid-source") - public Object[][] getInvalidSource() { + public static Object[][] getInvalidSource() { return new Object[][] { { nullStreamSource() }, { nullSaxSource() } }; } - @Test(dataProvider = "invalid-source", expectedExceptions = SAXParseException.class) - public void testNewSchemaWithInvalidSource(Source schema) throws SAXException { - sf.newSchema(schema); + @ParameterizedTest + @MethodSource("getInvalidSource") + public void testNewSchemaWithInvalidSource(Source schema) { + assertThrows(SAXParseException.class, () -> sf.newSchema(schema)); } - @Test(expectedExceptions = NullPointerException.class) - public void testNewSchemaWithNullSource() throws SAXException { - sf.newSchema((Source)null); + @Test + public void testNewSchemaWithNullSource() { + assertThrows(NullPointerException.class, () -> sf.newSchema((Source) null)); } - @DataProvider(name = "valid-sources") public Object[][] getValidSources() { return new Object[][] { { streamSource(xsd1), streamSource(xsd2) }, { saxSource(xsd1), saxSource(xsd2) }, { domSource(xsdDoc1), domSource(xsdDoc2) } }; - } - @Test(dataProvider = "valid-sources") + @ParameterizedTest + @MethodSource("getValidSources") public void testNewSchemaWithValidSourceArray(Source schema1, Source schema2) throws SAXException, IOException { validate(sf.newSchema(new Source[] { schema1, schema2 })); } - @DataProvider(name = "invalid-sources") public Object[][] getInvalidSources() { return new Object[][] { { streamSource(xsd1), nullStreamSource() }, @@ -238,12 +247,12 @@ public Object[][] getInvalidSources() { { nullSaxSource(), nullSaxSource() } }; } - @Test(dataProvider = "invalid-sources", expectedExceptions = SAXParseException.class) - public void testNewSchemaWithInvalidSourceArray(Source schema1, Source schema2) throws SAXException { - sf.newSchema(new Source[] { schema1, schema2 }); + @ParameterizedTest + @MethodSource("getInvalidSources") + public void testNewSchemaWithInvalidSourceArray(Source schema1, Source schema2) { + assertThrows(SAXParseException.class, () -> sf.newSchema(new Source[] { schema1, schema2 })); } - @DataProvider(name = "null-sources") public Object[][] getNullSources() { return new Object[][] { { new Source[] { domSource(xsdDoc1), null } }, @@ -252,14 +261,15 @@ public Object[][] getNullSources() { } - @Test(dataProvider = "null-sources", expectedExceptions = NullPointerException.class) - public void testNewSchemaWithNullSourceArray(Source[] schemas) throws SAXException { - sf.newSchema(schemas); + @ParameterizedTest + @MethodSource("getNullSources") + public void testNewSchemaWithNullSourceArray(Source[] schemas) { + assertThrows(NullPointerException.class, () -> sf.newSchema(schemas)); } - @Test(expectedExceptions = NullPointerException.class) - public void testNewSchemaWithNullUrl() throws SAXException { - sf.newSchema((URL) null); + @Test + public void testNewSchemaWithNullUrl() { + assertThrows(NullPointerException.class, () -> sf.newSchema((URL) null)); } @@ -270,70 +280,66 @@ public void testErrorHandler() { ErrorHandler handler = new MyErrorHandler(); sf.setErrorHandler(handler); - assertSame(sf.getErrorHandler(), handler); + assertSame(handler, sf.getErrorHandler()); sf.setErrorHandler(null); assertNull(sf.getErrorHandler()); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testGetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetUnrecognizedProperty() { SchemaFactory sf = newSchemaFactory(); - sf.getProperty(UNRECOGNIZED_NAME); - + assertThrows(SAXNotRecognizedException.class, () -> sf.getProperty(UNRECOGNIZED_NAME)); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testSetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetUnrecognizedProperty() { SchemaFactory sf = newSchemaFactory(); - sf.setProperty(UNRECOGNIZED_NAME, "test"); + assertThrows(SAXNotRecognizedException.class, () -> sf.setProperty(UNRECOGNIZED_NAME, "test")); } - @Test(expectedExceptions = NullPointerException.class) - public void testGetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetNullProperty() { SchemaFactory sf = newSchemaFactory(); assertNotNull(sf); - sf.getProperty(null); - + assertThrows(NullPointerException.class, () -> sf.getProperty(null)); } - @Test(expectedExceptions = NullPointerException.class) - public void testSetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetNullProperty() { SchemaFactory sf = newSchemaFactory(); assertNotNull(sf); - sf.setProperty(null, "test"); + assertThrows(NullPointerException.class, () -> sf.setProperty(null, "test")); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testGetUnrecognizedFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetUnrecognizedFeature() { SchemaFactory sf = newSchemaFactory(); - sf.getFeature(UNRECOGNIZED_NAME); + assertThrows(SAXNotRecognizedException.class, () -> sf.getFeature(UNRECOGNIZED_NAME)); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testSetUnrecognizedFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetUnrecognizedFeature() { SchemaFactory sf = newSchemaFactory(); - sf.setFeature(UNRECOGNIZED_NAME, true); + assertThrows(SAXNotRecognizedException.class, () -> sf.setFeature(UNRECOGNIZED_NAME, true)); } - @Test(expectedExceptions = NullPointerException.class) - public void testGetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetNullFeature() { SchemaFactory sf = newSchemaFactory(); assertNotNull(sf); - sf.getFeature(null); - + assertThrows(NullPointerException.class, () -> sf.getFeature(null)); } - @Test(expectedExceptions = NullPointerException.class) - public void testSetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetNullFeature() { SchemaFactory sf = newSchemaFactory(); assertNotNull(sf); - sf.setFeature(null, true); + assertThrows(NullPointerException.class, () -> sf.setFeature(null, true)); } - @DataProvider(name = "source-feature") - public Object[][] getSourceFeature() { + public static Object[][] getSourceFeature() { return new Object[][] { { StreamSource.FEATURE }, { SAXSource.FEATURE }, @@ -346,7 +352,8 @@ public Object[][] getSourceFeature() { * Return true for each of the JAXP Source features to indicate that this * SchemaFactory supports all of the built-in JAXP Source types. */ - @Test(dataProvider = "source-feature") + @ParameterizedTest + @MethodSource("getSourceFeature") public void testSourceFeatureGet(String sourceFeature) throws Exception { assertTrue(newSchemaFactory().getFeature(sourceFeature)); } @@ -355,46 +362,52 @@ public void testSourceFeatureGet(String sourceFeature) throws Exception { * JAXP Source features are read-only because this SchemaFactory always * supports all JAXP Source types. */ - @Test(dataProvider = "source-feature", expectedExceptions = SAXNotSupportedException.class) - public void testSourceFeatureSet(String sourceFeature) throws Exception { - newSchemaFactory().setFeature(sourceFeature, false); + @ParameterizedTest + @MethodSource("getSourceFeature") + public void testSourceFeatureSet(String sourceFeature) { + assertThrows( + SAXNotSupportedException.class, + () -> newSchemaFactory().setFeature(sourceFeature, false)); } - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testInvalidSchemaLanguage() { final String INVALID_SCHEMA_LANGUAGE = "http://relaxng.org/ns/structure/1.0"; - SchemaFactory.newInstance(INVALID_SCHEMA_LANGUAGE); + assertThrows( + IllegalArgumentException.class, + () -> SchemaFactory.newInstance(INVALID_SCHEMA_LANGUAGE)); } - @Test(expectedExceptions = NullPointerException.class) + @Test public void testNullSchemaLanguage() { - SchemaFactory.newInstance(null); + assertThrows(NullPointerException.class, () -> SchemaFactory.newInstance(null)); } private void validate(Schema schema) throws SAXException, IOException { schema.newValidator().validate(new StreamSource(new ByteArrayInputStream(xml))); } - private InputStream newInputStream(byte[] xsd) { + + private static InputStream newInputStream(byte[] xsd) { return new ByteArrayInputStream(xsd); } - private Source streamSource(byte[] xsd) { + private static Source streamSource(byte[] xsd) { return new StreamSource(newInputStream(xsd)); } - private Source nullStreamSource() { + private static Source nullStreamSource() { return new StreamSource((InputStream) null); } - private Source saxSource(byte[] xsd) { + private static Source saxSource(byte[] xsd) { return new SAXSource(new InputSource(newInputStream(xsd))); } - private Source nullSaxSource() { + private static Source nullSaxSource() { return new SAXSource(new InputSource((InputStream) null)); } - private Source domSource(Document xsdDoc) { + private static Source domSource(Document xsdDoc) { return new DOMSource(xsdDoc); } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/TypeInfoProviderTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/TypeInfoProviderTest.java index b8c7a2d54e73..d4802728b5bb 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/TypeInfoProviderTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/TypeInfoProviderTest.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 @@ -22,15 +22,12 @@ */ package javax.xml.validation.ptests; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - -import java.io.File; -import java.io.IOException; +import org.junit.jupiter.api.Test; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; @@ -38,18 +35,20 @@ import javax.xml.validation.SchemaFactory; import javax.xml.validation.TypeInfoProvider; import javax.xml.validation.ValidatorHandler; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; -import org.testng.annotations.Test; -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.DefaultHandler; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.validation.ptests.TypeInfoProviderTest + * @run junit/othervm javax.xml.validation.ptests.TypeInfoProviderTest * @summary test ValidatorHandler.getTypeInfoProvider() */ public class TypeInfoProviderTest { @@ -65,7 +64,8 @@ public void test() throws SAXException, ParserConfigurationException, IOExceptio MyDefaultHandler myDefaultHandler = new MyDefaultHandler(); validatorHandler.setContentHandler(myDefaultHandler); - InputSource is = new InputSource(filenameToURL(XML_DIR + "shiporder11.xml")); + String xmlPathUri = Path.of(XML_DIR).resolve("shiporder11.xml").toUri().toASCIIString(); + InputSource is = new InputSource(xmlPathUri); SAXParserFactory parserFactory = SAXParserFactory.newInstance(); parserFactory.setNamespaceAware(true); @@ -77,13 +77,13 @@ public void test() throws SAXException, ParserConfigurationException, IOExceptio private class MyDefaultHandler extends DefaultHandler { - public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { + public void startElement(String namespaceURI, String localName, String qName, Attributes atts) { TypeInfoProvider typeInfoProvider = validatorHandler.getTypeInfoProvider(); int index = atts.getIndex("orderid"); if (index != -1) { System.out.println(" Index " + index); System.out.println(" ElementType " + typeInfoProvider.getElementTypeInfo().getTypeName()); - assertEquals(typeInfoProvider.getAttributeTypeInfo(index).getTypeName(), "string"); + assertEquals("string", typeInfoProvider.getAttributeTypeInfo(index).getTypeName()); assertTrue(typeInfoProvider.isSpecified(index)); assertFalse(typeInfoProvider.isIdAttribute(index)); } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorHandlerTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorHandlerTest.java index 2bbed60ba37c..b6c66070417c 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorHandlerTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorHandlerTest.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 @@ -22,37 +22,39 @@ */ package javax.xml.validation.ptests; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertTrue; - -import java.io.File; - -import javax.xml.validation.Schema; -import javax.xml.validation.SchemaFactory; -import javax.xml.validation.ValidatorHandler; - -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.xml.sax.ContentHandler; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.helpers.DefaultHandler; +import org.junit.jupiter.api.BeforeAll; + +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.ValidatorHandler; +import java.io.File; + +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.validation.ptests.ValidatorHandlerTest + * @run junit/othervm javax.xml.validation.ptests.ValidatorHandlerTest * @summary Class containing the test cases for ValidatorHandler API */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class ValidatorHandlerTest { - @BeforeClass + @BeforeAll public void setup() throws SAXException { schema = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI).newSchema(new File(XML_DIR + "test.xsd")); @@ -66,60 +68,57 @@ public void testErrorHandler() { ErrorHandler handler = new MyErrorHandler(); validatorHandler.setErrorHandler(handler); - assertSame(validatorHandler.getErrorHandler(), handler); - + assertSame(handler, validatorHandler.getErrorHandler()); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testGetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetUnrecognizedProperty() { ValidatorHandler validatorHandler = getValidatorHandler(); - validatorHandler.getProperty(FEATURE_NAME); - + assertThrows(SAXNotRecognizedException.class, () -> validatorHandler.getProperty(FEATURE_NAME)); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testSetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetUnrecognizedProperty() { ValidatorHandler validatorHandler = getValidatorHandler(); - validatorHandler.setProperty(FEATURE_NAME, "test"); + assertThrows(SAXNotRecognizedException.class, () -> validatorHandler.setProperty(FEATURE_NAME, "test")); } - @Test(expectedExceptions = NullPointerException.class) - public void testGetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetNullProperty() { ValidatorHandler validatorHandler = getValidatorHandler(); assertNotNull(validatorHandler); - validatorHandler.getProperty(null); + assertThrows(NullPointerException.class, () -> validatorHandler.getProperty(null)); } - @Test(expectedExceptions = NullPointerException.class) - public void testSetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetNullProperty() { ValidatorHandler validatorHandler = getValidatorHandler(); assertNotNull(validatorHandler); - validatorHandler.setProperty(null, "test"); + assertThrows(NullPointerException.class, () -> validatorHandler.setProperty(null, "test")); } + @Test public void testFeature() throws SAXNotRecognizedException, SAXNotSupportedException { ValidatorHandler validatorHandler = getValidatorHandler(); assertFalse(validatorHandler.getFeature(FEATURE_NAME), "The feature should be false by default."); validatorHandler.setFeature(FEATURE_NAME, true); assertTrue(validatorHandler.getFeature(FEATURE_NAME), "The feature should be false by default."); - } - @Test(expectedExceptions = NullPointerException.class) - public void testGetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetNullFeature() { ValidatorHandler validatorHandler = getValidatorHandler(); assertNotNull(validatorHandler); - validatorHandler.getFeature(null); - + assertThrows(NullPointerException.class, () -> validatorHandler.getFeature(null)); } - @Test(expectedExceptions = NullPointerException.class) - public void testSetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetNullFeature() { ValidatorHandler validatorHandler = getValidatorHandler(); assertNotNull(validatorHandler); - validatorHandler.setFeature(null, true); + assertThrows(NullPointerException.class, () -> validatorHandler.setFeature(null, true)); } @Test @@ -129,11 +128,10 @@ public void testContentHandler() { ContentHandler handler = new DefaultHandler(); validatorHandler.setContentHandler(handler); - assertSame(validatorHandler.getContentHandler(), handler); + assertSame(handler, validatorHandler.getContentHandler()); validatorHandler.setContentHandler(null); assertNull(validatorHandler.getContentHandler()); - } private ValidatorHandler getValidatorHandler() { diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorTest.java index 6dbd38a7fa1e..f2cbeb1ebeb3 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorTest.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 @@ -22,15 +22,18 @@ */ package javax.xml.validation.ptests; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertSame; - -import java.io.File; -import java.io.IOException; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -44,33 +47,33 @@ import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.xml.sax.ErrorHandler; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.SAXNotRecognizedException; -import org.xml.sax.SAXNotSupportedException; -import org.xml.sax.helpers.DefaultHandler; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.validation.ptests.ValidatorTest + * @run junit/othervm javax.xml.validation.ptests.ValidatorTest * @summary Class containing the test cases for Validator API */ +@TestInstance(Lifecycle.PER_CLASS) public class ValidatorTest { - @BeforeClass + @BeforeAll public void setup() throws SAXException, IOException, ParserConfigurationException { schema = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI).newSchema(new File(XML_DIR + "test.xsd")); assertNotNull(schema); - xmlFileUri = filenameToURL(XML_DIR + "test.xml"); + xmlFileUri = Paths.get(XML_DIR).resolve("test.xml").toUri().toASCIIString(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); @@ -84,11 +87,11 @@ public void testValidateStreamSource() throws SAXException, IOException { validator.validate(getStreamSource()); } - @Test(expectedExceptions = NullPointerException.class) - public void testValidateNullSource() throws SAXException, IOException { + @Test + public void testValidateNullSource() { Validator validator = getValidator(); assertNotNull(validator); - validator.validate(null); + assertThrows(NullPointerException.class, () -> validator.validate(null)); } @Test @@ -98,11 +101,10 @@ public void testErrorHandler() { ErrorHandler mh = new MyErrorHandler(); validator.setErrorHandler(mh); - assertSame(validator.getErrorHandler(), mh); + assertSame(mh, validator.getErrorHandler()); } - @DataProvider(name = "source-result") public Object[][] getSourceAndResult() { return new Object[][] { { getStreamSource(), null }, @@ -112,66 +114,65 @@ public Object[][] getSourceAndResult() { { getDOMSource(), null } }; } - @Test(dataProvider = "source-result") + @ParameterizedTest + @MethodSource("getSourceAndResult") public void testValidateWithResult(Source source, Result result) throws SAXException, IOException { Validator validator = getValidator(); validator.validate(source, result); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testGetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetUnrecognizedProperty() { Validator validator = getValidator(); - validator.getProperty(UNRECOGNIZED_NAME); + assertThrows(SAXNotRecognizedException.class, () -> validator.getProperty(UNRECOGNIZED_NAME)); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testSetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetUnrecognizedProperty() { Validator validator = getValidator(); - validator.setProperty(UNRECOGNIZED_NAME, "test"); + assertThrows(SAXNotRecognizedException.class, () -> validator.setProperty(UNRECOGNIZED_NAME, "test")); } - @Test(expectedExceptions = NullPointerException.class) - public void testGetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetNullProperty() { Validator validator = getValidator(); assertNotNull(validator); - validator.getProperty(null); - + assertThrows(NullPointerException.class, () -> validator.getProperty(null)); } - @Test(expectedExceptions = NullPointerException.class) - public void testSetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetNullProperty() { Validator validator = getValidator(); assertNotNull(validator); - validator.setProperty(null, "test"); + assertThrows(NullPointerException.class, () -> validator.setProperty(null, "test")); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testGetUnrecognizedFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetUnrecognizedFeature() { Validator validator = getValidator(); - validator.getFeature(UNRECOGNIZED_NAME); + assertThrows(SAXNotRecognizedException.class, () -> validator.getFeature(UNRECOGNIZED_NAME)); } - @Test(expectedExceptions = SAXNotRecognizedException.class) - public void testSetUnrecognizedFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetUnrecognizedFeature() { Validator validator = getValidator(); - validator.setFeature(UNRECOGNIZED_NAME, true); + assertThrows(SAXNotRecognizedException.class, () -> validator.setFeature(UNRECOGNIZED_NAME, true)); } - @Test(expectedExceptions = NullPointerException.class) - public void testGetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testGetNullFeature() { Validator validator = getValidator(); assertNotNull(validator); - validator.getFeature(null); - + assertThrows(NullPointerException.class, () -> validator.getFeature(null)); } - @Test(expectedExceptions = NullPointerException.class) - public void testSetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + @Test + public void testSetNullFeature() { Validator validator = getValidator(); assertNotNull(validator); - validator.setFeature(null, true); + assertThrows(NullPointerException.class, () -> validator.setFeature(null, true)); } private Validator getValidator() { @@ -204,5 +205,4 @@ private Result getDOMResult() { private String xmlFileUri; private Schema schema; private Document xmlDoc; - } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathEvaluationResultTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathEvaluationResultTest.java index 5daf8adcfa60..81a9fdfa0996 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathEvaluationResultTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathEvaluationResultTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, 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 @@ -23,34 +23,32 @@ package javax.xml.xpath.ptests; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.Node; import javax.xml.namespace.QName; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathEvaluationResult; import javax.xml.xpath.XPathNodes; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; /* * @test * @bug 8183266 * @summary verifies the specification of the XPathEvaluationResult API * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.xpath.ptests.XPathEvaluationResultTest + * @run junit/othervm javax.xml.xpath.ptests.XPathEvaluationResultTest */ public class XPathEvaluationResultTest { /* * Test getQNameType returns QName type for supported types and Number subtypes */ - @Test(dataProvider = "supportedTypes") + @ParameterizedTest + @MethodSource("getSupportedTypes") public void testQNameTypeSupportedTypes(QName expectedQName, Class type) { QName qName = XPathEvaluationResult.XPathResultType.getQNameType(type); assertNotNull(qName); @@ -60,53 +58,16 @@ public void testQNameTypeSupportedTypes(QName expectedQName, Class type) { /* * Test getQNameType returns null when type is not supported */ - @Test(dataProvider = "unsupportedTypes") - public void testQNameTypeUnsupportedTypes(Class type) { - QName qName = XPathEvaluationResult.XPathResultType.getQNameType(type); - assertNull(qName); - } - - /* - * Test getQNameType is null safe - */ - @Test - public void testQNameTypeNullType() { - QName qName = XPathEvaluationResult.XPathResultType.getQNameType(null); - assertNull(qName); - } - - /* - * DataProvider: Class types supported - */ - @DataProvider(name = "supportedTypes") - public Object[][] getSupportedTypes() { - return new Object[][]{ - {XPathConstants.STRING, String.class}, - {XPathConstants.BOOLEAN, Boolean.class}, - {XPathConstants.NODESET, XPathNodes.class}, - {XPathConstants.NODE, Node.class}, - {XPathConstants.NUMBER, Long.class}, - {XPathConstants.NUMBER, Integer.class}, - {XPathConstants.NUMBER, Double.class}, - {XPathConstants.NUMBER, Number.class} - }; - } - - /* - * DataProvider: Class types not supported - */ - @DataProvider(name = "unsupportedTypes") - public Object[][] getUnsupportedTypes() { - return new Object[][]{ - new Object[]{AtomicInteger.class}, - new Object[]{AtomicLong.class}, - new Object[]{BigDecimal.class}, - new Object[]{BigInteger.class}, - new Object[]{Byte.class}, - new Object[]{Float.class}, - new Object[]{Short.class}, - new Object[]{Character.class}, - new Object[]{StringBuilder.class}, + public static Object[][] getSupportedTypes() { + return new Object[][] { + { XPathConstants.STRING, String.class }, + { XPathConstants.BOOLEAN, Boolean.class }, + { XPathConstants.NODESET, XPathNodes.class }, + { XPathConstants.NODE, Node.class }, + { XPathConstants.NUMBER, Long.class }, + { XPathConstants.NUMBER, Integer.class }, + { XPathConstants.NUMBER, Double.class }, + { XPathConstants.NUMBER, Number.class } }; } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathExpressionTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathExpressionTest.java index c63b161aae9f..bdd21937504e 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathExpressionTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathExpressionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,32 +23,33 @@ package javax.xml.xpath.ptests; -import static javax.xml.xpath.XPathConstants.BOOLEAN; -import static javax.xml.xpath.XPathConstants.NODE; -import static javax.xml.xpath.XPathConstants.NODESET; -import static javax.xml.xpath.XPathConstants.NUMBER; -import static javax.xml.xpath.XPathConstants.STRING; -import static javax.xml.xpath.ptests.XPathTestConst.XML_DIR; -import static org.testng.Assert.assertEquals; - -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; import javax.xml.XMLConstants; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; +import static javax.xml.xpath.XPathConstants.BOOLEAN; +import static javax.xml.xpath.XPathConstants.NODE; +import static javax.xml.xpath.XPathConstants.NODESET; +import static javax.xml.xpath.XPathConstants.NUMBER; +import static javax.xml.xpath.XPathConstants.STRING; +import static javax.xml.xpath.ptests.XPathTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Class containing the test cases for XPathExpression API. @@ -56,7 +57,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.xpath.ptests.XPathExpressionTest + * @run junit/othervm javax.xml.xpath.ptests.XPathExpressionTest */ public class XPathExpressionTest { /** @@ -77,7 +78,7 @@ public class XPathExpressionTest { /** * XML File Path. */ - private static final Path XML_PATH = Paths.get(XML_DIR + "widgets.xml"); + private static final Path XML_PATH = XML_DIR.resolve("widgets.xml"); /** * An expression name which locate at "/widgets/widget[@name='a']/@quantity" @@ -93,7 +94,7 @@ public class XPathExpressionTest { * Create Document object and XPath object for every time * @throws Exception If any errors occur. */ - @BeforeTest + @BeforeEach public void setup() throws Exception { document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(XML_PATH.toFile()); xpath = XPathFactory.newInstance().newXPath(); @@ -102,303 +103,190 @@ public void setup() throws Exception { /** * Test for evaluate(java.lang.Object item,QName returnType)throws * XPathExpressionException. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression01() throws XPathExpressionException { - assertEquals(xpath.compile(EXPRESSION_NAME_A). - evaluate(document, STRING), "6"); + assertEquals("6", xpath.compile(EXPRESSION_NAME_A). + evaluate(document, STRING)); } /** * evaluate(java.lang.Object item,QName returnType) throws NPE if input * source is null. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPathExpression02() throws XPathExpressionException { - xpath.compile(EXPRESSION_NAME_A).evaluate(null, STRING); + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + assertThrows(NullPointerException.class, () -> expr.evaluate(null, STRING)); } /** * evaluate(java.lang.Object item,QName returnType) throws NPE if returnType * is null. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPathExpression03() throws XPathExpressionException { - xpath.compile(EXPRESSION_NAME_A).evaluate(document, null); + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + assertThrows(NullPointerException.class, () -> expr.evaluate(document, null)); } /** * Test for method evaluate(java.lang.Object item,QName returnType).If a * request is made to evaluate the expression in the absence of a context * item, simple expressions, such as "1+1", can be evaluated. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression04() throws XPathExpressionException { - assertEquals(xpath.compile("1+1").evaluate(document, STRING), "2"); + assertEquals("2", xpath.compile("1+1").evaluate(document, STRING)); } /** * evaluate(java.lang.Object item,QName returnType) throws IAE If returnType * is not one of the types defined in XPathConstants. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testCheckXPathExpression05() throws XPathExpressionException { - xpath.compile(EXPRESSION_NAME_A).evaluate(document, TEST_QNAME); + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + assertThrows(IllegalArgumentException.class, () -> expr.evaluate(document, TEST_QNAME)); } /** * evaluate(java.lang.Object item,QName returnType) return correct boolean * value if returnType is Boolean. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression06() throws XPathExpressionException { - assertEquals(xpath.compile(EXPRESSION_NAME_A). - evaluate(document, BOOLEAN), true); + assertEquals(true, xpath.compile(EXPRESSION_NAME_A). + evaluate(document, BOOLEAN)); } /** * evaluate(java.lang.Object item,QName returnType) return correct boolean * value if returnType is Boolean. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression07() throws XPathExpressionException { - assertEquals(xpath.compile(EXPRESSION_NAME_B). - evaluate(document, BOOLEAN), false); + assertEquals(false, xpath.compile(EXPRESSION_NAME_B). + evaluate(document, BOOLEAN)); } /** * evaluate(java.lang.Object item,QName returnType) return correct number * value when return type is Double. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression08() throws XPathExpressionException { - assertEquals(xpath.compile(EXPRESSION_NAME_A). - evaluate(document, NUMBER), 6d); + assertEquals(6d, xpath.compile(EXPRESSION_NAME_A). + evaluate(document, NUMBER)); } /** * evaluate(java.lang.Object item,QName returnType) evaluate an attribute * value which returnType is Node. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression09() throws XPathExpressionException { Attr attr = (Attr) xpath.compile(EXPRESSION_NAME_A). evaluate(document, NODE); - assertEquals(attr.getValue(), "6"); + assertEquals("6", attr.getValue()); } /** * evaluate(java.lang.Object item,QName returnType) evaluate an attribute * value which returnType is NodeList. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression10() throws XPathExpressionException { NodeList nodeList = (NodeList) xpath.compile(EXPRESSION_NAME_A). evaluate(document, NODESET); Attr attr = (Attr) nodeList.item(0); - assertEquals(attr.getValue(), "6"); + assertEquals("6", attr.getValue()); } /** * Test for evaluate(java.lang.Object item) when returnType is left off of * the XPath.evaluate method, all expressions are evaluated to a String * value. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression11() throws XPathExpressionException { - assertEquals(xpath.compile(EXPRESSION_NAME_A).evaluate(document), "6"); - } - - /** - * evaluate(java.lang.Object item) throws NPE if expression is null. - * - * @throws XPathExpressionException If the expression cannot be evaluated. - */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPathExpression12() throws XPathExpressionException { - xpath.compile(null).evaluate(document); + assertEquals("6", xpath.compile(EXPRESSION_NAME_A).evaluate(document)); } /** * evaluate(java.lang.Object item) when a request is made to evaluate the * expression in the absence of a context item, simple expressions, such as * "1+1", can be evaluated. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ @Test public void testCheckXPathExpression13() throws XPathExpressionException { - assertEquals(xpath.compile("1+1").evaluate(document), "2"); + assertEquals("2", xpath.compile("1+1").evaluate(document)); } /** * evaluate(java.lang.Object item) throws NPE if document is null. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPathExpression14() throws XPathExpressionException { - xpath.compile(EXPRESSION_NAME_A).evaluate(null); + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + assertThrows(NullPointerException.class, () -> expr.evaluate(null)); } /** * valuate(InputSource source) return a string value if return type is * String. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPathExpression15() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.compile(EXPRESSION_NAME_A). - evaluate(new InputSource(is)), "6"); + assertEquals("6", xpath.compile(EXPRESSION_NAME_A). + evaluate(new InputSource(is))); } } /** * evaluate(InputSource source) throws NPE if input source is null. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPathExpression16() throws XPathExpressionException { - xpath.compile(EXPRESSION_NAME_A).evaluate(null); - } - - /** - * evaluate(InputSource source) throws NPE if expression is null. - * - * @throws Exception If any errors occur. - */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPathExpression17() throws Exception { - try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile(null).evaluate(new InputSource(is)); - } - } - - /** - * evaluate(InputSource source) throws XPathExpressionException if - * returnType is String junk characters. - * - * @throws Exception If any errors occur. - */ - @Test(expectedExceptions = XPathExpressionException.class) - public void testCheckXPathExpression18() throws Exception { - try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile("-*&").evaluate(new InputSource(is)); - } - } - - /** - * evaluate(InputSource source) throws XPathExpressionException if - * expression is a blank string " ". - * - * @throws Exception If any errors occur. - */ - @Test(expectedExceptions = XPathExpressionException.class) - public void testCheckXPathExpression19() throws Exception { - try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile(" ").evaluate(new InputSource(is)); - } + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + assertThrows(NullPointerException.class, () -> expr.evaluate(null)); } /** * Test for evaluate(InputSource source,QName returnType) returns a string * value if returnType is String. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPathExpression20() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.compile(EXPRESSION_NAME_A). - evaluate(new InputSource(is), STRING), "6"); + assertEquals("6", xpath.compile(EXPRESSION_NAME_A). + evaluate(new InputSource(is), STRING)); } } /** * evaluate(InputSource source,QName returnType) throws NPE if source is * null. - * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPathExpression21() throws XPathExpressionException { - xpath.compile(EXPRESSION_NAME_A).evaluate(null, STRING); - } - - /** - * evaluate(InputSource source,QName returnType) throws NPE if expression is - * null. - * - * @throws Exception If any errors occur. - */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPathExpression22() throws Exception { - try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile(null).evaluate(new InputSource(is), STRING); - } + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + assertThrows(NullPointerException.class, () -> expr.evaluate(null, STRING)); } /** * evaluate(InputSource source,QName returnType) throws NPE if returnType is * null. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPathExpression23() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile(EXPRESSION_NAME_A).evaluate(new InputSource(is), null); - } - } - - /** - * evaluate(InputSource source,QName returnType) throws - * XPathExpressionException if expression is junk characters. - * - * @throws Exception If any errors occur. - */ - @Test(expectedExceptions = XPathExpressionException.class) - public void testCheckXPathExpression24() throws Exception { - try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile("-*&").evaluate(new InputSource(is), STRING); - } - } - - /** - * evaluate(InputSource source,QName returnType) throws - * XPathExpressionException if expression is blank " ". - * - * @throws Exception If any errors occur. - */ - @Test(expectedExceptions = XPathExpressionException.class) - public void testCheckXPathExpression25() throws Exception { - try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile(" ").evaluate(new InputSource(is), STRING); + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + InputSource source = new InputSource(is); + assertThrows( + NullPointerException.class, + () -> expr.evaluate(source, null)); } } @@ -406,85 +294,75 @@ public void testCheckXPathExpression25() throws Exception { * evaluate(InputSource source,QName returnType) throws * IllegalArgumentException if returnType is not one of the types defined * in XPathConstants. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testCheckXPathExpression26() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.compile(EXPRESSION_NAME_A).evaluate(new InputSource(is), TEST_QNAME); + XPathExpression expr = xpath.compile(EXPRESSION_NAME_A); + InputSource source = new InputSource(is); + assertThrows(IllegalArgumentException.class, () -> expr.evaluate(source, TEST_QNAME)); } } /** * evaluate(InputSource source,QName returnType) return a correct boolean * value if returnType is Boolean. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPathExpression27() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.compile(EXPRESSION_NAME_A). - evaluate(new InputSource(is), BOOLEAN), true); + assertEquals(true, xpath.compile(EXPRESSION_NAME_A). + evaluate(new InputSource(is), BOOLEAN)); } } /** * evaluate(InputSource source,QName returnType) return a correct boolean * value if returnType is Boolean. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPathExpression28() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.compile(EXPRESSION_NAME_B). - evaluate(new InputSource(is), BOOLEAN), false); + assertEquals(false, xpath.compile(EXPRESSION_NAME_B). + evaluate(new InputSource(is), BOOLEAN)); } } /** * evaluate(InputSource source,QName returnType) return a correct number * value if returnType is Number. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPathExpression29() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.compile(EXPRESSION_NAME_A). - evaluate(new InputSource(is), NUMBER), 6d); + assertEquals(6d, xpath.compile(EXPRESSION_NAME_A). + evaluate(new InputSource(is), NUMBER)); } } /** * Test for evaluate(InputSource source,QName returnType) returns a node if * returnType is Node. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPathExpression30() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { Attr attr = (Attr) xpath.compile(EXPRESSION_NAME_A). - evaluate(new InputSource(is), NODE); - assertEquals(attr.getValue(), "6"); + evaluate(new InputSource(is), NODE); + assertEquals("6", attr.getValue()); } } /** * Test for evaluate(InputSource source,QName returnType) return a node list * if returnType is NodeList. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPathExpression31() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { NodeList nodeList = (NodeList) xpath.compile(EXPRESSION_NAME_A). - evaluate(new InputSource(is), NODESET); - assertEquals(((Attr) nodeList.item(0)).getValue(), "6"); + evaluate(new InputSource(is), NODESET); + assertEquals("6", ((Attr) nodeList.item(0)).getValue()); } } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFactoryTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFactoryTest.java index 561c6923edad..3c276f249086 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFactoryTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,23 +23,24 @@ package javax.xml.xpath.ptests; -import static javax.xml.xpath.XPathConstants.DOM_OBJECT_MODEL; -import static javax.xml.xpath.XPathFactory.DEFAULT_OBJECT_MODEL_URI; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertFalse; +import jaxp.library.JAXPDataProvider; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathFactoryConfigurationException; -import jaxp.library.JAXPDataProvider; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static javax.xml.xpath.XPathConstants.DOM_OBJECT_MODEL; +import static javax.xml.xpath.XPathFactory.DEFAULT_OBJECT_MODEL_URI; +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.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Class containing the test cases for XPathFactory API. @@ -48,7 +49,8 @@ * @test * @bug 8169778 * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.xpath.ptests.XPathFactoryTest + * @build jaxp.library.JAXPDataProvider + * @run junit/othervm javax.xml.xpath.ptests.XPathFactoryTest */ public class XPathFactoryTest { /** @@ -78,9 +80,11 @@ public class XPathFactoryTest { * * @return a data provider contains XPathFactory instantiation parameters. */ - @DataProvider(name = "parameters") - public Object[][] getValidateParameters() { - return new Object[][] { { VALID_URL, XPATH_FACTORY_CLASSNAME, null }, { VALID_URL, XPATH_FACTORY_CLASSNAME, this.getClass().getClassLoader() } }; + public static Object[][] getValidateParameters() { + return new Object[][] { + { VALID_URL, XPATH_FACTORY_CLASSNAME, null }, + { VALID_URL, XPATH_FACTORY_CLASSNAME, XPathFactoryTest.class.getClassLoader() }, + }; } /** @@ -94,8 +98,8 @@ public void testDefaultInstance() throws Exception { XPathFactory xpf2 = XPathFactory.newInstance(DEFAULT_OBJECT_MODEL_URI); assertNotSame(xpf1, xpf2, "same instance returned:"); assertSame(xpf1.getClass(), xpf2.getClass(), - "unexpected class mismatch for newDefaultInstance():"); - assertEquals(xpf1.getClass().getName(), DEFAULT_IMPL_CLASS); + "unexpected class mismatch for newDefaultInstance():"); + assertEquals(DEFAULT_IMPL_CLASS, xpf1.getClass().getName()); assertTrue(xpf1.isObjectModelSupported(DEFAULT_OBJECT_MODEL_URI), "isObjectModelSupported(DEFAULT_OBJECT_MODEL_URI):"); assertFalse(xpf1.isObjectModelSupported(INVALID_URL), @@ -107,13 +111,9 @@ public void testDefaultInstance() throws Exception { * factoryClassName, java.lang.ClassLoader classLoader) factoryClassName * points to correct implementation of javax.xml.xpath.XPathFactory , should * return newInstance of XPathFactory - * - * @param uri - * @param factoryClassName - * @param classLoader - * @throws XPathFactoryConfigurationException */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("getValidateParameters") public void testNewInstance(String uri, String factoryClassName, ClassLoader classLoader) throws XPathFactoryConfigurationException { XPathFactory xpf = XPathFactory.newInstance(uri, factoryClassName, classLoader); XPath xpath = xpf.newXPath(); @@ -123,39 +123,34 @@ public void testNewInstance(String uri, String factoryClassName, ClassLoader cla /** * Test for XPathFactory.newInstance(java.lang.String uri, java.lang.String * factoryClassName, java.lang.ClassLoader classLoader) - * - * @param factoryClassName - * @param classLoader - * @throws XPathFactoryConfigurationException - * is expected when factoryClassName is null - */ - @Test(expectedExceptions = XPathFactoryConfigurationException.class, dataProvider = "new-instance-neg", dataProviderClass = JAXPDataProvider.class) - public void testNewInstanceWithNullFactoryClassName(String factoryClassName, ClassLoader classLoader) throws XPathFactoryConfigurationException { - XPathFactory.newInstance(VALID_URL, factoryClassName, classLoader); + */ + @ParameterizedTest + @MethodSource("jaxp.library.JAXPDataProvider#newInstanceNeg") + public void testNewInstanceWithNullFactoryClassName(String factoryClassName, ClassLoader classLoader) { + assertThrows( + XPathFactoryConfigurationException.class, + () -> XPathFactory.newInstance(VALID_URL, factoryClassName, classLoader)); } /** * Test for XPathFactory.newInstance(java.lang.String uri, java.lang.String * factoryClassName, java.lang.ClassLoader classLoader) uri is null , should * throw NPE - * - * @throws XPathFactoryConfigurationException */ - @Test(expectedExceptions = NullPointerException.class) - public void testNewInstanceWithNullUri() throws XPathFactoryConfigurationException { - XPathFactory.newInstance(null, XPATH_FACTORY_CLASSNAME, this.getClass().getClassLoader()); + @Test + public void testNewInstanceWithNullUri() { + assertThrows( + NullPointerException.class, + () -> XPathFactory.newInstance(null, XPATH_FACTORY_CLASSNAME, this.getClass().getClassLoader())); } /** * Test for XPathFactory.newInstance(java.lang.String uri, java.lang.String * factoryClassName, java.lang.ClassLoader classLoader) - * - * @throws IllegalArgumentException - * is expected when uri is empty */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testNewInstanceWithEmptyUri() throws XPathFactoryConfigurationException { - XPathFactory.newInstance("", XPATH_FACTORY_CLASSNAME, this.getClass().getClassLoader()); + @Test + public void testNewInstanceWithEmptyUri() { + assertThrows(IllegalArgumentException.class, () -> XPathFactory.newInstance("", XPATH_FACTORY_CLASSNAME, this.getClass().getClassLoader())); } /** @@ -169,24 +164,20 @@ public void testCheckXPathFactory01() { /** * XPathFactory.newInstance(String uri) throws NPE if uri is null. * - * @throws XPathFactoryConfigurationException If the specified object model - * is unavailable, or if there is a configuration error. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPathFactory02() throws XPathFactoryConfigurationException { - XPathFactory.newInstance(null); + @Test + public void testCheckXPathFactory02() { + assertThrows(NullPointerException.class, () -> XPathFactory.newInstance(null)); } /** * XPathFactory.newInstance(String uri) throws XPFCE if uri is just a blank * string. * - * @throws XPathFactoryConfigurationException If the specified object model - * is unavailable, or if there is a configuration error. */ - @Test(expectedExceptions = XPathFactoryConfigurationException.class) - public void testCheckXPathFactory03() throws XPathFactoryConfigurationException { - XPathFactory.newInstance(" "); + @Test + public void testCheckXPathFactory03() { + assertThrows(XPathFactoryConfigurationException.class, () -> XPathFactory.newInstance(" ")); } /** @@ -205,12 +196,10 @@ public void testCheckXPathFactory04() throws XPathFactoryConfigurationException * Test for constructor - XPathFactory.newInstance(String uri) with invalid * url - "http://java.sun.com/jaxp/xpath/dom1". * - * @throws XPathFactoryConfigurationException If the specified object model - * is unavailable, or if there is a configuration error. */ - @Test(expectedExceptions = XPathFactoryConfigurationException.class) - public void testCheckXPathFactory05() throws XPathFactoryConfigurationException { - XPathFactory.newInstance(INVALID_URL); + @Test + public void testCheckXPathFactory05() { + assertThrows(XPathFactoryConfigurationException.class, () -> XPathFactory.newInstance(INVALID_URL)); } /** diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFunctionResolverTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFunctionResolverTest.java index 07d439e5aa8b..f74540d8f688 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFunctionResolverTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathFunctionResolverTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,14 +23,15 @@ package javax.xml.xpath.ptests; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Class containing the test cases for XPathFunctionResolver. @@ -38,7 +39,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.xpath.ptests.XPathFunctionResolverTest + * @run junit/othervm javax.xml.xpath.ptests.XPathFunctionResolverTest */ public class XPathFunctionResolverTest { /** @@ -50,7 +51,7 @@ public class XPathFunctionResolverTest { * Create XPath object before every test. Make sure function resolver has * been set for XPath object. */ - @BeforeTest + @BeforeEach public void setup() { xpath = XPathFactory.newInstance().newXPath(); if (xpath.getXPathFunctionResolver() == null) { @@ -65,17 +66,16 @@ public void setup() { */ @Test public void testCheckXPathFunctionResolver01() throws XPathExpressionException { - assertEquals(xpath.evaluate("round(1.7)", (Object)null), "2"); + assertEquals("2", xpath.evaluate("round(1.7)", (Object) null)); } /** * Test for resolveFunction(QName functionName,int arity); evaluate throws * NPE if functionName is null. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPathFunctionResolver02() throws XPathExpressionException { - assertEquals(xpath.evaluate(null, "5"), "2"); + @Test + public void testCheckXPathFunctionResolver02() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(null, "5")); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathTest.java b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathTest.java index e2eb0d6a03a1..7bbfaae6b760 100644 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/javax/xml/xpath/ptests/XPathTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,21 +23,12 @@ package javax.xml.xpath.ptests; -import static javax.xml.xpath.XPathConstants.BOOLEAN; -import static javax.xml.xpath.XPathConstants.NODE; -import static javax.xml.xpath.XPathConstants.NODESET; -import static javax.xml.xpath.XPathConstants.NUMBER; -import static javax.xml.xpath.XPathConstants.STRING; -import static javax.xml.xpath.ptests.XPathTestConst.XML_DIR; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertNull; - -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Iterator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; @@ -46,13 +37,21 @@ import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Iterator; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; +import static javax.xml.xpath.XPathConstants.BOOLEAN; +import static javax.xml.xpath.XPathConstants.NODE; +import static javax.xml.xpath.XPathConstants.NODESET; +import static javax.xml.xpath.XPathConstants.NUMBER; +import static javax.xml.xpath.XPathConstants.STRING; +import static javax.xml.xpath.ptests.XPathTestConst.XML_DIR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Class containing the test cases for XPath API. @@ -60,7 +59,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm javax.xml.xpath.ptests.XPathTest + * @run junit/othervm javax.xml.xpath.ptests.XPathTest */ public class XPathTest { /** @@ -81,7 +80,7 @@ public class XPathTest { /** * XML File Path. */ - private static final Path XML_PATH = Paths.get(XML_DIR + "widgets.xml"); + private static final Path XML_PATH = XML_DIR.resolve("widgets.xml"); /** * An expression name which locate at "/widgets/widget[@name='a']/@quantity" @@ -97,7 +96,7 @@ public class XPathTest { * Create Document object and XPath object for every time * @throws Exception If any errors occur. */ - @BeforeTest + @BeforeEach public void setup() throws Exception { document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(XML_PATH.toFile()); xpath = XPathFactory.newInstance().newXPath(); @@ -111,7 +110,7 @@ public void setup() throws Exception { */ @Test public void testCheckXPath01() throws XPathExpressionException { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, document, STRING), "6"); + assertEquals("6", xpath.evaluate(EXPRESSION_NAME_A, document, STRING)); } @@ -123,7 +122,7 @@ public void testCheckXPath01() throws XPathExpressionException { */ @Test public void testCheckXPath02() throws XPathExpressionException { - assertEquals(xpath.compile(EXPRESSION_NAME_A).evaluate(document, STRING), "6"); + assertEquals("6", xpath.compile(EXPRESSION_NAME_A).evaluate(document, STRING)); } /** @@ -135,40 +134,37 @@ public void testCheckXPath02() throws XPathExpressionException { */ @Test public void testCheckXPath03() throws XPathExpressionException { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, document), "6"); + assertEquals("6", xpath.evaluate(EXPRESSION_NAME_A, document)); } /** * Test for XPath.compile(java.lang.String expression). If expression is * null, should throw NPE. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath04() throws XPathExpressionException { - xpath.compile(null); + @Test + public void testCheckXPath04() { + assertThrows(NullPointerException.class, () -> xpath.compile(null)); } /** * Test for XPath.compile(java.lang.String expression). If expression cannot * be compiled junk characters, should throw XPathExpressionException. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = XPathExpressionException.class) - public void testCheckXPath05() throws XPathExpressionException { - xpath.compile("-*&"); + @Test + public void testCheckXPath05() { + assertThrows(XPathExpressionException.class, () -> xpath.compile("-*&")); } /** * Test for XPath.compile(java.lang.String expression). If expression is * blank, should throw XPathExpressionException * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = XPathExpressionException.class) - public void testCheckXPath06() throws XPathExpressionException { - xpath.compile(" "); + @Test + public void testCheckXPath06() { + assertThrows(XPathExpressionException.class, () -> xpath.compile(" ")); } /** @@ -179,7 +175,7 @@ public void testCheckXPath06() throws XPathExpressionException { */ @Test public void testCheckXPath07() throws XPathExpressionException { - assertEquals(xpath.compile(EXPRESSION_NAME_B).evaluate(document, STRING), ""); + assertEquals("", xpath.compile(EXPRESSION_NAME_B).evaluate(document, STRING)); } @@ -187,33 +183,30 @@ public void testCheckXPath07() throws XPathExpressionException { * Test for XPath.evaluate(java.lang.String expression, java.lang.Object * item, QName returnType). If String expression is null, should throw NPE. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath08() throws XPathExpressionException { - xpath.evaluate(null, document, STRING); + @Test + public void testCheckXPath08() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(null, document, STRING)); } /** * Test for XPath.evaluate(java.lang.String expression, java.lang.Object * item, QName returnType). If item is null, should throw NPE. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath09() throws XPathExpressionException { - xpath.evaluate(EXPRESSION_NAME_A, null, STRING); + @Test + public void testCheckXPath09() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, null, STRING)); } /** * Test for XPath.evaluate(java.lang.String expression, java.lang.Object * item, QName returnType). If returnType is null, should throw NPE. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath10() throws XPathExpressionException { - xpath.evaluate(EXPRESSION_NAME_A, document, null); + @Test + public void testCheckXPath10() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, document, null)); } /** @@ -226,7 +219,7 @@ public void testCheckXPath10() throws XPathExpressionException { */ @Test public void testCheckXPath11() throws XPathExpressionException { - assertEquals(xpath.evaluate("1+1", document, STRING), "2"); + assertEquals("2", xpath.evaluate("1+1", document, STRING)); } /** @@ -234,11 +227,10 @@ public void testCheckXPath11() throws XPathExpressionException { * returnType) throws XPathExpressionException if expression is a empty * string "". * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = XPathExpressionException.class) - public void testCheckXPath12() throws XPathExpressionException { - xpath.evaluate("", document, STRING); + @Test + public void testCheckXPath12() { + assertThrows(XPathExpressionException.class, () -> xpath.evaluate("", document, STRING)); } /** @@ -246,11 +238,10 @@ public void testCheckXPath12() throws XPathExpressionException { * returnType) throws IllegalArgumentException if returnType is not one of * the types defined in XPathConstants. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testCheckXPath13() throws XPathExpressionException { - xpath.evaluate(EXPRESSION_NAME_A, document, TEST_QNAME); + @Test + public void testCheckXPath13() { + assertThrows(IllegalArgumentException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, document, TEST_QNAME)); } /** @@ -261,7 +252,7 @@ public void testCheckXPath13() throws XPathExpressionException { */ @Test public void testCheckXPath14() throws XPathExpressionException { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, document, BOOLEAN), true); + assertEquals(true, xpath.evaluate(EXPRESSION_NAME_A, document, BOOLEAN)); } /** @@ -273,7 +264,7 @@ public void testCheckXPath14() throws XPathExpressionException { */ @Test public void testCheckXPath15() throws XPathExpressionException { - assertEquals(xpath.evaluate(EXPRESSION_NAME_B, document, BOOLEAN), false); + assertEquals(false, xpath.evaluate(EXPRESSION_NAME_B, document, BOOLEAN)); } /** @@ -284,7 +275,7 @@ public void testCheckXPath15() throws XPathExpressionException { */ @Test public void testCheckXPath16() throws XPathExpressionException { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, document, NUMBER), 6d); + assertEquals(6d, xpath.evaluate(EXPRESSION_NAME_A, document, NUMBER)); } @@ -296,7 +287,7 @@ public void testCheckXPath16() throws XPathExpressionException { */ @Test public void testCheckXPath17() throws XPathExpressionException { - assertEquals(((Attr)xpath.evaluate(EXPRESSION_NAME_A, document, NODE)).getValue(), "6"); + assertEquals("6", ((Attr) xpath.evaluate(EXPRESSION_NAME_A, document, NODE)).getValue()); } /** @@ -308,19 +299,18 @@ public void testCheckXPath17() throws XPathExpressionException { */ @Test public void testCheckXPath18() throws XPathExpressionException { - NodeList nodeList = (NodeList)xpath.evaluate(EXPRESSION_NAME_A, document, NODESET); - assertEquals(((Attr) nodeList.item(0)).getValue(), "6"); + NodeList nodeList = (NodeList) xpath.evaluate(EXPRESSION_NAME_A, document, NODESET); + assertEquals("6", ((Attr) nodeList.item(0)).getValue()); } /** * Test for XPath.evaluate(java.lang.String expression, java.lang.Object * item). If expression is null, should throw NPE. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath19() throws XPathExpressionException { - xpath.evaluate(null, document); + @Test + public void testCheckXPath19() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(null, document)); } /** @@ -332,30 +322,27 @@ public void testCheckXPath19() throws XPathExpressionException { */ @Test public void testCheckXPath20() throws XPathExpressionException { - assertEquals(xpath.evaluate("1+1", document), "2"); + assertEquals("2", xpath.evaluate("1+1", document)); } /** * XPath.evaluate(java.lang.String expression, java.lang.Object item) throws * NPE if InputSource is null. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath21() throws XPathExpressionException { - xpath.evaluate(EXPRESSION_NAME_A, null); + @Test + public void testCheckXPath21() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, null)); } /** * XPath.evaluate(java.lang.String expression, InputSource source) return * correct value by looking for Node. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath22() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is)), "6"); + assertEquals("6", xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is))); } } @@ -363,23 +350,20 @@ public void testCheckXPath22() throws Exception { * XPath.evaluate(java.lang.String expression, InputSource source) throws * NPE if InputSource is null. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath23() throws XPathExpressionException { - xpath.evaluate(EXPRESSION_NAME_A, null); + @Test + public void testCheckXPath23() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, null)); } /** * XPath.evaluate(java.lang.String expression, InputSource source) throws * NPE if String expression is null. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPath24() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate(null, new InputSource(is)); + assertThrows(NullPointerException.class, () -> xpath.evaluate(null, new InputSource(is))); } } @@ -387,39 +371,33 @@ public void testCheckXPath24() throws Exception { * Test for XPath.evaluate(java.lang.String expression, InputSource source). * If expression is junk characters, expression cannot be evaluated, should * throw XPathExpressionException. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = XPathExpressionException.class) + @Test public void testCheckXPath25() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate("-*&", new InputSource(is)); + assertThrows(XPathExpressionException.class, () -> xpath.evaluate("-*&", new InputSource(is))); } } /** * XPath.evaluate(java.lang.String expression, InputSource source) throws * XPathExpressionException if expression is blank " ". - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = XPathExpressionException.class) + @Test public void testCheckXPath26() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate(" ", new InputSource(is)); + assertThrows(XPathExpressionException.class, () -> xpath.evaluate(" ", new InputSource(is))); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, QName * returnType) returns correct string value which return type is String. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath27() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), STRING), "6"); + assertEquals("6", xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), STRING)); } } @@ -427,62 +405,53 @@ public void testCheckXPath27() throws Exception { * XPath.evaluate(java.lang.String expression, InputSource source, QName * returnType) throws NPE if source is null. * - * @throws XPathExpressionException If the expression cannot be evaluated. */ - @Test(expectedExceptions = NullPointerException.class) - public void testCheckXPath28() throws XPathExpressionException { - xpath.evaluate(EXPRESSION_NAME_A, null, STRING); + @Test + public void testCheckXPath28() { + assertThrows(NullPointerException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, null, STRING)); } /** * XPath.evaluate(java.lang.String expression, InputSource source, QName * returnType) throws NPE if expression is null. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPath29() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate(null, new InputSource(is), STRING); + assertThrows(NullPointerException.class, () -> xpath.evaluate(null, new InputSource(is), STRING)); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, * QName returnType) throws NPE if returnType is null. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPath30() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), null); + assertThrows(NullPointerException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), null)); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, QName * returnType) throws XPathExpressionException if expression is junk characters. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = XPathExpressionException.class) + @Test public void testCheckXPath31() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate("-*&", new InputSource(is), STRING); + assertThrows(XPathExpressionException.class, () -> xpath.evaluate("-*&", new InputSource(is), STRING)); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, QName * returnType) throws XPathExpressionException if expression is blank " ". - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = XPathExpressionException.class) + @Test public void testCheckXPath32() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate(" ", new InputSource(is), STRING); + assertThrows(XPathExpressionException.class, () -> xpath.evaluate(" ", new InputSource(is), STRING)); } } @@ -490,84 +459,72 @@ public void testCheckXPath32() throws Exception { * XPath.evaluate(java.lang.String expression, InputSource source, * QName returnType) throws IllegalArgumentException if returnType is not * one of the types defined in XPathConstants. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testCheckXPath33() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), TEST_QNAME); + assertThrows(IllegalArgumentException.class, () -> xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), TEST_QNAME)); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, * QName returnType) return correct boolean value if return type is Boolean. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath34() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), - BOOLEAN), true); + assertEquals(true, xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), + BOOLEAN)); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, * QName returnType) return correct boolean value if return type is Boolean. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath35() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.evaluate(EXPRESSION_NAME_B, new InputSource(is), - BOOLEAN), false); + assertEquals(false, xpath.evaluate(EXPRESSION_NAME_B, new InputSource(is), + BOOLEAN)); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, * QName returnType) return correct number value if return type is Number. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath36() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), - NUMBER), 6d); + assertEquals(6d, xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), + NUMBER)); } } /** * XPath.evaluate(java.lang.String expression, InputSource source, * QName returnType) return correct string value if return type is Node. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath37() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(((Attr)xpath.evaluate(EXPRESSION_NAME_A, - new InputSource(is), NODE)).getValue(), "6"); + assertEquals("6", ((Attr) xpath.evaluate(EXPRESSION_NAME_A, + new InputSource(is), NODE)).getValue()); } } /** * Test for XPath.evaluate(java.lang.String expression, InputSource source, * QName returnType) which return type is NodeList. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath38() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - NodeList nodeList = (NodeList)xpath.evaluate(EXPRESSION_NAME_A, - new InputSource(is), NODESET); - assertEquals(((Attr) nodeList.item(0)).getValue(), "6"); + NodeList nodeList = (NodeList) xpath.evaluate(EXPRESSION_NAME_A, + new InputSource(is), NODESET); + assertEquals("6", ((Attr) nodeList.item(0)).getValue()); } } @@ -575,57 +532,49 @@ public void testCheckXPath38() throws Exception { * Test for XPath.evaluate(java.lang.String expression, InputSource iSource, * QName returnType). If return type is Boolean, should return false as * expression is not successful in evaluating to any result. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath52() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.evaluate(EXPRESSION_NAME_B, new InputSource(is), - BOOLEAN), false); + assertEquals(false, xpath.evaluate(EXPRESSION_NAME_B, new InputSource(is), + BOOLEAN)); } } /** * XPath.evaluate(java.lang.String expression, InputSource iSource, QName * returnType) returns correct number value which return type is Number. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath53() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), - NUMBER), 6d); + assertEquals(6d, xpath.evaluate(EXPRESSION_NAME_A, new InputSource(is), + NUMBER)); } } /** * XPath.evaluate(java.lang.String expression, InputSource iSource, QName * returnType) returns a node value if returnType is Node. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath54() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - assertEquals(((Attr)xpath.evaluate(EXPRESSION_NAME_A, - new InputSource(is), NODE)).getValue(), "6"); + assertEquals("6", ((Attr) xpath.evaluate(EXPRESSION_NAME_A, + new InputSource(is), NODE)).getValue()); } } /** * XPath.evaluate(java.lang.String expression, InputSource iSource, QName * returnType) returns a node list if returnType is NodeList. - * - * @throws Exception If any errors occur. */ @Test public void testCheckXPath55() throws Exception { try (InputStream is = Files.newInputStream(XML_PATH)) { - NodeList nodeList = (NodeList)xpath.evaluate(EXPRESSION_NAME_A, - new InputSource(is), NODESET); - assertEquals(((Attr) nodeList.item(0)).getValue(), "6"); + NodeList nodeList = (NodeList) xpath.evaluate(EXPRESSION_NAME_A, + new InputSource(is), NODESET); + assertEquals("6", ((Attr) nodeList.item(0)).getValue()); } } @@ -647,18 +596,18 @@ public void testCheckXPath56() { */ @Test public void testCheckXPath57() { - MyNamespaceContext myNamespaceContext = new MyNamespaceContext(); - xpath.setNamespaceContext(myNamespaceContext); - assertEquals(xpath.getNamespaceContext(), myNamespaceContext); + MyNamespaceContext expectedNamespaceContext = new MyNamespaceContext(); + xpath.setNamespaceContext(expectedNamespaceContext); + assertEquals(expectedNamespaceContext, xpath.getNamespaceContext()); } /** * Test for XPath.setNamespaceContext(NamespaceContext nsContext) Establish * a namespace context. NullPointerException is thrown if nsContext is null. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPath58() { - xpath.setNamespaceContext(null); + assertThrows(NullPointerException.class, () -> xpath.setNamespaceContext(null)); } /** @@ -685,9 +634,9 @@ public void testCheckXPath60() { * Test for XPath.setXPathFunctionResolver(XPathFunctionResolver resolver). * set resolver as null, should throw NPE. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPath61() { - xpath.setXPathFunctionResolver(null); + assertThrows(NullPointerException.class, () -> xpath.setXPathFunctionResolver(null)); } /** @@ -714,9 +663,9 @@ public void testCheckXPath63() { * Test for XPath.setXPathVariableResolver(XPathVariableResolver resolver). * Set resolver as null, should throw NPE. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void testCheckXPath64() { - xpath.setXPathVariableResolver(null); + assertThrows(NullPointerException.class, () -> xpath.setXPathVariableResolver(null)); } /** diff --git a/test/jaxp/javax/xml/jaxp/isolatedjdk/catalog/PropertiesTest.java b/test/jaxp/javax/xml/jaxp/isolatedjdk/catalog/PropertiesTest.java index 3c871d703bf1..c6e6e2a6745d 100644 --- a/test/jaxp/javax/xml/jaxp/isolatedjdk/catalog/PropertiesTest.java +++ b/test/jaxp/javax/xml/jaxp/isolatedjdk/catalog/PropertiesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,19 @@ package catalog; +import org.junit.jupiter.api.Test; + +import javax.xml.catalog.CatalogResolver; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import static catalog.CatalogTestUtils.DEFER_FALSE; import static catalog.CatalogTestUtils.FEATURE_DEFER; import static catalog.CatalogTestUtils.FEATURE_FILES; @@ -36,27 +49,14 @@ import static catalog.CatalogTestUtils.deleteJAXPProps; import static catalog.CatalogTestUtils.generateJAXPProps; import static catalog.CatalogTestUtils.getCatalogPath; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.xml.catalog.CatalogResolver; - -import org.testng.Assert; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; /* * @test * @library /javax/xml/jaxp/libs /javax/xml/jaxp/isolatedjdk * @run shell/timeout=600 ../IsolatedJDK.sh JAXP_PROPS - * @run testng catalog.PropertiesTest + * @run junit catalog.PropertiesTest * @run shell/timeout=600 ../IsolatedJDK.sh JAXP_PROPS remove * @summary This test case tests if the properties FILES, DEFER, PREFER, * RESOLVE in jaxp.properties and system properties are used. @@ -75,11 +75,11 @@ public class PropertiesTest { public void test() throws Exception { // get required properties and do some assertions String javaclasspath = System.getProperty("java.class.path"); - Assert.assertNotNull(javaclasspath, "Test class path is null"); + assertNotNull(javaclasspath, "Test class path is null"); String testclasspath = System.getProperty("test.class.path"); - Assert.assertNotNull(testclasspath, "Test class path is null"); + assertNotNull(testclasspath, "Test class path is null"); String testsourcepath = System.getProperty("test.src"); - Assert.assertNotNull(testsourcepath, "Test source path is null"); + assertNotNull(testsourcepath, "Test source path is null"); // start the child process List testCall = new ArrayList<>(6); @@ -123,7 +123,7 @@ public void test() throws Exception { // trace exit value and assert 0 int exitValue = test.exitValue(); System.out.println("Process Exit code: " + exitValue); - Assert.assertEquals(exitValue, 0, "PropertiesTest returned nonzero exit code."); + assertEquals(0, exitValue, "PropertiesTest returned nonzero exit code."); } public static void main(String[] args) throws Exception { diff --git a/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/MyCHandler.java b/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/MyCHandler.java index c43390d43b93..9a32b0a1ac1a 100644 --- a/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/MyCHandler.java +++ b/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/MyCHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, 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 @@ -22,22 +22,23 @@ */ package javax.xml.parsers.ptests; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import static jaxp.library.JAXPTestUtilities.ERROR_MSG_HEADER; import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.LocatorImpl; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + /** * Customized DefaultHandler which writes output document when methods are * called by Transformer. Test may use output document to compare with golden * file for verification. */ class MyCHandler extends DefaultHandler implements AutoCloseable { + private static final String ERROR_MSG_HEADER = "Unexcepted exception thrown:"; private final BufferedWriter bWriter; private final Locator locator = new LocatorImpl(); diff --git a/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/ParserTestConst.java b/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/ParserTestConst.java index ac50221fec8b..dc5ea806c9d2 100644 --- a/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/ParserTestConst.java +++ b/test/jaxp/javax/xml/jaxp/libs/javax/xml/parsers/ptests/ParserTestConst.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, 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 @@ -22,25 +22,28 @@ */ package javax.xml.parsers.ptests; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; - +import java.io.File; +import java.nio.file.Path; /** * Utility interface which includes final variables of XML, golden file * directories. */ public class ParserTestConst { + private static final Path SRC_ROOT = Path.of(System.getProperty("test.src")).toAbsolutePath(); + + private static String forwardSlashDir(Path p) { + // Convention in these tests is to include trailing '/' in directory strings. + return p.toString().replace(File.separatorChar, '/') + '/'; + } + /** * XML source file directory. */ - public static final String XML_DIR = getPathByClassName(ParserTestConst.class, - ".." + FILE_SEP + "xmlfiles"); - + public static final String XML_DIR = forwardSlashDir(SRC_ROOT.resolveSibling("xmlfiles")); /** * Golden validation files directory. */ - public static final String GOLDEN_DIR = getPathByClassName(ParserTestConst.class, - ".." + FILE_SEP + "xmlfiles" + FILE_SEP + "out"); + public static final String GOLDEN_DIR = XML_DIR + "out/"; } diff --git a/test/jaxp/javax/xml/jaxp/libs/javax/xml/transform/ptests/TransformerTestConst.java b/test/jaxp/javax/xml/jaxp/libs/javax/xml/transform/ptests/TransformerTestConst.java index 5f1193604323..23c2a73a3b20 100644 --- a/test/jaxp/javax/xml/jaxp/libs/javax/xml/transform/ptests/TransformerTestConst.java +++ b/test/jaxp/javax/xml/jaxp/libs/javax/xml/transform/ptests/TransformerTestConst.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, 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 @@ -22,23 +22,27 @@ */ package javax.xml.transform.ptests; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; +import java.io.File; +import java.nio.file.Path; /** * This is the Base test class provide basic support for JAXP functional test */ public class TransformerTestConst { + private static final Path SRC_ROOT = Path.of(System.getProperty("test.src")).toAbsolutePath(); + + private static String forwardSlashDir(Path p) { + // Convention in these tests is to include trailing '/' in directory strings. + return p.toString().replace(File.separatorChar, '/') + '/'; + } + /** * XML source file directory. */ - public static final String XML_DIR = getPathByClassName(TransformerTestConst.class, - ".." + FILE_SEP + "xmlfiles"); - + public static final String XML_DIR = forwardSlashDir(SRC_ROOT.resolveSibling("xmlfiles")); /** * Golden validation files directory. */ - public static final String GOLDEN_DIR = getPathByClassName(TransformerTestConst.class, - ".." + FILE_SEP + "xmlfiles" + FILE_SEP + "out"); + public static final String GOLDEN_DIR = XML_DIR + "out/"; } diff --git a/test/jaxp/javax/xml/jaxp/libs/javax/xml/validation/ptests/ValidationTestConst.java b/test/jaxp/javax/xml/jaxp/libs/javax/xml/validation/ptests/ValidationTestConst.java index 50f4c86d0e55..f8c6aba18d30 100644 --- a/test/jaxp/javax/xml/jaxp/libs/javax/xml/validation/ptests/ValidationTestConst.java +++ b/test/jaxp/javax/xml/jaxp/libs/javax/xml/validation/ptests/ValidationTestConst.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,22 +22,22 @@ */ package javax.xml.validation.ptests; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; +import java.io.File; +import java.nio.file.Path; /** * This class defines the path constant */ public class ValidationTestConst { - /** - * XML source file directory. - */ - public static final String XML_DIR = getPathByClassName(ValidationTestConst.class, - ".." + FILE_SEP + "xmlfiles"); + private static final Path SRC_ROOT = Path.of(System.getProperty("test.src")).toAbsolutePath(); + + private static String forwardSlashDir(Path p) { + // Convention in these tests is to include trailing '/' in directory strings. + return p.toString().replace(File.separatorChar, '/') + '/'; + } /** - * Golden validation files directory. + * XML source file directory. */ - public static final String GOLDEN_DIR = getPathByClassName(ValidationTestConst.class, - ".." + FILE_SEP + "xmlfiles" + FILE_SEP + "out"); + public static final String XML_DIR = forwardSlashDir(SRC_ROOT.resolveSibling("xmlfiles")); } diff --git a/test/jaxp/javax/xml/jaxp/libs/javax/xml/xpath/ptests/XPathTestConst.java b/test/jaxp/javax/xml/jaxp/libs/javax/xml/xpath/ptests/XPathTestConst.java index 605dada16568..cfbb4a869df5 100644 --- a/test/jaxp/javax/xml/jaxp/libs/javax/xml/xpath/ptests/XPathTestConst.java +++ b/test/jaxp/javax/xml/jaxp/libs/javax/xml/xpath/ptests/XPathTestConst.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, 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 @@ -22,16 +22,16 @@ */ package javax.xml.xpath.ptests; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; +import java.nio.file.Path; /** * This is the Base test class provide basic support for XPath functional test */ public class XPathTestConst { + private static final Path SRC_ROOT = Path.of(System.getProperty("test.src")).toAbsolutePath(); + /** * XML source file directory. */ - public static final String XML_DIR = getPathByClassName(XPathTestConst.class, - ".." + FILE_SEP + "xmlfiles"); + public static final Path XML_DIR = SRC_ROOT.resolveSibling("xmlfiles"); } diff --git a/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPDataProvider.java b/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPDataProvider.java index d09c3d78b0b9..9f925334895b 100644 --- a/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPDataProvider.java +++ b/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPDataProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,16 +23,14 @@ package jaxp.library; -import org.testng.annotations.DataProvider; - /** * Provide invalid parameters for negative testing Factory.newInstance. */ public class JAXPDataProvider { - - @DataProvider(name = "new-instance-neg") - public static Object[][] getNewInstanceNeg() { - return new Object[][] { { null, null }, { null, JAXPDataProvider.class.getClassLoader() } }; + public static Object[][] newInstanceNeg() { + return new Object[][] { + { null, null }, + { null, JAXPDataProvider.class.getClassLoader() }, + }; } - } From 2240ff41795d1de76aab1ce69758fcbd827208de Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Mon, 16 Mar 2026 13:31:38 +0000 Subject: [PATCH 60/97] 8379967: (process) Improve ProcessBuilder error reporting Reviewed-by: mbaesken, rriggs --- make/modules/java.base/Launcher.gmk | 3 +- .../unix/native/jspawnhelper/jspawnhelper.c | 77 ++++++----- .../unix/native/libjava/ProcessImpl_md.c | 46 +++++-- src/java.base/unix/native/libjava/childproc.c | 128 ++++++++++++------ src/java.base/unix/native/libjava/childproc.h | 7 - .../native/libjava/childproc_errorcodes.c | 63 +++++++++ .../native/libjava/childproc_errorcodes.h | 117 ++++++++++++++++ .../lang/ProcessBuilder/InvalidWorkDir.java | 62 +++++++++ .../ProcessBuilder/JspawnhelperWarnings.java | 46 +++++-- 9 files changed, 449 insertions(+), 100 deletions(-) create mode 100644 src/java.base/unix/native/libjava/childproc_errorcodes.c create mode 100644 src/java.base/unix/native/libjava/childproc_errorcodes.h create mode 100644 test/jdk/java/lang/ProcessBuilder/InvalidWorkDir.java diff --git a/make/modules/java.base/Launcher.gmk b/make/modules/java.base/Launcher.gmk index 3a3920acb126..bfae0925c076 100644 --- a/make/modules/java.base/Launcher.gmk +++ b/make/modules/java.base/Launcher.gmk @@ -95,7 +95,8 @@ ifeq ($(call isTargetOsType, unix), true) CFLAGS := $(VERSION_CFLAGS), \ EXTRA_HEADER_DIRS := libjava, \ EXTRA_OBJECT_FILES := \ - $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjava/childproc$(OBJ_SUFFIX), \ + $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjava/childproc$(OBJ_SUFFIX) \ + $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjava/childproc_errorcodes$(OBJ_SUFFIX), \ LD_SET_ORIGIN := false, \ OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/modules_libs/$(MODULE), \ )) diff --git a/src/java.base/unix/native/jspawnhelper/jspawnhelper.c b/src/java.base/unix/native/jspawnhelper/jspawnhelper.c index d2302d0c2e76..f26d0d618e3e 100644 --- a/src/java.base/unix/native/jspawnhelper/jspawnhelper.c +++ b/src/java.base/unix/native/jspawnhelper/jspawnhelper.c @@ -34,6 +34,7 @@ #include #include "childproc.h" +#include "childproc_errorcodes.h" extern int errno; @@ -41,36 +42,34 @@ extern int errno; void *mptr; \ mptr = malloc (Y); \ if (mptr == 0) { \ - error (fdout, ERR_MALLOC); \ + sendErrorCodeAndExit (fdout, ESTEP_JSPAWN_ALLOC_FAILED, (int)Y, errno); \ } \ X = mptr; \ } -#define ERR_MALLOC 1 -#define ERR_PIPE 2 -#define ERR_ARGS 3 - #ifndef VERSION_STRING #error VERSION_STRING must be defined #endif -void error (int fd, int err) { - if (write (fd, &err, sizeof(err)) != sizeof(err)) { - /* Not sure what to do here. I have no one to speak to. */ - exit(0x80 + err); +/* Attempts to send an error code to the parent (which may or may not + * work depending on whether the fail pipe exists); then exits with an + * error code corresponding to the fail step. */ +void sendErrorCodeAndExit(int failpipe_fd, int step, int hint, int errno_) { + errcode_t errcode; + buildErrorCode(&errcode, step, hint, errno_); + if (failpipe_fd == -1 || !sendErrorCode(failpipe_fd, errcode)) { + /* Write error code to stdout, in the hope someone reads this. */ + printf("jspawnhelper fail: " ERRCODE_FORMAT "\n", ERRCODE_FORMAT_ARGS(errcode)); } - exit (1); + exit(exitCodeFromErrorCode(errcode)); } -void shutItDown() { - fprintf(stdout, "jspawnhelper version %s\n", VERSION_STRING); - fprintf(stdout, "This command is not for general use and should "); - fprintf(stdout, "only be run as the result of a call to\n"); - fprintf(stdout, "ProcessBuilder.start() or Runtime.exec() in a java "); - fprintf(stdout, "application\n"); - fflush(stdout); - _exit(1); -} +static const char* usageErrorText = + "jspawnhelper version " VERSION_STRING "\n" + "This command is not for general use and should " + "only be run as the result of a call to\n" + "ProcessBuilder.start() or Runtime.exec() in a java " + "application\n"; /* * read the following off the pipefd @@ -84,22 +83,31 @@ void initChildStuff (int fdin, int fdout, ChildStuff *c) { int bufsize, offset=0; int magic; int res; + const int step = ESTEP_JSPAWN_RCV_CHILDSTUFF_COMM_FAIL; + int substep = 0; res = readFully (fdin, &magic, sizeof(magic)); - if (res != 4 || magic != magicNumber()) { - error (fdout, ERR_PIPE); + if (res != 4) { + sendErrorCodeAndExit(fdout, step, substep, errno); + } + + substep ++; + if (magic != magicNumber()) { + sendErrorCodeAndExit(fdout, step, substep, errno); } #ifdef DEBUG jtregSimulateCrash(0, 5); #endif + substep ++; if (readFully (fdin, c, sizeof(*c)) != sizeof(*c)) { - error (fdout, ERR_PIPE); + sendErrorCodeAndExit(fdout, step, substep, errno); } + substep ++; if (readFully (fdin, &sp, sizeof(sp)) != sizeof(sp)) { - error (fdout, ERR_PIPE); + sendErrorCodeAndExit(fdout, step, substep, errno); } bufsize = sp.argvBytes + sp.envvBytes + @@ -107,8 +115,9 @@ void initChildStuff (int fdin, int fdout, ChildStuff *c) { ALLOC(buf, bufsize); + substep++; if (readFully (fdin, buf, bufsize) != bufsize) { - error (fdout, ERR_PIPE); + sendErrorCodeAndExit(fdout, step, substep, errno); } /* Initialize argv[] */ @@ -150,25 +159,29 @@ int main(int argc, char *argv[]) { #endif if (argc != 3) { - fprintf(stdout, "Incorrect number of arguments: %d\n", argc); - shutItDown(); + printf("Incorrect number of arguments: %d\n", argc); + puts(usageErrorText); + sendErrorCodeAndExit(-1, ESTEP_JSPAWN_ARG_ERROR, 0, 0); } if (strcmp(argv[1], VERSION_STRING) != 0) { - fprintf(stdout, "Incorrect Java version: %s\n", argv[1]); - shutItDown(); + printf("Incorrect Java version: %s\n", argv[1]); + puts(usageErrorText); + sendErrorCodeAndExit(-1, ESTEP_JSPAWN_VERSION_ERROR, 0, 0); } r = sscanf (argv[2], "%d:%d:%d", &fdinr, &fdinw, &fdout); if (r == 3 && fcntl(fdinr, F_GETFD) != -1 && fcntl(fdinw, F_GETFD) != -1) { fstat(fdinr, &buf); if (!S_ISFIFO(buf.st_mode)) { - fprintf(stdout, "Incorrect input pipe\n"); - shutItDown(); + printf("Incorrect input pipe\n"); + puts(usageErrorText); + sendErrorCodeAndExit(-1, ESTEP_JSPAWN_NOT_A_PIPE, fdinr, errno); } } else { - fprintf(stdout, "Incorrect FD array data: %s\n", argv[2]); - shutItDown(); + printf("Incorrect FD array data: %s\n", argv[2]); + puts(usageErrorText); + sendErrorCodeAndExit(-1, ESTEP_JSPAWN_NOT_A_PIPE, fdinr, errno); } // Close the writing end of the pipe we use for reading from the parent. diff --git a/src/java.base/unix/native/libjava/ProcessImpl_md.c b/src/java.base/unix/native/libjava/ProcessImpl_md.c index 12597fbb6502..29522f3c8220 100644 --- a/src/java.base/unix/native/libjava/ProcessImpl_md.c +++ b/src/java.base/unix/native/libjava/ProcessImpl_md.c @@ -47,6 +47,7 @@ #include #include "childproc.h" +#include "childproc_errorcodes.h" /* * @@ -678,7 +679,6 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, jintArray std_fds, jboolean redirectErrorStream) { - int errnum; int resultPid = -1; int in[2], out[2], err[2], fail[2], childenv[2]; jint *fds = NULL; @@ -782,9 +782,11 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, } close(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec (childproc.c) */ + errcode_t errcode; + /* If we expect the child to ping aliveness, wait for it. */ if (c->sendAlivePing) { - switch(readFully(fail[0], &errnum, sizeof(errnum))) { + switch(readFully(fail[0], &errcode, sizeof(errcode))) { case 0: /* First exec failed; */ { int tmpStatus = 0; @@ -792,13 +794,15 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, throwExitCause(env, p, tmpStatus, c->mode); goto Catch; } - case sizeof(errnum): - if (errnum != CHILD_IS_ALIVE) { - /* This can happen if the spawn helper encounters an error - * before or during the handshake with the parent. */ - throwInternalIOException(env, 0, - "Bad code from spawn helper (Failed to exec spawn helper)", - c->mode); + case sizeof(errcode): + if (errcode.step != ESTEP_CHILD_ALIVE) { + /* This can happen if the child process encounters an error + * before or during initial handshake with the parent. */ + char msg[256]; + snprintf(msg, sizeof(msg), + "Bad early code from spawn helper " ERRCODE_FORMAT " (Failed to exec spawn helper)", + ERRCODE_FORMAT_ARGS(errcode)); + throwInternalIOException(env, 0, msg, c->mode); goto Catch; } break; @@ -808,11 +812,29 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, } } - switch (readFully(fail[0], &errnum, sizeof(errnum))) { + switch (readFully(fail[0], &errcode, sizeof(errcode))) { case 0: break; /* Exec succeeded */ - case sizeof(errnum): + case sizeof(errcode): + /* Always reap first! */ waitpid(resultPid, NULL, 0); - throwIOException(env, errnum, "Exec failed"); + /* Most of these errors are implementation errors and should result in an internal IOE, but + * a few can be caused by bad user input and need to be communicated to the end user. */ + switch(errcode.step) { + case ESTEP_CHDIR_FAIL: + throwIOException(env, errcode.errno_, "Failed to access working directory"); + break; + case ESTEP_EXEC_FAIL: + throwIOException(env, errcode.errno_, "Exec failed"); + break; + default: { + /* Probably implementation error */ + char msg[256]; + snprintf(msg, sizeof(msg), + "Bad code from spawn helper " ERRCODE_FORMAT " (Failed to exec spawn helper)", + ERRCODE_FORMAT_ARGS(errcode)); + throwInternalIOException(env, 0, msg, c->mode); + } + }; goto Catch; default: throwInternalIOException(env, errno, "Read failed", c->mode); diff --git a/src/java.base/unix/native/libjava/childproc.c b/src/java.base/unix/native/libjava/childproc.c index 9c6334e52d2e..a45674e3f828 100644 --- a/src/java.base/unix/native/libjava/childproc.c +++ b/src/java.base/unix/native/libjava/childproc.c @@ -34,16 +34,27 @@ #include #include "childproc.h" +#include "childproc_errorcodes.h" #include "jni_util.h" const char * const *parentPathv; -static int -restartableDup2(int fd_from, int fd_to) +/* All functions taking an errcode_t* as output behave the same: upon error, they populate + * errcode_t::hint and errcode_t::errno, but leave errcode_t::step as ESTEP_UNKNOWN since + * this information will be provided by the outer caller */ + +static bool +restartableDup2(int fd_from, int fd_to, errcode_t* errcode) { int err; RESTARTABLE(dup2(fd_from, fd_to), err); - return err; + if (err == -1) { + /* use fd_to (the destination descriptor) as hint: it is a bit more telling + * than fd_from in our case */ + buildErrorCode(errcode, ESTEP_UNKNOWN, fd_to, errno); + return false; + } + return true; } int @@ -52,6 +63,16 @@ closeSafely(int fd) return (fd == -1) ? 0 : close(fd); } +/* Like closeSafely, but sets errcode (hint = fd, errno) on error and returns false */ +static bool +closeSafely2(int fd, errcode_t* errcode) { + if (closeSafely(fd) == -1) { + buildErrorCode(errcode, ESTEP_UNKNOWN, fd, errno); + return false; + } + return true; +} + int markCloseOnExec(int fd) { @@ -128,15 +149,19 @@ markDescriptorsCloseOnExec(void) return 0; } -static int -moveDescriptor(int fd_from, int fd_to) +static bool +moveDescriptor(int fd_from, int fd_to, errcode_t* errcode) { if (fd_from != fd_to) { - if ((restartableDup2(fd_from, fd_to) == -1) || - (close(fd_from) == -1)) - return -1; + if (!restartableDup2(fd_from, fd_to, errcode)) { + return false; + } + if (close(fd_from) == -1) { + buildErrorCode(errcode, ESTEP_UNKNOWN, fd_from, errno); + return false; + } } - return 0; + return true; } int @@ -367,15 +392,16 @@ int childProcess(void *arg) { const ChildStuff* p = (const ChildStuff*) arg; + int fail_pipe_fd = p->fail[1]; + /* error information for WhyCantJohnnyExec */ + errcode_t errcode; - if (p->sendAlivePing) { - /* Child shall signal aliveness to parent at the very first - * moment. */ - int code = CHILD_IS_ALIVE; - if (writeFully(fail_pipe_fd, &code, sizeof(code)) != sizeof(code)) { - goto WhyCantJohnnyExec; - } + /* Child shall signal aliveness to parent at the very first + * moment. */ + if (p->sendAlivePing && !sendAlivePing(fail_pipe_fd)) { + buildErrorCode(&errcode, ESTEP_SENDALIVE_FAIL, fail_pipe_fd, errno); + goto WhyCantJohnnyExec; } #ifdef DEBUG @@ -384,34 +410,49 @@ childProcess(void *arg) /* Close the parent sides of the pipes. Closing pipe fds here is redundant, since markDescriptorsCloseOnExec() would do it anyways, but a little paranoia is a good thing. */ - if ((closeSafely(p->in[1]) == -1) || - (closeSafely(p->out[0]) == -1) || - (closeSafely(p->err[0]) == -1) || - (closeSafely(p->childenv[0]) == -1) || - (closeSafely(p->childenv[1]) == -1) || - (closeSafely(p->fail[0]) == -1)) + if (!closeSafely2(p->in[1], &errcode) || + !closeSafely2(p->out[0], &errcode) || + !closeSafely2(p->err[0], &errcode) || + !closeSafely2(p->childenv[0], &errcode) || + !closeSafely2(p->childenv[1], &errcode) || + !closeSafely2(p->fail[0], &errcode)) + { + errcode.step = ESTEP_PIPECLOSE_FAIL; goto WhyCantJohnnyExec; + } /* Give the child sides of the pipes the right fileno's. */ /* Note: it is possible for in[0] == 0 */ - if ((moveDescriptor(p->in[0] != -1 ? p->in[0] : p->fds[0], - STDIN_FILENO) == -1) || - (moveDescriptor(p->out[1]!= -1 ? p->out[1] : p->fds[1], - STDOUT_FILENO) == -1)) + if (!moveDescriptor(p->in[0] != -1 ? p->in[0] : p->fds[0], + STDIN_FILENO, &errcode)) { + errcode.step = ESTEP_DUP2_STDIN_FAIL; goto WhyCantJohnnyExec; + } + + if (!moveDescriptor(p->out[1] != -1 ? p->out[1] : p->fds[1], + STDOUT_FILENO, &errcode)) { + errcode.step = ESTEP_DUP2_STDOUT_FAIL; + goto WhyCantJohnnyExec; + } if (p->redirectErrorStream) { - if ((closeSafely(p->err[1]) == -1) || - (restartableDup2(STDOUT_FILENO, STDERR_FILENO) == -1)) + if (!closeSafely2(p->err[1], &errcode) || + !restartableDup2(STDOUT_FILENO, STDERR_FILENO, &errcode)) { + errcode.step = ESTEP_DUP2_STDERR_REDIRECT_FAIL; goto WhyCantJohnnyExec; + } } else { - if (moveDescriptor(p->err[1] != -1 ? p->err[1] : p->fds[2], - STDERR_FILENO) == -1) + if (!moveDescriptor(p->err[1] != -1 ? p->err[1] : p->fds[2], + STDERR_FILENO, &errcode)) { + errcode.step = ESTEP_DUP2_STDERR_REDIRECT_FAIL; goto WhyCantJohnnyExec; + } } - if (moveDescriptor(fail_pipe_fd, FAIL_FILENO) == -1) + if (!moveDescriptor(fail_pipe_fd, FAIL_FILENO, &errcode)) { + errcode.step = ESTEP_DUP2_FAILPIPE_FAIL; goto WhyCantJohnnyExec; + } /* We moved the fail pipe fd */ fail_pipe_fd = FAIL_FILENO; @@ -424,14 +465,19 @@ childProcess(void *arg) if (markDescriptorsCloseOnExec() == -1) { /* failed, close the old way */ int max_fd = (int)sysconf(_SC_OPEN_MAX); int fd; - for (fd = STDERR_FILENO + 1; fd < max_fd; fd++) - if (markCloseOnExec(fd) == -1 && errno != EBADF) + for (fd = STDERR_FILENO + 1; fd < max_fd; fd++) { + if (markCloseOnExec(fd) == -1 && errno != EBADF) { + buildErrorCode(&errcode, ESTEP_CLOEXEC_FAIL, fd, errno); goto WhyCantJohnnyExec; + } + } } /* change to the new working directory */ - if (p->pdir != NULL && chdir(p->pdir) < 0) + if (p->pdir != NULL && chdir(p->pdir) < 0) { + buildErrorCode(&errcode, ESTEP_CHDIR_FAIL, 0, errno); goto WhyCantJohnnyExec; + } // Reset any mask signals from parent, but not in VFORK mode if (p->mode != MODE_VFORK) { @@ -442,28 +488,32 @@ childProcess(void *arg) // Children should be started with default signal disposition for SIGPIPE if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) { + buildErrorCode(&errcode, ESTEP_SET_SIGPIPE, 0, errno); goto WhyCantJohnnyExec; } JDK_execvpe(p->mode, p->argv[0], p->argv, p->envv); + /* Still here. Hmm. */ + buildErrorCode(&errcode, ESTEP_EXEC_FAIL, 0, errno); + WhyCantJohnnyExec: /* We used to go to an awful lot of trouble to predict whether the * child would fail, but there is no reliable way to predict the * success of an operation without *trying* it, and there's no way * to try a chdir or exec in the parent. Instead, all we need is a * way to communicate any failure back to the parent. Easy; we just - * send the errno back to the parent over a pipe in case of failure. + * send the errorcode back to the parent over a pipe in case of failure. * The tricky thing is, how do we communicate the *success* of exec? * We use FD_CLOEXEC together with the fact that a read() on a pipe * yields EOF when the write ends (we have two of them!) are closed. */ - { - int errnum = errno; - writeFully(fail_pipe_fd, &errnum, sizeof(errnum)); + if (!sendErrorCode(fail_pipe_fd, errcode)) { + printf("childproc fail: " ERRCODE_FORMAT "\n", ERRCODE_FORMAT_ARGS(errcode)); } + int exitcode = exitCodeFromErrorCode(errcode); close(fail_pipe_fd); - _exit(-1); + _exit(exitcode); return 0; /* Suppress warning "no return value from function" */ } diff --git a/src/java.base/unix/native/libjava/childproc.h b/src/java.base/unix/native/libjava/childproc.h index 974fac3bdddd..4271c7866351 100644 --- a/src/java.base/unix/native/libjava/childproc.h +++ b/src/java.base/unix/native/libjava/childproc.h @@ -107,13 +107,6 @@ typedef struct _SpawnInfo { int parentPathvBytes; /* total number of bytes in parentPathv array */ } SpawnInfo; -/* If ChildStuff.sendAlivePing is true, child shall signal aliveness to - * the parent the moment it gains consciousness, before any subsequent - * pre-exec errors could happen. - * This code must fit into an int and not be a valid errno value on any of - * our platforms. */ -#define CHILD_IS_ALIVE 65535 - /** * The cached and split version of the JDK's effective PATH. * (We don't support putenv("PATH=...") in native code) diff --git a/src/java.base/unix/native/libjava/childproc_errorcodes.c b/src/java.base/unix/native/libjava/childproc_errorcodes.c new file mode 100644 index 000000000000..4dc8e9276166 --- /dev/null +++ b/src/java.base/unix/native/libjava/childproc_errorcodes.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 "childproc.h" +#include "childproc_errorcodes.h" + +void buildErrorCode(errcode_t* errcode, int step, int hint, int errno_) { + errcode_t e; + + assert(step < (1 << 8)); + e.step = step; + + assert(errno_ < (1 << 8)); + e.errno_ = errno_; + + const int maxhint = (1 << 16); + e.hint = hint < maxhint ? hint : maxhint; + + (*errcode) = e; +} + +int exitCodeFromErrorCode(errcode_t errcode) { + /* We use the fail step number as exit code, but avoid 0 and 1 + * and try to avoid the [128..256) range since that one is used by + * shells to codify abnormal kills by signal. */ + return 0x10 + errcode.step; +} + +bool sendErrorCode(int fd, errcode_t errcode) { + return writeFully(fd, &errcode, sizeof(errcode)) == sizeof(errcode); +} + +bool sendAlivePing(int fd) { + errcode_t errcode; + buildErrorCode(&errcode, ESTEP_CHILD_ALIVE, getpid(), 0); + return sendErrorCode(fd, errcode); +} diff --git a/src/java.base/unix/native/libjava/childproc_errorcodes.h b/src/java.base/unix/native/libjava/childproc_errorcodes.h new file mode 100644 index 000000000000..8379db4ad2b8 --- /dev/null +++ b/src/java.base/unix/native/libjava/childproc_errorcodes.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef CHILDPROC_ERRORCODES_H +#define CHILDPROC_ERRORCODES_H + +#include +#include + +typedef struct errcode_t_ { + unsigned step : 8; + unsigned hint : 16; + unsigned errno_ : 8; +} errcode_t; + +/* Helper macros for printing an errcode_t */ +#define ERRCODE_FORMAT "(%u-%u-%u)" +#define ERRCODE_FORMAT_ARGS(errcode) errcode.step, errcode.hint, errcode.errno_ + + +/* Builds up an error code. + * Note: + * - hint will be capped at 2^16 + * - both step and errno_ must fit into 8 bits. */ +void buildErrorCode(errcode_t* errcode, int step, int hint, int errno_); + +/* Sends an error code down a pipe. Returns true if sent successfully. */ +bool sendErrorCode(int fd, errcode_t errcode); + +/* Build an exit code for an errcode (used as child process exit code + * in addition to the errcode being sent to parent). */ +int exitCodeFromErrorCode(errcode_t errcode); + +/* Sends alive ping down a pipe. Returns true if sent successfully. */ +bool sendAlivePing(int fd); + +#define ESTEP_UNKNOWN 0 + +/* not an error code, but an "I am alive" ping from the child. + * hint is child pid, errno is 0. */ +#define ESTEP_CHILD_ALIVE 255 + +/* JspawnHelper */ +#define ESTEP_JSPAWN_ARG_ERROR 1 +#define ESTEP_JSPAWN_VERSION_ERROR 2 + +/* Checking file descriptor setup + * hint is the (16-bit-capped) fd number */ +#define ESTEP_JSPAWN_INVALID_FD 3 +#define ESTEP_JSPAWN_NOT_A_PIPE 4 + +/* Allocation fail in jspawnhelper. + * hint is the (16-bit-capped) fail size */ +#define ESTEP_JSPAWN_ALLOC_FAILED 5 + +/* Receiving Childstuff from parent, communication error. + * hint is the substep. */ +#define ESTEP_JSPAWN_RCV_CHILDSTUFF_COMM_FAIL 6 + +/* Expand if needed ... */ + +/* childproc() */ + +/* Failed to send aliveness ping + * hint is the (16-bit-capped) fd. */ +#define ESTEP_SENDALIVE_FAIL 10 + +/* Failed to close a pipe in fork mode + * hint is the (16-bit-capped) fd. */ +#define ESTEP_PIPECLOSE_FAIL 11 + +/* Failed to dup2 a file descriptor in fork mode. + * hint is the (16-bit-capped) fd_to (!) */ +#define ESTEP_DUP2_STDIN_FAIL 13 +#define ESTEP_DUP2_STDOUT_FAIL 14 +#define ESTEP_DUP2_STDERR_REDIRECT_FAIL 15 +#define ESTEP_DUP2_STDERR_FAIL 16 +#define ESTEP_DUP2_FAILPIPE_FAIL 17 + +/* Failed to mark a file descriptor as CLOEXEC + * hint is the (16-bit-capped) fd */ +#define ESTEP_CLOEXEC_FAIL 18 + +/* Failed to chdir into the target working directory */ +#define ESTEP_CHDIR_FAIL 19 + +/* Failed to change signal disposition for SIGPIPE to default */ +#define ESTEP_SET_SIGPIPE 20 + +/* Expand if needed ... */ + +/* All modes: exec() failed */ +#define ESTEP_EXEC_FAIL 30 + +#endif /* CHILDPROC_MD_H */ diff --git a/test/jdk/java/lang/ProcessBuilder/InvalidWorkDir.java b/test/jdk/java/lang/ProcessBuilder/InvalidWorkDir.java new file mode 100644 index 000000000000..310ecf03f977 --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/InvalidWorkDir.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test id=FORK + * @bug 8379967 + * @summary Check that passing an invalid work dir yields a corresponding IOE text. + * @requires (os.family != "windows") + * @requires vm.flagless + * @library /test/lib + * @run main/othervm -Xmx64m -Djdk.lang.Process.launchMechanism=FORK InvalidWorkDir + */ + +/** + * @test id=POSIX_SPAWN + * @bug 8379967 + * @summary Check that passing an invalid work dir yields a corresponding IOE text. + * @requires (os.family != "windows") + * @requires vm.flagless + * @library /test/lib + * @run main/othervm -Xmx64m -Djdk.lang.Process.launchMechanism=FORK InvalidWorkDir + */ + +import jdk.test.lib.process.OutputAnalyzer; + +import java.io.File; +import java.io.IOException; + +public class InvalidWorkDir { + + public static void main(String[] args) { + ProcessBuilder bld = new ProcessBuilder("ls").directory(new File("./doesnotexist")); + try(Process p = bld.start()) { + throw new RuntimeException("IOE expected"); + } catch (IOException e) { + if (!e.getMessage().matches(".*Failed to access working directory.*No such file or directory.*")) { + throw new RuntimeException(String.format("got IOE but with different text (%s)", e.getMessage())); + } + } + } + +} diff --git a/test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java b/test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java index d9896f16e005..cb2d504951d5 100644 --- a/test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java +++ b/test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java @@ -22,11 +22,19 @@ */ /* - * @test - * @bug 8325567 8325621 + * @test id=badargs + * @bug 8325567 8325621 8379967 * @requires (os.family == "linux") | (os.family == "aix") | (os.family == "mac") * @library /test/lib - * @run driver JspawnhelperWarnings + * @run driver JspawnhelperWarnings badargs + */ + +/* + * @test id=badversion + * @bug 8325567 8325621 8379967 + * @requires (os.family == "linux") | (os.family == "aix") | (os.family == "mac") + * @library /test/lib + * @run driver JspawnhelperWarnings badversion */ import java.nio.file.Paths; @@ -36,6 +44,13 @@ public class JspawnhelperWarnings { + // See childproc_errorcodes.h + static final int ESTEP_JSPAWN_ARG_ERROR = 1; + static final int ESTEP_JSPAWN_VERSION_ERROR = 2; + + // See exitCodeFromErrorCode() in childproc_errorcodes.c + static final int EXITCODE_OFFSET = 0x10; + private static void tryWithNArgs(int nArgs) throws Exception { System.out.println("Running jspawnhelper with " + nArgs + " args"); String[] args = new String[nArgs + 1]; @@ -43,7 +58,8 @@ private static void tryWithNArgs(int nArgs) throws Exception { args[0] = Paths.get(System.getProperty("java.home"), "lib", "jspawnhelper").toString(); Process p = ProcessTools.startProcess("jspawnhelper", new ProcessBuilder(args)); OutputAnalyzer oa = new OutputAnalyzer(p); - oa.shouldHaveExitValue(1); + oa.shouldHaveExitValue(EXITCODE_OFFSET + ESTEP_JSPAWN_ARG_ERROR); + oa.shouldContain("jspawnhelper fail: (1-0-0)"); oa.shouldContain("This command is not for general use"); if (nArgs != 2) { oa.shouldContain("Incorrect number of arguments"); @@ -59,16 +75,28 @@ private static void testVersion() throws Exception { args[2] = "1:1:1"; Process p = ProcessTools.startProcess("jspawnhelper", new ProcessBuilder(args)); OutputAnalyzer oa = new OutputAnalyzer(p); - oa.shouldHaveExitValue(1); + oa.shouldHaveExitValue(EXITCODE_OFFSET + ESTEP_JSPAWN_VERSION_ERROR); + oa.shouldContain("jspawnhelper fail: (2-0-0)"); oa.shouldContain("This command is not for general use"); oa.shouldContain("Incorrect Java version: wrongVersion"); } public static void main(String[] args) throws Exception { - for (int nArgs = 0; nArgs < 10; nArgs++) { - tryWithNArgs(nArgs); + if (args.length != 1) { + throw new RuntimeException("test argument error"); + } + switch (args[0]) { + case "badargs" -> { + for (int nArgs = 0; nArgs < 10; nArgs++) { + if (nArgs != 2) { + tryWithNArgs(nArgs); + } + } + } + case "badversion" -> { + testVersion(); + } + default -> throw new RuntimeException("test argument error"); } - - testVersion(); } } From c7438a92a7cc368f2629744191dbd51d104d3be1 Mon Sep 17 00:00:00 2001 From: Alexander Zvegintsev Date: Mon, 16 Mar 2026 14:10:19 +0000 Subject: [PATCH 61/97] 8376050: awt/event/MouseEvent/AltGraphModifierTest/AltGraphModifierTest.java fails on Ubuntu Reviewed-by: dmarkov, serb, aivanov, honkar --- src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java | 8 ++++++-- .../AltGraphModifierTest/AltGraphModifierTest.java | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java b/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java index 78cd4a7e57df..5dcd1b763e14 100644 --- a/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java +++ b/src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1770,7 +1770,11 @@ static void setupModifierMap() { final int altL = keysymToPrimaryKeycode(XKeySymConstants.XK_Alt_L); final int altR = keysymToPrimaryKeycode(XKeySymConstants.XK_Alt_R); final int numLock = keysymToPrimaryKeycode(XKeySymConstants.XK_Num_Lock); - final int modeSwitch = keysymToPrimaryKeycode(XKeySymConstants.XK_Mode_switch); + int modeSwitchTmp = keysymToPrimaryKeycode(XKeySymConstants.XK_Mode_switch); + if (modeSwitchTmp == 0) { + modeSwitchTmp = keysymToPrimaryKeycode(XKeySymConstants.XK_ISO_Level3_Shift); + } + final int modeSwitch = modeSwitchTmp; final int shiftLock = keysymToPrimaryKeycode(XKeySymConstants.XK_Shift_Lock); final int capsLock = keysymToPrimaryKeycode(XKeySymConstants.XK_Caps_Lock); diff --git a/test/jdk/java/awt/event/MouseEvent/AltGraphModifierTest/AltGraphModifierTest.java b/test/jdk/java/awt/event/MouseEvent/AltGraphModifierTest/AltGraphModifierTest.java index c8730b289644..615e2cdd6af2 100644 --- a/test/jdk/java/awt/event/MouseEvent/AltGraphModifierTest/AltGraphModifierTest.java +++ b/test/jdk/java/awt/event/MouseEvent/AltGraphModifierTest/AltGraphModifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8041928 8158616 + * @bug 8041928 8158616 8376050 * @requires (os.family != "mac") * @summary Confirm that the Alt-Gr Modifier bit is set correctly. * @library /java/awt/regtesthelpers From e0fa3d2f8182fa4178b8ab3a532fbc4ea82e9bb8 Mon Sep 17 00:00:00 2001 From: Kangcheng Xu Date: Mon, 16 Mar 2026 14:11:27 +0000 Subject: [PATCH 62/97] 8353290: C2: Refactor PhaseIdealLoop::is_counted_loop() Reviewed-by: chagedorn, roland --- src/hotspot/share/opto/loopnode.cpp | 1269 ++++++++++------- src/hotspot/share/opto/loopnode.hpp | 268 +++- src/hotspot/share/opto/loopopts.cpp | 62 +- .../TestStressLongCountedLoopLimitChecks.java | 117 ++ 4 files changed, 1104 insertions(+), 612 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestStressLongCountedLoopLimitChecks.java diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index d68505836d45..1523372e64cf 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -375,17 +375,20 @@ IdealLoopTree* PhaseIdealLoop::create_outer_strip_mined_loop(Node* init_control, return outer_ilt; } -void PhaseIdealLoop::insert_loop_limit_check_predicate(ParsePredicateSuccessProj* loop_limit_check_parse_proj, - Node* cmp_limit, Node* bol) { + +void CountedLoopConverter::insert_loop_limit_check_predicate(const ParsePredicateSuccessProj* loop_limit_check_parse_proj, + Node* bol) const { assert(loop_limit_check_parse_proj->in(0)->is_ParsePredicate(), "must be parse predicate"); - Node* new_predicate_proj = create_new_if_for_predicate(loop_limit_check_parse_proj, nullptr, - Deoptimization::Reason_loop_limit_check, - Op_If); + Node* new_predicate_proj = _phase->create_new_if_for_predicate(loop_limit_check_parse_proj, nullptr, + Deoptimization::Reason_loop_limit_check, + Op_If); + + PhaseIterGVN& igvn = _phase->igvn(); Node* iff = new_predicate_proj->in(0); - cmp_limit = _igvn.register_new_node_with_optimizer(cmp_limit); - bol = _igvn.register_new_node_with_optimizer(bol); - set_subtree_ctrl(bol, false); - _igvn.replace_input_of(iff, 1, bol); + Node* cmp_limit = igvn.register_new_node_with_optimizer(bol->in(1)); + bol = igvn.register_new_node_with_optimizer(bol); + _phase->set_subtree_ctrl(bol, false); + igvn.replace_input_of(iff, 1, bol); #ifndef PRODUCT // report that the loop predication has been actually performed @@ -397,14 +400,38 @@ void PhaseIdealLoop::insert_loop_limit_check_predicate(ParsePredicateSuccessProj #endif } -Node* PhaseIdealLoop::loop_exit_control(Node* x, IdealLoopTree* loop) { +void CountedLoopConverter::insert_stride_overflow_limit_check() const { + const jlong stride_con = _structure.stride_con(); + + jlong adjusted_stride_con = (stride_con > 0 + ? max_signed_integer(_iv_bt) + : min_signed_integer(_iv_bt)) - _structure.final_limit_correction(); + Node* cmp_limit = CmpNode::make(_structure.limit(), + _phase->igvn().integercon(adjusted_stride_con, _iv_bt), _iv_bt); + Node* bol = new BoolNode(cmp_limit, stride_con > 0 ? BoolTest::le : BoolTest::ge); + + insert_loop_limit_check_predicate(_head->in(LoopNode::EntryControl)->as_IfTrue(), bol); +} + +void CountedLoopConverter::insert_init_trip_limit_check() const { + const jlong stride_con = _structure.stride_con(); + + Node* cmp_limit = CmpNode::make(_structure.phi()->in(LoopNode::EntryControl), _structure.limit(), _iv_bt); + Node* bol = new BoolNode(cmp_limit, stride_con > 0 ? BoolTest::lt : BoolTest::gt); + + insert_loop_limit_check_predicate(_head->in(LoopNode::EntryControl)->as_IfTrue(), bol); +} + +Node* PhaseIdealLoop::loop_exit_control(const IdealLoopTree* loop) const { + Node* head = loop->_head; + // Counted loop head must be a good RegionNode with only 3 not null // control input edges: Self, Entry, LoopBack. - if (x->in(LoopNode::Self) == nullptr || x->req() != 3 || loop->_irreducible) { + if (head->in(LoopNode::Self) == nullptr || head->req() != 3 || loop->_irreducible) { return nullptr; } - Node *init_control = x->in(LoopNode::EntryControl); - Node *back_control = x->in(LoopNode::LoopBackControl); + Node* init_control = head->in(LoopNode::EntryControl); + Node* back_control = head->in(LoopNode::LoopBackControl); if (init_control == nullptr || back_control == nullptr) { // Partially dead return nullptr; } @@ -437,77 +464,7 @@ Node* PhaseIdealLoop::loop_exit_control(Node* x, IdealLoopTree* loop) { return iftrue; } -Node* PhaseIdealLoop::loop_exit_test(Node* back_control, IdealLoopTree* loop, Node*& incr, Node*& limit, BoolTest::mask& bt, float& cl_prob) { - Node* iftrue = back_control; - uint iftrue_op = iftrue->Opcode(); - Node* iff = iftrue->in(0); - BoolNode* test = iff->in(1)->as_Bool(); - bt = test->_test._test; - cl_prob = iff->as_If()->_prob; - if (iftrue_op == Op_IfFalse) { - bt = BoolTest(bt).negate(); - cl_prob = 1.0 - cl_prob; - } - // Get backedge compare - Node* cmp = test->in(1); - if (!cmp->is_Cmp()) { - return nullptr; - } - - // Find the trip-counter increment & limit. Limit must be loop invariant. - incr = cmp->in(1); - limit = cmp->in(2); - - // --------- - // need 'loop()' test to tell if limit is loop invariant - // --------- - - if (!ctrl_is_member(loop, incr)) { // Swapped trip counter and limit? - Node* tmp = incr; // Then reverse order into the CmpI - incr = limit; - limit = tmp; - bt = BoolTest(bt).commute(); // And commute the exit test - } - if (ctrl_is_member(loop, limit)) { // Limit must be loop-invariant - return nullptr; - } - if (!ctrl_is_member(loop, incr)) { // Trip counter must be loop-variant - return nullptr; - } - return cmp; -} - -Node* PhaseIdealLoop::loop_iv_incr(Node* incr, Node* x, IdealLoopTree* loop, Node*& phi_incr) { - if (incr->is_Phi()) { - if (incr->as_Phi()->region() != x || incr->req() != 3) { - return nullptr; // Not simple trip counter expression - } - phi_incr = incr; - incr = phi_incr->in(LoopNode::LoopBackControl); // Assume incr is on backedge of Phi - if (!ctrl_is_member(loop, incr)) { // Trip counter must be loop-variant - return nullptr; - } - } - return incr; -} - -Node* PhaseIdealLoop::loop_iv_stride(Node* incr, Node*& xphi) { - assert(incr->Opcode() == Op_AddI || incr->Opcode() == Op_AddL, "caller resp."); - // Get merge point - xphi = incr->in(1); - Node *stride = incr->in(2); - if (!stride->is_Con()) { // Oops, swap these - if (!xphi->is_Con()) { // Is the other guy a constant? - return nullptr; // Nope, unknown stride, bail out - } - Node *tmp = xphi; // 'incr' is commutative, so ok to swap - xphi = stride; - stride = tmp; - } - return stride; -} - -PhiNode* PhaseIdealLoop::loop_iv_phi(Node* xphi, Node* phi_incr, Node* x) { +PhiNode* PhaseIdealLoop::loop_iv_phi(const Node* xphi, const Node* phi_incr, const Node* head) { if (!xphi->is_Phi()) { return nullptr; // Too much math on the trip counter } @@ -517,44 +474,31 @@ PhiNode* PhaseIdealLoop::loop_iv_phi(Node* xphi, Node* phi_incr, Node* x) { PhiNode *phi = xphi->as_Phi(); // Phi must be of loop header; backedge must wrap to increment - if (phi->region() != x) { + if (phi->region() != head) { return nullptr; } return phi; } -static int check_stride_overflow(jlong final_correction, const TypeInteger* limit_t, BasicType bt) { +CountedLoopConverter::StrideOverflowState CountedLoopConverter::check_stride_overflow(jlong final_correction, + const TypeInteger* limit_t, + BasicType bt) { if (final_correction > 0) { if (limit_t->lo_as_long() > (max_signed_integer(bt) - final_correction)) { - return -1; + return Overflow; } if (limit_t->hi_as_long() > (max_signed_integer(bt) - final_correction)) { - return 1; + return RequireLimitCheck; } } else { if (limit_t->hi_as_long() < (min_signed_integer(bt) - final_correction)) { - return -1; + return Overflow; } if (limit_t->lo_as_long() < (min_signed_integer(bt) - final_correction)) { - return 1; + return RequireLimitCheck; } } - return 0; -} - -static bool condition_stride_ok(BoolTest::mask bt, jlong stride_con) { - // If the condition is inverted and we will be rolling - // through MININT to MAXINT, then bail out. - if (bt == BoolTest::eq || // Bail out, but this loop trips at most twice! - // Odd stride - (bt == BoolTest::ne && stride_con != 1 && stride_con != -1) || - // Count down loop rolls through MAXINT - ((bt == BoolTest::le || bt == BoolTest::lt) && stride_con < 0) || - // Count up loop rolls through MININT - ((bt == BoolTest::ge || bt == BoolTest::gt) && stride_con > 0)) { - return false; // Bail out - } - return true; + return NoOverflow; } Node* PhaseIdealLoop::loop_nest_replace_iv(Node* iv_to_replace, Node* inner_iv, Node* outer_phi, Node* inner_head, @@ -647,10 +591,10 @@ void PhaseIdealLoop::add_parse_predicate(Deoptimization::DeoptReason reason, Nod // Find a safepoint node that dominates the back edge. We need a // SafePointNode so we can use its jvm state to create empty // predicates. -static bool no_side_effect_since_safepoint(Compile* C, Node* x, Node* mem, MergeMemNode* mm, PhaseIdealLoop* phase) { +static bool no_side_effect_since_safepoint(Compile* C, const Node* head, const Node* mem, MergeMemNode* mm, const PhaseIdealLoop* phase) { SafePointNode* safepoint = nullptr; - for (DUIterator_Fast imax, i = x->fast_outs(imax); i < imax; i++) { - Node* u = x->fast_out(i); + for (DUIterator_Fast imax, i = head->fast_outs(imax); i < imax; i++) { + Node* u = head->fast_out(i); if (u->is_memory_phi()) { Node* m = u->in(LoopNode::LoopBackControl); if (u->adr_type() == TypePtr::BOTTOM) { @@ -700,14 +644,14 @@ static bool no_side_effect_since_safepoint(Compile* C, Node* x, Node* mem, Merge return true; } -SafePointNode* PhaseIdealLoop::find_safepoint(Node* back_control, Node* x, IdealLoopTree* loop) { +SafePointNode* PhaseIdealLoop::find_safepoint(Node* back_control, const Node* head, const IdealLoopTree* loop) { IfNode* exit_test = back_control->in(0)->as_If(); SafePointNode* safepoint = nullptr; if (exit_test->in(0)->is_SafePoint() && exit_test->in(0)->outcnt() == 1) { safepoint = exit_test->in(0)->as_SafePoint(); } else { Node* c = back_control; - while (c != x && c->Opcode() != Op_SafePoint) { + while (c != head && c->Opcode() != Op_SafePoint) { c = idom(c); } @@ -746,7 +690,7 @@ SafePointNode* PhaseIdealLoop::find_safepoint(Node* back_control, Node* x, Ideal } } #endif - if (!no_side_effect_since_safepoint(C, x, mem, mm, this)) { + if (!no_side_effect_since_safepoint(C, head, mem, mm, this)) { safepoint = nullptr; } else { assert(mm == nullptr|| _igvn.transform(mm) == mem->as_MergeMem()->base_memory(), "all memory state should have been processed"); @@ -1744,51 +1688,275 @@ LoopNode* PhaseIdealLoop::create_inner_head(IdealLoopTree* loop, BaseCountedLoop } #ifdef ASSERT -void PhaseIdealLoop::check_counted_loop_shape(IdealLoopTree* loop, Node* x, BasicType bt) { - Node* back_control = loop_exit_control(x, loop); +void PhaseIdealLoop::check_counted_loop_shape(IdealLoopTree* loop, Node* head, BasicType bt) { + Node* back_control = loop_exit_control(loop); assert(back_control != nullptr, "no back control"); - BoolTest::mask mask = BoolTest::illegal; - float cl_prob = 0; - Node* incr = nullptr; - Node* limit = nullptr; + LoopExitTest exit_test(back_control, loop, this); + exit_test.build(); + assert(exit_test.is_valid_with_bt(bt), "no exit test"); - Node* cmp = loop_exit_test(back_control, loop, incr, limit, mask, cl_prob); - assert(cmp != nullptr && cmp->Opcode() == Op_Cmp(bt), "no exit test"); + LoopIVIncr iv_incr(head, loop); + iv_incr.build(exit_test.incr()); + assert(iv_incr.is_valid_with_bt(bt), "no incr"); - Node* phi_incr = nullptr; - incr = loop_iv_incr(incr, x, loop, phi_incr); - assert(incr != nullptr && incr->Opcode() == Op_Add(bt), "no incr"); + LoopIVStride stride = LoopIVStride(bt); + stride.build(iv_incr.incr()); + assert(stride.is_valid(), "no stride"); - Node* xphi = nullptr; - Node* stride = loop_iv_stride(incr, xphi); + PhiNode* phi = loop_iv_phi(stride.xphi(), iv_incr.phi_incr(), head); + assert(phi != nullptr && phi->in(LoopNode::LoopBackControl) == iv_incr.incr(), "No phi"); - assert(stride != nullptr, "no stride"); + assert(stride.compute_non_zero_stride_con(exit_test.mask(), bt) != 0, "illegal condition"); - PhiNode* phi = loop_iv_phi(xphi, phi_incr, x); + assert(exit_test.mask() != BoolTest::ne, "unexpected condition"); + assert(iv_incr.phi_incr() == nullptr, "bad loop shape"); + assert(exit_test.cmp()->in(1) == iv_incr.incr(), "bad exit test shape"); - assert(phi != nullptr && phi->in(LoopNode::LoopBackControl) == incr, "No phi"); + // Safepoint on backedge not supported + assert(head->in(LoopNode::LoopBackControl)->Opcode() != Op_SafePoint, "no safepoint on backedge"); +} +#endif - jlong stride_con = stride->get_integer_as_long(bt); +void PhaseIdealLoop::LoopExitTest::build() { + _is_valid = false; - assert(condition_stride_ok(mask, stride_con), "illegal condition"); + const Node* iftrue = _back_control; + uint iftrue_op = iftrue->Opcode(); + Node* iff = iftrue->in(0); + BoolNode* test = iff->in(1)->as_Bool(); + _mask = test->_test._test; + _cl_prob = iff->as_If()->_prob; + if (iftrue_op == Op_IfFalse) { + _mask = BoolTest(_mask).negate(); + _cl_prob = 1.0f - _cl_prob; + } + // Get backedge compare + _cmp = test->in(1); + if (!_cmp->is_Cmp()) { + return; + } - assert(mask != BoolTest::ne, "unexpected condition"); - assert(phi_incr == nullptr, "bad loop shape"); - assert(cmp->in(1) == incr, "bad exit test shape"); + // Find the trip-counter increment & limit. Limit must be loop invariant. + _incr = _cmp->in(1); + _limit = _cmp->in(2); - // Safepoint on backedge not supported - assert(x->in(LoopNode::LoopBackControl)->Opcode() != Op_SafePoint, "no safepoint on backedge"); + // --------- + // need 'loop()' test to tell if limit is loop invariant + // --------- + + if (_loop->is_invariant(_incr)) { // Swapped trip counter and limit? + swap(_incr, _limit); // Then reverse order into the CmpI + _mask = BoolTest(_mask).commute(); // And commute the exit test + } + + if (!_loop->is_invariant(_limit)) { // Limit must be loop-invariant + return; + } + if (_loop->is_invariant(_incr)) { // Trip counter must be loop-variant + return; + } + + _is_valid = true; +} + +// Canonicalize the loop condition if it is 'ne'. +void PhaseIdealLoop::LoopExitTest::canonicalize_mask(jlong stride_con) { + if (_mask != BoolTest::ne) { + return; + } + + assert(stride_con == 1 || stride_con == -1, "simple increment only - checked in CountedLoopConverter"); + + if (stride_con == 1) { + // 'ne' can be replaced with 'lt' only when init < limit. + // This is ensured by the inserted predicate in CountedLoopConverter + _mask = BoolTest::lt; + } else { + // 'ne' can be replaced with 'gt' only when init > limit. + // This is ensured by the inserted predicate in CountedLoopConverter. + _mask = BoolTest::gt; + } +} + +void PhaseIdealLoop::LoopIVIncr::build(Node* old_incr) { + _is_valid = false; + + Node* incr = old_incr; + // Trip-counter increment must be commutative & associative. + if (incr->is_Phi()) { + if (incr->as_Phi()->region() != _head || incr->req() != 3) { + return; // Not simple trip counter expression + } + Node* phi_incr = incr; + Node* back_control = phi_incr->in(LoopNode::LoopBackControl); // Assume incr is on backedge of Phi + if (_loop->_phase->ctrl_is_member(_loop, back_control)) { // Trip counter must be loop-variant + _incr = back_control; + _phi_incr = phi_incr; + _is_valid = true; + return; + } + } + _incr = incr; + _phi_incr = nullptr; + + _is_valid = true; +} + +void PhaseIdealLoop::LoopIVStride::build(const Node* incr) { + _is_valid = false; + + assert(incr->Opcode() == Op_AddI || incr->Opcode() == Op_AddL, "caller resp."); + // Get merge point + _xphi = incr->in(1); + _stride_node = incr->in(2); + if (!_stride_node->is_Con()) { // Oops, swap these + if (!_xphi->is_Con()) { // Is the other guy a constant? + return; // Nope, unknown stride, bail out + } + swap(_xphi, _stride_node); // 'incr' is commutative, so ok to swap + } + + // Iteratively uncast the loop induction variable + // until no more CastII/CastLL nodes are found. + while (_xphi->Opcode() == Op_Cast(_iv_bt)) { + _xphi = _xphi->in(1); + } + + _is_valid = true; +} + +jlong PhaseIdealLoop::LoopIVStride::compute_non_zero_stride_con(const BoolTest::mask mask, const BasicType iv_bt) const { + jlong stride_con = stride_node()->get_integer_as_long(iv_bt); + assert(stride_con != 0, "missed some peephole opt"); // stride constant can never be 0! + + // If the condition is inverted and we will be rolling + // through MININT to MAXINT, then bail out. + if (mask == BoolTest::eq || // Bail out, but this loop trips at most twice! + // Odd stride + (mask == BoolTest::ne && stride_con != 1 && stride_con != -1) || + // Count down loop rolls through MAXINT + ((mask == BoolTest::le || mask == BoolTest::lt) && stride_con < 0) || + // Count up loop rolls through MININT + ((mask == BoolTest::ge || mask == BoolTest::gt) && stride_con > 0)) { + return 0; // Bail out with sentinel = 0 + } + + // Bail out if the stride is too big. + if (stride_con == min_signed_integer(iv_bt) || (ABS(stride_con) > max_signed_integer(iv_bt) / 2)) { + return 0; // Bail out with sentinel = 0 + } + + return stride_con; +} + +void CountedLoopConverter::LoopStructure::build() { + _is_valid = false; + + if (_back_control == nullptr) { + return; + } + + _exit_test.build(); + if (!_exit_test.is_valid_with_bt(_iv_bt)) { + return; // Avoid pointer & float & 64-bit compares + } + + Node* incr = _exit_test.incr(); + if (_exit_test.incr()->Opcode() == Op_Cast(_iv_bt)) { + incr = incr->in(1); + } + + _iv_incr.build(incr); + if (!_iv_incr.is_valid()) { + return; + } + + _truncated_increment.build(_iv_incr.incr()); + if (!_truncated_increment.is_valid()) { + return; // Funny increment opcode + } + assert(_truncated_increment.incr()->Opcode() == Op_Add(_iv_bt), "wrong increment code"); + + _stride.build(_truncated_increment.incr()); + if (!_stride.is_valid()) { + return; + } + + _phi = PhaseIdealLoop::loop_iv_phi(_stride.xphi(), _iv_incr.phi_incr(), _head); + if (_phi == nullptr || + (_truncated_increment.outer_trunc() == nullptr && _phi->in(LoopNode::LoopBackControl) != _truncated_increment.incr()) || + (_truncated_increment.outer_trunc() != nullptr && _phi->in(LoopNode::LoopBackControl) != _truncated_increment.outer_trunc())) { + return; + } + + Node* safepoint = _back_control->in(0)->in(0); + if (_loop->_child != nullptr) { + if (safepoint->Opcode() == Op_SafePoint) { + _safepoint = safepoint->as_SafePoint(); + } else { + _safepoint = nullptr; + } + } else { + _safepoint = _phase->find_safepoint(_back_control, _head, _loop); + } + + _is_valid = true; +} + +// We need to canonicalize the loop exit check by using different values for adjusted_limit: +// (LE1) iv_post_i < limit: Already canonicalized. We can directly use limit as adjusted_limit. +// -> adjusted_limit = limit. +// (LE2) iv_post_i <= limit: +// iv_post_i < limit + 1 +// -> adjusted limit = limit + 1 +// (LE3) iv_pre_i < limit: +// iv_pre_i + stride < limit + stride +// iv_post_i < limit + stride +// -> adjusted_limit = limit + stride +// (LE4) iv_pre_i <= limit: +// iv_pre_i < limit + 1 +// iv_pre_i + stride < limit + stride + 1 +// iv_post_i < limit + stride + 1 +// -> adjusted_limit = limit + stride + 1 +// +// Note that: +// (AL) limit <= adjusted_limit. +jlong CountedLoopConverter::LoopStructure::final_limit_correction() const { + const jlong stride_con = _stride.compute_non_zero_stride_con(_exit_test.mask(), _iv_bt); + + // Accounting for (LE3) and (LE4) where we use pre-incremented phis in the loop exit check. + const jlong limit_correction_for_pre_iv_exit_check = _iv_incr.phi_incr() != nullptr ? stride_con : 0; + + // Accounting for (LE2) and (LE4) where we use <= or >= in the loop exit check. + const jlong limit_correction_for_le_ge_exit_check = _exit_test.should_include_limit() + ? (stride_con > 0 ? 1 : -1) + : 0; + + const jlong limit_correction = limit_correction_for_pre_iv_exit_check + limit_correction_for_le_ge_exit_check; + const jlong canonicalized_correction = stride_con + (stride_con > 0 ? -1 : 1); + + return canonicalized_correction + limit_correction; // final_correction } -#endif #ifdef ASSERT -// convert an int counted loop to a long counted to stress handling of -// long counted loops -bool PhaseIdealLoop::convert_to_long_loop(Node* cmp, Node* phi, IdealLoopTree* loop) { +bool CountedLoopConverter::should_stress_long_counted_loop() { + assert(_checked_for_counted_loop, "must check for counted loop before stressing"); + + return StressLongCountedLoop > 0 && + _iv_bt == T_INT && + !_head->as_Loop()->is_loop_nest_inner_loop() && + _structure.truncated_increment().trunc_type() == TypeInt::INT; // Only stress an int loop (i.e., not char, byte or short) +} + +// Convert an int counted loop to a long counted to stress handling of long counted loops. Returns true upon success. +bool CountedLoopConverter::stress_long_counted_loop() { + assert(should_stress_long_counted_loop(), "stress condition not satisfied"); + + PhaseIterGVN* igvn = &_phase->igvn(); Unique_Node_List iv_nodes; Node_List old_new; - iv_nodes.push(cmp); + iv_nodes.push(_structure.exit_test().cmp()); bool failed = false; for (uint i = 0; i < iv_nodes.size() && !failed; i++) { @@ -1818,12 +1986,12 @@ bool PhaseIdealLoop::convert_to_long_loop(Node* cmp, Node* phi, IdealLoopTree* l fatal("unexpected"); } - for (uint i = 1; i < n->req(); i++) { - Node* in = n->in(i); + for (uint j = 1; j < n->req(); j++) { + Node* in = n->in(j); if (in == nullptr) { continue; } - if (ctrl_is_member(loop, in)) { + if (_loop->is_member(_phase->get_loop(_phase->get_ctrl(in)))) { iv_nodes.push(in); } } @@ -1834,243 +2002,142 @@ bool PhaseIdealLoop::convert_to_long_loop(Node* cmp, Node* phi, IdealLoopTree* l Node* n = iv_nodes.at(i); Node* clone = old_new[n->_idx]; if (clone != nullptr) { - _igvn.remove_dead_node(clone); + igvn->remove_dead_node(clone); } } return false; } + // Make sure we have loop limit checks in place to preserve overflows behaviour after casting to long. + if (_should_insert_stride_overflow_limit_check) { + insert_stride_overflow_limit_check(); + } + + if (_should_insert_init_trip_limit_check) { + insert_init_trip_limit_check(); + } + for (uint i = 0; i < iv_nodes.size(); i++) { Node* n = iv_nodes.at(i); Node* clone = old_new[n->_idx]; - for (uint i = 1; i < n->req(); i++) { - Node* in = n->in(i); + for (uint j = 1; j < n->req(); j++) { + Node* in = n->in(j); if (in == nullptr) { continue; } Node* in_clone = old_new[in->_idx]; if (in_clone == nullptr) { - assert(_igvn.type(in)->isa_int(), ""); + assert(igvn->type(in)->isa_int(), ""); in_clone = new ConvI2LNode(in); - _igvn.register_new_node_with_optimizer(in_clone); - set_subtree_ctrl(in_clone, false); + igvn->register_new_node_with_optimizer(in_clone); + _phase->set_subtree_ctrl(in_clone, false); } if (in_clone->in(0) == nullptr) { - in_clone->set_req(0, C->top()); - clone->set_req(i, in_clone); + in_clone->set_req(0, _phase->C->top()); + clone->set_req(j, in_clone); in_clone->set_req(0, nullptr); } else { - clone->set_req(i, in_clone); + clone->set_req(j, in_clone); } } - _igvn.register_new_node_with_optimizer(clone); + igvn->register_new_node_with_optimizer(clone); } - set_ctrl(old_new[phi->_idx], phi->in(0)); + _phase->set_ctrl(old_new[_structure.phi()->_idx], _structure.phi()->in(0)); for (uint i = 0; i < iv_nodes.size(); i++) { Node* n = iv_nodes.at(i); Node* clone = old_new[n->_idx]; - set_subtree_ctrl(clone, false); + _phase->set_subtree_ctrl(clone, false); Node* m = n->Opcode() == Op_CmpI ? clone : nullptr; - for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { - Node* u = n->fast_out(i); + for (DUIterator_Fast imax, j = n->fast_outs(imax); j < imax; j++) { + Node* u = n->fast_out(j); if (iv_nodes.member(u)) { continue; } if (m == nullptr) { m = new ConvL2INode(clone); - _igvn.register_new_node_with_optimizer(m); - set_subtree_ctrl(m, false); + igvn->register_new_node_with_optimizer(m); + _phase->set_subtree_ctrl(m, false); } - _igvn.rehash_node_delayed(u); - int nb = u->replace_edge(n, m, &_igvn); - --i, imax -= nb; + igvn->rehash_node_delayed(u); + int nb = u->replace_edge(n, m, igvn); + --j, imax -= nb; } } return true; } #endif -//------------------------------is_counted_loop-------------------------------- -bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv_bt) { - PhaseGVN *gvn = &_igvn; +bool PhaseIdealLoop::try_convert_to_counted_loop(Node* head, IdealLoopTree*& loop, const BasicType iv_bt) { + CountedLoopConverter converter(this, head, loop, iv_bt); + if (converter.is_counted_loop()) { +#ifdef ASSERT + // Stress by converting int counted loops to long counted loops + if (converter.should_stress_long_counted_loop() && converter.stress_long_counted_loop()) { + return false; + } +#endif - Node* back_control = loop_exit_control(x, loop); - if (back_control == nullptr) { - return false; + loop = converter.convert(); + return true; } - BoolTest::mask bt = BoolTest::illegal; - float cl_prob = 0; - Node* incr = nullptr; - Node* limit = nullptr; - Node* cmp = loop_exit_test(back_control, loop, incr, limit, bt, cl_prob); - if (cmp == nullptr || cmp->Opcode() != Op_Cmp(iv_bt)) { - return false; // Avoid pointer & float & 64-bit compares - } + return false; +} - // Trip-counter increment must be commutative & associative. - if (incr->Opcode() == Op_Cast(iv_bt)) { - incr = incr->in(1); - } +bool CountedLoopConverter::is_counted_loop() { + PhaseIterGVN* igvn = &_phase->igvn(); - Node* phi_incr = nullptr; - incr = loop_iv_incr(incr, x, loop, phi_incr); - if (incr == nullptr) { + _structure.build(); + if (!_structure.is_valid()) { return false; } - Node* trunc1 = nullptr; - Node* trunc2 = nullptr; - const TypeInteger* iv_trunc_t = nullptr; - Node* orig_incr = incr; - if (!(incr = CountedLoopNode::match_incr_with_optional_truncation(incr, &trunc1, &trunc2, &iv_trunc_t, iv_bt))) { - return false; // Funny increment opcode - } - assert(incr->Opcode() == Op_Add(iv_bt), "wrong increment code"); - - Node* xphi = nullptr; - Node* stride = loop_iv_stride(incr, xphi); + // ================================================= + // ---- Is the loop trip counted? ---- - if (stride == nullptr) { + // Check trip counter will end up higher than the limit + if (_structure.is_infinite_loop()) { return false; } - // Iteratively uncast the loop induction variable - // until no more CastII/CastLL nodes are found. - while (xphi->Opcode() == Op_Cast(iv_bt)) { - xphi = xphi->in(1); - } - // Stride must be constant - jlong stride_con = stride->get_integer_as_long(iv_bt); - assert(stride_con != 0, "missed some peephole opt"); - - PhiNode* phi = loop_iv_phi(xphi, phi_incr, x); - - if (phi == nullptr || - (trunc1 == nullptr && phi->in(LoopNode::LoopBackControl) != incr) || - (trunc1 != nullptr && phi->in(LoopNode::LoopBackControl) != trunc1)) { + const jlong stride_con = _structure.stride_con(); + if (stride_con == 0) { return false; } - Node* iftrue = back_control; - uint iftrue_op = iftrue->Opcode(); - Node* iff = iftrue->in(0); - BoolNode* test = iff->in(1)->as_Bool(); - - const TypeInteger* limit_t = gvn->type(limit)->is_integer(iv_bt); - if (trunc1 != nullptr) { - // When there is a truncation, we must be sure that after the truncation - // the trip counter will end up higher than the limit, otherwise we are looking - // at an endless loop. Can happen with range checks. - - // Example: - // int i = 0; - // while (true) - // sum + = array[i]; - // i++; - // i = i && 0x7fff; - // } - // - // If the array is shorter than 0x8000 this exits through a AIOOB - // - Counted loop transformation is ok - // If the array is longer then this is an endless loop - // - No transformation can be done. - - const TypeInteger* incr_t = gvn->type(orig_incr)->is_integer(iv_bt); - if (limit_t->hi_as_long() > incr_t->hi_as_long()) { - // if the limit can have a higher value than the increment (before the phi) - return false; - } - } - - Node *init_trip = phi->in(LoopNode::EntryControl); - - // If iv trunc type is smaller than int, check for possible wrap. - if (!TypeInteger::bottom(iv_bt)->higher_equal(iv_trunc_t)) { - assert(trunc1 != nullptr, "must have found some truncation"); - - // Get a better type for the phi (filtered thru if's) - const TypeInteger* phi_ft = filtered_type(phi); - - // Can iv take on a value that will wrap? - // - // Ensure iv's limit is not within "stride" of the wrap value. - // - // Example for "short" type - // Truncation ensures value is in the range -32768..32767 (iv_trunc_t) - // If the stride is +10, then the last value of the induction - // variable before the increment (phi_ft->_hi) must be - // <= 32767 - 10 and (phi_ft->_lo) must be >= -32768 to - // ensure no truncation occurs after the increment. - - if (stride_con > 0) { - if (iv_trunc_t->hi_as_long() - phi_ft->hi_as_long() < stride_con || - iv_trunc_t->lo_as_long() > phi_ft->lo_as_long()) { - return false; // truncation may occur - } - } else if (stride_con < 0) { - if (iv_trunc_t->lo_as_long() - phi_ft->lo_as_long() > stride_con || - iv_trunc_t->hi_as_long() < phi_ft->hi_as_long()) { - return false; // truncation may occur - } - } - // No possibility of wrap so truncation can be discarded - // Promote iv type to Int - } else { - assert(trunc1 == nullptr && trunc2 == nullptr, "no truncation for int"); - } - - if (!condition_stride_ok(bt, stride_con)) { + // Check iv type can be promoted to int for short/char/byte loops + if (has_truncation_wrap(_structure.truncated_increment(), _structure.phi(), stride_con)) { return false; } - const TypeInteger* init_t = gvn->type(init_trip)->is_integer(iv_bt); - - if (stride_con > 0) { - if (init_t->lo_as_long() > max_signed_integer(iv_bt) - stride_con) { - return false; // cyclic loop - } - } else { - if (init_t->hi_as_long() < min_signed_integer(iv_bt) - stride_con) { - return false; // cyclic loop - } - } - - if (phi_incr != nullptr && bt != BoolTest::ne) { - // check if there is a possibility of IV overflowing after the first increment - if (stride_con > 0) { - if (init_t->hi_as_long() > max_signed_integer(iv_bt) - stride_con) { - return false; - } - } else { - if (init_t->lo_as_long() < min_signed_integer(iv_bt) - stride_con) { - return false; - } - } + // Check iv is not overflowing + Node* init_trip = _structure.phi()->in(LoopNode::EntryControl); + const TypeInteger* init_t = igvn->type(init_trip)->is_integer(_iv_bt); + if (is_iv_overflowing(init_t, stride_con, _structure.iv_incr().phi_incr(), _structure.exit_test().mask())) { + return false; } // ================================================= // ---- SUCCESS! Found A Trip-Counted Loop! ----- - // - if (x->Opcode() == Op_Region) { - // x has not yet been transformed to Loop or LongCountedLoop. + if (_head->Opcode() == Op_Region) { + // head has not yet been transformed to Loop or LongCountedLoop. // This should only happen if we are inside an infinite loop. // It happens like this: // build_loop_tree -> do not attach infinite loop and nested loops // beautify_loops -> does not transform the infinite and nested loops to LoopNode, because not attached yet // build_loop_tree -> find and attach infinite and nested loops // counted_loop -> nested Regions are not yet transformed to LoopNodes, we land here - assert(x->as_Region()->is_in_infinite_subgraph(), - "x can only be a Region and not Loop if inside infinite loop"); + assert(_head->as_Region()->is_in_infinite_subgraph(), + "head can only be a Region and not Loop if inside infinite loop"); // Come back later when Region is transformed to LoopNode return false; } - assert(x->Opcode() == Op_Loop || x->Opcode() == Op_LongCountedLoop, "regular loops only"); - C->print_method(PHASE_BEFORE_CLOOPS, 3, x); + assert(_head->Opcode() == Op_Loop || _head->Opcode() == Op_LongCountedLoop, "regular loops only"); + _phase->C->print_method(PHASE_BEFORE_CLOOPS, 3, _head); // =================================================== // We can only convert this loop to a counted loop if we can guarantee that the iv phi will never overflow at runtime. @@ -2123,23 +2190,10 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv // When converting a loop to a counted loop, we want to have a canonicalized loop exit check of the form: // iv_post_i < adjusted_limit // - // If that is not the case, we need to canonicalize the loop exit check by using different values for adjusted_limit: - // (LE1) iv_post_i < limit: Already canonicalized. We can directly use limit as adjusted_limit. - // -> adjusted_limit = limit. - // (LE2) iv_post_i <= limit: - // iv_post_i < limit + 1 - // -> adjusted limit = limit + 1 - // (LE3) iv_pre_i < limit: - // iv_pre_i + stride < limit + stride - // iv_post_i < limit + stride - // -> adjusted_limit = limit + stride - // (LE4) iv_pre_i <= limit: - // iv_pre_i < limit + 1 - // iv_pre_i + stride < limit + stride + 1 - // iv_post_i < limit + stride + 1 - // -> adjusted_limit = limit + stride + 1 + // If that is not the case, we need to canonicalize the loop exit check by using different values for adjusted_limit + // (see LoopStructure::final_limit_correction()). // - // Note that: + // Note that after canonicalization: // (AL) limit <= adjusted_limit. // // The following loop invariant has to hold for counted loops with n iterations (i.e. loop exit check true after n-th @@ -2259,78 +2313,63 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv // there is no overflow of the iv phi after the first iteration. In this case, we don't need to check (ii) // again and can skip the predicate. - // Check (vi) and bail out if the stride is too big. - if (stride_con == min_signed_integer(iv_bt) || (ABS(stride_con) > max_signed_integer(iv_bt) / 2)) { - return false; - } - - // Accounting for (LE3) and (LE4) where we use pre-incremented phis in the loop exit check. - const jlong limit_correction_for_pre_iv_exit_check = (phi_incr != nullptr) ? stride_con : 0; - - // Accounting for (LE2) and (LE4) where we use <= or >= in the loop exit check. - const bool includes_limit = (bt == BoolTest::le || bt == BoolTest::ge); - const jlong limit_correction_for_le_ge_exit_check = (includes_limit ? (stride_con > 0 ? 1 : -1) : 0); + const TypeInteger* limit_t = igvn->type(_structure.limit())->is_integer(_iv_bt); + StrideOverflowState stride_overflow_state = check_stride_overflow(_structure.final_limit_correction(), limit_t, _iv_bt); - const jlong limit_correction = limit_correction_for_pre_iv_exit_check + limit_correction_for_le_ge_exit_check; - const jlong canonicalized_correction = stride_con + (stride_con > 0 ? -1 : 1); - const jlong final_correction = canonicalized_correction + limit_correction; + Node* init_control = _head->in(LoopNode::EntryControl); + const Predicates predicates(init_control); + const PredicateBlock* loop_limit_check_predicate_block = predicates.loop_limit_check_predicate_block(); - int sov = check_stride_overflow(final_correction, limit_t, iv_bt); - Node* init_control = x->in(LoopNode::EntryControl); + if (stride_overflow_state == Overflow) { + return false; // Bailout: integer overflow is certain. + } - // If sov==0, limit's type always satisfies the condition, for + // If stride_overflow_state == NO_OVERFLOW, limit's type always satisfies the condition, for // example, when it is an array length. - if (sov != 0) { - if (sov < 0) { - return false; // Bailout: integer overflow is certain. - } + + _should_insert_stride_overflow_limit_check = false; + if (stride_overflow_state == RequireLimitCheck) { // (1) Loop Limit Check Predicate is required because we could not statically prove that // limit + final_correction = adjusted_limit - 1 + stride <= max_int - assert(!x->as_Loop()->is_loop_nest_inner_loop(), "loop was transformed"); - const Predicates predicates(init_control); - const PredicateBlock* loop_limit_check_predicate_block = predicates.loop_limit_check_predicate_block(); + assert(!_head->as_Loop()->is_loop_nest_inner_loop(), "loop was transformed"); if (!loop_limit_check_predicate_block->has_parse_predicate()) { // The Loop Limit Check Parse Predicate is not generated if this method trapped here before. #ifdef ASSERT if (TraceLoopLimitCheck) { tty->print("Missing Loop Limit Check Parse Predicate:"); - loop->dump_head(); - x->dump(1); + _loop->dump_head(); + _head->dump(1); } #endif return false; } ParsePredicateNode* loop_limit_check_parse_predicate = loop_limit_check_predicate_block->parse_predicate(); - if (!is_dominator(get_ctrl(limit), loop_limit_check_parse_predicate->in(0))) { + if (!_phase->is_dominator(_phase->get_ctrl(_structure.limit()), loop_limit_check_parse_predicate->in(0))) { return false; } - Node* cmp_limit; - Node* bol; - - if (stride_con > 0) { - cmp_limit = CmpNode::make(limit, _igvn.integercon(max_signed_integer(iv_bt) - final_correction, iv_bt), iv_bt); - bol = new BoolNode(cmp_limit, BoolTest::le); - } else { - cmp_limit = CmpNode::make(limit, _igvn.integercon(min_signed_integer(iv_bt) - final_correction, iv_bt), iv_bt); - bol = new BoolNode(cmp_limit, BoolTest::ge); - } - - insert_loop_limit_check_predicate(init_control->as_IfTrue(), cmp_limit, bol); + _should_insert_stride_overflow_limit_check = true; } // (2.3) const bool init_plus_stride_could_overflow = - (stride_con > 0 && init_t->hi_as_long() > max_signed_integer(iv_bt) - stride_con) || - (stride_con < 0 && init_t->lo_as_long() < min_signed_integer(iv_bt) - stride_con); + (stride_con > 0 && init_t->hi_as_long() > max_signed_integer(_iv_bt) - stride_con) || + (stride_con < 0 && init_t->lo_as_long() < min_signed_integer(_iv_bt) - stride_con); + // (2.1) - const bool init_gte_limit = (stride_con > 0 && init_t->hi_as_long() >= limit_t->lo_as_long()) || - (stride_con < 0 && init_t->lo_as_long() <= limit_t->hi_as_long()); + const bool init_gte_limit = + (stride_con > 0 && init_t->hi_as_long() >= limit_t->lo_as_long()) || + (stride_con < 0 && init_t->lo_as_long() <= limit_t->hi_as_long()); + _should_insert_init_trip_limit_check = false; if (init_gte_limit && // (2.1) - ((bt == BoolTest::ne || init_plus_stride_could_overflow) && // (2.3) - !has_dominating_loop_limit_check(init_trip, limit, stride_con, iv_bt, init_control))) { // (2.2) + ((_structure.exit_test().mask() == BoolTest::ne || init_plus_stride_could_overflow) && // (2.3) + !has_dominating_loop_limit_check(init_trip, + _structure.limit(), + stride_con, + _iv_bt, + init_control))) { // (2.2) // (2) Iteration Loop Limit Check Predicate is required because neither (2.1), (2.2), nor (2.3) holds. // We use the following condition: // - stride > 0: init < limit @@ -2340,15 +2379,13 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv // a requirement). We transform the loop exit check by using a less-than-operator. By doing so, we must always // check that init < limit. Otherwise, we could have a different number of iterations at runtime. - const Predicates predicates(init_control); - const PredicateBlock* loop_limit_check_predicate_block = predicates.loop_limit_check_predicate_block(); if (!loop_limit_check_predicate_block->has_parse_predicate()) { // The Loop Limit Check Parse Predicate is not generated if this method trapped here before. #ifdef ASSERT if (TraceLoopLimitCheck) { tty->print("Missing Loop Limit Check Parse Predicate:"); - loop->dump_head(); - x->dump(1); + _loop->dump_head(); + _head->dump(1); } #endif return false; @@ -2356,81 +2393,188 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv ParsePredicateNode* loop_limit_check_parse_predicate = loop_limit_check_predicate_block->parse_predicate(); Node* parse_predicate_entry = loop_limit_check_parse_predicate->in(0); - if (!is_dominator(get_ctrl(limit), parse_predicate_entry) || - !is_dominator(get_ctrl(init_trip), parse_predicate_entry)) { + if (!_phase->is_dominator(_phase->get_ctrl(_structure.limit()), parse_predicate_entry) || + !_phase->is_dominator(_phase->get_ctrl(init_trip), parse_predicate_entry)) { return false; } - Node* cmp_limit; - Node* bol; + _should_insert_init_trip_limit_check = true; + } + + _structure.exit_test().canonicalize_mask(stride_con); + + if (is_safepoint_invalid(_structure.sfpt())) { + return false; + } + +#ifdef ASSERT + _checked_for_counted_loop = true; +#endif + + return true; +} + +bool CountedLoopConverter::is_iv_overflowing(const TypeInteger* init_t, jlong stride_con, Node* phi_increment, + BoolTest::mask mask) const { + if (stride_con > 0) { + if (init_t->lo_as_long() > max_signed_integer(_iv_bt) - stride_con) { + return true; // cyclic loop + } + } else { + if (init_t->hi_as_long() < min_signed_integer(_iv_bt) - stride_con) { + return true; // cyclic loop + } + } + if (phi_increment != nullptr && mask != BoolTest::ne) { + // check if there is a possibility of IV overflowing after the first increment if (stride_con > 0) { - cmp_limit = CmpNode::make(init_trip, limit, iv_bt); - bol = new BoolNode(cmp_limit, BoolTest::lt); + if (init_t->hi_as_long() > max_signed_integer(_iv_bt) - stride_con) { + return true; + } } else { - cmp_limit = CmpNode::make(init_trip, limit, iv_bt); - bol = new BoolNode(cmp_limit, BoolTest::gt); + if (init_t->lo_as_long() < min_signed_integer(_iv_bt) - stride_con) { + return true; + } } + } + + return false; +} - insert_loop_limit_check_predicate(init_control->as_IfTrue(), cmp_limit, bol); +bool CountedLoopConverter::LoopStructure::is_infinite_loop() const { + PhaseIterGVN& igvn = _phase->igvn(); + const TypeInteger* limit_t = igvn.type(limit())->is_integer(_iv_bt); + + if (_truncated_increment.outer_trunc() != nullptr) { + // When there is a truncation, we must be sure that after the truncation + // the trip counter will end up higher than the limit, otherwise we are looking + // at an endless loop. Can happen with range checks. + + // Example: + // int i = 0; + // while (true) { + // sum + = array[i]; + // i++; + // i = i && 0x7fff; + // } + // + // If the array is shorter than 0x8000 this exits through an AIOOB + // - Counted loop transformation is ok + // If the array is longer then this is an endless loop + // - No transformation can be done. + + const TypeInteger* incr_t = igvn.type(_iv_incr.incr())->is_integer(_iv_bt); + if (limit_t->hi_as_long() > incr_t->hi_as_long()) { + // if the limit can have a higher value than the increment (before the phi) + return true; + } } - if (bt == BoolTest::ne) { - // Now we need to canonicalize the loop condition if it is 'ne'. - assert(stride_con == 1 || stride_con == -1, "simple increment only - checked before"); + return false; +} + +bool CountedLoopConverter::has_truncation_wrap(const TruncatedIncrement& truncation, Node* phi, jlong stride_con) { + // If iv trunc type is smaller than int (i.e., short/char/byte), check for possible wrap. + if (!TypeInteger::bottom(_iv_bt)->higher_equal(truncation.trunc_type())) { + assert(truncation.outer_trunc() != nullptr, "must have found some truncation"); + + // Get a better type for the phi (filtered thru if's) + const TypeInteger* phi_ft = filtered_type(phi); + + // Can iv take on a value that will wrap? + // + // Ensure iv's limit is not within "stride" of the wrap value. + // + // Example for "short" type + // Truncation ensures value is in the range -32768..32767 (iv_trunc_t) + // If the stride is +10, then the last value of the induction + // variable before the increment (phi_ft->_hi) must be + // <= 32767 - 10 and (phi_ft->_lo) must be >= -32768 to + // ensure no truncation occurs after the increment. + if (stride_con > 0) { - // 'ne' can be replaced with 'lt' only when init < limit. This is ensured by the inserted predicate above. - bt = BoolTest::lt; - } else { - assert(stride_con < 0, "must be"); - // 'ne' can be replaced with 'gt' only when init > limit. This is ensured by the inserted predicate above. - bt = BoolTest::gt; + if (truncation.trunc_type()->hi_as_long() - phi_ft->hi_as_long() < stride_con || + truncation.trunc_type()->lo_as_long() > phi_ft->lo_as_long()) { + return true; // truncation may occur + } + } else if (stride_con < 0) { + if (truncation.trunc_type()->lo_as_long() - phi_ft->lo_as_long() > stride_con || + truncation.trunc_type()->hi_as_long() < phi_ft->hi_as_long()) { + return true; // truncation may occur + } } - } - Node* sfpt = nullptr; - if (loop->_child == nullptr) { - sfpt = find_safepoint(back_control, x, loop); + // No possibility of wrap so truncation can be discarded + // Promote iv type to Int } else { - sfpt = iff->in(0); - if (sfpt->Opcode() != Op_SafePoint) { - sfpt = nullptr; - } + assert(Type::equals(truncation.trunc_type(), TypeInt::INT) || Type::equals(truncation.trunc_type(), TypeLong::LONG), + "unexpected truncation type"); + assert(truncation.outer_trunc() == nullptr && truncation.inner_trunc() == nullptr, "no truncation for int"); } - if (x->in(LoopNode::LoopBackControl)->Opcode() == Op_SafePoint) { - Node* backedge_sfpt = x->in(LoopNode::LoopBackControl); - if (((iv_bt == T_INT && LoopStripMiningIter != 0) || - iv_bt == T_LONG) && + return false; +} + +SafePointNode* CountedLoopConverter::find_safepoint(Node* iftrue) { + Node* iff = iftrue->in(0); + + if (_loop->_child == nullptr) { + return _phase->find_safepoint(iftrue, _head, _loop); + } + + Node* sfpt = iff->in(0); + if (sfpt->Opcode() == Op_SafePoint) { + return sfpt->as_SafePoint(); + } + return nullptr; +} + +bool CountedLoopConverter::is_safepoint_invalid(SafePointNode* sfpt) const { + if (_head->in(LoopNode::LoopBackControl)->Opcode() == Op_SafePoint) { + if (((_iv_bt == T_INT && LoopStripMiningIter != 0) || + _iv_bt == T_LONG) && sfpt == nullptr) { // Leaving the safepoint on the backedge and creating a // CountedLoop will confuse optimizations. We can't move the // safepoint around because its jvm state wouldn't match a new // location. Give up on that loop. - return false; - } - if (is_deleteable_safept(backedge_sfpt)) { - replace_node_and_forward_ctrl(backedge_sfpt, iftrue); - if (loop->_safepts != nullptr) { - loop->_safepts->yank(backedge_sfpt); - } - loop->_tail = iftrue; + return true; } } + return false; +} +IdealLoopTree* CountedLoopConverter::convert() { #ifdef ASSERT - if (iv_bt == T_INT && - !x->as_Loop()->is_loop_nest_inner_loop() && - StressLongCountedLoop > 0 && - trunc1 == nullptr && - convert_to_long_loop(cmp, phi, loop)) { - return false; - } + assert(_checked_for_counted_loop, "must check for counted loop before conversion"); #endif - Node* adjusted_limit = limit; - if (phi_incr != nullptr) { + PhaseIterGVN* igvn = &_phase->igvn(); + + if (_should_insert_stride_overflow_limit_check) { + insert_stride_overflow_limit_check(); + } + + if (_should_insert_init_trip_limit_check) { + insert_init_trip_limit_check(); + } + + Node* back_control = _phase->loop_exit_control(_loop); + if (_head->in(LoopNode::LoopBackControl)->Opcode() == Op_SafePoint) { + Node* backedge_sfpt = _head->in(LoopNode::LoopBackControl); + if (_phase->is_deleteable_safept(backedge_sfpt)) { + _phase->replace_node_and_forward_ctrl(backedge_sfpt, back_control); + if (_loop->_safepts != nullptr) { + _loop->_safepts->yank(backedge_sfpt); + } + _loop->_tail = back_control; + } + } + + Node* adjusted_limit = _structure.limit(); + if (_structure.iv_incr().phi_incr() != nullptr) { // If compare points directly to the phi we need to adjust // the compare so that it points to the incr. Limit have // to be adjusted to keep trip count the same and we @@ -2440,128 +2584,147 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv // is converted to // i = init; do {} while(++i < limit+1); // - adjusted_limit = gvn->transform(AddNode::make(limit, stride, iv_bt)); + adjusted_limit = igvn->transform(AddNode::make(_structure.limit(), _structure.stride().stride_node(), _iv_bt)); } - if (includes_limit) { + BoolTest::mask mask = _structure.exit_test().mask(); + if (_structure.exit_test().should_include_limit()) { // The limit check guaranties that 'limit <= (max_jint - stride)' so // we can convert 'i <= limit' to 'i < limit+1' since stride != 0. - // - Node* one = (stride_con > 0) ? gvn->integercon( 1, iv_bt) : gvn->integercon(-1, iv_bt); - adjusted_limit = gvn->transform(AddNode::make(adjusted_limit, one, iv_bt)); - if (bt == BoolTest::le) - bt = BoolTest::lt; - else if (bt == BoolTest::ge) - bt = BoolTest::gt; - else + Node* one = (_structure.stride_con() > 0) ? igvn->integercon(1, _iv_bt) : igvn->integercon(-1, _iv_bt); + adjusted_limit = igvn->transform(AddNode::make(adjusted_limit, one, _iv_bt)); + if (mask == BoolTest::le) { + mask = BoolTest::lt; + } else if (mask == BoolTest::ge) { + mask = BoolTest::gt; + } else { ShouldNotReachHere(); + } } - set_subtree_ctrl(adjusted_limit, false); + _phase->set_subtree_ctrl(adjusted_limit, false); // Build a canonical trip test. // Clone code, as old values may be in use. - incr = incr->clone(); - incr->set_req(1,phi); - incr->set_req(2,stride); - incr = _igvn.register_new_node_with_optimizer(incr); - set_early_ctrl(incr, false); - _igvn.rehash_node_delayed(phi); - phi->set_req_X( LoopNode::LoopBackControl, incr, &_igvn ); + Node* incr = _structure.truncated_increment().incr()->clone(); + incr->set_req(1, _structure.phi()); + incr->set_req(2, _structure.stride().stride_node()); + incr = igvn->register_new_node_with_optimizer(incr); + _phase->set_early_ctrl(incr, false); + igvn->rehash_node_delayed(_structure.phi()); + _structure.phi()->set_req_X(LoopNode::LoopBackControl, incr, igvn); // If phi type is more restrictive than Int, raise to // Int to prevent (almost) infinite recursion in igvn // which can only handle integer types for constants or minint..maxint. - if (!TypeInteger::bottom(iv_bt)->higher_equal(phi->bottom_type())) { - Node* nphi = PhiNode::make(phi->in(0), phi->in(LoopNode::EntryControl), TypeInteger::bottom(iv_bt)); + Node* phi = _structure.phi(); + if (!TypeInteger::bottom(_iv_bt)->higher_equal(phi->bottom_type())) { + Node* nphi = + PhiNode::make(phi->in(0), phi->in(LoopNode::EntryControl), TypeInteger::bottom(_iv_bt)); nphi->set_req(LoopNode::LoopBackControl, phi->in(LoopNode::LoopBackControl)); - nphi = _igvn.register_new_node_with_optimizer(nphi); - set_ctrl(nphi, get_ctrl(phi)); - _igvn.replace_node(phi, nphi); + nphi = igvn->register_new_node_with_optimizer(nphi); + _phase->set_ctrl(nphi, _phase->get_ctrl(phi)); + igvn->replace_node(phi, nphi); phi = nphi->as_Phi(); } - cmp = cmp->clone(); - cmp->set_req(1,incr); - cmp->set_req(2, adjusted_limit); - cmp = _igvn.register_new_node_with_optimizer(cmp); - set_ctrl(cmp, iff->in(0)); - test = test->clone()->as_Bool(); - (*(BoolTest*)&test->_test)._test = bt; - test->set_req(1,cmp); - _igvn.register_new_node_with_optimizer(test); - set_ctrl(test, iff->in(0)); + Node* iftrue = back_control; + const uint iftrue_op = iftrue->Opcode(); + Node* iff = iftrue->in(0); + + // Replace the old CmpNode with new adjusted_limit + Node* new_cmp = _structure.exit_test().cmp()->clone(); + new_cmp->set_req(1, incr); + new_cmp->set_req(2, adjusted_limit); + new_cmp = igvn->register_new_node_with_optimizer(new_cmp); + _phase->set_ctrl(new_cmp, iff->in(0)); + + // Replace the old BoolNode with new CmpNode + BoolNode* new_test = iff->in(1)->clone()->as_Bool(); + const_cast(&new_test->_test)->_test = mask; // Yes, it's a const, but it's a newly cloned node so we should be fine. + new_test->set_req(1, new_cmp); + igvn->register_new_node_with_optimizer(new_test); + _phase->set_ctrl(new_test, iff->in(0)); // Replace the old IfNode with a new LoopEndNode - Node *lex = _igvn.register_new_node_with_optimizer(BaseCountedLoopEndNode::make(iff->in(0), test, cl_prob, iff->as_If()->_fcnt, iv_bt)); - IfNode *le = lex->as_If(); - uint dd = dom_depth(iff); - set_idom(le, le->in(0), dd); // Update dominance for loop exit - set_loop(le, loop); + Node* loop_end = igvn->register_new_node_with_optimizer(BaseCountedLoopEndNode::make(iff->in(0), + new_test, + _structure.exit_test().cl_prob(), + iff->as_If()->_fcnt, + _iv_bt)); + IfNode* loop_end_exit = loop_end->as_If(); + const uint dd = _phase->dom_depth(iff); + _phase->set_idom(loop_end_exit, loop_end_exit->in(0), dd); // Update dominance for loop exit + _phase->set_loop(loop_end_exit, _loop); // Get the loop-exit control - Node *iffalse = iff->as_If()->proj_out(!(iftrue_op == Op_IfTrue)); + Node* iffalse = iff->as_If()->proj_out(!(iftrue_op == Op_IfTrue)); // Need to swap loop-exit and loop-back control? if (iftrue_op == Op_IfFalse) { - Node *ift2=_igvn.register_new_node_with_optimizer(new IfTrueNode (le)); - Node *iff2=_igvn.register_new_node_with_optimizer(new IfFalseNode(le)); + Node* ift2 = igvn->register_new_node_with_optimizer(new IfTrueNode(loop_end_exit)); + Node* iff2 = igvn->register_new_node_with_optimizer(new IfFalseNode(loop_end_exit)); - loop->_tail = back_control = ift2; - set_loop(ift2, loop); - set_loop(iff2, get_loop(iffalse)); + _loop->_tail = back_control = ift2; + _phase->set_loop(ift2, _loop); + _phase->set_loop(iff2, _phase->get_loop(iffalse)); // Lazy update of 'get_ctrl' mechanism. - replace_node_and_forward_ctrl(iffalse, iff2); - replace_node_and_forward_ctrl(iftrue, ift2); + _phase->replace_node_and_forward_ctrl(iffalse, iff2); + _phase->replace_node_and_forward_ctrl(iftrue, ift2); // Swap names iffalse = iff2; - iftrue = ift2; + iftrue = ift2; } else { - _igvn.rehash_node_delayed(iffalse); - _igvn.rehash_node_delayed(iftrue); - iffalse->set_req_X( 0, le, &_igvn ); - iftrue ->set_req_X( 0, le, &_igvn ); + igvn->rehash_node_delayed(iffalse); + igvn->rehash_node_delayed(iftrue); + iffalse->set_req_X(0, loop_end_exit, igvn); + iftrue->set_req_X(0, loop_end_exit, igvn); } - set_idom(iftrue, le, dd+1); - set_idom(iffalse, le, dd+1); + _phase->set_idom(iftrue, loop_end_exit, dd + 1); + _phase->set_idom(iffalse, loop_end_exit, dd + 1); assert(iff->outcnt() == 0, "should be dead now"); - replace_node_and_forward_ctrl(iff, le); // fix 'get_ctrl' + _phase->replace_node_and_forward_ctrl(iff, loop_end_exit); // fix 'get_ctrl' + Node* init_control = _head->in(LoopNode::EntryControl); Node* entry_control = init_control; - bool strip_mine_loop = iv_bt == T_INT && - loop->_child == nullptr && - sfpt != nullptr && - !loop->_has_call && - is_deleteable_safept(sfpt); + bool strip_mine_loop = _iv_bt == T_INT && + _loop->_child == nullptr && + _structure.sfpt() != nullptr && + !_loop->_has_call && + _phase->is_deleteable_safept(_structure.sfpt()); IdealLoopTree* outer_ilt = nullptr; if (strip_mine_loop) { - outer_ilt = create_outer_strip_mined_loop(init_control, loop, cl_prob, le->_fcnt, - entry_control, iffalse); + outer_ilt = _phase->create_outer_strip_mined_loop(init_control, + _loop, + _structure.exit_test().cl_prob(), + loop_end_exit->_fcnt, + entry_control, + iffalse); } // Now setup a new CountedLoopNode to replace the existing LoopNode - BaseCountedLoopNode *l = BaseCountedLoopNode::make(entry_control, back_control, iv_bt); - l->set_unswitch_count(x->as_Loop()->unswitch_count()); // Preserve + BaseCountedLoopNode* l = BaseCountedLoopNode::make(entry_control, back_control, _iv_bt); + l->set_unswitch_count(_head->as_Loop()->unswitch_count()); // Preserve // The following assert is approximately true, and defines the intention // of can_be_counted_loop. It fails, however, because phase->type // is not yet initialized for this loop and its parts. //assert(l->can_be_counted_loop(this), "sanity"); - _igvn.register_new_node_with_optimizer(l); - set_loop(l, loop); - loop->_head = l; + igvn->register_new_node_with_optimizer(l); + _phase->set_loop(l, _loop); + _loop->_head = l; // Fix all data nodes placed at the old loop head. // Uses the lazy-update mechanism of 'get_ctrl'. - replace_node_and_forward_ctrl(x, l); - set_idom(l, entry_control, dom_depth(entry_control) + 1); + _phase->replace_node_and_forward_ctrl(_head, l); + _phase->set_idom(l, entry_control, _phase->dom_depth(entry_control) + 1); - if (iv_bt == T_INT && (LoopStripMiningIter == 0 || strip_mine_loop)) { + if (_iv_bt == T_INT && (LoopStripMiningIter == 0 || strip_mine_loop)) { // Check for immediately preceding SafePoint and remove - if (sfpt != nullptr && (strip_mine_loop || is_deleteable_safept(sfpt))) { + if (_structure.sfpt() != nullptr && (strip_mine_loop || _phase->is_deleteable_safept(_structure.sfpt()))) { if (strip_mine_loop) { Node* outer_le = outer_ilt->_tail->in(0); - Node* sfpt_clone = sfpt->clone(); + Node* sfpt_clone = _structure.sfpt()->clone(); sfpt_clone->set_req(0, iffalse); outer_le->set_req(0, sfpt_clone); @@ -2570,40 +2733,42 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv // Polling load should be pinned outside inner loop. Node* new_polladdr = polladdr->clone(); new_polladdr->set_req(0, iffalse); - _igvn.register_new_node_with_optimizer(new_polladdr, polladdr); - set_ctrl(new_polladdr, iffalse); + igvn->register_new_node_with_optimizer(new_polladdr, polladdr); + _phase->set_ctrl(new_polladdr, iffalse); sfpt_clone->set_req(TypeFunc::Parms, new_polladdr); } // When this code runs, loop bodies have not yet been populated. const bool body_populated = false; - register_control(sfpt_clone, outer_ilt, iffalse, body_populated); - set_idom(outer_le, sfpt_clone, dom_depth(sfpt_clone)); + _phase->register_control(sfpt_clone, outer_ilt, iffalse, body_populated); + _phase->set_idom(outer_le, sfpt_clone, _phase->dom_depth(sfpt_clone)); } - replace_node_and_forward_ctrl(sfpt, sfpt->in(TypeFunc::Control)); - if (loop->_safepts != nullptr) { - loop->_safepts->yank(sfpt); + _phase->replace_node_and_forward_ctrl(_structure.sfpt(), _structure.sfpt()->in(TypeFunc::Control)); + if (_loop->_safepts != nullptr) { + _loop->_safepts->yank(_structure.sfpt()); } } } #ifdef ASSERT - assert(l->is_valid_counted_loop(iv_bt), "counted loop shape is messed up"); - assert(l == loop->_head && l->phi() == phi && l->loopexit_or_null() == lex, "" ); + assert(l->is_valid_counted_loop(_iv_bt), "counted loop shape is messed up"); + assert(l == _loop->_head && l->phi() == phi && l->loopexit_or_null() == loop_end, "" ); #endif + #ifndef PRODUCT if (TraceLoopOpts) { tty->print("Counted "); - loop->dump_head(); + _loop->dump_head(); } #endif - C->print_method(PHASE_AFTER_CLOOPS, 3, l); - // Capture bounds of the loop in the induction variable Phi before // subsequent transformation (iteration splitting) obscures the // bounds - l->phi()->as_Phi()->set_type(l->phi()->Value(&_igvn)); + l->phi()->as_Phi()->set_type(l->phi()->Value(igvn)); + + _phase->C->print_method(PHASE_AFTER_CLOOPS, 3, l); + IdealLoopTree* loop = _loop; if (strip_mine_loop) { l->mark_strip_mined(); l->verify_strip_mined(1); @@ -2612,21 +2777,24 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv } #ifndef PRODUCT - if (x->as_Loop()->is_loop_nest_inner_loop() && iv_bt == T_LONG) { + if (_head->as_Loop()->is_loop_nest_inner_loop() && _iv_bt == T_LONG) { AtomicAccess::inc(&_long_loop_counted_loops); } #endif - if (iv_bt == T_LONG && x->as_Loop()->is_loop_nest_outer_loop()) { + + if (_iv_bt == T_LONG && _head->as_Loop()->is_loop_nest_outer_loop()) { l->mark_loop_nest_outer_loop(); } - return true; + return loop; } // Check if there is a dominating loop limit check of the form 'init < limit' starting at the loop entry. // If there is one, then we do not need to create an additional Loop Limit Check Predicate. -bool PhaseIdealLoop::has_dominating_loop_limit_check(Node* init_trip, Node* limit, const jlong stride_con, - const BasicType iv_bt, Node* loop_entry) { +bool CountedLoopConverter::has_dominating_loop_limit_check(Node* init_trip, Node* limit, const jlong stride_con, + const BasicType iv_bt, Node* loop_entry) const { + PhaseIterGVN& _igvn = _phase->igvn(); + // Eagerly call transform() on the Cmp and Bool node to common them up if possible. This is required in order to // successfully find a dominated test with the If node below. Node* cmp_limit; @@ -2649,8 +2817,8 @@ bool PhaseIdealLoop::has_dominating_loop_limit_check(Node* init_trip, Node* limi const bool found_dominating_test = dominated_iff != nullptr && dominated_iff->is_ConI(); // Kill the If with its projections again in the next IGVN round by cutting it off from the graph. - _igvn.replace_input_of(iff, 0, C->top()); - _igvn.replace_input_of(iff, 1, C->top()); + _igvn.replace_input_of(iff, 0, _phase->C->top()); + _igvn.replace_input_of(iff, 1, _phase->C->top()); return found_dominating_test; } @@ -2953,24 +3121,23 @@ Node* LoopLimitNode::Identity(PhaseGVN* phase) { return this; } -//============================================================================= -//----------------------match_incr_with_optional_truncation-------------------- // Match increment with optional truncation: // CHAR: (i+1)&0x7fff, BYTE: ((i+1)<<8)>>8, or SHORT: ((i+1)<<16)>>16 -// Return null for failure. Success returns the increment node. -Node* CountedLoopNode::match_incr_with_optional_truncation(Node* expr, Node** trunc1, Node** trunc2, - const TypeInteger** trunc_type, - BasicType bt) { +void CountedLoopConverter::TruncatedIncrement::build(Node* expr) { + _is_valid = false; + // Quick cutouts: - if (expr == nullptr || expr->req() != 3) return nullptr; + if (expr == nullptr || expr->req() != 3) { + return; + } - Node *t1 = nullptr; - Node *t2 = nullptr; + Node* t1 = nullptr; + Node* t2 = nullptr; Node* n1 = expr; int n1op = n1->Opcode(); - const TypeInteger* trunc_t = TypeInteger::bottom(bt); + const TypeInteger* trunc_t = TypeInteger::bottom(_bt); - if (bt == T_INT) { + if (_bt == T_INT) { // Try to strip (n1 & M) or (n1 << N >> N) from n1. if (n1op == Op_AndI && n1->in(2)->is_Con() && @@ -3002,15 +3169,14 @@ Node* CountedLoopNode::match_incr_with_optional_truncation(Node* expr, Node** tr } // If (maybe after stripping) it is an AddI, we won: - if (n1op == Op_Add(bt)) { - *trunc1 = t1; - *trunc2 = t2; - *trunc_type = trunc_t; - return n1; - } + if (n1op == Op_Add(_bt)) { + _incr = n1; + _outer_trunc = t1; + _inner_trunc = t2; + _trunc_type = trunc_t; - // failed - return nullptr; + _is_valid = true; + } } IfNode* CountedLoopNode::find_multiversion_if_from_multiversion_fast_main_loop() { @@ -3662,18 +3828,18 @@ Node *OuterStripMinedLoopEndNode::Ideal(PhaseGVN *phase, bool can_reshape) { // i = ? // } while ( i < 10) // -const TypeInt* PhaseIdealLoop::filtered_type( Node *n, Node* n_ctrl) { +const TypeInt* CountedLoopConverter::filtered_type(Node* n, Node* n_ctrl) { assert(n && n->bottom_type()->is_int(), "must be int"); const TypeInt* filtered_t = nullptr; if (!n->is_Phi()) { - assert(n_ctrl != nullptr || n_ctrl == C->top(), "valid control"); + assert(n_ctrl != nullptr || n_ctrl == _phase->C->top(), "valid control"); filtered_t = filtered_type_from_dominators(n, n_ctrl); } else { Node* phi = n->as_Phi(); Node* region = phi->in(0); assert(n_ctrl == nullptr || n_ctrl == region, "ctrl parameter must be region"); - if (region && region != C->top()) { + if (region && region != _phase->C->top()) { for (uint i = 1; i < phi->req(); i++) { Node* val = phi->in(i); Node* use_c = region->in(i); @@ -3688,7 +3854,7 @@ const TypeInt* PhaseIdealLoop::filtered_type( Node *n, Node* n_ctrl) { } } } - const TypeInt* n_t = _igvn.type(n)->is_int(); + const TypeInt* n_t = _phase->igvn().type(n)->is_int(); if (filtered_t != nullptr) { n_t = n_t->join(filtered_t)->is_int(); } @@ -3698,22 +3864,22 @@ const TypeInt* PhaseIdealLoop::filtered_type( Node *n, Node* n_ctrl) { //------------------------------filtered_type_from_dominators-------------------------------- // Return a possibly more restrictive type for val based on condition control flow of dominators -const TypeInt* PhaseIdealLoop::filtered_type_from_dominators( Node* val, Node *use_ctrl) { +const TypeInt* CountedLoopConverter::filtered_type_from_dominators(Node* val, Node* use_ctrl) { if (val->is_Con()) { return val->bottom_type()->is_int(); } uint if_limit = 10; // Max number of dominating if's visited const TypeInt* rtn_t = nullptr; - if (use_ctrl && use_ctrl != C->top()) { - Node* val_ctrl = get_ctrl(val); - uint val_dom_depth = dom_depth(val_ctrl); + if (use_ctrl && use_ctrl != _phase->C->top()) { + Node* val_ctrl = _phase->get_ctrl(val); + uint val_dom_depth = _phase->dom_depth(val_ctrl); Node* pred = use_ctrl; uint if_cnt = 0; while (if_cnt < if_limit) { if ((pred->Opcode() == Op_IfTrue || pred->Opcode() == Op_IfFalse)) { if_cnt++; - const TypeInt* if_t = IfNode::filtered_int_type(&_igvn, val, pred); + const TypeInt* if_t = IfNode::filtered_int_type(&_phase->igvn(), val, pred); if (if_t != nullptr) { if (rtn_t == nullptr) { rtn_t = if_t; @@ -3722,12 +3888,12 @@ const TypeInt* PhaseIdealLoop::filtered_type_from_dominators( Node* val, Node *u } } } - pred = idom(pred); - if (pred == nullptr || pred == C->top()) { + pred = _phase->idom(pred); + if (pred == nullptr || pred == _phase->C->top()) { break; } // Stop if going beyond definition block of val - if (dom_depth(pred) < val_dom_depth) { + if (_phase->dom_depth(pred) < val_dom_depth) { break; } } @@ -4335,7 +4501,7 @@ void IdealLoopTree::check_safepts(VectorSet &visited, Node_List &stack) { //---------------------------is_deleteable_safept---------------------------- // Is safept not required by an outer loop? -bool PhaseIdealLoop::is_deleteable_safept(Node* sfpt) { +bool PhaseIdealLoop::is_deleteable_safept(Node* sfpt) const { assert(sfpt->Opcode() == Op_SafePoint, ""); IdealLoopTree* lp = get_loop(sfpt)->_parent; while (lp != nullptr) { @@ -4553,9 +4719,7 @@ void IdealLoopTree::counted_loop( PhaseIdealLoop *phase ) { } IdealLoopTree* loop = this; - if (_head->is_CountedLoop() || - phase->is_counted_loop(_head, loop, T_INT)) { - + if (_head->is_CountedLoop() || phase->try_convert_to_counted_loop(_head, loop, T_INT)) { if (LoopStripMiningIter == 0 || _head->as_CountedLoop()->is_strip_mined()) { // Indicate we do not need a safepoint here _has_sfpt = 1; @@ -4567,8 +4731,7 @@ void IdealLoopTree::counted_loop( PhaseIdealLoop *phase ) { // Look for induction variables phase->replace_parallel_iv(this); - } else if (_head->is_LongCountedLoop() || - phase->is_counted_loop(_head, loop, T_LONG)) { + } else if (_head->is_LongCountedLoop() || phase->try_convert_to_counted_loop(_head, loop, T_LONG)) { remove_safepoints(phase, true); } else { assert(!_head->is_Loop() || !_head->as_Loop()->is_loop_nest_inner_loop(), "transformation to counted loop should not fail"); @@ -5373,9 +5536,15 @@ int PhaseIdealLoop::_loop_invokes=0;// Count of PhaseIdealLoop invokes int PhaseIdealLoop::_loop_work=0; // Sum of PhaseIdealLoop x unique volatile int PhaseIdealLoop::_long_loop_candidates=0; // Number of long loops seen volatile int PhaseIdealLoop::_long_loop_nests=0; // Number of long loops successfully transformed to a nest -volatile int PhaseIdealLoop::_long_loop_counted_loops=0; // Number of long loops successfully transformed to a counted loop +// Number of long loops successfully transformed to a counted loop +volatile int CountedLoopConverter::_long_loop_counted_loops = 0; void PhaseIdealLoop::print_statistics() { - tty->print_cr("PhaseIdealLoop=%d, sum _unique=%d, long loops=%d/%d/%d", _loop_invokes, _loop_work, _long_loop_counted_loops, _long_loop_nests, _long_loop_candidates); + tty->print_cr("PhaseIdealLoop=%d, sum _unique=%d, long loops=%d/%d/%d", + _loop_invokes, + _loop_work, + CountedLoopConverter::_long_loop_counted_loops, + _long_loop_nests, + _long_loop_candidates); } #endif diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 986cfdaa3f1d..6667c71511c6 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -273,11 +273,6 @@ class CountedLoopNode : public BaseCountedLoopNode { CountedLoopEndNode* loopexit() const { return (CountedLoopEndNode*) BaseCountedLoopNode::loopexit(); } int stride_con() const; - // Match increment with optional truncation - static Node* - match_incr_with_optional_truncation(Node* expr, Node** trunc1, Node** trunc2, const TypeInteger** trunc_type, - BasicType bt); - // A 'main' loop has a pre-loop and a post-loop. The 'main' loop // can run short a few iterations and may start a few iterations in. // It will be RCE'd and unrolled and aligned. @@ -1029,8 +1024,6 @@ class PhaseIdealLoop : public PhaseTransform { void rewire_old_target_loop_entry_dependency_to_new_entry(CountedLoopNode* target_loop_head, const Node* old_target_loop_entry, uint node_index_before_new_assertion_predicate_nodes); - void insert_loop_limit_check_predicate(ParsePredicateSuccessProj* loop_limit_check_parse_proj, Node* cmp_limit, - Node* bol); void log_loop_tree(); public: @@ -1294,7 +1287,7 @@ class PhaseIdealLoop : public PhaseTransform { void recompute_dom_depth(); // Is safept not required by an outer loop? - bool is_deleteable_safept(Node* sfpt); + bool is_deleteable_safept(Node* sfpt) const; // Replace parallel induction variable (parallel to trip counter) void replace_parallel_iv(IdealLoopTree *loop); @@ -1345,21 +1338,109 @@ class PhaseIdealLoop : public PhaseTransform { // Per-Node transform virtual Node* transform(Node* n) { return nullptr; } - Node* loop_exit_control(Node* x, IdealLoopTree* loop); - Node* loop_exit_test(Node* back_control, IdealLoopTree* loop, Node*& incr, Node*& limit, BoolTest::mask& bt, float& cl_prob); - Node* loop_iv_incr(Node* incr, Node* x, IdealLoopTree* loop, Node*& phi_incr); - Node* loop_iv_stride(Node* incr, Node*& xphi); - PhiNode* loop_iv_phi(Node* xphi, Node* phi_incr, Node* x); + Node* loop_exit_control(const IdealLoopTree* loop) const; + + class LoopExitTest { + bool _is_valid; + + const Node* _back_control; + const IdealLoopTree* _loop; + PhaseIdealLoop* _phase; + + Node* _cmp; + Node* _incr; + Node* _limit; + BoolTest::mask _mask; + float _cl_prob; + + public: + LoopExitTest(const Node* back_control, const IdealLoopTree* loop, PhaseIdealLoop* phase) : + _is_valid(false), + _back_control(back_control), + _loop(loop), + _phase(phase), + _cmp(nullptr), + _incr(nullptr), + _limit(nullptr), + _mask(BoolTest::illegal), + _cl_prob(0.0f) {} + + void build(); + void canonicalize_mask(jlong stride_con); + + bool is_valid_with_bt(BasicType bt) const { + return _is_valid && _cmp != nullptr && _cmp->Opcode() == Op_Cmp(bt); + } + + bool should_include_limit() const { return _mask == BoolTest::le || _mask == BoolTest::ge; } + + CmpNode* cmp() const { return _cmp->as_Cmp(); } + Node* incr() const { return _incr; } + Node* limit() const { return _limit; } + BoolTest::mask mask() const { return _mask; } + float cl_prob() const { return _cl_prob; } + }; + + class LoopIVIncr { + bool _is_valid; + + const Node* _head; + const IdealLoopTree* _loop; + + Node* _incr; + Node* _phi_incr; + + public: + LoopIVIncr(const Node* head, const IdealLoopTree* loop) : + _is_valid(false), + _head(head), + _loop(loop), + _incr(nullptr), + _phi_incr(nullptr) {} + + void build(Node* old_incr); + + bool is_valid() const { return _is_valid; } + bool is_valid_with_bt(const BasicType bt) const { + return _is_valid && _incr->Opcode() == Op_Add(bt); + } + + Node* incr() const { return _incr; } + Node* phi_incr() const { return _phi_incr; } + }; + + class LoopIVStride { + bool _is_valid; + + BasicType _iv_bt; + Node* _stride_node; + Node* _xphi; + + public: + LoopIVStride(BasicType iv_bt) : + _is_valid(false), + _iv_bt(iv_bt), + _stride_node(nullptr), + _xphi(nullptr) {} + + void build(const Node* incr); - bool is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_bt); + bool is_valid() const { return _is_valid && _stride_node != nullptr; } + Node* stride_node() const { return _stride_node; } + Node* xphi() const { return _xphi; } + + jlong compute_non_zero_stride_con(BoolTest::mask mask, BasicType iv_bt) const; + }; + + static PhiNode* loop_iv_phi(const Node* xphi, const Node* phi_incr, const Node* head); + + bool try_convert_to_counted_loop(Node* head, IdealLoopTree*& loop, BasicType iv_bt); Node* loop_nest_replace_iv(Node* iv_to_replace, Node* inner_iv, Node* outer_phi, Node* inner_head, BasicType bt); bool create_loop_nest(IdealLoopTree* loop, Node_List &old_new); -#ifdef ASSERT - bool convert_to_long_loop(Node* cmp, Node* phi, IdealLoopTree* loop); -#endif + void add_parse_predicate(Deoptimization::DeoptReason reason, Node* inner_head, IdealLoopTree* loop, SafePointNode* sfpt); - SafePointNode* find_safepoint(Node* back_control, Node* x, IdealLoopTree* loop); + SafePointNode* find_safepoint(Node* back_control, const Node* head, const IdealLoopTree* loop); void add_parse_predicates(IdealLoopTree* outer_ilt, LoopNode* inner_head, SafePointNode* cloned_sfpt); @@ -1496,8 +1577,6 @@ class PhaseIdealLoop : public PhaseTransform { Node* clone_nodes_with_same_ctrl(Node* start_node, ProjNode* old_uncommon_proj, Node* new_uncommon_proj); void fix_cloned_data_node_controls(const ProjNode* orig, Node* new_uncommon_proj, const OrigToNewHashtable& orig_to_clone); - bool has_dominating_loop_limit_check(Node* init_trip, Node* limit, jlong stride_con, BasicType iv_bt, - Node* loop_entry); public: void register_control(Node* n, IdealLoopTree *loop, Node* pred, bool update_body = true); @@ -1755,12 +1834,6 @@ class PhaseIdealLoop : public PhaseTransform { Node*& shift, Node*& offset); private: - // Return a type based on condition control flow - const TypeInt* filtered_type( Node *n, Node* n_ctrl); - const TypeInt* filtered_type( Node *n ) { return filtered_type(n, nullptr); } - // Helpers for filtered type - const TypeInt* filtered_type_from_dominators( Node* val, Node *val_ctrl); - // Helper functions Node *spinup( Node *iff, Node *new_false, Node *new_true, Node *region, Node *phi, small_cache *cache ); Node *find_use_block( Node *use, Node *def, Node *old_false, Node *new_false, Node *old_true, Node *new_true ); @@ -1889,7 +1962,6 @@ class PhaseIdealLoop : public PhaseTransform { static int _loop_work; // Sum of PhaseIdealLoop x _unique static volatile int _long_loop_candidates; static volatile int _long_loop_nests; - static volatile int _long_loop_counted_loops; #endif #ifdef ASSERT @@ -1901,7 +1973,7 @@ class PhaseIdealLoop : public PhaseTransform { void rpo(Node* start, Node_Stack &stk, VectorSet &visited, Node_List &rpo_list) const; - void check_counted_loop_shape(IdealLoopTree* loop, Node* x, BasicType bt) NOT_DEBUG_RETURN; + void check_counted_loop_shape(IdealLoopTree* loop, Node* head, BasicType bt) NOT_DEBUG_RETURN; LoopNode* create_inner_head(IdealLoopTree* loop, BaseCountedLoopNode* head, IfNode* exit_test); @@ -1979,6 +2051,146 @@ class PhaseIdealLoop : public PhaseTransform { ConNode* zerocon(BasicType bt); }; +class CountedLoopConverter { + friend class PhaseIdealLoop; + + // Match increment with optional truncation + class TruncatedIncrement { + bool _is_valid; + + BasicType _bt; + + Node* _incr; + Node* _outer_trunc; + Node* _inner_trunc; + const TypeInteger* _trunc_type; + + public: + TruncatedIncrement(BasicType bt) : + _is_valid(false), + _bt(bt), + _incr(nullptr), + _outer_trunc(nullptr), + _inner_trunc(nullptr), + _trunc_type(nullptr) {} + + void build(Node* expr); + + bool is_valid() const { return _is_valid; } + Node* incr() const { return _incr; } + + // Optional truncation for: CHAR: (i+1)&0x7fff, BYTE: ((i+1)<<8)>>8, or SHORT: ((i+1)<<16)>>16 + Node* outer_trunc() const { return _outer_trunc; } // the outermost truncating node (either the & or the final >>) + Node* inner_trunc() const { return _inner_trunc; } // the inner truncating node, if applicable (the << in a <> pair) + const TypeInteger* trunc_type() const { return _trunc_type; } + }; + + class LoopStructure { + bool _is_valid; + + const Node* _head; + const IdealLoopTree* _loop; + PhaseIdealLoop* _phase; + BasicType _iv_bt; + + Node* _back_control; + PhaseIdealLoop::LoopExitTest _exit_test; + PhaseIdealLoop::LoopIVIncr _iv_incr; + TruncatedIncrement _truncated_increment; + PhaseIdealLoop::LoopIVStride _stride; + PhiNode* _phi; + SafePointNode* _safepoint; + + public: + LoopStructure(const Node* head, const IdealLoopTree* loop, PhaseIdealLoop* phase, const BasicType iv_bt) : + _is_valid(false), + _head(head), + _loop(loop), + _phase(phase), + _iv_bt(iv_bt), + _back_control(_phase->loop_exit_control(_loop)), + _exit_test(_back_control, _loop, _phase), + _iv_incr(_head, _loop), + _truncated_increment(_iv_bt), + _stride(PhaseIdealLoop::LoopIVStride(_iv_bt)), + _phi(nullptr), + _safepoint(nullptr) {} + + void build(); + + jlong final_limit_correction() const; // compute adjusted loop limit correction + bool is_infinite_loop() const; + + bool is_valid() const { return _is_valid; } + + Node* back_control() const { return _back_control; } + PhaseIdealLoop::LoopExitTest& exit_test() { return _exit_test; } + PhaseIdealLoop::LoopIVIncr& iv_incr() { return _iv_incr; } + TruncatedIncrement& truncated_increment() { return _truncated_increment; } + PhaseIdealLoop::LoopIVStride& stride() { return _stride; } + PhiNode* phi() const { return _phi; } + SafePointNode* sfpt() const { return _safepoint; } + jlong stride_con() const { return _stride.compute_non_zero_stride_con(_exit_test.mask(), _iv_bt); } + Node* limit() const { return _exit_test.limit(); } + }; + + PhaseIdealLoop* const _phase; + Node* const _head; + IdealLoopTree* const _loop; + const BasicType _iv_bt; + + LoopStructure _structure; + bool _should_insert_stride_overflow_limit_check = false; + bool _should_insert_init_trip_limit_check = false; + + DEBUG_ONLY(bool _checked_for_counted_loop = false;) + + // stats for PhaseIdealLoop::print_statistics() + static volatile int _long_loop_counted_loops; + + // Return a type based on condition control flow + const TypeInt* filtered_type(Node* n, Node* n_ctrl); + const TypeInt* filtered_type(Node* n) { return filtered_type(n, nullptr); } + // Helpers for filtered type + const TypeInt* filtered_type_from_dominators(Node* val, Node* val_ctrl); + + void insert_loop_limit_check_predicate(const ParsePredicateSuccessProj* loop_limit_check_parse_proj, Node* bol) const; + void insert_stride_overflow_limit_check() const; + void insert_init_trip_limit_check() const; + bool has_dominating_loop_limit_check(Node* init_trip, Node* limit, jlong stride_con, BasicType iv_bt, + Node* loop_entry) const; + + bool is_iv_overflowing(const TypeInteger* init_t, jlong stride_con, Node* phi_increment, BoolTest::mask mask) const; + bool has_truncation_wrap(const TruncatedIncrement& truncation, Node* phi, jlong stride_con); + SafePointNode* find_safepoint(Node* iftrue); + bool is_safepoint_invalid(SafePointNode* sfpt) const; + + public: + CountedLoopConverter(PhaseIdealLoop* phase, Node* head, IdealLoopTree* loop, const BasicType iv_bt) + : _phase(phase), + _head(head), + _loop(loop), + _iv_bt(iv_bt), + _structure(LoopStructure(_head, _loop, _phase, _iv_bt)) { + assert(phase != nullptr, "must be"); // Fail early if mandatory parameters are null. + assert(head != nullptr, "must be"); + assert(loop != nullptr, "must be"); + assert(iv_bt == T_INT || iv_bt == T_LONG, "either int or long loops"); + } + + bool is_counted_loop(); + IdealLoopTree* convert(); + + DEBUG_ONLY(bool should_stress_long_counted_loop();) + DEBUG_ONLY(bool stress_long_counted_loop();) + + enum StrideOverflowState { + Overflow = -1, + NoOverflow = 0, + RequireLimitCheck = 1 + }; + static StrideOverflowState check_stride_overflow(jlong final_correction, const TypeInteger* limit_t, BasicType bt); +}; class AutoNodeBudget : public StackObj { diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 862cb7067ecd..a21fa43b30c8 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -2829,8 +2829,6 @@ void PhaseIdealLoop::clone_loop_body(const Node_List& body, Node_List &old_new, // with an optional truncation (left-shift followed by a right-shift) // of the add. Returns zero if not an iv. int PhaseIdealLoop::stride_of_possible_iv(Node* iff) { - Node* trunc1 = nullptr; - Node* trunc2 = nullptr; const TypeInteger* ttype = nullptr; if (!iff->is_If() || iff->in(1) == nullptr || !iff->in(1)->is_Bool()) { return 0; @@ -2851,23 +2849,23 @@ int PhaseIdealLoop::stride_of_possible_iv(Node* iff) { Node* phi = cmp1; for (uint i = 1; i < phi->req(); i++) { Node* in = phi->in(i); - Node* add = CountedLoopNode::match_incr_with_optional_truncation(in, - &trunc1, &trunc2, &ttype, T_INT); - if (add && add->in(1) == phi) { - add2 = add->in(2); + CountedLoopConverter::TruncatedIncrement add(T_INT); + add.build(in); + if (add.is_valid() && add.incr()->in(1) == phi) { + add2 = add.incr()->in(2); break; } } } else { // (If (Bool (CmpX addtrunc:(Optional-trunc((AddI (Phi ...addtrunc...) add2)) ))) Node* addtrunc = cmp1; - Node* add = CountedLoopNode::match_incr_with_optional_truncation(addtrunc, - &trunc1, &trunc2, &ttype, T_INT); - if (add && add->in(1)->is_Phi()) { - Node* phi = add->in(1); + CountedLoopConverter::TruncatedIncrement add(T_INT); + add.build(addtrunc); + if (add.is_valid() && add.incr()->in(1)->is_Phi()) { + Node* phi = add.incr()->in(1); for (uint i = 1; i < phi->req(); i++) { if (phi->in(i) == addtrunc) { - add2 = add->in(2); + add2 = add.incr()->in(2); break; } } @@ -4294,54 +4292,50 @@ bool PhaseIdealLoop::duplicate_loop_backedge(IdealLoopTree *loop, Node_List &old #endif //ASSERT { // Is the shape of the loop that of a counted loop... - Node* back_control = loop_exit_control(head, loop); + Node* back_control = loop_exit_control(loop); if (back_control == nullptr) { return false; } - BoolTest::mask bt = BoolTest::illegal; - float cl_prob = 0; - Node* incr = nullptr; - Node* limit = nullptr; - Node* cmp = loop_exit_test(back_control, loop, incr, limit, bt, cl_prob); - if (cmp == nullptr || cmp->Opcode() != Op_CmpI) { + LoopExitTest loop_exit(back_control, loop, this); + loop_exit.build(); + if (!loop_exit.is_valid_with_bt(T_INT)) { return false; } + const Node* loop_incr = loop_exit.incr(); + // With an extra phi for the candidate iv? // Or the region node is the loop head - if (!incr->is_Phi() || incr->in(0) == head) { + if (!loop_incr->is_Phi() || loop_incr->in(0) == head) { return false; } PathFrequency pf(head, this); - region = incr->in(0); + region = loop_incr->in(0); // Go over all paths for the extra phi's region and see if that // path is frequent enough and would match the expected iv shape // if the extra phi is removed inner = 0; - for (uint i = 1; i < incr->req(); ++i) { - Node* in = incr->in(i); - Node* trunc1 = nullptr; - Node* trunc2 = nullptr; - const TypeInteger* iv_trunc_t = nullptr; - Node* orig_in = in; - if (!(in = CountedLoopNode::match_incr_with_optional_truncation(in, &trunc1, &trunc2, &iv_trunc_t, T_INT))) { + for (uint i = 1; i < loop_incr->req(); ++i) { + CountedLoopConverter::TruncatedIncrement increment(T_INT); + increment.build(loop_incr->in(i)); + if (!increment.is_valid()) { continue; } - assert(in->Opcode() == Op_AddI, "wrong increment code"); - Node* xphi = nullptr; - Node* stride = loop_iv_stride(in, xphi); + assert(increment.incr()->Opcode() == Op_AddI, "wrong increment code"); - if (stride == nullptr) { + LoopIVStride stride = LoopIVStride(T_INT); + stride.build(increment.incr()); + if (!stride.is_valid()) { continue; } - PhiNode* phi = loop_iv_phi(xphi, nullptr, head); + PhiNode* phi = loop_iv_phi(stride.xphi(), nullptr, head); if (phi == nullptr || - (trunc1 == nullptr && phi->in(LoopNode::LoopBackControl) != incr) || - (trunc1 != nullptr && phi->in(LoopNode::LoopBackControl) != trunc1)) { + (increment.outer_trunc() == nullptr && phi->in(LoopNode::LoopBackControl) != loop_exit.incr()) || + (increment.outer_trunc() != nullptr && phi->in(LoopNode::LoopBackControl) != increment.outer_trunc())) { return false; } diff --git a/test/hotspot/jtreg/compiler/loopopts/TestStressLongCountedLoopLimitChecks.java b/test/hotspot/jtreg/compiler/loopopts/TestStressLongCountedLoopLimitChecks.java new file mode 100644 index 000000000000..fd0143acdb63 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestStressLongCountedLoopLimitChecks.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2026 IBM and/or its affiliates. All rights reserved. + * 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.loopopts; + +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +import java.io.IOException; +import java.util.Arrays; +import java.util.stream.Stream; + +/** + * @test + * @bug 8353290 + * @summary test loop limit checks are inserted when stressing int counted loops to long counted loops + * @library /test/lib + * @requires vm.debug == true + * @run driver compiler.loopopts.TestStressLongCountedLoopLimitChecks + */ +public class TestStressLongCountedLoopLimitChecks { + public static void main(String[] args) throws Exception { + test(BasicLauncher.class, 1, "-XX:StressLongCountedLoop=0"); + test(BasicLauncher.class, 1, "-XX:StressLongCountedLoop=2000000"); + + test(LargeStrideLauncher.class, 2, "-XX:StressLongCountedLoop=0"); + test(LargeStrideLauncher.class, 2, "-XX:StressLongCountedLoop=2000000"); + } + + private static void test(Class launcher, int limitChecks, String... flags) throws IOException { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + Stream.concat(Arrays.stream(flags), Stream.of( + "-XX:+IgnoreUnrecognizedVMOptions", + "-XX:+TraceLoopLimitCheck", + "-XX:CompileOnly=" + launcher.getName() + "::test*", + "-Xcomp", + launcher.getName() + )).toList() + ); + + OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); + analyzer.shouldHaveExitValue(0); + + analyzer.outputTo(System.out); + analyzer.errorTo(System.err); + + Asserts.assertEQ( + limitChecks, + (int) analyzer.asLines().stream().filter( + l -> l.trim().matches("Counted Loop Limit Check generated:") + ).count(), + "wrong numbers of loop limit checks" + ); + } + + public static class BasicLauncher { + static int x, y, z; + + public static void main(String[] args) throws Exception { + test(); + } + + static void test() { + int i = x; // Any int + do { + x += y; + i++; // Could overflow and thus we need a Loop Limit Check Predicate "i < z" + } while (i < z); + } + } + + public static class LargeStrideLauncher { + static final int STRIDE = 100_000; + + public static void main(String[] args) throws Exception { + Asserts.assertEQ(10_000_000L / STRIDE, test(0, 10_000_000), "loop not stopped"); + Asserts.assertEQ(-1L, test(0, Integer.MAX_VALUE), "loop stopped prematurely"); + } + + static long ONE = 1; // Just so the compiler doesn't try to IV replace the whole thing + + public static long test(int init, int limit) { + final int stride = 100_000; + + long iterations = 0; + for (int i = init; i < limit; i += 100000) { + iterations += ONE; + + if (iterations > (limit / stride) + 1) { // No it's not stopping, as we should expect. + return -1; + } + } + return iterations; // Possibly stopping prematurely. + } + } +} From 02bce505eebd255c7b53a23dbc2b5d745573b5b1 Mon Sep 17 00:00:00 2001 From: Simon Tooke Date: Mon, 16 Mar 2026 14:41:59 +0000 Subject: [PATCH 63/97] 8377456: GetObjectSizeIntrinsicsTest.java, ARRAY_HEADER_SIZE wrong for 32-bit Reviewed-by: stuefe --- test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java b/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java index 1e473ccd9746..44276a9f7f52 100644 --- a/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java +++ b/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java @@ -315,7 +315,7 @@ public class GetObjectSizeIntrinsicsTest extends ASimpleInstrumentationTestCase static final int LARGE_OBJ_ARRAY_SIZE = (4096/(int)REF_SIZE)*1024*1024 + 1024; static final boolean CCP = WhiteBox.getWhiteBox().getBooleanVMFlag("UseCompressedClassPointers"); - static final int ARRAY_HEADER_SIZE = CCP ? 16 : (Platform.is64bit() ? 20 : 16); + static final int ARRAY_HEADER_SIZE = CCP ? 16 : (Platform.is64bit() ? 20 : 12); final String mode; From 3ddfdd94d92135e2038f48913a4c3e0f9754049b Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Mon, 16 Mar 2026 14:42:39 +0000 Subject: [PATCH 64/97] 8353567: Tighten NMT lock scope in os::release_memory and os::uncommit_memory Reviewed-by: azafari, stuefe --- src/hotspot/os/posix/perfMemory_posix.cpp | 15 ++------ src/hotspot/os/windows/perfMemory_windows.cpp | 13 +++---- src/hotspot/share/gc/z/zNMT.cpp | 6 ++-- src/hotspot/share/nmt/memTracker.hpp | 3 +- src/hotspot/share/nmt/threadStackTracker.cpp | 3 +- src/hotspot/share/runtime/os.cpp | 34 ++++--------------- 6 files changed, 19 insertions(+), 55 deletions(-) diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp index d9bde6fa8255..b8be77c5e057 100644 --- a/src/hotspot/os/posix/perfMemory_posix.cpp +++ b/src/hotspot/os/posix/perfMemory_posix.cpp @@ -1084,18 +1084,9 @@ static char* mmap_create_shared(size_t size) { // release a named shared memory region that was mmap-ed. // static void unmap_shared(char* addr, size_t bytes) { - int res; - if (MemTracker::enabled()) { - MemTracker::NmtVirtualMemoryLocker nvml; - res = ::munmap(addr, bytes); - if (res == 0) { - MemTracker::record_virtual_memory_release(addr, bytes); - } - } else { - res = ::munmap(addr, bytes); - } - if (res != 0) { - log_info(os)("os::release_memory failed (" PTR_FORMAT ", %zu)", p2i(addr), bytes); + MemTracker::record_virtual_memory_release(addr, bytes); + if (::munmap(addr, bytes) != 0) { + fatal("os::release_memory failed (" PTR_FORMAT ", %zu)", p2i(addr), bytes); } } diff --git a/src/hotspot/os/windows/perfMemory_windows.cpp b/src/hotspot/os/windows/perfMemory_windows.cpp index f54a2b52ccad..dad2804f18ac 100644 --- a/src/hotspot/os/windows/perfMemory_windows.cpp +++ b/src/hotspot/os/windows/perfMemory_windows.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1682,12 +1682,7 @@ void PerfMemory::detach(char* addr, size_t bytes) { return; } - if (MemTracker::enabled()) { - // it does not go through os api, the operation has to record from here - MemTracker::NmtVirtualMemoryLocker nvml; - remove_file_mapping(addr); - MemTracker::record_virtual_memory_release(addr, bytes); - } else { - remove_file_mapping(addr); - } + // it does not go through os api, the operation has to record from here + MemTracker::record_virtual_memory_release(addr, bytes); + remove_file_mapping(addr); } diff --git a/src/hotspot/share/gc/z/zNMT.cpp b/src/hotspot/share/gc/z/zNMT.cpp index 1019bcfdd961..b82afac47bc3 100644 --- a/src/hotspot/share/gc/z/zNMT.cpp +++ b/src/hotspot/share/gc/z/zNMT.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,9 +45,7 @@ void ZNMT::unreserve(zaddress_unsafe start, size_t size) { if (MemTracker::enabled()) { // We are the owner of the reserved memory, and any failure to unreserve - // are fatal, so so we don't need to hold a lock while unreserving memory. - - MemTracker::NmtVirtualMemoryLocker nvml; + // are fatal, so we don't need to hold a lock while unreserving memory. // The current NMT implementation does not support unreserving a memory // region that was built up from smaller memory reservations. Workaround diff --git a/src/hotspot/share/nmt/memTracker.hpp b/src/hotspot/share/nmt/memTracker.hpp index d9ebf4dc30ef..6b5b6affa14a 100644 --- a/src/hotspot/share/nmt/memTracker.hpp +++ b/src/hotspot/share/nmt/memTracker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -140,6 +140,7 @@ class MemTracker : AllStatic { assert_post_init(); if (!enabled()) return; if (addr != nullptr) { + NmtVirtualMemoryLocker nvml; VirtualMemoryTracker::Instance::remove_released_region((address)addr, size); } } diff --git a/src/hotspot/share/nmt/threadStackTracker.cpp b/src/hotspot/share/nmt/threadStackTracker.cpp index 3e649d882c46..6fb17c93782d 100644 --- a/src/hotspot/share/nmt/threadStackTracker.cpp +++ b/src/hotspot/share/nmt/threadStackTracker.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2019, 2024, Red Hat, Inc. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,7 +60,6 @@ void ThreadStackTracker::delete_thread_stack(void* base, size_t size) { assert(base != nullptr, "Should have been filtered"); align_thread_stack_boundaries_inward(base, size); - MemTracker::NmtVirtualMemoryLocker nvml; MemTracker::record_virtual_memory_release((address)base, size); _thread_count--; } diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 8f7e4d3cb6dc..129f8f76e73e 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -2302,24 +2302,13 @@ void os::uncommit_memory(char* addr, size_t bytes, bool executable) { log_debug(os, map)("Uncommitted " RANGEFMT, RANGEFMTARGS(addr, bytes)); } -// The scope of NmtVirtualMemoryLocker covers both pd_release_memory and record_virtual_memory_release because -// these operations must happen atomically to avoid races causing NMT to fall out os sync with the OS reality. -// We do not have the same lock protection for pd_reserve_memory and record_virtual_memory_reserve. -// We assume that there is some external synchronization that prevents a region from being released -// before it is finished being reserved. +// pd_release_memory is called outside the protection of the NMT lock. +// Until pd_release_memory is called, The OS is unable to give away the about-to-be-released range to another thread. +// So there is no risk of another thread re-reserving the range before this function is done with it. void os::release_memory(char* addr, size_t bytes) { assert_nonempty_range(addr, bytes); - bool res; - if (MemTracker::enabled()) { - MemTracker::NmtVirtualMemoryLocker nvml; - res = pd_release_memory(addr, bytes); - if (res) { - MemTracker::record_virtual_memory_release(addr, bytes); - } - } else { - res = pd_release_memory(addr, bytes); - } - if (!res) { + MemTracker::record_virtual_memory_release(addr, bytes); + if (!pd_release_memory(addr, bytes)) { fatal("Failed to release " RANGEFMT, RANGEFMTARGS(addr, bytes)); } log_debug(os, map)("Released " RANGEFMT, RANGEFMTARGS(addr, bytes)); @@ -2392,17 +2381,8 @@ char* os::map_memory(int fd, const char* file_name, size_t file_offset, } void os::unmap_memory(char *addr, size_t bytes) { - bool result; - if (MemTracker::enabled()) { - MemTracker::NmtVirtualMemoryLocker nvml; - result = pd_unmap_memory(addr, bytes); - if (result) { - MemTracker::record_virtual_memory_release(addr, bytes); - } - } else { - result = pd_unmap_memory(addr, bytes); - } - if (!result) { + MemTracker::record_virtual_memory_release(addr, bytes); + if (!pd_unmap_memory(addr, bytes)) { fatal("Failed to unmap memory " RANGEFMT, RANGEFMTARGS(addr, bytes)); } } From 7695b1f9c25921c25af3a37c70c3830b2c9cd41d Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Mon, 16 Mar 2026 16:01:23 +0000 Subject: [PATCH 65/97] 8379155: Refactor Files TestNG tests to use JUnit Reviewed-by: alanb --- .../java/nio/file/Files/BytesAndLines.java | 145 ++++----- .../nio/file/Files/CallWithInterruptSet.java | 28 +- .../jdk/java/nio/file/Files/CopyProcFile.java | 48 +-- .../nio/file/Files/CreateDirectories.java | 22 +- test/jdk/java/nio/file/Files/Mismatch.java | 277 +++++++++--------- .../java/nio/file/Files/ReadWriteString.java | 226 +++++++------- .../nio/file/Files/SetLastModifiedTime.java | 53 ++-- test/jdk/java/nio/file/Files/StreamTest.java | 165 ++++++----- test/jdk/java/nio/file/Files/SubstDrive.java | 97 +++--- .../nio/file/Files/walkFileTree/FindTest.java | 20 +- .../jdk/java/nio/file/spi/TestDelegation.java | 87 +++--- 11 files changed, 591 insertions(+), 577 deletions(-) diff --git a/test/jdk/java/nio/file/Files/BytesAndLines.java b/test/jdk/java/nio/file/Files/BytesAndLines.java index ca486ab87fc0..1d51021e7869 100644 --- a/test/jdk/java/nio/file/Files/BytesAndLines.java +++ b/test/jdk/java/nio/file/Files/BytesAndLines.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, 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 @@ -24,38 +24,47 @@ /* @test * @bug 7006126 8020669 8024788 8019526 * @build BytesAndLines PassThroughFileSystem - * @run testng BytesAndLines + * @run junit BytesAndLines * @summary Unit test for methods for Files readAllBytes, readAllLines and * and write methods. * @key randomness */ +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.MalformedInputException; +import java.nio.charset.UnmappableCharacterException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.file.Files; +import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.OpenOption; -import static java.nio.file.StandardOpenOption.*; -import java.nio.charset.Charset; -import java.nio.charset.CharacterCodingException; -import java.nio.charset.MalformedInputException; -import java.nio.charset.UnmappableCharacterException; -import static java.nio.charset.StandardCharsets.*; import java.util.Arrays; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; import java.util.concurrent.Callable; -import java.io.IOException; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static java.nio.charset.StandardCharsets.*; +import static java.nio.file.StandardOpenOption.*; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; -@Test(groups = "unit") public class BytesAndLines { // data for text files @@ -66,15 +75,15 @@ public class BytesAndLines { private static Random RAND = new Random(); // file used by most tests - private Path tmpfile; + private static Path tmpfile; - @BeforeClass - void setup() throws IOException { + @BeforeAll + static void setup() throws IOException { tmpfile = Files.createTempFile("blah", null); } - @AfterClass - void cleanup() throws IOException { + @AfterAll + static void cleanup() throws IOException { Files.deleteIfExists(tmpfile); } @@ -90,6 +99,7 @@ private byte[] genBytes(int size) { /** * Exercise NullPointerException */ + @Test public void testNulls() { Path file = Paths.get("foo"); byte[] bytes = new byte[100]; @@ -118,18 +128,13 @@ public void testNulls() { } private void checkNullPointerException(Callable c) { - try { - c.call(); - fail("NullPointerException expected"); - } catch (NullPointerException ignore) { - } catch (Exception e) { - fail(e + " not expected"); - } + assertThrows(NullPointerException.class, () -> c.call()); } /** * Exercise Files.readAllBytes(Path) on varied file sizes */ + @Test public void testReadAllBytes() throws IOException { int size = 0; while (size <= 16*1024) { @@ -145,7 +150,7 @@ private void testReadAllBytes(int size) throws IOException { // check expected bytes are read byte[] read = Files.readAllBytes(tmpfile); - assertTrue(Arrays.equals(read, expected), "Bytes read not the same as written"); + assertArrayEquals(expected, read, "Bytes read not the same as written"); } /** @@ -153,13 +158,14 @@ private void testReadAllBytes(int size) throws IOException { * special because file sizes are reported as 0 even though the file * has content. */ + @Test + @EnabledOnOs(OS.LINUX) public void testReadAllBytesOnProcFS() throws IOException { // read from procfs - if (System.getProperty("os.name").equals("Linux")) { - Path statFile = Paths.get("/proc/self/stat"); - byte[] data = Files.readAllBytes(statFile); - assertTrue(data.length > 0, "Files.readAllBytes('" + statFile + "') failed to read"); - } + Path statFile = Paths.get("/proc/self/stat"); + byte[] data = Files.readAllBytes(statFile); + assertTrue(data.length > 0, + "Files.readAllBytes('" + statFile + "') failed to read"); } /** @@ -167,6 +173,7 @@ public void testReadAllBytesOnProcFS() throws IOException { * because readAllBytes was originally implemented to use FileChannel * and so may not be supported by custom file system providers. */ + @Test public void testReadAllBytesOnCustomFS() throws IOException { Path myfile = PassThroughFileSystem.create().getPath("myfile"); try { @@ -175,7 +182,7 @@ public void testReadAllBytesOnCustomFS() throws IOException { byte[] b1 = genBytes(size); Files.write(myfile, b1); byte[] b2 = Files.readAllBytes(myfile); - assertTrue(Arrays.equals(b1, b2), "bytes not equal"); + assertArrayEquals(b1, b2, "bytes not equal"); size += 512; } } finally { @@ -186,6 +193,7 @@ public void testReadAllBytesOnCustomFS() throws IOException { /** * Exercise Files.write(Path, byte[], OpenOption...) on various sizes */ + @Test public void testWriteBytes() throws IOException { int size = 0; while (size < 16*1024) { @@ -198,10 +206,10 @@ public void testWriteBytes() throws IOException { private void testWriteBytes(int size, boolean append) throws IOException { byte[] bytes = genBytes(size); Path result = Files.write(tmpfile, bytes); - assertTrue(result == tmpfile); + assertSame(tmpfile, result); if (append) { Files.write(tmpfile, bytes, APPEND); - assertTrue(Files.size(tmpfile) == size*2); + assertEquals(size*2, Files.size(tmpfile)); } byte[] expected; @@ -214,12 +222,13 @@ private void testWriteBytes(int size, boolean append) throws IOException { } byte[] read = Files.readAllBytes(tmpfile); - assertTrue(Arrays.equals(read, expected), "Bytes read not the same as written"); + assertArrayEquals(expected, read, "Bytes read not the same as written"); } /** * Exercise Files.readAllLines(Path, Charset) */ + @Test public void testReadAllLines() throws IOException { // zero lines Files.write(tmpfile, new byte[0]); @@ -230,23 +239,22 @@ public void testReadAllLines() throws IOException { byte[] hi = { (byte)'h', (byte)'i' }; Files.write(tmpfile, hi); lines = Files.readAllLines(tmpfile, US_ASCII); - assertTrue(lines.size() == 1, "One line expected"); - assertTrue(lines.get(0).equals("hi"), "'Hi' expected"); + assertEquals(1, lines.size(), "One line expected"); + assertEquals("hi", lines.get(0), "'Hi' expected"); // two lines using platform's line separator List expected = Arrays.asList("hi", "there"); Files.write(tmpfile, expected, US_ASCII); assertTrue(Files.size(tmpfile) > 0, "File is empty"); lines = Files.readAllLines(tmpfile, US_ASCII); - assertTrue(lines.equals(expected), "Unexpected lines"); + assertLinesMatch(expected, lines, "Unexpected lines"); // MalformedInputException byte[] bad = { (byte)0xff, (byte)0xff }; Files.write(tmpfile, bad); - try { - Files.readAllLines(tmpfile, US_ASCII); - fail("MalformedInputException expected"); - } catch (MalformedInputException ignore) { } + assertThrows(MalformedInputException.class, + () -> Files.readAllLines(tmpfile, US_ASCII), + "MalformedInputException expected"); } /** @@ -254,24 +262,27 @@ public void testReadAllLines() throws IOException { * is special because file sizes are reported as 0 even though the file * has content. */ + @Test + @EnabledOnOs(OS.LINUX) public void testReadAllLinesOnProcFS() throws IOException { - if (System.getProperty("os.name").equals("Linux")) { - Path statFile = Paths.get("/proc/self/stat"); - List lines = Files.readAllLines(statFile); - assertTrue(lines.size() > 0, "Files.readAllLines('" + statFile + "') failed to read"); - } + Path statFile = Paths.get("/proc/self/stat"); + List lines = Files.readAllLines(statFile); + assertTrue(lines.size() > 0, + "Files.readAllLines('" + statFile + "') failed to read"); } /** * Exercise Files.readAllLines(Path) */ + @Test public void testReadAllLinesUTF8() throws IOException { Files.write(tmpfile, encodeAsUTF8(EN_STRING + "\n" + JA_STRING)); List lines = Files.readAllLines(tmpfile); - assertTrue(lines.size() == 2, "Read " + lines.size() + " lines instead of 2"); - assertTrue(lines.get(0).equals(EN_STRING)); - assertTrue(lines.get(1).equals(JA_STRING)); + assertEquals(2, lines.size(), + "Read " + lines.size() + " lines instead of 2"); + assertEquals(EN_STRING, lines.get(0)); + assertEquals(JA_STRING, lines.get(1)); // a sample of malformed sequences testReadAllLinesMalformedUTF8((byte)0xFF); // one-byte sequence @@ -284,57 +295,55 @@ private byte[] encodeAsUTF8(String s) throws CharacterCodingException { ByteBuffer bb = UTF_8.newEncoder().encode(CharBuffer.wrap(s)); byte[] result = new byte[bb.limit()]; bb.get(result); - assertTrue(bb.remaining() == 0); + assertEquals(0, bb.remaining()); return result; } private void testReadAllLinesMalformedUTF8(byte... bytes) throws IOException { Files.write(tmpfile, bytes); - try { - Files.readAllLines(tmpfile); - fail("MalformedInputException expected"); - } catch (MalformedInputException ignore) { } + assertThrows(MalformedInputException.class, + () -> Files.readAllLines(tmpfile)); } /** * Exercise Files.write(Path, Iterable, Charset, OpenOption...) */ + @Test public void testWriteLines() throws IOException { // zero lines Path result = Files.write(tmpfile, Collections.emptyList(), US_ASCII); - assert(Files.size(tmpfile) == 0); - assert(result == tmpfile); + assertEquals(0, Files.size(tmpfile)); + assertSame(tmpfile, result); // two lines List lines = Arrays.asList("hi", "there"); Files.write(tmpfile, lines, US_ASCII); List actual = Files.readAllLines(tmpfile, US_ASCII); - assertTrue(actual.equals(lines), "Unexpected lines"); + assertLinesMatch(lines, actual, "Unexpected lines"); // append two lines Files.write(tmpfile, lines, US_ASCII, APPEND); List expected = new ArrayList<>(); expected.addAll(lines); expected.addAll(lines); - assertTrue(expected.size() == 4, "List should have 4 elements"); + assertEquals(4, expected.size()); actual = Files.readAllLines(tmpfile, US_ASCII); - assertTrue(actual.equals(expected), "Unexpected lines"); + assertLinesMatch(expected, actual, "Unexpected lines"); // UnmappableCharacterException - try { - String s = "\u00A0\u00A1"; - Files.write(tmpfile, Arrays.asList(s), US_ASCII); - fail("UnmappableCharacterException expected"); - } catch (UnmappableCharacterException ignore) { } + String s = "\u00A0\u00A1"; + assertThrows(UnmappableCharacterException.class, + () -> Files.write(tmpfile, Arrays.asList(s), US_ASCII)); } /** * Exercise Files.write(Path, Iterable, OpenOption...) */ + @Test public void testWriteLinesUTF8() throws IOException { List lines = Arrays.asList(EN_STRING, JA_STRING); Files.write(tmpfile, lines); List actual = Files.readAllLines(tmpfile, UTF_8); - assertTrue(actual.equals(lines), "Unexpected lines"); + assertLinesMatch(lines, actual, "Unexpected lines"); } } diff --git a/test/jdk/java/nio/file/Files/CallWithInterruptSet.java b/test/jdk/java/nio/file/Files/CallWithInterruptSet.java index 3d7bfd2c518d..59d6a6a7ecba 100644 --- a/test/jdk/java/nio/file/Files/CallWithInterruptSet.java +++ b/test/jdk/java/nio/file/Files/CallWithInterruptSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,22 +24,24 @@ /* * @test * @bug 8205612 - * @run testng CallWithInterruptSet + * @run junit CallWithInterruptSet * @summary Test invoking Files methods with the interrupted status set */ -import java.io.InputStream; -import java.io.OutputStream; -import java.io.IOException; import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class CallWithInterruptSet { @@ -49,7 +51,7 @@ public void testReadAllBytes() throws Exception { Thread.currentThread().interrupt(); try { byte[] bytes = Files.readAllBytes(file); - assertTrue(bytes.length == 100); + assertEquals(100, bytes.length); } finally { assertTrue(Thread.interrupted()); // clear interrupt } @@ -76,7 +78,7 @@ public void testOutputStream() throws Exception { } finally { assertTrue(Thread.interrupted()); // clear interrupt } - assertTrue(Files.size(file) == 10); + assertEquals(10, Files.size(file)); } @Test @@ -86,7 +88,7 @@ public void testReadString() throws Exception { Thread.currentThread().interrupt(); try { String msg = Files.readString(file); - assertEquals(msg, "hello"); + assertEquals("hello", msg); } finally { assertTrue(Thread.interrupted()); // clear interrupt } @@ -102,7 +104,7 @@ public void testWriteString() throws Exception { assertTrue(Thread.interrupted()); // clear interrupt } String msg = Files.readString(file); - assertEquals(msg, "hello"); + assertEquals("hello", msg); } @Test @@ -112,7 +114,7 @@ public void testBufferedReader() throws Exception { Thread.currentThread().interrupt(); try (BufferedReader reader = Files.newBufferedReader(file)) { String msg = reader.readLine(); - assertEquals(msg, "hello"); + assertEquals("hello", msg); } finally { assertTrue(Thread.interrupted()); // clear interrupt } @@ -128,7 +130,7 @@ public void testBufferedWriter() throws Exception { assertTrue(Thread.interrupted()); // clear interrupt } String msg = Files.readString(file); - assertEquals(msg, "hello"); + assertEquals("hello", msg); } private Path mkfile() throws IOException { diff --git a/test/jdk/java/nio/file/Files/CopyProcFile.java b/test/jdk/java/nio/file/Files/CopyProcFile.java index 892ac6b0eb4b..c187aaed8619 100644 --- a/test/jdk/java/nio/file/Files/CopyProcFile.java +++ b/test/jdk/java/nio/file/Files/CopyProcFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,20 +33,23 @@ import java.util.ArrayList; import java.util.List; import java.util.function.ToLongBiFunction; +import java.util.stream.Stream; import static java.nio.file.StandardOpenOption.*; -import org.testng.Assert; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.*; /* * @test * @bug 8293502 * @requires (os.family == "linux") * @summary Ensure that copying from a file in /proc works - * @run testng/othervm CopyProcFile + * @run junit/othervm CopyProcFile */ public class CopyProcFile { static final String SOURCE = "/proc/version"; @@ -112,8 +115,8 @@ static long transferFrom(String src, String dst) { } } - @BeforeTest(alwaysRun=true) - public void createBufferedCopy() throws IOException { + @BeforeAll + public static void createBufferedCopy() throws IOException { System.out.printf("Using source file \"%s\"%n", SOURCE); try { theSize = bufferedCopy(SOURCE, BUFFERED_COPY); @@ -129,8 +132,8 @@ public void createBufferedCopy() throws IOException { } } - @AfterTest(alwaysRun=true) - public void deleteBufferedCopy() { + @AfterAll + public static void deleteBufferedCopy() { try { Files.delete(Path.of(BUFFERED_COPY)); } catch (IOException ignore) {} @@ -148,18 +151,17 @@ long apply(String src, String dst) { } } - @DataProvider - static Object[][] functions() throws IOException { - List funcs = new ArrayList<>(); - funcs.add(new Object[] {new FHolder((s, d) -> copy(s, d))}); - funcs.add(new Object[] {new FHolder((s, d) -> transferToIO(s, d))}); - funcs.add(new Object[] {new FHolder((s, d) -> transferToNIO(s, d))}); - funcs.add(new Object[] {new FHolder((s, d) -> transferFrom(s, d))}); - return funcs.toArray(Object[][]::new); + static Stream functions() throws IOException { + return Stream.of + (new FHolder((s, d) -> copy(s, d)), + new FHolder((s, d) -> transferToIO(s, d)), + new FHolder((s, d) -> transferToNIO(s, d)), + new FHolder((s, d) -> transferFrom(s, d))); } - @Test(dataProvider = "functions") - public static void testCopyAndTransfer(FHolder f) throws IOException { + @ParameterizedTest + @MethodSource("functions") + public void testCopyAndTransfer(FHolder f) throws IOException { try { long size = f.apply(SOURCE, TARGET); if (size != theSize) { @@ -168,9 +170,7 @@ public static void testCopyAndTransfer(FHolder f) throws IOException { } long mismatch = Files.mismatch(Path.of(BUFFERED_COPY), Path.of(TARGET)); - if (mismatch != -1) { - throw new RuntimeException("Target does not match copy"); - } + assertEquals(-1, mismatch, "Target does not match copy"); } finally { try { Files.delete(Path.of(TARGET)); diff --git a/test/jdk/java/nio/file/Files/CreateDirectories.java b/test/jdk/java/nio/file/Files/CreateDirectories.java index fc5a5025955d..9912b442cfd6 100644 --- a/test/jdk/java/nio/file/Files/CreateDirectories.java +++ b/test/jdk/java/nio/file/Files/CreateDirectories.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,16 +26,18 @@ import java.nio.file.Files; import java.nio.file.Path; -import static org.testng.Assert.*; -import org.testng.SkipException; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; + +import org.junit.jupiter.api.Assumptions; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @bug 8032220 8293792 8307976 * @summary Test java.nio.file.Files.createDirectories method * @library .. - * @run testng CreateDirectories + * @run junit CreateDirectories */ public class CreateDirectories { @@ -46,12 +48,10 @@ public class CreateDirectories { public void testSymlinkDir() throws Exception { // create a temp dir as the "root" in which we will run our tests. final Path top = TestUtil.createTemporaryDirectory(); - if (!TestUtil.supportsSymbolicLinks(top)) { - System.out.println("Skipping tests since symbolic links isn't " + - "supported under directory "+ top); - throw new SkipException("Symbolic links not supported"); - } - System.out.println("Running tests under directory " + top.toAbsolutePath()); + Assumptions.assumeTrue(TestUtil.supportsSymbolicLinks(top), + "Skipping as symbolic links are not supported under in " + top); + System.err.println("Running tests under directory " + + top.toAbsolutePath()); final Path fooDir = Files.createDirectory(top.resolve("foo")); assertTrue(Files.isDirectory(fooDir), fooDir + " was expected to be a directory but wasn't"); diff --git a/test/jdk/java/nio/file/Files/Mismatch.java b/test/jdk/java/nio/file/Files/Mismatch.java index 8746f448ad0c..0d0446ed96eb 100644 --- a/test/jdk/java/nio/file/Files/Mismatch.java +++ b/test/jdk/java/nio/file/Files/Mismatch.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,12 +21,6 @@ * questions. */ -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -36,13 +30,23 @@ import java.nio.file.Paths; import java.nio.file.spi.FileSystemProvider; import java.util.Map; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + /* @test * @bug 8202285 * @build Mismatch - * @run testng Mismatch + * @run junit Mismatch * @summary Unit test for the Files.mismatch method. */ public class Mismatch { @@ -55,29 +59,28 @@ public class Mismatch { private static final Map ZIPFS_MAP = Map.of("create", "true"); // temporary test directory where all test files will be created - Path testDir; + static Path testDir; - @BeforeClass - void setup() throws IOException { + @BeforeAll + static void setup() throws IOException { testDir = Files.createTempDirectory("testMismatch"); } - @AfterClass - void cleanup() throws IOException { + @AfterAll + static void cleanup() throws IOException { // clean up files created under the test directory Files.walk(testDir).map(Path::toFile).forEach(File::delete); Files.deleteIfExists(testDir); } /* - * DataProvider for mismatch test. Provides the following fields: + * MethodSource for mismatch test. Provides the following fields: * path1 -- the path to a file * path2 -- the path to another file * expected -- expected result of the mismatch method * note -- a note about the test */ - @DataProvider(name = "testMismatch") - public Object[][] getDataForMismatch() throws IOException { + public static Stream getDataForMismatch() throws IOException { // an non-existent file Path foo = Paths.get("nonexistentfile"); @@ -147,151 +150,148 @@ public Object[][] getDataForMismatch() throws IOException { Path test1065000m532500 = createASCIIFile(testDir, "test1065000m532500", size, size >> 1, '%'); Path test1065000m1064999 = createASCIIFile(testDir, "test1065000m1064999", size, 1064999, '$'); - return new Object[][]{ - // Spec Case 1: the two paths locate the same file , even if one does not exist - {foo, foo, MISMATCH_NO, "Same file, no mismatch"}, - {test1024a, test1024a, MISMATCH_NO, "Same file, no mismatch"}, - - // Spec Case 2: The two files are the same size, and every byte in the first file - // is identical to the corresponding byte in the second file. - {test0a, test0b, MISMATCH_NO, "Sizes == 0, no mismatch"}, - {test147a, test147b, MISMATCH_NO, "size = 147 < buffer = 8192, no mismatch"}, - {test1024a, test1024b, MISMATCH_NO, "size = 1024 < buffer = 8192, no mismatch"}, - {test8192a, test8192b, MISMATCH_NO, "size = 8192 = buffer = 8192, no mismatch"}, - {test65536a, test65536b, MISMATCH_NO, "read 8 * full buffer, no mismatch"}, - {test70025a, test70025b, MISMATCH_NO, "read 8 * full buffer plus a partial buffer, no mismatch"}, - - - /** - * Spec Case 3: the value returned is the position of the first mismatched byte - * Impl: the impl uses a buffer 8192. The testcases below covers a range of files - * with sizes <= and > the buffer size. The last buffer is either full or partially full. - */ - - // edge case, one of the file sizes is zero - // also covers Spec Case 4 and 6 - {test147a, test147m0, 0, "mismatch = 0 (at the beginning)"}, - {test65536m0, test65536a, 0, "mismatch = 0 (at the beginning)"}, - - /** - * Compares files of equal sizes - */ - // small files - {test147a, test147m70, 70, "read one partial buffer, mismatch = 70"}, - {test147a, test147m146, 146, "read one partial buffer, mismatch = 146 (end)"}, - {test1024a, test1024m512, 512, "read one partial buffer, mismatch = 512"}, - {test1024a, test1024m1023, 1023, "read one partial buffer, mismatch = 1023 (end)"}, - - // file size >= Impl's Buffer Size - {test8192a, test8192m4096, 4096, "read one buffer, mismatch = 4096 "}, - {test8192a, test8192m8191, 8191, "read one buffer, mismatch = 8191 (at the end)"}, - - // file size = n * Impl's Buffer Size - {test65536a, test65536m32768, 32768, "read through half of the file, mismatch = 32768"}, - {test65536a, test65536m65535, 65535, "read through the whole file, mismatch = 65535 (at the end)"}, - - // file size = n * Impl's Buffer Size + x - {test70025a, test70025m8400, 8400, "mismatch in the 2nd buffer, mismatch = 8400"}, - {test70025a, test70025m35000, 35000, "read about half of the file, mismatch = 35000"}, - {test70025a, test70025m70024, 70024, "read through the whole file, mismatch = 70024 (at the end)"}, - - /** - * Compares files of unequal sizes - */ - {test8192m8191, test70025m35000, 8191, "mismatch at the end of the 1st file/buffer, mismatch = 8191"}, - {test65536m32768, test70025m8400, 8400, "mismatch in the 2nd buffer, mismatch = 8400"}, - {test70025m70024, test1065000m532500, 70024, "mismatch at the end of the 1st file, mismatch = 70024"}, - - /** - * Spec Case 4: returns the size of the smaller file (in bytes) when the files are - * different sizes and every byte of the smaller file is identical to the corresponding - * byte of the larger file. - * Impl: similar to case 3, covers a range of file sizes - */ - {test147a, test1024a, 147, "mismatch is the length of the smaller file: 147"}, - {test1024a, test8192a, 1024, "mismatch is the length of the smaller file: 1024"}, - {test1024a, test65536a, 1024, "mismatch is the length of the smaller file: 1024"}, - {test8192a, test65536a, 8192, "mismatch is the length of the smaller file: 8192"}, - {test70025a, test65536a, 65536, "mismatch is the length of the smaller file: 65536"}, - {test1048576a, test1065000m1064999, 1048576, "mismatch is the length of the smaller file: 1048576"}, - - // Spec Case 5: This method is always reflexive (for Path f , mismatch(f,f) returns -1L) - // See tests for Spec Case 1. - - // Spec Case 6: If the file system and files remain static, then this method is symmetric - // (for two Paths f and g, mismatch(f,g) will return the same value as mismatch(g,f)). - // The following tests are selected from tests for Spec Case 3 with the order of - // file paths switched, the returned values are the same as those for Case 3: - {test147m70, test147a, 70, "read one partial buffer, mismatch = 70"}, - {test147m146, test147a, 146, "read one partial buffer, mismatch = 146 (end)"}, - {test1024m512, test1024a, 512, "read one partial buffer, mismatch = 512"}, - {test1024m1023, test1024a, 1023, "read one partial buffer, mismatch = 1023 (end)"}, - - {test70025m35000, test8192m8191, 8191, "mismatch at the end of the 1st file/buffer, mismatch = 8191"}, - {test70025m8400, test65536m32768, 8400, "mismatch in the 2nd buffer, mismatch = 8400"}, - {test1065000m532500, test70025m70024, 70024, "mismatch at the end of the 1st file, mismatch = 70024"}, - }; + return Stream.of + (// Spec Case 1: the two paths locate the same file , even if one does not exist + Arguments.of(foo, foo, MISMATCH_NO, "Same file, no mismatch"), + Arguments.of(test1024a, test1024a, MISMATCH_NO, "Same file, no mismatch"), + + // Spec Case 2: The two files are the same size, and every byte in the first file + // is identical to the corresponding byte in the second file. + Arguments.of(test0a, test0b, MISMATCH_NO, "Sizes == 0, no mismatch"), + Arguments.of(test147a, test147b, MISMATCH_NO, "size = 147 < buffer = 8192, no mismatch"), + Arguments.of(test1024a, test1024b, MISMATCH_NO, "size = 1024 < buffer = 8192, no mismatch"), + Arguments.of(test8192a, test8192b, MISMATCH_NO, "size = 8192 = buffer = 8192, no mismatch"), + Arguments.of(test65536a, test65536b, MISMATCH_NO, "read 8 * full buffer, no mismatch"), + Arguments.of(test70025a, test70025b, MISMATCH_NO, "read 8 * full buffer plus a partial buffer, no mismatch"), + + + /* + * Spec Case 3: the value returned is the position of the first mismatched byte + * Impl: the impl uses a buffer 8192. The testcases below covers a range of files + * with sizes <= and > the buffer size. The last buffer is either full or partially full. + */ + + // edge case, one of the file sizes is zero + // also covers Spec Case 4 and 6 + Arguments.of(test147a, test147m0, 0, "mismatch = 0 (at the beginning)"), + Arguments.of(test65536m0, test65536a, 0, "mismatch = 0 (at the beginning)"), + + /* + * Compares files of equal sizes + */ + // small files + Arguments.of(test147a, test147m70, 70, "read one partial buffer, mismatch = 70"), + Arguments.of(test147a, test147m146, 146, "read one partial buffer, mismatch = 146 (end)"), + Arguments.of(test1024a, test1024m512, 512, "read one partial buffer, mismatch = 512"), + Arguments.of(test1024a, test1024m1023, 1023, "read one partial buffer, mismatch = 1023 (end)"), + + // file size >= Impl's Buffer Size + Arguments.of(test8192a, test8192m4096, 4096, "read one buffer, mismatch = 4096 "), + Arguments.of(test8192a, test8192m8191, 8191, "read one buffer, mismatch = 8191 (at the end)"), + + // file size = n * Impl's Buffer Size + Arguments.of(test65536a, test65536m32768, 32768, "read through half of the file, mismatch = 32768"), + Arguments.of(test65536a, test65536m65535, 65535, "read through the whole file, mismatch = 65535 (at the end)"), + + // file size = n * Impl's Buffer Size + x + Arguments.of(test70025a, test70025m8400, 8400, "mismatch in the 2nd buffer, mismatch = 8400"), + Arguments.of(test70025a, test70025m35000, 35000, "read about half of the file, mismatch = 35000"), + Arguments.of(test70025a, test70025m70024, 70024, "read through the whole file, mismatch = 70024 (at the end)"), + + /* + * Compares files of unequal sizes + */ + Arguments.of(test8192m8191, test70025m35000, 8191, "mismatch at the end of the 1st file/buffer, mismatch = 8191"), + Arguments.of(test65536m32768, test70025m8400, 8400, "mismatch in the 2nd buffer, mismatch = 8400"), + Arguments.of(test70025m70024, test1065000m532500, 70024, "mismatch at the end of the 1st file, mismatch = 70024"), + + /* + * Spec Case 4: returns the size of the smaller file (in bytes) when the files are + * different sizes and every byte of the smaller file is identical to the corresponding + * byte of the larger file. + * Impl: similar to case 3, covers a range of file sizes + */ + Arguments.of(test147a, test1024a, 147, "mismatch is the length of the smaller file: 147"), + Arguments.of(test1024a, test8192a, 1024, "mismatch is the length of the smaller file: 1024"), + Arguments.of(test1024a, test65536a, 1024, "mismatch is the length of the smaller file: 1024"), + Arguments.of(test8192a, test65536a, 8192, "mismatch is the length of the smaller file: 8192"), + Arguments.of(test70025a, test65536a, 65536, "mismatch is the length of the smaller file: 65536"), + Arguments.of(test1048576a, test1065000m1064999, 1048576, "mismatch is the length of the smaller file: 1048576"), + + // Spec Case 5: This method is always reflexive (for Path f , mismatch(f,f) returns -1L) + // See tests for Spec Case 1. + + // Spec Case 6: If the file system and files remain static, then this method is symmetric + // (for two Paths f and g, mismatch(f,g) will return the same value as mismatch(g,f)). + // The following tests are selected from tests for Spec Case 3 with the order of + // file paths switched, the returned values are the same as those for Case 3: + Arguments.of(test147m70, test147a, 70, "read one partial buffer, mismatch = 70"), + Arguments.of(test147m146, test147a, 146, "read one partial buffer, mismatch = 146 (end)"), + Arguments.of(test1024m512, test1024a, 512, "read one partial buffer, mismatch = 512"), + Arguments.of(test1024m1023, test1024a, 1023, "read one partial buffer, mismatch = 1023 (end)"), + + Arguments.of(test70025m35000, test8192m8191, 8191, "mismatch at the end of the 1st file/buffer, mismatch = 8191"), + Arguments.of(test70025m8400, test65536m32768, 8400, "mismatch in the 2nd buffer, mismatch = 8400"), + Arguments.of(test1065000m532500, test70025m70024, 70024, "mismatch at the end of the 1st file, mismatch = 70024") + ); } /* - * DataProvider for mismatch tests involving ZipFS using a few test cases selected + * MethodSource for mismatch tests involving ZipFS using a few test cases selected * from those of the original mismatch tests. */ - @DataProvider(name = "testMismatchZipfs") - public Object[][] getDataForMismatchZipfs() throws IOException { + public static Stream getDataForMismatchZipfs() throws IOException { Path test1200 = createASCIIFile(testDir, "test1200", 1200, -1, ' '); Path test9500 = createASCIIFile(testDir, "test9500", 9500, -1, ' '); Path test9500m4200 = createASCIIFile(testDir, "test9500m4200", 9500, 4200, '!'); Path test80025 = createASCIIFile(testDir, "test80025", 80025, -1, ' '); Path test1028500 = createASCIIFile(testDir, "test1028500", 1028500, -1, ' '); - return new Object[][]{ - {test1200, test1200, MISMATCH_NO, "Compares the file and its copy in zip, no mismatch"}, - {test9500, test9500m4200, 4200, - "Compares a copy of test9500m4200 in zip with test9500, shall return 4200"}, - {test80025, test1028500, 80025, "mismatch is the length of the smaller file: 80025"}, - }; + return Stream.of + ( + Arguments.of(test1200, test1200, MISMATCH_NO, "Compares the file and its copy in zip, no mismatch"), + Arguments.of(test9500, test9500m4200, 4200, + "Compares a copy of test9500m4200 in zip with test9500, shall return 4200"), + Arguments.of(test80025, test1028500, 80025, + "mismatch is the length of the smaller file: 80025") + ); } /* - * DataProvider for verifying null handling. + * MethodSource for verifying null handling. */ - @DataProvider(name = "testFileNull") - public Object[][] getDataForNull() throws IOException { + public static Stream getDataForNull() throws IOException { Path test = createASCIIFile(testDir, "testNonNull", 2200, -1, ' '); - return new Object[][]{ - {(Path)null, (Path)null}, - {(Path)null, test}, - {test, (Path)null}, - }; + return Stream.of(Arguments.of((Path)null, (Path)null), + Arguments.of((Path)null, test), + Arguments.of(test, (Path)null)); } /* - * DataProvider for verifying how the mismatch method handles the situation + * MethodSource for verifying how the mismatch method handles the situation * when one or both files do not exist. */ - @DataProvider(name = "testFileNotExist") - public Object[][] getDataForFileNotExist() throws IOException { + public static Stream getDataForFileNotExist() throws IOException { Path test = createASCIIFile(testDir, "testFileNotExist", 3200, -1, ' '); - return new Object[][]{ - {Paths.get("foo"), Paths.get("bar")}, - {Paths.get("foo"), test}, - {test, Paths.get("bar")}, - }; + return Stream.of + (Arguments.of(Paths.get("foo"), Paths.get("bar")), + Arguments.of("foo", test), + Arguments.of(test, Paths.get("bar"))); } /** - * Tests the mismatch method. Refer to the dataProvider testMismatch for more - * details about the cases. + * Tests the mismatch method. Refer to the dataProvider getDataForNull for + * more details about the cases. * @param path the path to a file * @param path2 the path to another file * @param expected the expected result * @param msg the message about the test * @throws IOException if the test fails */ - @Test(dataProvider = "testMismatch", priority = 0) + @ParameterizedTest + @MethodSource("getDataForMismatch") public void testMismatch(Path path, Path path2, long expected, String msg) throws IOException { - Assert.assertEquals(Files.mismatch(path, path2), expected, msg); + assertEquals(expected, Files.mismatch(path, path2), msg); } /** @@ -302,7 +302,8 @@ public void testMismatch(Path path, Path path2, long expected, String msg) * @param msg the message about the test * @throws IOException if the test fails */ - @Test(dataProvider = "testMismatchZipfs", priority = 1) + @ParameterizedTest + @MethodSource("getDataForMismatchZipfs") public void testMismatchZipfs(Path path, Path path2, long expected, String msg) throws IOException { Path zipPath = Paths.get(testDir.toString(), "TestWithFSZip.zip"); @@ -311,9 +312,9 @@ public void testMismatchZipfs(Path path, Path path2, long expected, String msg) Files.copy(path, copy, REPLACE_EXISTING); if (path2 == null) { - Assert.assertEquals(Files.mismatch(copy, path), expected, msg); + assertEquals(expected, Files.mismatch(copy, path), msg); } else { - Assert.assertEquals(Files.mismatch(copy, path2), expected, msg); + assertEquals(expected, Files.mismatch(copy, path2), msg); } } } @@ -324,9 +325,11 @@ public void testMismatchZipfs(Path path, Path path2, long expected, String msg) * @param path2 the path to another file * @throws NullPointerException as expected */ - @Test(dataProvider = "testFileNull", priority = 2, expectedExceptions = NullPointerException.class) + @ParameterizedTest + @MethodSource("getDataForNull") public void testMismatchNull(Path path, Path path2) throws Exception { - long result = Files.mismatch(path, path2); + assertThrows(NullPointerException.class, + () -> Files.mismatch(path, path2)); } /** @@ -335,9 +338,11 @@ public void testMismatchNull(Path path, Path path2) throws Exception { * @param path2 the path to another file * @throws IOException as expected */ - @Test(dataProvider = "testFileNotExist", priority = 2, expectedExceptions = IOException.class) + @ParameterizedTest + @MethodSource("getDataForFileNotExist") public void testMismatchNotExist(Path path, Path path2) throws IOException { - long result = Files.mismatch(path, path2); + assertThrows(IOException.class, + () -> {long result = Files.mismatch(path, path2);}); } /** diff --git a/test/jdk/java/nio/file/Files/ReadWriteString.java b/test/jdk/java/nio/file/Files/ReadWriteString.java index 8b5241fa1cf0..3510f41e979a 100644 --- a/test/jdk/java/nio/file/Files/ReadWriteString.java +++ b/test/jdk/java/nio/file/Files/ReadWriteString.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,21 @@ import java.nio.charset.CharacterCodingException; import java.nio.charset.MalformedInputException; import java.nio.charset.UnmappableCharacterException; +import java.nio.file.Files; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.US_ASCII; import static java.nio.charset.StandardCharsets.UTF_8; @@ -36,36 +51,31 @@ import static java.nio.charset.StandardCharsets.UTF_32; import static java.nio.charset.StandardCharsets.UTF_32BE; import static java.nio.charset.StandardCharsets.UTF_32LE; -import java.nio.file.Files; -import java.nio.file.OpenOption; -import java.nio.file.Path; -import java.nio.file.Paths; + import static java.nio.file.StandardOpenOption.APPEND; import static java.nio.file.StandardOpenOption.CREATE; -import java.util.Arrays; -import java.util.Random; -import java.util.concurrent.Callable; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /* @test * @bug 8201276 8205058 8209576 8287541 8288589 8325590 * @build ReadWriteString PassThroughFileSystem - * @run testng ReadWriteString + * @run junit ReadWriteString * @summary Unit test for methods for Files readString and write methods. * @key randomness * @modules jdk.charsets */ -@Test(groups = "readwrite") public class ReadWriteString { // data for text files - final String TEXT_UNICODE = "\u201CHello\u201D"; - final String TEXT_ASCII = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n abcdefghijklmnopqrstuvwxyz\n 1234567890\n"; + final static String TEXT_UNICODE = "\u201CHello\u201D"; + final static String TEXT_ASCII = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n abcdefghijklmnopqrstuvwxyz\n 1234567890\n"; final static String TEXT_PERSON_CART_WHEELING = "\ud83e\udd38"; private static final String JA_STRING = "\u65e5\u672c\u8a9e\u6587\u5b57\u5217"; private static final Charset WINDOWS_1252 = Charset.forName("windows-1252"); @@ -85,96 +95,85 @@ static byte[] getData() { return baos.toByteArray(); } catch (IOException ex) { // in case it happens, fail the test - throw new RuntimeException(ex); + fail(ex); + return null; // appease the compiler } } // file used by testReadWrite, testReadString and testWriteString - private Path[] testFiles = new Path[3]; + private static Path[] testFiles = new Path[3]; /* - * DataProvider for malformed write test. Provides the following fields: + * MethodSource for malformed write test. Provides the following fields: * file path, malformed input string, charset */ - @DataProvider(name = "malformedWrite") - public Object[][] getMalformedWrite() throws IOException { + public static Stream getMalformedWrite() throws IOException { Path path = Files.createFile(Path.of("malformedWrite")); - return new Object[][]{ - {path, "\ud800", null}, //the default Charset is UTF_8 - {path, "\u00A0\u00A1", US_ASCII}, - {path, "\ud800", UTF_8}, - {path, JA_STRING, ISO_8859_1}, - {path, "\u041e", WINDOWS_1252}, // cyrillic capital letter O - {path, "\u091c", WINDOWS_31J}, // devanagari letter ja - }; + return Stream.of + (Arguments.of(path, "\ud800", null), //the default Charset is UTF_8 + Arguments.of(path, "\u00A0\u00A1", US_ASCII), + Arguments.of(path, "\ud800", UTF_8), + Arguments.of(path, JA_STRING, ISO_8859_1), + Arguments.of(path, "\u041e", WINDOWS_1252), // cyrillic capital letter O + Arguments.of(path, "\u091c", WINDOWS_31J)); // devanagari letter ja } /* - * DataProvider for illegal input test + * MethodSource for illegal input test * Writes the data in ISO8859 and reads with UTF_8, expects MalformedInputException */ - @DataProvider(name = "illegalInput") - public Object[][] getIllegalInput() throws IOException { + public static Stream getIllegalInput() throws IOException { Path path = Files.createFile(Path.of("illegalInput")); - return new Object[][]{ - {path, data, ISO_8859_1, null}, - {path, data, ISO_8859_1, UTF_8} - }; + return Stream.of(Arguments.of(path, data, ISO_8859_1, null), + Arguments.of(path, data, ISO_8859_1, UTF_8)); } /* - * DataProvider for illegal input bytes test + * MethodSource for illegal input bytes test */ - @DataProvider(name = "illegalInputBytes") - public Object[][] getIllegalInputBytes() throws IOException { - return new Object[][]{ - {new byte[] {(byte)0x00, (byte)0x20, (byte)0x00}, UTF_16, MalformedInputException.class}, - {new byte[] {-50}, UTF_16, MalformedInputException.class}, - {new byte[] {(byte)0x81}, WINDOWS_1252, UnmappableCharacterException.class}, // unused in Cp1252 - {new byte[] {(byte)0x81, (byte)0xff}, WINDOWS_31J, UnmappableCharacterException.class}, // invalid trailing byte - }; + public static Stream getIllegalInputBytes() throws IOException { + return Stream.of + (Arguments.of(new byte[] {(byte)0x00, (byte)0x20, (byte)0x00}, UTF_16, MalformedInputException.class), + Arguments.of(new byte[] {-50}, UTF_16, MalformedInputException.class), + Arguments.of(new byte[] {(byte)0x81}, WINDOWS_1252, UnmappableCharacterException.class), // unused in Cp1252 + Arguments.of(new byte[] {(byte)0x81, (byte)0xff}, WINDOWS_31J, UnmappableCharacterException.class)); // invalid trailing byte } /* - * DataProvider for writeString test + * MethodSource for writeString test * Writes the data using both the existing and new method and compares the results. */ - @DataProvider(name = "testWriteString") - public Object[][] getWriteString() { - - return new Object[][]{ - {testFiles[1], testFiles[2], TEXT_ASCII, US_ASCII, null}, - {testFiles[1], testFiles[2], TEXT_ASCII, US_ASCII, US_ASCII}, - {testFiles[1], testFiles[2], TEXT_UNICODE, UTF_8, null}, - {testFiles[1], testFiles[2], TEXT_UNICODE, UTF_8, UTF_8} - }; + public static Stream getWriteString() { + return Stream.of + (Arguments.of(testFiles[1], testFiles[2], TEXT_ASCII, US_ASCII, null), + Arguments.of(testFiles[1], testFiles[2], TEXT_ASCII, US_ASCII, US_ASCII), + Arguments.of(testFiles[1], testFiles[2], TEXT_UNICODE, UTF_8, null), + Arguments.of(testFiles[1], testFiles[2], TEXT_UNICODE, UTF_8, UTF_8)); } /* - * DataProvider for readString test + * MethodSource for readString test * Reads the file using both the existing and new method and compares the results. */ - @DataProvider(name = "testReadString") - public Object[][] getReadString() { - return new Object[][]{ - {testFiles[1], TEXT_ASCII, US_ASCII, US_ASCII}, - {testFiles[1], TEXT_ASCII, US_ASCII, UTF_8}, - {testFiles[1], TEXT_UNICODE, UTF_8, null}, - {testFiles[1], TEXT_UNICODE, UTF_8, UTF_8}, - {testFiles[1], TEXT_ASCII, US_ASCII, ISO_8859_1}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_16, UTF_16}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_16BE, UTF_16BE}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_16LE, UTF_16LE}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_32, UTF_32}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_32BE, UTF_32BE}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_32LE, UTF_32LE}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, WINDOWS_1252, WINDOWS_1252}, - {testFiles[1], TEXT_PERSON_CART_WHEELING, WINDOWS_31J, WINDOWS_31J} - }; + public static Stream getReadString() { + return Stream.of + (Arguments.of(testFiles[1], TEXT_ASCII, US_ASCII, US_ASCII), + Arguments.of(testFiles[1], TEXT_ASCII, US_ASCII, UTF_8), + Arguments.of(testFiles[1], TEXT_UNICODE, UTF_8, null), + Arguments.of(testFiles[1], TEXT_UNICODE, UTF_8, UTF_8), + Arguments.of(testFiles[1], TEXT_ASCII, US_ASCII, ISO_8859_1), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_16, UTF_16), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_16BE, UTF_16BE), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_16LE, UTF_16LE), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_32, UTF_32), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_32BE, UTF_32BE), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, UTF_32LE, UTF_32LE), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, WINDOWS_1252, WINDOWS_1252), + Arguments.of(testFiles[1], TEXT_PERSON_CART_WHEELING, WINDOWS_31J, WINDOWS_31J)); } - @BeforeClass - void setup() throws IOException { + @BeforeAll + static void setup() throws IOException { testFiles[0] = Files.createFile(Path.of("readWriteString")); testFiles[1] = Files.createFile(Path.of("writeString_file1")); testFiles[2] = Files.createFile(Path.of("writeString_file2")); @@ -225,7 +224,8 @@ public void testReadWrite() throws IOException { * This method compares the results written by the existing write method and * the writeString method added since 11. */ - @Test(dataProvider = "testWriteString") + @ParameterizedTest + @MethodSource("getWriteString") public void testWriteString(Path path, Path path2, String text, Charset cs, Charset cs2) throws IOException { Files.write(path, text.getBytes(cs)); @@ -237,14 +237,15 @@ public void testWriteString(Path path, Path path2, String text, Charset cs, Char } byte[] bytes = Files.readAllBytes(path); byte[] bytes2 = Files.readAllBytes(path2); - assertTrue((Arrays.compare(bytes, bytes2) == 0), "The bytes should be the same"); + assertArrayEquals(bytes2, bytes, "The bytes should be the same"); } /** * Verifies that the readString method added since 11 behaves the same as * constructing a string from the existing readAllBytes method. */ - @Test(dataProvider = "testReadString") + @ParameterizedTest + @MethodSource("getReadString") public void testReadString(Path path, String text, Charset cs, Charset cs2) throws IOException { Files.write(path, text.getBytes(cs)); String str = new String(Files.readAllBytes(path), cs); @@ -252,7 +253,7 @@ public void testReadString(Path path, String text, Charset cs, Charset cs2) thro // readString @since 11 String str2 = (cs2 == null) ? Files.readString(path) : Files.readString(path, cs2); - assertTrue((str.equals(str2)), "The strings should be the same"); + assertEquals(str, str2, "The strings should be the same"); } /** @@ -264,13 +265,17 @@ public void testReadString(Path path, String text, Charset cs, Charset cs2) thro * @param cs the Charset * @throws IOException if the input is malformed */ - @Test(dataProvider = "malformedWrite", expectedExceptions = UnmappableCharacterException.class) + @ParameterizedTest + @MethodSource("getMalformedWrite") public void testMalformedWrite(Path path, String s, Charset cs) throws IOException { - if (cs == null) { - Files.writeString(path, s); - } else { - Files.writeString(path, s, cs); - } + assertThrows(UnmappableCharacterException.class, + () -> { + if (cs == null) { + Files.writeString(path, s); + } else { + Files.writeString(path, s, cs); + } + }); } /** @@ -283,15 +288,19 @@ public void testMalformedWrite(Path path, String s, Charset cs) throws IOExcepti * @param csRead the Charset to use for reading the file * @throws IOException when the Charset used for reading the file is incorrect */ - @Test(dataProvider = "illegalInput", expectedExceptions = MalformedInputException.class) + @ParameterizedTest + @MethodSource("getIllegalInput") public void testMalformedRead(Path path, byte[] data, Charset csWrite, Charset csRead) throws IOException { String temp = new String(data, csWrite); Files.writeString(path, temp, csWrite); - if (csRead == null) { - Files.readString(path); - } else { - Files.readString(path, csRead); - } + assertThrows(MalformedInputException.class, + () -> { + if (csRead == null) { + Files.readString(path); + } else { + Files.readString(path, csRead); + } + }); } /** @@ -303,20 +312,19 @@ public void testMalformedRead(Path path, byte[] data, Charset csWrite, Charset c * @param expected exception class * @throws IOException when the Charset used for reading the file is incorrect */ - @Test(dataProvider = "illegalInputBytes") + @ParameterizedTest + @MethodSource("getIllegalInputBytes") public void testMalformedReadBytes(byte[] data, Charset csRead, Class expected) throws IOException { Path path = Path.of("illegalInputBytes"); Files.write(path, data); try { Files.readString(path, csRead); - } catch (MalformedInputException | UnmappableCharacterException e) { - if (expected.isInstance(e)) { - // success - return; - } + } catch (MalformedInputException e) { + assertInstanceOf(MalformedInputException.class, e); + } catch (UnmappableCharacterException e) { + assertInstanceOf(UnmappableCharacterException.class, e); } - throw new RuntimeException("An instance of " + expected + " should be thrown"); } // Verify File.readString with UTF16 to confirm proper string length and contents. @@ -326,22 +334,16 @@ public void testSingleUTF16() throws IOException { String original = "🤸"; // "\ud83e\udd38"; Files.writeString(testFiles[0], original, UTF_16); String actual = Files.readString(testFiles[0], UTF_16); - if (!actual.equals(original)) { + if (!original.equals(actual)) { System.out.printf("expected (%s), was (%s)\n", original, actual); System.out.printf("expected UTF_16 bytes: %s\n", Arrays.toString(original.getBytes(UTF_16))); System.out.printf("actual UTF_16 bytes: %s\n", Arrays.toString(actual.getBytes(UTF_16))); } - assertEquals(actual, original, "Round trip string mismatch with multi-byte encoding"); + assertEquals(original, actual, "Round trip string mismatch with multi-byte encoding"); } private void checkNullPointerException(Callable c) { - try { - c.call(); - fail("NullPointerException expected"); - } catch (NullPointerException ignore) { - } catch (Exception e) { - fail(e + " not expected"); - } + assertThrows(NullPointerException.class, () -> c.call()); } private void testReadWrite(int size, Charset cs, boolean append) throws IOException { @@ -355,14 +357,14 @@ private void testReadWrite(int size, Charset cs, boolean append) throws IOExcept } //System.out.println(result.toUri().toASCIIString()); - assertTrue(result == testFiles[0]); + assertSame(result, testFiles[0]); if (append) { if (cs == null) { Files.writeString(testFiles[0], str, APPEND); } else { Files.writeString(testFiles[0], str, cs, APPEND); } - assertTrue(Files.size(testFiles[0]) == size * 2); + assertEquals(size * 2, Files.size(testFiles[0])); } @@ -379,7 +381,7 @@ private void testReadWrite(int size, Charset cs, boolean append) throws IOExcept read = Files.readString(result, cs); } - assertTrue(read.equals(expected), "String read not the same as written"); + assertEquals(expected, read, "String read not the same as written"); } static final char[] CHARS = "abcdefghijklmnopqrstuvwxyz \r\n".toCharArray(); diff --git a/test/jdk/java/nio/file/Files/SetLastModifiedTime.java b/test/jdk/java/nio/file/Files/SetLastModifiedTime.java index d06b78b29227..c4e5547fb144 100644 --- a/test/jdk/java/nio/file/Files/SetLastModifiedTime.java +++ b/test/jdk/java/nio/file/Files/SetLastModifiedTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, 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 @@ -27,19 +27,20 @@ import java.nio.file.Paths; import java.nio.file.attribute.FileTime; -import org.testng.annotations.Test; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @test * @bug 4313887 8062949 8191872 * @library .. - * @run testng SetLastModifiedTime + * @run junit SetLastModifiedTime * @summary Unit test for Files.setLastModifiedTime */ @@ -47,13 +48,13 @@ public class SetLastModifiedTime { static Path testDir; - @BeforeClass - void createTestDirectory() throws Exception { + @BeforeAll + static void createTestDirectory() throws Exception { testDir = TestUtil.createTemporaryDirectory(); } - @AfterClass - void removeTestDirectory() throws Exception { + @AfterAll + static void removeTestDirectory() throws Exception { TestUtil.removeAll(testDir); } @@ -65,12 +66,12 @@ void test(Path path) throws Exception { FileTime zero = FileTime.fromMillis(0L); Path result = Files.setLastModifiedTime(path, zero); - assertTrue(result == path); - assertEquals(Files.getLastModifiedTime(path), zero); + assertSame(path, result); + assertEquals(zero, Files.getLastModifiedTime(path)); result = Files.setLastModifiedTime(path, now); - assertTrue(result == path); - assertEquals(Files.getLastModifiedTime(path), now); + assertSame(path, result); + assertEquals(now, Files.getLastModifiedTime(path)); } @Test @@ -100,20 +101,14 @@ public void testNulls() throws Exception { Path path = Paths.get("foo"); FileTime zero = FileTime.fromMillis(0L); - try { - Files.setLastModifiedTime(null, zero); - assertTrue(false); - } catch (NullPointerException expected) { } + assertThrows(NullPointerException.class, + () -> Files.setLastModifiedTime(null, zero)); - try { - Files.setLastModifiedTime(path, null); - assertTrue(false); - } catch (NullPointerException expected) { } + assertThrows(NullPointerException.class, + () -> Files.setLastModifiedTime(path, null)); - try { - Files.setLastModifiedTime(null, null); - assertTrue(false); - } catch (NullPointerException expected) { } + assertThrows(NullPointerException.class, + () -> Files.setLastModifiedTime(null, null)); } @Test @@ -127,7 +122,7 @@ public void testCompare() throws Exception { long nioTime = Files.getLastModifiedTime(path).toMillis(); assertTrue(ioTime == timeMillis || ioTime == 1000*(timeMillis/1000), "File.lastModified() not in {time, 1000*(time/1000)}"); - assertEquals(nioTime, ioTime, + assertEquals(ioTime, nioTime, "File.lastModified() != Files.getLastModifiedTime().toMillis()"); } } diff --git a/test/jdk/java/nio/file/Files/StreamTest.java b/test/jdk/java/nio/file/Files/StreamTest.java index 0c93a6223221..e9e6195ec0a8 100644 --- a/test/jdk/java/nio/file/Files/StreamTest.java +++ b/test/jdk/java/nio/file/Files/StreamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ * @bug 8006884 8019526 8132539 * @library .. * @build PassThroughFileSystem FaultyFileSystem - * @run testng StreamTest + * @run junit StreamTest * @summary Unit test for java.nio.file.Files methods that return a Stream */ @@ -35,9 +35,9 @@ import java.nio.charset.MalformedInputException; import java.nio.file.DirectoryIteratorException; import java.nio.file.DirectoryStream; +import java.nio.file.Files; import java.nio.file.FileSystemLoopException; import java.nio.file.FileVisitOption; -import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; @@ -51,14 +51,23 @@ import java.util.TreeSet; import java.util.concurrent.Callable; import java.util.function.BiPredicate; -import java.util.stream.Stream; import java.util.stream.Collectors; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; -@Test(groups = "unit") public class StreamTest { /** * Default test folder @@ -77,8 +86,8 @@ public class StreamTest { static Path[] all; static Path[] allFollowLinks; - @BeforeClass - void setupTestFolder() throws IOException { + @BeforeAll + static void setupTestFolder() throws IOException { testFolder = TestUtil.createTemporaryDirectory(); supportsSymbolicLinks = TestUtil.supportsSymbolicLinks(testFolder); TreeSet set = new TreeSet<>(); @@ -133,53 +142,57 @@ void setupTestFolder() throws IOException { allFollowLinks = set.toArray(new Path[0]); } - @AfterClass - void cleanupTestFolder() throws IOException { + @AfterAll + static void cleanupTestFolder() throws IOException { TestUtil.removeAll(testFolder); } + @Test public void testBasic() { try (Stream s = Files.list(testFolder)) { Object[] actual = s.sorted().toArray(); - assertEquals(actual, level1); + assertArrayEquals(level1, actual); } catch (IOException ioe) { fail("Unexpected IOException"); } try (Stream s = Files.list(testFolder.resolve("empty"))) { int count = s.mapToInt(p -> 1).reduce(0, Integer::sum); - assertEquals(count, 0, "Expect empty stream."); + assertEquals(0, count, "Expect empty stream."); } catch (IOException ioe) { fail("Unexpected IOException"); } } + @Test public void testWalk() { try (Stream s = Files.walk(testFolder)) { Object[] actual = s.sorted().toArray(); - assertEquals(actual, all); + assertArrayEquals(all, actual); } catch (IOException ioe) { fail("Unexpected IOException"); } } + @Test public void testWalkOneLevel() { try (Stream s = Files.walk(testFolder, 1)) { Object[] actual = s.filter(path -> ! path.equals(testFolder)) .sorted() .toArray(); - assertEquals(actual, level1); + assertArrayEquals(level1, actual); } catch (IOException ioe) { fail("Unexpected IOException"); } } + @Test public void testWalkFollowLink() { // If link is not supported, the directory structure won't have link. // We still want to test the behavior with FOLLOW_LINKS option. try (Stream s = Files.walk(testFolder, FileVisitOption.FOLLOW_LINKS)) { Object[] actual = s.sorted().toArray(); - assertEquals(actual, allFollowLinks); + assertArrayEquals(allFollowLinks, actual); } catch (IOException ioe) { fail("Unexpected IOException"); } @@ -211,6 +224,7 @@ private void validateFileSystemLoopException(Path start, Path... causes) { } } + @Test public void testWalkFollowLinkLoop() { if (!supportsSymbolicLinks) { return; @@ -257,6 +271,7 @@ public void testWalkFollowLinkLoop() { dir2.resolve(Paths.get("lnDir", "lnDir2"))); validateFileSystemLoopException(linkdir, linkdir.resolve(Paths.get("lnDir2", "lnDir"))); + Files.delete(cause); } catch(IOException ioe) { fail("Unexpected IOException " + ioe); } @@ -280,44 +295,46 @@ public Path[] visited() { } } + @Test public void testFind() throws IOException { PathBiPredicate pred = new PathBiPredicate((path, attrs) -> true); try (Stream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { Set result = s.collect(Collectors.toCollection(TreeSet::new)); - assertEquals(pred.visited(), all); - assertEquals(result.toArray(new Path[0]), pred.visited()); + assertArrayEquals(all, pred.visited()); + assertArrayEquals(pred.visited(), result.toArray(new Path[0])); } pred = new PathBiPredicate((path, attrs) -> attrs.isSymbolicLink()); try (Stream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { s.forEach(path -> assertTrue(Files.isSymbolicLink(path))); - assertEquals(pred.visited(), all); + assertArrayEquals(all, pred.visited()); } pred = new PathBiPredicate((path, attrs) -> path.getFileName().toString().startsWith("e")); try (Stream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { s.forEach(path -> assertEquals(path.getFileName().toString(), "empty")); - assertEquals(pred.visited(), all); + assertArrayEquals(all, pred.visited()); } pred = new PathBiPredicate((path, attrs) -> path.getFileName().toString().startsWith("l") && attrs.isRegularFile()); try (Stream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { s.forEach(path -> fail("Expect empty stream")); - assertEquals(pred.visited(), all); + assertArrayEquals(all, pred.visited()); } } // Test borrowed from BytesAndLines + @Test public void testLines() throws IOException { final Charset US_ASCII = Charset.forName("US-ASCII"); Path tmpfile = Files.createTempFile("blah", "txt"); try { // zero lines - assertTrue(Files.size(tmpfile) == 0, "File should be empty"); + assertEquals(0, Files.size(tmpfile), "File should be empty"); try (Stream s = Files.lines(tmpfile)) { checkLines(s, Collections.emptyList()); } @@ -367,31 +384,21 @@ public void testLines() throws IOException { private void checkLines(Stream s, List expected) { List lines = s.collect(Collectors.toList()); - assertTrue(lines.size() == expected.size(), "Unexpected number of lines"); + assertEquals(expected.size(), lines.size(), "Unexpected number of lines"); assertTrue(lines.equals(expected), "Unexpected content"); } private void checkMalformedInputException(Stream s) { - try { - List lines = s.collect(Collectors.toList()); - fail("UncheckedIOException expected"); - } catch (UncheckedIOException ex) { - IOException cause = ex.getCause(); - assertTrue(cause instanceof MalformedInputException, - "MalformedInputException expected"); - } + assertThrows(UncheckedIOException.class, + () -> s.collect(Collectors.toList()), + "MalformedInputException expected"); } private void checkNullPointerException(Callable c) { - try { - c.call(); - fail("NullPointerException expected"); - } catch (NullPointerException ignore) { - } catch (Exception e) { - fail(e + " not expected"); - } + assertThrows(NullPointerException.class, () -> c.call()); } + @Test public void testDirectoryIteratorException() throws IOException { Path dir = testFolder.resolve("dir2"); Path trigger = dir.resolve("DirectoryIteratorException"); @@ -404,22 +411,21 @@ public void testDirectoryIteratorException() throws IOException { Path fakeRoot = fs.getRoot(); try { try (Stream s = Files.list(fakeRoot)) { - s.forEach(path -> assertEquals(path.getFileName().toString(), "DirectoryIteratorException")); + s.forEach(path -> assertEquals("DirectoryIteratorException", path.getFileName().toString())); } } catch (UncheckedIOException uioe) { fail("Unexpected exception."); } fsp.setFaultyMode(true); - try { - try (DirectoryStream ds = Files.newDirectoryStream(fakeRoot)) { - Iterator itor = ds.iterator(); - while (itor.hasNext()) { - itor.next(); - } - } - fail("Shoule throw DirectoryIteratorException"); - } catch (DirectoryIteratorException die) { + try (DirectoryStream ds = Files.newDirectoryStream(fakeRoot)) { + Iterator itor = ds.iterator(); + assertThrows(DirectoryIteratorException.class, + () -> { + while (itor.hasNext()) { + itor.next(); + } + }); } try { @@ -427,7 +433,8 @@ public void testDirectoryIteratorException() throws IOException { s.forEach(path -> fail("should not get here")); } } catch (UncheckedIOException uioe) { - assertTrue(uioe.getCause() instanceof FaultyFileSystem.FaultyException); + assertInstanceOf(FaultyFileSystem.FaultyException.class, + uioe.getCause()); } catch (DirectoryIteratorException die) { fail("Should have been converted into UncheckedIOException."); } @@ -440,6 +447,7 @@ public void testDirectoryIteratorException() throws IOException { } } + @Test public void testUncheckedIOException() throws IOException { Path triggerFile = testFolder.resolve(Paths.get("dir2", "IOException")); Files.createFile(triggerFile); @@ -454,21 +462,22 @@ public void testUncheckedIOException() throws IOException { Path fakeRoot = fs.getRoot(); try (Stream s = Files.list(fakeRoot.resolve("dir2"))) { // only one file - s.forEach(path -> assertEquals(path.getFileName().toString(), "IOException")); + s.forEach(path -> assertEquals("IOException", path.getFileName().toString())); } try (Stream s = Files.walk(fakeRoot.resolve("empty"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); // ordered as depth-first - assertEquals(result, new String[] { "empty", "IOException", "file"}); + assertArrayEquals(new String[] { "empty", "IOException", "file"}, result); } fsp.setFaultyMode(true); try (Stream s = Files.list(fakeRoot.resolve("dir2"))) { s.forEach(path -> fail("should have caused exception")); } catch (UncheckedIOException uioe) { - assertTrue(uioe.getCause() instanceof FaultyFileSystem.FaultyException); + assertInstanceOf(FaultyFileSystem.FaultyException.class, + uioe.getCause()); } try (Stream s = Files.walk(fakeRoot.resolve("empty"))) { @@ -476,7 +485,8 @@ public void testUncheckedIOException() throws IOException { .toArray(String[]::new); fail("should not reach here due to IOException"); } catch (UncheckedIOException uioe) { - assertTrue(uioe.getCause() instanceof FaultyFileSystem.FaultyException); + assertInstanceOf(FaultyFileSystem.FaultyException.class, + uioe.getCause()); } try (Stream s = Files.walk( @@ -486,7 +496,7 @@ public void testUncheckedIOException() throws IOException { .toArray(String[]::new); fail("should not reach here due to IOException"); } catch (IOException ioe) { - assertTrue(ioe instanceof FaultyFileSystem.FaultyException); + assertInstanceOf(FaultyFileSystem.FaultyException.class, ioe); } catch (UncheckedIOException ex) { fail("Top level should be repored as is"); } @@ -500,52 +510,47 @@ public void testUncheckedIOException() throws IOException { } } + @Test public void testConstructException() { try (Stream s = Files.lines(testFolder.resolve("notExist"), Charset.forName("UTF-8"))) { - s.forEach(l -> fail("File is not even exist!")); + s.forEach(l -> fail("File does not even exist!")); } catch (IOException ioe) { - assertTrue(ioe instanceof NoSuchFileException); + assertInstanceOf(NoSuchFileException.class, ioe); } } + @Test public void testClosedStream() throws IOException { try (Stream s = Files.list(testFolder)) { s.close(); - Object[] actual = s.sorted().toArray(); - fail("Operate on closed stream should throw IllegalStateException"); - } catch (IllegalStateException ex) { - // expected + assertThrows(IllegalStateException.class, + () -> s.sorted().toArray()); } try (Stream s = Files.walk(testFolder)) { s.close(); - Object[] actual = s.sorted().toArray(); - fail("Operate on closed stream should throw IllegalStateException"); - } catch (IllegalStateException ex) { - // expected + assertThrows(IllegalStateException.class, + () -> s.sorted().toArray()); } try (Stream s = Files.find(testFolder, Integer.MAX_VALUE, (p, attr) -> true)) { s.close(); - Object[] actual = s.sorted().toArray(); - fail("Operate on closed stream should throw IllegalStateException"); - } catch (IllegalStateException ex) { - // expected + assertThrows(IllegalStateException.class, + () -> s.sorted().toArray()); } } + @Test + @EnabledOnOs(OS.LINUX) public void testProcFile() throws IOException { - if (System.getProperty("os.name").equals("Linux")) { - Path path = Paths.get("/proc/cpuinfo"); - if (Files.exists(path)) { - String NEW_LINE = System.getProperty("line.separator"); - String s = - Files.lines(path).collect(Collectors.joining(NEW_LINE)); - if (s.length() == 0) { - fail("Files.lines(\"" + path + "\") returns no data"); - } - } + Path path = Paths.get("/proc/cpuinfo"); + if (Files.exists(path)) { + String NEW_LINE = System.getProperty("line.separator"); + String s = + Files.lines(path).collect(Collectors.joining(NEW_LINE)); + assertNotEquals(0, s.length(), + "Files.lines(\"" + path + "\") returns no data"); } } } diff --git a/test/jdk/java/nio/file/Files/SubstDrive.java b/test/jdk/java/nio/file/Files/SubstDrive.java index 94fdeadcef36..fd21b9f007e5 100644 --- a/test/jdk/java/nio/file/Files/SubstDrive.java +++ b/test/jdk/java/nio/file/Files/SubstDrive.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2020 Microsoft Corporation. All rights reserved. - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,32 +23,35 @@ * */ -import java.util.Map; -import java.util.Optional; -import java.util.stream.Stream; -import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.PrintStream; +import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.nio.file.*; - -import static org.testng.Assert.*; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.Test; -import org.testng.SkipException; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + /* @test * @summary Test Files' public APIs with drives created using the subst command on Windows. * @requires (os.family == "windows") * @library /test/lib .. * @build SubstDrive - * @run testng SubstDrive + * @run junit SubstDrive */ public class SubstDrive { @@ -62,26 +65,24 @@ public class SubstDrive { * deleted when the test finishes. * + Find a drive that is available for use with subst. */ - @BeforeClass - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { TEST_TEMP_DIRECTORY = Files.createTempDirectory("tmp"); - System.out.printf("Test directory is at %s\n", TEST_TEMP_DIRECTORY); + System.err.printf("Test directory is at %s\n", TEST_TEMP_DIRECTORY); Optional substDrive = findAvailableDrive(TEST_TEMP_DIRECTORY); - if (!substDrive.isPresent()) { - throw new SkipException( - "Could not find any available drive to use with subst, skipping the tests"); - } + Assumptions.assumeTrue(substDrive.isPresent(), + "Could not find any available drive to use with subst"); SUBST_DRIVE = substDrive.get(); - System.out.printf("Using drive %s\n with subst", SUBST_DRIVE); + System.err.printf("Using drive %s\n with subst", SUBST_DRIVE); } /** * Delete the root temporary directory together with all of its contents * when all tests finish. */ - @AfterClass - public void removeRootTempDirectory() throws IOException { + @AfterAll + public static void removeRootTempDirectory() throws IOException { TestUtil.removeAll(TEST_TEMP_DIRECTORY); } @@ -90,7 +91,7 @@ public void removeRootTempDirectory() throws IOException { * unmap the drive after every test so that subsequent ones can reuse * the drive. */ - @AfterMethod + @AfterEach public void deleteSubstDrive() throws IOException { Stream substitutedDrives = substFindMappedDrives(); // Only delete `SUBST_DRIVE` if it is currently being substituted @@ -114,7 +115,7 @@ public void testCreateAndDeleteFile() throws IOException { assertTrue(Files.exists(p)); Files.writeString(p, fileContents); - assertEquals(Files.readString(p), fileContents); + assertEquals(fileContents, Files.readString(p)); } /** @@ -193,7 +194,7 @@ public void testGetSetAttributes() throws IOException { Map attr1 = Files.readAttributes(SUBST_DRIVE, "*"); Map attr2 = Files.readAttributes(tempDirectory, "*"); - assertEquals(attr1, attr2); + assertEquals(attr2, attr1); } /** @@ -287,19 +288,19 @@ public void testGetResolvedSymlinkAttribute() throws IOException { Path tempFile = Path.of(SUBST_DRIVE.toString(), "test.txt"); String contents = "Hello world!"; Files.writeString(tempFile, contents); - assertEquals(Files.readString(tempFile), contents); + assertEquals(contents, Files.readString(tempFile)); Path link = Path.of(SUBST_DRIVE.toString(), "link"); Files.createSymbolicLink(link, tempFile); - assertEquals(Files.readString(link), contents); - assertEquals(Files.isExecutable(link), Files.isExecutable(tempFile)); - assertEquals(Files.isReadable(link), Files.isReadable(tempFile)); - assertEquals(Files.isDirectory(link), Files.isDirectory(tempFile)); - assertEquals(Files.isHidden(link), Files.isHidden(tempFile)); - assertEquals(Files.isRegularFile(link), Files.isRegularFile(tempFile)); - assertEquals(Files.isWritable(link), Files.isWritable(tempFile)); - assertEquals(Files.getOwner(link), Files.getOwner(tempFile)); + assertEquals(contents, Files.readString(link)); + assertEquals(Files.isExecutable(tempFile), Files.isExecutable(link)); + assertEquals(Files.isReadable(tempFile), Files.isReadable(link)); + assertEquals(Files.isDirectory(tempFile), Files.isDirectory(link)); + assertEquals(Files.isHidden(tempFile), Files.isHidden(link)); + assertEquals(Files.isRegularFile(tempFile), Files.isRegularFile(link)); + assertEquals(Files.isWritable(tempFile), Files.isWritable(link)); + assertEquals(Files.getOwner(tempFile), Files.getOwner(link)); } /** @@ -319,16 +320,15 @@ public void testSubstWithSymlinkedDirectory() throws IOException { substCreate(SUBST_DRIVE, tempLink); - assertEquals( - Files.readAttributes(SUBST_DRIVE, "*"), - Files.readAttributes(tempDirectory, "*")); + assertTrue(Files.readAttributes(tempDirectory, "*") + .equals(Files.readAttributes(SUBST_DRIVE, "*"))); assertTrue(Files.isWritable(SUBST_DRIVE)); Path tempFile = Files.createTempFile(SUBST_DRIVE, "prefix", "suffix"); String contents = "Hello world!"; Files.writeString(tempFile, contents); - assertEquals(Files.readString(tempFile), contents); + assertEquals(contents, Files.readString(tempFile)); Path tempDirectory2 = Files.createTempDirectory(TEST_TEMP_DIRECTORY, "tmp"); Path copy = Path.of(tempDirectory2.toString(), "copied"); @@ -341,7 +341,7 @@ public void testSubstWithSymlinkedDirectory() throws IOException { Files.move(tempFile, cut); assertTrue(Files.notExists(tempFile)); assertTrue(Files.exists(cut)); - assertEquals(Files.readString(cut), contents); + assertEquals(contents, Files.readString(cut)); } /** @@ -361,9 +361,8 @@ public void testMoveAndCopyFilesToSymlinkedDrive() throws IOException { substCreate(SUBST_DRIVE, tempLink); - assertEquals( - Files.readAttributes(SUBST_DRIVE, "*"), - Files.readAttributes(tempDirectory, "*")); + assertTrue(Files.readAttributes(tempDirectory, "*") + .equals(Files.readAttributes(SUBST_DRIVE, "*"))); assertTrue(Files.isWritable(SUBST_DRIVE)); } @@ -372,7 +371,7 @@ public void testMoveAndCopyFilesToSymlinkedDrive() throws IOException { * Run a command and optionally prints stdout contents to * `customOutputStream`. */ - private void runCmd(ProcessBuilder pb, PrintStream customOutputStream) { + private static void runCmd(ProcessBuilder pb, PrintStream customOutputStream) { try { PrintStream ps = customOutputStream != null ? customOutputStream : @@ -383,8 +382,8 @@ private void runCmd(ProcessBuilder pb, PrintStream customOutputStream) { int exitCode = outputAnalyzer.getExitValue(); assertEquals( - exitCode /* actual value */, 0 /* expected value */, + exitCode /* actual value */, String.format( "Command `%s` failed with exit code %d", pb.command(), @@ -402,7 +401,7 @@ private void runCmd(ProcessBuilder pb, PrintStream customOutputStream) { * For reference, see: * https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/subst */ - private void substCreate(Path drive, Path path) { + private static void substCreate(Path drive, Path path) { runCmd( new ProcessBuilder( "cmd", "/c", "subst", drive.toString(), path.toString()), @@ -414,7 +413,7 @@ private void substCreate(Path drive, Path path) { * For reference, see: * https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/subst */ - private void substDelete(Path drive) throws IOException { + private static void substDelete(Path drive) throws IOException { runCmd( new ProcessBuilder( "cmd", "/c", "subst", drive.toString(), "/D"), @@ -454,7 +453,7 @@ private Stream substFindMappedDrives() throws UnsupportedEncodingExcepti * subst can fail if the drive to be mapped already exists. The method returns * a drive that is available. */ - private Optional findAvailableDrive(Path tempDirectory) { + private static Optional findAvailableDrive(Path tempDirectory) { for (char letter = 'Z'; letter >= 'A'; letter--) { try { Path p = Path.of(letter + ":"); diff --git a/test/jdk/java/nio/file/Files/walkFileTree/FindTest.java b/test/jdk/java/nio/file/Files/walkFileTree/FindTest.java index 9fb16a7b14cc..beacee44bbb0 100644 --- a/test/jdk/java/nio/file/Files/walkFileTree/FindTest.java +++ b/test/jdk/java/nio/file/Files/walkFileTree/FindTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,15 +34,15 @@ * jdk.test.lib.Platform * jdk.test.lib.process.* * CreateFileTree - * @run testng/othervm -Djava.io.tmpdir=. FindTest + * @run junit/othervm -Djava.io.tmpdir=. FindTest */ import java.io.IOException; +import java.nio.file.Files; import java.nio.file.FileSystemLoopException; import java.nio.file.FileVisitOption; -import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; -import java.nio.file.Files; +import java.nio.file.FileVisitResult; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; @@ -56,11 +56,11 @@ import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class FindTest { @@ -69,7 +69,7 @@ public class FindTest { private static Path top; private static String TOP; - @BeforeClass + @BeforeAll public static void createFileTree() throws Exception { top = CreateFileTree.create(); TOP = top.toAbsolutePath().toString(); @@ -107,7 +107,7 @@ private void assertOutputEquals(List actual, OutputAnalyzer expected) throws IOException { List expectedList = Arrays.asList(expected.getStdout() .split(System.lineSeparator())); - assertEquals(actual.size(), expectedList.size()); + assertEquals(expectedList.size(), actual.size()); assertTrue(actual.removeAll(expectedList)); } diff --git a/test/jdk/java/nio/file/spi/TestDelegation.java b/test/jdk/java/nio/file/spi/TestDelegation.java index 0b9bc9de7f70..f09b636c1560 100644 --- a/test/jdk/java/nio/file/spi/TestDelegation.java +++ b/test/jdk/java/nio/file/spi/TestDelegation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,11 +21,6 @@ * questions. */ -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.io.IOException; import java.net.URI; import java.nio.file.*; @@ -34,26 +29,34 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import static org.testng.AssertJUnit.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @test * @summary Verifies that a FileSystemProvider's implementation of the exists * and readAttributesIfExists methods are invoked * @compile testfsp/testfsp/TestProvider.java - * @run testng TestDelegation + * @run junit TestDelegation */ public class TestDelegation { // Non-existent Path to be used by the test - private Path nonExistentFile; + private static Path nonExistentFile; // Path to Temp directory used by the test - private Path tempDirectory; + private static Path tempDirectory; // Valid file Path used by the test - private Path fileThatExists; + private static Path fileThatExists; // The FileSystemProvider used by the test - private MyProvider myProvider; + private static MyProvider myProvider; /** * Create the FileSystemProvider, the FileSystem and @@ -61,8 +64,8 @@ public class TestDelegation { * * @throws IOException if an error occurs */ - @BeforeClass - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { myProvider = new MyProvider(); FileSystem fs = myProvider.getFileSystem(URI.create("/")); // Path to Current Working Directory @@ -73,7 +76,7 @@ public void setup() throws IOException { } /** - * DataProvider that is used to test Files::exists. The DataProvider's + * MethodSource that is used to test Files::exists. The Arguments' * elements are: *

    *
  • Path to validate
  • @@ -81,17 +84,14 @@ public void setup() throws IOException { *
* @return The test parameter data */ - @DataProvider - private Object[][] testExists() { - return new Object[][]{ - {tempDirectory, true}, - {fileThatExists, true}, - {nonExistentFile, false} - }; + private static Stream testExists() { + return Stream.of(Arguments.of(tempDirectory, true), + Arguments.of(fileThatExists, true), + Arguments.of(nonExistentFile, false)); } /** - * DataProvider that is used to test Files::isDirectory. The DataProvider's + * MethodSource that is used to test Files::isDirectory. The Arguments' * elements are: *
    *
  • Path to validate
  • @@ -99,16 +99,13 @@ private Object[][] testExists() { *
* @return The test parameter data */ - @DataProvider - private Object[][] testIsDirectory() { - return new Object[][]{ - {tempDirectory, true}, - {fileThatExists, false}, - {nonExistentFile, false} - }; + private static Stream testIsDirectory() { + return Stream.of(Arguments.of(tempDirectory, true), + Arguments.of(fileThatExists, false), + Arguments.of(nonExistentFile, false)); } /** - * DataProvider that is used to test Files::isRegularFile. The DataProvider's + * MethodSource that is used to test Files::isRegularFile. The MethodSource's * elements are: *
    *
  • Path to validate
  • @@ -116,19 +113,16 @@ private Object[][] testIsDirectory() { *
* @return The test parameter data */ - @DataProvider - private Object[][] testIsRegularFile() { - return new Object[][]{ - {tempDirectory, false}, - {fileThatExists, true}, - {nonExistentFile, false} - }; + private static Stream testIsRegularFile() { + return Stream.of(Arguments.of(tempDirectory, false), + Arguments.of(fileThatExists, true), + Arguments.of(nonExistentFile, false)); } /** * Clear our Map prior to each test run */ - @BeforeMethod + @BeforeEach public void resetParams() { myProvider.resetCalls(); } @@ -140,9 +134,10 @@ public void resetParams() { * @param p the path to the file to test * @param exists does the path exist */ - @Test(dataProvider = "testExists") + @ParameterizedTest + @MethodSource("testExists") public void testExists(Path p, boolean exists) { - assertEquals(Files.exists(p), exists); + assertEquals(exists, Files.exists(p)); // We should only have called exists once assertEquals(1, myProvider.findCall("exists").size()); assertEquals(0, myProvider.findCall("readAttributesIfExists").size()); @@ -155,9 +150,10 @@ public void testExists(Path p, boolean exists) { * @param p the path to the file to test * @param isDir is the path a directory */ - @Test(dataProvider = "testIsDirectory") + @ParameterizedTest + @MethodSource("testIsDirectory") public void testIsDirectory(Path p, boolean isDir) { - assertEquals(Files.isDirectory(p), isDir); + assertEquals(isDir, Files.isDirectory(p)); // We should only have called readAttributesIfExists once assertEquals(0, myProvider.findCall("exists").size()); assertEquals(1, myProvider.findCall("readAttributesIfExists").size()); @@ -170,9 +166,10 @@ public void testIsDirectory(Path p, boolean isDir) { * @param p the path to the file to test * @param isFile is the path a regular file */ - @Test(dataProvider = "testIsRegularFile") + @ParameterizedTest + @MethodSource("testIsRegularFile") public void testIsRegularFile(Path p, boolean isFile) { - assertEquals(Files.isRegularFile(p), isFile); + assertEquals(isFile, Files.isRegularFile(p)); // We should only have called readAttributesIfExists once assertEquals(0, myProvider.findCall("exists").size()); assertEquals(1, myProvider.findCall("readAttributesIfExists").size()); From 9dc3f488b4da333f436fcdddb92b34ec9dbbb925 Mon Sep 17 00:00:00 2001 From: Mohamed Issa Date: Mon, 16 Mar 2026 19:21:05 +0000 Subject: [PATCH 66/97] 8380079: Add separate flag for platforms on which copy and clear operations are faster with AVX3Threshold set to 0 Reviewed-by: kvn, asmehra, sviswanathan --- src/hotspot/cpu/x86/globals_x86.hpp | 15 ++++++++++-- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 13 +++++------ src/hotspot/cpu/x86/stubGenerator_x86_64.hpp | 4 ++-- .../cpu/x86/stubGenerator_x86_64_adler.cpp | 2 +- .../x86/stubGenerator_x86_64_arraycopy.cpp | 20 ++++++++-------- src/hotspot/cpu/x86/vm_version_x86.cpp | 23 ++++++++++--------- src/hotspot/cpu/x86/vm_version_x86.hpp | 2 -- .../flags/jvmFlagConstraintsCompiler.cpp | 11 +++++++++ .../flags/jvmFlagConstraintsCompiler.hpp | 1 + 9 files changed, 55 insertions(+), 36 deletions(-) diff --git a/src/hotspot/cpu/x86/globals_x86.hpp b/src/hotspot/cpu/x86/globals_x86.hpp index 4f5b6d31e755..084793dc2624 100644 --- a/src/hotspot/cpu/x86/globals_x86.hpp +++ b/src/hotspot/cpu/x86/globals_x86.hpp @@ -168,16 +168,27 @@ define_pd_global(intx, InitArrayShortSize, 8*BytesPerLong); "Perform Ecore Optimization") \ \ /* Minimum array size in bytes to use AVX512 intrinsics */ \ - /* for copy, inflate and fill which don't bail out early based on any */ \ + /* for inflate and fill which don't bail out early based on any */ \ /* condition. When this value is set to zero compare operations like */ \ /* compare, vectorizedMismatch, compress can also use AVX512 intrinsics.*/\ product(int, AVX3Threshold, 4096, DIAGNOSTIC, \ "Minimum array size in bytes to use AVX512 intrinsics" \ - "for copy, inflate and fill. When this value is set as zero" \ + "for inflate and fill. When this value is set as zero" \ "compare operations can also use AVX512 intrinsics.") \ range(0, max_jint) \ constraint(AVX3ThresholdConstraintFunc,AfterErgo) \ \ + /* Minimum array size in bytes to use AVX512 intrinsics */ \ + /* for copy and fill which don't bail out early based on any */ \ + /* condition. When this value is set to zero clear operations that */ \ + /* work on memory blocks can also use AVX512 intrinsics. */ \ + product(int, CopyAVX3Threshold, 4096, DIAGNOSTIC, \ + "Minimum array size in bytes to use AVX512 intrinsics" \ + "for copy and fill. When this value is set as zero" \ + "clear operations can also use AVX512 intrinsics.") \ + range(0, max_jint) \ + constraint(CopyAVX3ThresholdConstraintFunc,AfterErgo) \ + \ product(bool, IntelJccErratumMitigation, true, DIAGNOSTIC, \ "Turn off JVM mitigations related to Intel micro code " \ "mitigations for the Intel JCC erratum") \ diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 2d46a50d4269..1d77be26bd93 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -5820,7 +5820,7 @@ void MacroAssembler::xmm_clear_mem(Register base, Register cnt, Register rtmp, X // cnt - number of qwords (8-byte words). // base - start address, qword aligned. Label L_zero_64_bytes, L_loop, L_sloop, L_tail, L_end; - bool use64byteVector = (MaxVectorSize == 64) && (VM_Version::avx3_threshold() == 0); + bool use64byteVector = (MaxVectorSize == 64) && (CopyAVX3Threshold == 0); if (use64byteVector) { vpxor(xtmp, xtmp, xtmp, AVX_512bit); } else if (MaxVectorSize >= 32) { @@ -5884,7 +5884,7 @@ void MacroAssembler::xmm_clear_mem(Register base, Register cnt, Register rtmp, X // Clearing constant sized memory using YMM/ZMM registers. void MacroAssembler::clear_mem(Register base, int cnt, Register rtmp, XMMRegister xtmp, KRegister mask) { assert(UseAVX > 2 && VM_Version::supports_avx512vl(), ""); - bool use64byteVector = (MaxVectorSize > 32) && (VM_Version::avx3_threshold() == 0); + bool use64byteVector = (MaxVectorSize > 32) && (CopyAVX3Threshold == 0); int vector64_count = (cnt & (~0x7)) >> 3; cnt = cnt & 0x7; @@ -6109,8 +6109,8 @@ void MacroAssembler::generate_fill(BasicType t, bool aligned, // Fill 64-byte chunks Label L_fill_64_bytes_loop_avx3, L_check_fill_64_bytes_avx2; - // If number of bytes to fill < VM_Version::avx3_threshold(), perform fill using AVX2 - cmpptr(count, VM_Version::avx3_threshold()); + // If number of bytes to fill < CopyAVX3Threshold, perform fill using AVX2 + cmpptr(count, CopyAVX3Threshold); jccb(Assembler::below, L_check_fill_64_bytes_avx2); vpbroadcastd(xtmp, xtmp, Assembler::AVX_512bit); @@ -9483,7 +9483,6 @@ void MacroAssembler::generate_fill_avx3(BasicType type, Register to, Register va Label L_fill_zmm_sequence; int shift = -1; - int avx3threshold = VM_Version::avx3_threshold(); switch(type) { case T_BYTE: shift = 0; break; @@ -9499,10 +9498,10 @@ void MacroAssembler::generate_fill_avx3(BasicType type, Register to, Register va fatal("Unhandled type: %s\n", type2name(type)); } - if ((avx3threshold != 0) || (MaxVectorSize == 32)) { + if ((CopyAVX3Threshold != 0) || (MaxVectorSize == 32)) { if (MaxVectorSize == 64) { - cmpq(count, avx3threshold >> shift); + cmpq(count, CopyAVX3Threshold >> shift); jcc(Assembler::greater, L_fill_zmm_sequence); } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index 64b56442c90d..332add6dcd48 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -166,12 +166,12 @@ class StubGenerator: public StubCodeGenerator { // - If target supports AVX3 features (BW+VL+F) then implementation uses 32 byte vectors (YMMs) // for both special cases (various small block sizes) and aligned copy loop. This is the // default configuration. - // - If copy length is above AVX3Threshold, then implementation use 64 byte vectors (ZMMs) + // - If copy length is above CopyAVX3Threshold, then implementation use 64 byte vectors (ZMMs) // for main copy loop (and subsequent tail) since bulk of the cycles will be consumed in it. // - If user forces MaxVectorSize=32 then above 4096 bytes its seen that REP MOVs shows a // better performance for disjoint copies. For conjoint/backward copy vector based // copy performs better. - // - If user sets AVX3Threshold=0, then special cases for small blocks sizes operate over + // - If user sets CopyAVX3Threshold=0, then special cases for small blocks sizes operate over // 64 byte vector registers (ZMMs). address generate_disjoint_copy_avx3_masked(StubId stub_id, address* entry); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp index 2799997a761d..1d3e7afde1d2 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp @@ -144,7 +144,7 @@ address StubGenerator::generate_updateBytesAdler32() { __ align32(); if (VM_Version::supports_avx512vl()) { // AVX2 performs better for smaller inputs because of leaner post loop reduction sequence.. - __ cmpl(s, MAX2(128, VM_Version::avx3_threshold())); + __ cmpl(s, MAX2(128, CopyAVX3Threshold)); __ jcc(Assembler::belowEqual, SLOOP1A_AVX2); __ lea(end, Address(s, data, Address::times_1, - (2*CHUNKSIZE -1))); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp index d53fafafdb44..01e004b7b438 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp @@ -511,12 +511,12 @@ void StubGenerator::copy_bytes_backward(Register from, Register dest, // - If target supports AVX3 features (BW+VL+F) then implementation uses 32 byte vectors (YMMs) // for both special cases (various small block sizes) and aligned copy loop. This is the // default configuration. -// - If copy length is above AVX3Threshold, then implementation use 64 byte vectors (ZMMs) +// - If copy length is above CopyAVX3Threshold, then implementation use 64 byte vectors (ZMMs) // for main copy loop (and subsequent tail) since bulk of the cycles will be consumed in it. // - If user forces MaxVectorSize=32 then above 4096 bytes its seen that REP MOVs shows a // better performance for disjoint copies. For conjoint/backward copy vector based // copy performs better. -// - If user sets AVX3Threshold=0, then special cases for small blocks sizes operate over +// - If user sets CopyAVX3Threshold=0, then special cases for small blocks sizes operate over // 64 byte vector registers (ZMMs). // Inputs: @@ -575,8 +575,7 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(StubId stub_id, addres StubCodeMark mark(this, stub_id); address start = __ pc(); - int avx3threshold = VM_Version::avx3_threshold(); - bool use64byteVector = (MaxVectorSize > 32) && (avx3threshold == 0); + bool use64byteVector = (MaxVectorSize > 32) && (CopyAVX3Threshold == 0); const int large_threshold = 2621440; // 2.5 MB Label L_main_loop, L_main_loop_64bytes, L_tail, L_tail64, L_exit, L_entry; Label L_repmovs, L_main_pre_loop, L_main_pre_loop_64bytes, L_pre_main_post_64; @@ -647,7 +646,7 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(StubId stub_id, addres __ cmpq(temp2, large_threshold); __ jcc(Assembler::greaterEqual, L_copy_large); } - if (avx3threshold != 0) { + if (CopyAVX3Threshold != 0) { __ cmpq(count, threshold[shift]); if (MaxVectorSize == 64) { // Copy using 64 byte vectors. @@ -659,7 +658,7 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(StubId stub_id, addres } } - if ((MaxVectorSize < 64) || (avx3threshold != 0)) { + if ((MaxVectorSize < 64) || (CopyAVX3Threshold != 0)) { // Partial copy to make dst address 32 byte aligned. __ movq(temp2, to); __ andq(temp2, 31); @@ -913,8 +912,7 @@ address StubGenerator::generate_conjoint_copy_avx3_masked(StubId stub_id, addres StubCodeMark mark(this, stub_id); address start = __ pc(); - int avx3threshold = VM_Version::avx3_threshold(); - bool use64byteVector = (MaxVectorSize > 32) && (avx3threshold == 0); + bool use64byteVector = (MaxVectorSize > 32) && (CopyAVX3Threshold == 0); Label L_main_pre_loop, L_main_pre_loop_64bytes, L_pre_main_post_64; Label L_main_loop, L_main_loop_64bytes, L_tail, L_tail64, L_exit, L_entry; @@ -979,12 +977,12 @@ address StubGenerator::generate_conjoint_copy_avx3_masked(StubId stub_id, addres // PRE-MAIN-POST loop for aligned copy. __ BIND(L_entry); - if ((MaxVectorSize > 32) && (avx3threshold != 0)) { + if ((MaxVectorSize > 32) && (CopyAVX3Threshold != 0)) { __ cmpq(temp1, threshold[shift]); __ jcc(Assembler::greaterEqual, L_pre_main_post_64); } - if ((MaxVectorSize < 64) || (avx3threshold != 0)) { + if ((MaxVectorSize < 64) || (CopyAVX3Threshold != 0)) { // Partial copy to make dst address 32 byte aligned. __ leaq(temp2, Address(to, temp1, (Address::ScaleFactor)(shift), 0)); __ andq(temp2, 31); @@ -1199,7 +1197,7 @@ void StubGenerator::arraycopy_avx3_special_cases_conjoint(XMMRegister xmm, KRegi bool use64byteVector, Label& L_entry, Label& L_exit) { Label L_entry_64, L_entry_96, L_entry_128; Label L_entry_160, L_entry_192; - bool avx3 = (MaxVectorSize > 32) && (VM_Version::avx3_threshold() == 0); + bool avx3 = (MaxVectorSize > 32) && (CopyAVX3Threshold == 0); int size_mat[][6] = { /* T_BYTE */ {32 , 64, 96 , 128 , 160 , 192 }, diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index b352de77d6f1..a800feea0a8e 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1967,6 +1967,18 @@ void VM_Version::get_processor_features() { if (FLAG_IS_DEFAULT(UseCopySignIntrinsic)) { FLAG_SET_DEFAULT(UseCopySignIntrinsic, true); } + // CopyAVX3Threshold is the threshold at which 64-byte instructions are used + // for implementing the array copy and clear operations. + // The Intel platforms that supports the serialize instruction + // have improved implementation of 64-byte load/stores and so the default + // threshold is set to 0 for these platforms. + if (FLAG_IS_DEFAULT(CopyAVX3Threshold)) { + if (is_intel() && is_intel_server_family() && supports_serialize()) { + FLAG_SET_DEFAULT(CopyAVX3Threshold, 0); + } else { + FLAG_SET_DEFAULT(CopyAVX3Threshold, AVX3Threshold); + } + } } void VM_Version::print_platform_virtualization_info(outputStream* st) { @@ -2122,17 +2134,6 @@ bool VM_Version::is_intel_darkmont() { return is_intel() && is_intel_server_family() && (_model == 0xCC || _model == 0xDD); } -// avx3_threshold() sets the threshold at which 64-byte instructions are used -// for implementing the array copy and clear operations. -// The Intel platforms that supports the serialize instruction -// has improved implementation of 64-byte load/stores and so the default -// threshold is set to 0 for these platforms. -int VM_Version::avx3_threshold() { - return (is_intel_server_family() && - supports_serialize() && - FLAG_IS_DEFAULT(AVX3Threshold)) ? 0 : AVX3Threshold; -} - void VM_Version::clear_apx_test_state() { clear_apx_test_state_stub(); } diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index 9f0446df7c64..a42558a80232 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -958,8 +958,6 @@ class VM_Version : public Abstract_VM_Version { static bool is_intel_darkmont(); - static int avx3_threshold(); - static bool is_intel_tsc_synched_at_init(); static void insert_features_names(VM_Version::VM_Features features, stringStream& ss); diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp index 444ce3217592..36eece6f013f 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp +++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp @@ -274,6 +274,17 @@ JVMFlag::Error AVX3ThresholdConstraintFunc(int value, bool verbose) { return JVMFlag::SUCCESS; } +JVMFlag::Error CopyAVX3ThresholdConstraintFunc(int value, bool verbose) { + if (value != 0 && !is_power_of_2(value)) { + JVMFlag::printError(verbose, + "CopyAVX3Threshold ( %d ) must be 0 or " + "a power of two value between 0 and MAX_INT\n", value); + return JVMFlag::VIOLATES_CONSTRAINT; + } + + return JVMFlag::SUCCESS; +} + JVMFlag::Error ArraycopySrcPrefetchDistanceConstraintFunc(uintx value, bool verbose) { if (value >= 4032) { JVMFlag::printError(verbose, diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.hpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.hpp index cf785800cfc7..45e91058e0b5 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.hpp +++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.hpp @@ -46,6 +46,7 @@ f(uintx, ArraycopyDstPrefetchDistanceConstraintFunc) \ f(uintx, ArraycopySrcPrefetchDistanceConstraintFunc) \ f(int, AVX3ThresholdConstraintFunc) \ + f(int, CopyAVX3ThresholdConstraintFunc) \ f(uint, TypeProfileLevelConstraintFunc) \ f(uint, VerifyIterativeGVNConstraintFunc) \ f(intx, InitArrayShortSizeConstraintFunc) \ From 8d11b977def8356b6fd8504562538d2af88c082b Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Mon, 16 Mar 2026 22:20:04 +0000 Subject: [PATCH 67/97] =?UTF-8?q?8380037:=20JFR:=20Don=E2=80=99t=20format?= =?UTF-8?q?=20identifiers=20as=20numbers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: mgronlun --- src/jdk.jfr/share/classes/jdk/jfr/internal/query/Field.java | 5 ++++- .../share/classes/jdk/jfr/internal/query/FieldBuilder.java | 3 ++- .../share/classes/jdk/jfr/internal/query/FieldFormatter.java | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Field.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Field.java index b7fc43816700..f9c2ab351bf9 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Field.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Field.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 @@ -118,6 +118,9 @@ final class Field { // An integral type (byte, short, int, long) boolean integralType; + // An integral type that should be treated like a symbol, e.g. PID. + boolean identifier; + // A java.time.Duration boolean timespan; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldBuilder.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldBuilder.java index 64791b1976a1..8d0b3371c7da 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldBuilder.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldBuilder.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 @@ -274,6 +274,7 @@ private void configureNumericTypes() { case "int", "long", "short", "byte": field.integralType = true; field.alignLeft = false; + field.identifier = fieldName.equals("id") || fieldName.endsWith("Id") || field.label.endsWith("Identifier"); break; case "float", "double": field.fractionalType = true; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldFormatter.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldFormatter.java index 989e2231eb18..ec032ce9f34f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldFormatter.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldFormatter.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 @@ -165,7 +165,7 @@ private static String format(Field field, Object object, boolean compact) { return object + " Hz"; } } - if (object instanceof Number number) { + if (object instanceof Number number && !field.identifier) { return ValueFormatter.formatNumber(number); } return object.toString(); From 921da0a9734d9fae2e7b0e129d2cc6949ad0b5a6 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Tue, 17 Mar 2026 01:28:03 +0000 Subject: [PATCH 68/97] 8378211: Test ChangedJarFile.java failed: missing "timestamp has changed" Reviewed-by: dholmes, liach --- .../jtreg/runtime/cds/appcds/aotCache/ChangedJarFile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ChangedJarFile.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ChangedJarFile.java index a717b2673475..e86defdf816b 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ChangedJarFile.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ChangedJarFile.java @@ -59,7 +59,7 @@ public static void main(String[] args) throws Exception { tester.productionRun(new String[] {"-XX:AOTMode=auto", "-Xlog:aot"}, new String[] {"jarHasChanged"}); out.shouldMatch("This file is not the one used while building the " + - "AOT cache: '.*app.jar', timestamp has changed, size has changed"); + "AOT cache: '.*app.jar',.* size has changed"); } static class Tester extends CDSAppTester { From 3e231755a03e75a29b66aaa32784397cf5022da1 Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Tue, 17 Mar 2026 02:41:24 +0000 Subject: [PATCH 69/97] 8380083: Enable some vector mask cast IR matching tests for RISC-V Reviewed-by: fyang --- .../vectorapi/VectorMaskCastIdentityTest.java | 6 ++-- .../vectorapi/VectorMaskCastTest.java | 28 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastIdentityTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastIdentityTest.java index be9d8e6390c2..e4f166f510a6 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastIdentityTest.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastIdentityTest.java @@ -50,7 +50,7 @@ public class VectorMaskCastIdentityTest { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 2" }, applyIfCPUFeatureOr = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 2" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static int testTwoCastToDifferentType() { // The types before and after the two casts are not the same, so the cast cannot be eliminated. VectorMask mFloat64 = VectorMask.fromArray(FloatVector.SPECIES_64, mr, 0); @@ -84,7 +84,7 @@ public static void testTwoCastToDifferentType2_runner() { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static int testTwoCastToSameType() { // The types before and after the two casts are the same, so the cast will be eliminated. VectorMask mInt128 = VectorMask.fromArray(IntVector.SPECIES_128, mr, 0); @@ -101,7 +101,7 @@ public static void testTwoCastToSameType_runner() { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 1" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 1" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static int testOneCastToDifferentType() { // The types before and after the only cast are different, the cast will not be eliminated. VectorMask mFloat128 = VectorMask.fromArray(FloatVector.SPECIES_128, mr, 0).not(); diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastTest.java index 25594d5caf94..1f346cffd2fa 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastTest.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastTest.java @@ -74,7 +74,7 @@ public class VectorMaskCastTest { // Byte @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static VectorMask testByte64ToShort128(VectorMask v) { return v.cast(ShortVector.SPECIES_128); } @@ -201,7 +201,7 @@ public static void testByte256ToShort512_runner() { // Short @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static VectorMask testShort64ToInt128(VectorMask v) { return v.cast(IntVector.SPECIES_128); } @@ -215,7 +215,7 @@ public static void testShort64ToInt128_runner() { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static VectorMask testShort64ToFloat128(VectorMask v) { return v.cast(FloatVector.SPECIES_128); } @@ -257,7 +257,7 @@ public static void testShort64ToDouble256_runner() { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static VectorMask testShort128ToByte64(VectorMask v) { return v.cast(ByteVector.SPECIES_64); } @@ -384,7 +384,7 @@ public static void testShort512ToByte256_runner() { // Int @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testInt64ToLong128(VectorMask v) { return v.cast(LongVector.SPECIES_128); } @@ -398,7 +398,7 @@ public static void testInt64ToLong128_runner() { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testInt64ToDouble128(VectorMask v) { return v.cast(DoubleVector.SPECIES_128); } @@ -412,7 +412,7 @@ public static void testInt64ToDouble128_runner() { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static VectorMask testInt128ToShort64(VectorMask v) { return v.cast(ShortVector.SPECIES_64); } @@ -539,7 +539,7 @@ public static void testInt512ToByte128_runner() { // Float @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testFloat64ToLong128(VectorMask v) { return v.cast(LongVector.SPECIES_128); } @@ -553,7 +553,7 @@ public static void testFloat64ToLong128_runner() { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testFloat64ToDouble128(VectorMask v) { return v.cast(DoubleVector.SPECIES_128); } @@ -567,7 +567,7 @@ public static void testFloat64ToDouble128_runner() { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) public static VectorMask testFloat128ToShort64(VectorMask v) { return v.cast(ShortVector.SPECIES_64); } @@ -694,7 +694,7 @@ public static void testFloat512ToByte128_runner() { // Long @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testLong128ToInt64(VectorMask v) { return v.cast(IntVector.SPECIES_64); } @@ -708,7 +708,7 @@ public static void testLong128ToInt64_runner() { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testLong128ToFloat64(VectorMask v) { return v.cast(FloatVector.SPECIES_64); } @@ -821,7 +821,7 @@ public static void testLong512ToByte64_runner() { // Double @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testDouble128ToInt64(VectorMask v) { return v.cast(IntVector.SPECIES_64); } @@ -835,7 +835,7 @@ public static void testDouble128ToInt64_runner() { } @Test - @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = { IRNode.VECTOR_MASK_CAST, "> 0" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"}) public static VectorMask testDouble128ToFloat64(VectorMask v) { return v.cast(FloatVector.SPECIES_64); } From a1e4621b30e08c8b20bd4542e4f1655aeb0ec576 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Tue, 17 Mar 2026 05:58:33 +0000 Subject: [PATCH 70/97] 8378152: Upstream AOT heap object improvements from Leyden repo Reviewed-by: jrose, kvn --- src/hotspot/share/cds/aotMappedHeapWriter.cpp | 52 +++--- src/hotspot/share/cds/aotMappedHeapWriter.hpp | 18 +-- .../share/cds/aotReferenceObjSupport.cpp | 2 +- .../share/cds/aotStreamedHeapWriter.cpp | 22 +-- .../share/cds/aotStreamedHeapWriter.hpp | 2 - src/hotspot/share/cds/cdsHeapVerifier.hpp | 2 +- src/hotspot/share/cds/heapShared.cpp | 152 +++++++++++++++--- src/hotspot/share/cds/heapShared.hpp | 35 ++-- src/hotspot/share/classfile/stringTable.cpp | 2 +- 9 files changed, 184 insertions(+), 103 deletions(-) diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.cpp b/src/hotspot/share/cds/aotMappedHeapWriter.cpp index 64c0e3c40e82..2e21a1d15d8b 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.cpp @@ -64,6 +64,11 @@ HeapRootSegments AOTMappedHeapWriter::_heap_root_segments; address AOTMappedHeapWriter::_requested_bottom; address AOTMappedHeapWriter::_requested_top; +static size_t _num_strings = 0; +static size_t _string_bytes = 0; +static size_t _num_packages = 0; +static size_t _num_protection_domains = 0; + GrowableArrayCHeap* AOTMappedHeapWriter::_native_pointers; GrowableArrayCHeap* AOTMappedHeapWriter::_source_objs; GrowableArrayCHeap* AOTMappedHeapWriter::_source_objs_order; @@ -71,8 +76,6 @@ GrowableArrayCHeap* AOTMappedH AOTMappedHeapWriter::BufferOffsetToSourceObjectTable* AOTMappedHeapWriter::_buffer_offset_to_source_obj_table = nullptr; -DumpedInternedStrings *AOTMappedHeapWriter::_dumped_interned_strings = nullptr; - typedef HashTable< size_t, // offset of a filler from AOTMappedHeapWriter::buffer_bottom() size_t, // size of this filler (in bytes) @@ -87,7 +90,6 @@ void AOTMappedHeapWriter::init() { Universe::heap()->collect(GCCause::_java_lang_system_gc); _buffer_offset_to_source_obj_table = new (mtClassShared) BufferOffsetToSourceObjectTable(/*size (prime)*/36137, /*max size*/1 * M); - _dumped_interned_strings = new (mtClass)DumpedInternedStrings(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE); _fillers = new (mtClassShared) FillersTable(); _requested_bottom = nullptr; _requested_top = nullptr; @@ -141,9 +143,6 @@ int AOTMappedHeapWriter::narrow_oop_shift() { void AOTMappedHeapWriter::delete_tables_with_raw_oops() { delete _source_objs; _source_objs = nullptr; - - delete _dumped_interned_strings; - _dumped_interned_strings = nullptr; } void AOTMappedHeapWriter::add_source_obj(oop src_obj) { @@ -181,25 +180,6 @@ bool AOTMappedHeapWriter::is_too_large_to_archive(size_t size) { } } -// Keep track of the contents of the archived interned string table. This table -// is used only by CDSHeapVerifier. -void AOTMappedHeapWriter::add_to_dumped_interned_strings(oop string) { - assert_at_safepoint(); // DumpedInternedStrings uses raw oops - assert(!is_string_too_large_to_archive(string), "must be"); - bool created; - _dumped_interned_strings->put_if_absent(string, true, &created); - if (created) { - // Prevent string deduplication from changing the value field to - // something not in the archive. - java_lang_String::set_deduplication_forbidden(string); - _dumped_interned_strings->maybe_grow(); - } -} - -bool AOTMappedHeapWriter::is_dumped_interned_string(oop o) { - return _dumped_interned_strings->get(o) != nullptr; -} - // Various lookup functions between source_obj, buffered_obj and requested_obj bool AOTMappedHeapWriter::is_in_requested_range(oop o) { assert(_requested_bottom != nullptr, "do not call before _requested_bottom is initialized"); @@ -430,6 +410,7 @@ void AOTMappedHeapWriter::copy_source_objs_to_buffer(GrowableArrayCHeapset_buffer_offset(buffer_offset); + assert(buffer_offset <= 0x7fffffff, "sanity"); OopHandle handle(Universe::vm_global(), src_obj); _buffer_offset_to_source_obj_table->put_when_absent(buffer_offset, handle); @@ -442,6 +423,9 @@ void AOTMappedHeapWriter::copy_source_objs_to_buffer(GrowableArrayCHeaplength() + 1, roots->length(), _num_native_ptrs); + log_info(aot)(" strings = %8zu (%zu bytes)", _num_strings, _string_bytes); + log_info(aot)(" packages = %8zu", _num_packages); + log_info(aot)(" protection domains = %8zu", _num_protection_domains); } size_t AOTMappedHeapWriter::filler_array_byte_size(int length) { @@ -530,7 +514,25 @@ void update_buffered_object_field(address buffered_obj, int field_offset, T valu *field_addr = value; } +void AOTMappedHeapWriter::update_stats(oop src_obj) { + if (java_lang_String::is_instance(src_obj)) { + _num_strings ++; + _string_bytes += src_obj->size() * HeapWordSize; + _string_bytes += java_lang_String::value(src_obj)->size() * HeapWordSize; + } else { + Klass* k = src_obj->klass(); + Symbol* name = k->name(); + if (name->equals("java/lang/NamedPackage") || name->equals("java/lang/Package")) { + _num_packages ++; + } else if (name->equals("java/security/ProtectionDomain")) { + _num_protection_domains ++; + } + } +} + size_t AOTMappedHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) { + update_stats(src_obj); + assert(!is_too_large_to_archive(src_obj), "already checked"); size_t byte_size = src_obj->size() * HeapWordSize; assert(byte_size > 0, "no zero-size objects"); diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.hpp b/src/hotspot/share/cds/aotMappedHeapWriter.hpp index 7481e7922a0a..2420e68d9fea 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.hpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.hpp @@ -40,20 +40,6 @@ class MemRegion; #if INCLUDE_CDS_JAVA_HEAP -class DumpedInternedStrings : - public ResizeableHashTable -{ -public: - DumpedInternedStrings(unsigned size, unsigned max_size) : - ResizeableHashTable(size, max_size) {} -}; - class AOTMappedHeapWriter : AllStatic { friend class HeapShared; friend class AOTMappedHeapLoader; @@ -131,7 +117,6 @@ class AOTMappedHeapWriter : AllStatic { static GrowableArrayCHeap* _native_pointers; static GrowableArrayCHeap* _source_objs; - static DumpedInternedStrings *_dumped_interned_strings; // We sort _source_objs_order to minimize the number of bits in ptrmap and oopmap. // See comments near the body of AOTMappedHeapWriter::compare_objs_by_oop_fields(). @@ -190,6 +175,7 @@ class AOTMappedHeapWriter : AllStatic { static void copy_roots_to_buffer(GrowableArrayCHeap* roots); static void copy_source_objs_to_buffer(GrowableArrayCHeap* roots); static size_t copy_one_source_obj_to_buffer(oop src_obj); + static void update_stats(oop src_obj); static void maybe_fill_gc_region_gap(size_t required_byte_size); static size_t filler_array_byte_size(int length); @@ -227,8 +213,6 @@ class AOTMappedHeapWriter : AllStatic { static bool is_too_large_to_archive(size_t size); static bool is_too_large_to_archive(oop obj); static bool is_string_too_large_to_archive(oop string); - static bool is_dumped_interned_string(oop o); - static void add_to_dumped_interned_strings(oop string); static void write(GrowableArrayCHeap*, AOTMappedHeapInfo* heap_info); static address requested_address(); // requested address of the lowest achived heap object static size_t get_filler_size_at(address buffered_addr); diff --git a/src/hotspot/share/cds/aotReferenceObjSupport.cpp b/src/hotspot/share/cds/aotReferenceObjSupport.cpp index 0c27c8ce5f01..2d5fc8c7f217 100644 --- a/src/hotspot/share/cds/aotReferenceObjSupport.cpp +++ b/src/hotspot/share/cds/aotReferenceObjSupport.cpp @@ -96,7 +96,7 @@ class KeepAliveObjectsTable : public HashTable {}; + HeapShared::oop_address_hash> {}; static KeepAliveObjectsTable* _keep_alive_objs_table; static OopHandle _keep_alive_objs_array; diff --git a/src/hotspot/share/cds/aotStreamedHeapWriter.cpp b/src/hotspot/share/cds/aotStreamedHeapWriter.cpp index f52532b2f2a3..ad363f21fdb2 100644 --- a/src/hotspot/share/cds/aotStreamedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotStreamedHeapWriter.cpp @@ -242,20 +242,6 @@ void AOTStreamedHeapWriter::copy_roots_max_dfs_to_buffer(int roots_length) { } } -static bool is_interned_string(oop obj) { - if (!java_lang_String::is_instance(obj)) { - return false; - } - - ResourceMark rm; - int len; - jchar* name = java_lang_String::as_unicode_string_or_null(obj, len); - if (name == nullptr) { - fatal("Insufficient memory for dumping"); - } - return StringTable::lookup(name, len) == obj; -} - static BitMap::idx_t bit_idx_for_buffer_offset(size_t buffer_offset) { if (UseCompressedOops) { return BitMap::idx_t(buffer_offset / sizeof(narrowOop)); @@ -264,10 +250,6 @@ static BitMap::idx_t bit_idx_for_buffer_offset(size_t buffer_offset) { } } -bool AOTStreamedHeapWriter::is_dumped_interned_string(oop obj) { - return is_interned_string(obj) && HeapShared::get_cached_oop_info(obj) != nullptr; -} - void AOTStreamedHeapWriter::copy_source_objs_to_buffer(GrowableArrayCHeap* roots) { for (int i = 0; i < _source_objs->length(); i++) { oop src_obj = _source_objs->at(i); @@ -325,7 +307,7 @@ size_t AOTStreamedHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) { ensure_buffer_space(new_used); - if (is_interned_string(src_obj)) { + if (HeapShared::is_interned_string(src_obj)) { java_lang_String::hash_code(src_obj); // Sets the hash code field(s) java_lang_String::set_deduplication_forbidden(src_obj); // Allows faster interning at runtime assert(java_lang_String::hash_is_set(src_obj), "hash must be set"); @@ -402,7 +384,7 @@ void AOTStreamedHeapWriter::update_header_for_buffered_addr(address buffered_add mw = mw.copy_set_hash(src_hash); } - if (is_interned_string(src_obj)) { + if (HeapShared::is_interned_string(src_obj)) { // Mark the mark word of interned string so the loader knows to link these to // the string table at runtime. mw = mw.set_marked(); diff --git a/src/hotspot/share/cds/aotStreamedHeapWriter.hpp b/src/hotspot/share/cds/aotStreamedHeapWriter.hpp index ab5aec0327b4..c3cc9f2c0921 100644 --- a/src/hotspot/share/cds/aotStreamedHeapWriter.hpp +++ b/src/hotspot/share/cds/aotStreamedHeapWriter.hpp @@ -148,8 +148,6 @@ class AOTStreamedHeapWriter : AllStatic { return size_t(buffered_addr) - size_t(buffer_bottom()); } - static bool is_dumped_interned_string(oop obj); - static size_t source_obj_to_buffered_offset(oop src_obj); static address source_obj_to_buffered_addr(oop src_obj); diff --git a/src/hotspot/share/cds/cdsHeapVerifier.hpp b/src/hotspot/share/cds/cdsHeapVerifier.hpp index 7f1bdb1d2494..f8e090801bbc 100644 --- a/src/hotspot/share/cds/cdsHeapVerifier.hpp +++ b/src/hotspot/share/cds/cdsHeapVerifier.hpp @@ -53,7 +53,7 @@ class CDSHeapVerifier : public KlassClosure { 15889, // prime number AnyObj::C_HEAP, mtClassShared, - HeapShared::oop_hash> _table; + HeapShared::oop_address_hash> _table; GrowableArray _exclusions; GrowableArray _shared_secret_accessors; diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 0c0f70eac0a9..8cab56dda8de 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -175,23 +175,39 @@ oop HeapShared::CachedOopInfo::orig_referrer() const { return _orig_referrer.resolve(); } -unsigned HeapShared::oop_hash(oop const& p) { +// This is a simple hashing of the oop's address. This function is used +// while copying the oops into the AOT heap region. We don't want to +// have any side effects during the copying, so we avoid calling +// p->identity_hash() which can update the object header. +unsigned HeapShared::oop_address_hash(oop const& p) { assert(SafepointSynchronize::is_at_safepoint() || JavaThread::current()->is_in_no_safepoint_scope(), "sanity"); - // Do not call p->identity_hash() as that will update the - // object header. return primitive_hash(cast_from_oop(p)); } -unsigned int HeapShared::oop_handle_hash_raw(const OopHandle& oh) { - return oop_hash(oh.resolve()); -} - -unsigned int HeapShared::oop_handle_hash(const OopHandle& oh) { +// About the hashcode in the cached objects: +// - If a source object has a hashcode, it must be copied into the cache. +// That's because some cached hashtables are laid out using this hashcode. +// - If a source object doesn't have a hashcode, we avoid computing it while +// copying the objects into the cache. This will allow the hashcode to be +// dynamically and randomly computed in each production, which generally +// desirable to make the hashcodes more random between runs. +unsigned HeapShared::archived_object_cache_hash(OopHandle const& oh) { oop o = oh.resolve(); if (o == nullptr) { return 0; + } + if (!_use_identity_hash_for_archived_object_cache) { + // This is called while we are copying the objects. Don't call o->identity_hash() + // as that will update the object header. + return oop_address_hash(o); } else { + // This is called after all objects are copied. It's OK to update + // the object's hashcode. + // + // This may be called after we have left the AOT dumping safepoint. + // Objects in archived_object_cache() may be moved by the GC, so we + // can't use the address of o for computing the hash. return o->identity_hash(); } } @@ -271,6 +287,12 @@ void HeapShared::prepare_for_archiving(TRAPS) { HeapShared::ArchivedObjectCache* HeapShared::_archived_object_cache = nullptr; +// Controls the hashing method for the _archived_object_cache. +// Changes from false to true once, after all objects are copied, +// inside make_archived_object_cache_gc_safe(). +// See archived_object_cache_hash() for more details. +bool HeapShared::_use_identity_hash_for_archived_object_cache = false; + bool HeapShared::is_archived_heap_in_use() { if (HeapShared::is_loading()) { if (HeapShared::is_loading_streaming_mode()) { @@ -384,9 +406,8 @@ void HeapShared::materialize_thread_object() { } } -void HeapShared::add_to_dumped_interned_strings(oop string) { +void HeapShared::archive_interned_string(oop string) { assert(HeapShared::is_writing_mapping_mode(), "Only used by this mode"); - AOTMappedHeapWriter::add_to_dumped_interned_strings(string); bool success = archive_reachable_objects_from(1, _dump_time_special_subgraph, string); assert(success, "shared strings array must not point to arrays or strings that are too large to archive"); } @@ -404,6 +425,24 @@ void HeapShared::finalize_initialization(FileMapInfo* static_mapinfo) { } } +void HeapShared::make_archived_object_cache_gc_safe() { + ArchivedObjectCache* new_cache = new (mtClass)ArchivedObjectCache(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE); + + // It's safe to change the behavior of the hash function now, because iterate_all() + // doesn't call the hash function. + // See archived_object_cache_hash() for more details. + assert(_use_identity_hash_for_archived_object_cache == false, "happens only once"); + _use_identity_hash_for_archived_object_cache = true; + + // Copy all CachedOopInfo into a new table using a different hashing algorithm + archived_object_cache()->iterate_all([&] (OopHandle oh, CachedOopInfo info) { + new_cache->put_when_absent(oh, info); + }); + + destroy_archived_object_cache(); + _archived_object_cache = new_cache; +} + HeapShared::CachedOopInfo* HeapShared::get_cached_oop_info(oop obj) { OopHandle oh(Universe::vm_global(), obj); CachedOopInfo* result = _archived_object_cache->get(oh); @@ -417,14 +456,53 @@ bool HeapShared::has_been_archived(oop obj) { } int HeapShared::append_root(oop obj) { + assert(SafepointSynchronize::is_at_safepoint(), "sanity"); assert(CDSConfig::is_dumping_heap(), "dump-time only"); - if (obj != nullptr) { - assert(has_been_archived(obj), "must be"); + assert(_pending_roots != nullptr, "sanity"); + + if (obj == nullptr) { + assert(_pending_roots->at(0) == nullptr, "root index 0 always maps to null"); + return 0; + } else if (CDSConfig::is_dumping_aot_linked_classes()) { + // The AOT compiler may refer the same obj many times, so we + // should use the same index for this oop to avoid excessive entries + // in the roots array. + CachedOopInfo* obj_info = get_cached_oop_info(obj); + assert(obj_info != nullptr, "must be archived"); + + if (obj_info->root_index() > 0) { + return obj_info->root_index(); + } else { + assert(obj_info->root_index() < 0, "must not be zero"); + int i = _pending_roots->append(obj); + obj_info->set_root_index(i); + return i; + } + } else { + return _pending_roots->append(obj); + } +} + +int HeapShared::get_root_index(oop obj) { + if (java_lang_Class::is_instance(obj)) { + obj = scratch_java_mirror(obj); } - // No GC should happen since we aren't scanning _pending_roots. - assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread"); - return _pending_roots->append(obj); + CachedOopInfo* obj_info = get_cached_oop_info(obj); + const char* error = nullptr; + if (obj_info == nullptr) { + error = "Not a cached oop"; + } else if (obj_info->root_index() < 0) { + error = "Not a cached oop root"; + } else { + return obj_info->root_index(); + } + + ResourceMark rm; + log_debug(aot, codecache, oops)("%s: " INTPTR_FORMAT " (%s)", error, + cast_from_oop(obj), + obj->klass()->external_name()); + return -1; } oop HeapShared::get_root(int index, bool clear) { @@ -453,6 +531,13 @@ void HeapShared::finish_materialize_objects() { } void HeapShared::clear_root(int index) { + if (CDSConfig::is_using_aot_linked_classes()) { + // When AOT linked classes are in use, all roots will be in use all + // the time, there's no benefit for clearing the roots. Also, we + // can't clear the roots as they can be shared. + return; + } + assert(index >= 0, "sanity"); assert(CDSConfig::is_using_archive(), "must be"); if (is_archived_heap_in_use()) { @@ -600,9 +685,10 @@ objArrayOop HeapShared::scratch_resolved_references(ConstantPool* src) { return (objArrayOop)_scratch_objects_table->get_oop(src); } - void HeapShared::init_dumping() { - _scratch_objects_table = new (mtClass)MetaspaceObjToOopHandleTable(); - _pending_roots = new GrowableArrayCHeap(500); +void HeapShared::init_dumping() { + _scratch_objects_table = new (mtClass)MetaspaceObjToOopHandleTable(); + _pending_roots = new GrowableArrayCHeap(500); + _pending_roots->append(nullptr); // root index 0 represents a null oop } void HeapShared::init_scratch_objects_for_basic_type_mirrors(TRAPS) { @@ -883,6 +969,11 @@ void HeapShared::write_heap(AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeap ArchiveBuilder::OtherROAllocMark mark; write_subgraph_info_table(); + + delete _pending_roots; + _pending_roots = nullptr; + + make_archived_object_cache_gc_safe(); } void HeapShared::scan_java_mirror(oop orig_mirror) { @@ -1911,6 +2002,11 @@ void HeapShared::verify_subgraph_from(oop orig_obj) { void HeapShared::verify_reachable_objects_from(oop obj) { _num_total_verifications ++; if (java_lang_Class::is_instance(obj)) { + Klass* k = java_lang_Class::as_Klass(obj); + if (RegeneratedClasses::has_been_regenerated(k)) { + k = RegeneratedClasses::get_regenerated_object(k); + obj = k->java_mirror(); + } obj = scratch_java_mirror(obj); assert(obj != nullptr, "must be"); } @@ -2264,12 +2360,22 @@ void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[], #endif } -bool HeapShared::is_dumped_interned_string(oop o) { - if (is_writing_mapping_mode()) { - return AOTMappedHeapWriter::is_dumped_interned_string(o); - } else { - return AOTStreamedHeapWriter::is_dumped_interned_string(o); +bool HeapShared::is_interned_string(oop obj) { + if (!java_lang_String::is_instance(obj)) { + return false; + } + + ResourceMark rm; + int len = 0; + jchar* name = java_lang_String::as_unicode_string_or_null(obj, len); + if (name == nullptr) { + fatal("Insufficient memory for dumping"); } + return StringTable::lookup(name, len) == obj; +} + +bool HeapShared::is_dumped_interned_string(oop o) { + return is_interned_string(o) && has_been_archived(o); } // These tables should be used only within the CDS safepoint, so diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index 2cb330160e42..92f41784268a 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -40,7 +40,6 @@ #include "utilities/hashTable.hpp" #if INCLUDE_CDS_JAVA_HEAP -class DumpedInternedStrings; class FileMapInfo; class KlassSubGraphInfo; class MetaspaceObjToOopHandleTable; @@ -176,7 +175,7 @@ class HeapShared: AllStatic { static void initialize_streaming() NOT_CDS_JAVA_HEAP_RETURN; static void enable_gc() NOT_CDS_JAVA_HEAP_RETURN; static void materialize_thread_object() NOT_CDS_JAVA_HEAP_RETURN; - static void add_to_dumped_interned_strings(oop string) NOT_CDS_JAVA_HEAP_RETURN; + static void archive_interned_string(oop string); static void finalize_initialization(FileMapInfo* static_mapinfo) NOT_CDS_JAVA_HEAP_RETURN; private: @@ -195,13 +194,8 @@ class HeapShared: AllStatic { static void print_stats(); public: static void debug_trace(); - static unsigned oop_hash(oop const& p); - static unsigned oop_handle_hash(OopHandle const& oh); - static unsigned oop_handle_hash_raw(OopHandle const& oh); + static unsigned oop_address_hash(oop const& p); static bool oop_handle_equals(const OopHandle& a, const OopHandle& b); - static unsigned string_oop_hash(oop const& string) { - return java_lang_String::hash_code(string); - } class CopyKlassSubGraphInfoToArchive; @@ -217,27 +211,37 @@ class HeapShared: AllStatic { // One or more fields in this object are pointing to MetaspaceObj bool _has_native_pointers; + + // >= 0 if this oop has been append to the list of roots + int _root_index; public: CachedOopInfo(OopHandle orig_referrer, bool has_oop_pointers) : _orig_referrer(orig_referrer), _buffer_offset(0), _has_oop_pointers(has_oop_pointers), - _has_native_pointers(false) {} + _has_native_pointers(false), + _root_index(-1) {} oop orig_referrer() const; void set_buffer_offset(size_t offset) { _buffer_offset = offset; } size_t buffer_offset() const { return _buffer_offset; } bool has_oop_pointers() const { return _has_oop_pointers; } bool has_native_pointers() const { return _has_native_pointers; } void set_has_native_pointers() { _has_native_pointers = true; } + int root_index() const { return _root_index; } + void set_root_index(int i) { _root_index = i; } }; private: static const int INITIAL_TABLE_SIZE = 15889; // prime number static const int MAX_TABLE_SIZE = 1000000; + static bool _use_identity_hash_for_archived_object_cache; + + static unsigned archived_object_cache_hash(OopHandle const& oh); + typedef ResizeableHashTable ArchivedObjectCache; static ArchivedObjectCache* _archived_object_cache; @@ -297,7 +301,7 @@ class HeapShared: AllStatic { typedef ResizeableHashTable SeenObjectsTable; + HeapShared::oop_address_hash> SeenObjectsTable; static SeenObjectsTable *_seen_objects_table; @@ -394,6 +398,7 @@ class HeapShared: AllStatic { delete _archived_object_cache; _archived_object_cache = nullptr; } + static void make_archived_object_cache_gc_safe(); static ArchivedObjectCache* archived_object_cache() { return _archived_object_cache; } @@ -406,6 +411,7 @@ class HeapShared: AllStatic { KlassSubGraphInfo* subgraph_info, oop orig_obj); + static bool is_interned_string(oop obj); static bool is_dumped_interned_string(oop o); // Scratch objects for archiving Klass::java_mirror() @@ -437,6 +443,11 @@ class HeapShared: AllStatic { // Dump-time only. Returns the index of the root, which can be used at run time to read // the root using get_root(index, ...). static int append_root(oop obj); + + // AOT-compile time only. + // Returns -1 if obj is not in the heap root set. + static int get_root_index(oop obj) NOT_CDS_JAVA_HEAP_RETURN_(-1); + static GrowableArrayCHeap* pending_roots() { return _pending_roots; } // Dump-time and runtime @@ -445,9 +456,7 @@ class HeapShared: AllStatic { // Run-time only static void clear_root(int index); - static void get_segment_indexes(int index, int& segment_index, int& internal_index); - static void setup_test_class(const char* test_class_name) PRODUCT_RETURN; #endif // INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp index 2b8b7780a41f..c3f60487b9cf 100644 --- a/src/hotspot/share/classfile/stringTable.cpp +++ b/src/hotspot/share/classfile/stringTable.cpp @@ -946,7 +946,7 @@ void StringTable::init_shared_table() { // so we are all good. // - If there's a reference to it, we will report an error inside HeapShared.cpp and // dumping will fail. - HeapShared::add_to_dumped_interned_strings(string); + HeapShared::archive_interned_string(string); } n++; return true; From b0831572e2cd9dbff9ee2abcdf81a493ddcecc7e Mon Sep 17 00:00:00 2001 From: Kieran Farrell Date: Tue, 17 Mar 2026 06:00:32 +0000 Subject: [PATCH 71/97] 8359706: Add file descriptor count to VM.info Reviewed-by: kevinw, stuefe --- src/hotspot/os/aix/os_aix.cpp | 4 ++ src/hotspot/os/bsd/os_bsd.cpp | 44 +++++++++++++++++++++ src/hotspot/os/bsd/os_bsd.hpp | 2 + src/hotspot/os/linux/os_linux.cpp | 34 ++++++++++++++++ src/hotspot/os/windows/os_windows.cpp | 4 ++ src/hotspot/share/runtime/os.hpp | 3 ++ src/hotspot/share/utilities/vmError.cpp | 9 +++++ test/jdk/sun/tools/jcmd/TestJcmdSanity.java | 6 ++- 8 files changed, 105 insertions(+), 1 deletion(-) diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 7c08d6de2db8..3cad24d388ce 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -2667,3 +2667,7 @@ void os::print_memory_mappings(char* addr, size_t bytes, outputStream* st) {} void os::jfr_report_memory_info() {} #endif // INCLUDE_JFR + +void os::print_open_file_descriptors(outputStream* st) { + // File descriptor counting not implemented on AIX +} diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 29ebe65e0dbc..207968a7a591 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -76,6 +76,7 @@ # include # include # include +# include # include # include # include @@ -102,6 +103,7 @@ #endif #ifdef __APPLE__ + #include #include #include #endif @@ -2596,3 +2598,45 @@ bool os::pd_dll_unload(void* libhandle, char* ebuf, int ebuflen) { return res; } // end: os::pd_dll_unload() + +void os::print_open_file_descriptors(outputStream* st) { +#ifdef __APPLE__ + char buf[1024 * sizeof(struct proc_fdinfo)]; + os::Bsd::print_open_file_descriptors(st, buf, sizeof(buf)); +#else + st->print_cr("Open File Descriptors: unknown"); +#endif +} + +void os::Bsd::print_open_file_descriptors(outputStream* st, char* buf, size_t buflen) { +#ifdef __APPLE__ + pid_t my_pid; + + // ensure the scratch buffer is big enough for at least one FD info struct + assert(buflen >= sizeof(struct proc_fdinfo)); + kern_return_t kres = pid_for_task(mach_task_self(), &my_pid); + if (kres != KERN_SUCCESS) { + st->print_cr("Open File Descriptors: unknown"); + return; + } + size_t max_fds = buflen / sizeof(struct proc_fdinfo); + struct proc_fdinfo* fds = reinterpret_cast(buf); + + // fill our buffer with FD info, up to the available buffer size + int res = proc_pidinfo(my_pid, PROC_PIDLISTFDS, 0, fds, max_fds * sizeof(struct proc_fdinfo)); + if (res <= 0) { + st->print_cr("Open File Descriptors: unknown"); + return; + } + + // print lower threshold if count exceeds buffer size + int nfiles = res / sizeof(struct proc_fdinfo); + if ((size_t)nfiles >= max_fds) { + st->print_cr("Open File Descriptors: > %zu", max_fds); + return; + } + st->print_cr("Open File Descriptors: %d", nfiles); +#else + st->print_cr("Open File Descriptors: unknown"); +#endif +} \ No newline at end of file diff --git a/src/hotspot/os/bsd/os_bsd.hpp b/src/hotspot/os/bsd/os_bsd.hpp index da73211b9a7c..e87a680b2d2f 100644 --- a/src/hotspot/os/bsd/os_bsd.hpp +++ b/src/hotspot/os/bsd/os_bsd.hpp @@ -123,6 +123,8 @@ class os::Bsd { static int get_node_by_cpu(int cpu_id); static void print_uptime_info(outputStream* st); + static void print_open_file_descriptors(outputStream* st, char* buf, size_t buflen); + static void print_open_file_descriptors(outputStream* st); }; #endif // OS_BSD_OS_BSD_HPP diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 17cd0d05f9ad..5e911ce0f44b 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -83,6 +83,7 @@ #endif # include +# include # include # include # include @@ -113,6 +114,7 @@ # include # include # include +# include # include #ifdef __GLIBC__ # include @@ -2161,6 +2163,8 @@ void os::print_os_info(outputStream* st) { os::Posix::print_rlimit_info(st); + os::print_open_file_descriptors(st); + os::Posix::print_load_average(st); st->cr(); @@ -5429,3 +5433,33 @@ bool os::pd_dll_unload(void* libhandle, char* ebuf, int ebuflen) { return res; } // end: os::pd_dll_unload() + +void os::print_open_file_descriptors(outputStream* st) { + DIR* dirp = opendir("/proc/self/fd"); + int fds = 0; + struct dirent* dentp; + const jlong TIMEOUT_NS = 50000000L; // 50 ms in nanoseconds + bool timed_out = false; + + // limit proc file read to 50ms + jlong start = os::javaTimeNanos(); + assert(dirp != nullptr, "No proc fs?"); + while ((dentp = readdir(dirp)) != nullptr && !timed_out) { + if (isdigit(dentp->d_name[0])) fds++; + if (fds % 100 == 0) { + jlong now = os::javaTimeNanos(); + if ((now - start) > TIMEOUT_NS) { + timed_out = true; + } + } + } + + closedir(dirp); + if (timed_out) { + st->print_cr("Open File Descriptors: > %d", fds); + } else { + st->print_cr("Open File Descriptors: %d", fds); + } +} + + diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 76f47640e5ac..01162ef930d3 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -6276,6 +6276,10 @@ const void* os::get_saved_assert_context(const void** sigInfo) { return nullptr; } +void os::print_open_file_descriptors(outputStream* st) { + // File descriptor counting not supported on Windows. +} + /* * Windows/x64 does not use stack frames the way expected by Java: * [1] in most cases, there is no frame pointer. All locals are addressed via RSP diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index e185188384f7..d0544cb5b514 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -893,6 +893,9 @@ class os: AllStatic { static void print_date_and_time(outputStream* st, char* buf, size_t buflen); static void print_elapsed_time(outputStream* st, double time); + // Prints the number of open file descriptors for the current process + static void print_open_file_descriptors(outputStream* st); + static void print_user_info(outputStream* st); static void print_active_locale(outputStream* st); diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index e79c9cb694e5..48fae6868ab3 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -1329,6 +1329,13 @@ void VMError::report(outputStream* st, bool _verbose) { STEP_IF("printing OS information", _verbose) os::print_os_info(st); st->cr(); +#ifdef __APPLE__ + // Avoid large stack allocation on Mac for FD count during signal-handling. + os::Bsd::print_open_file_descriptors(st, buf, sizeof(buf)); + st->cr(); +#else + os::print_open_file_descriptors(st); +#endif STEP_IF("printing CPU info", _verbose) os::print_cpu_info(st, buf, sizeof(buf)); @@ -1550,6 +1557,8 @@ void VMError::print_vm_info(outputStream* st) { os::print_os_info(st); st->cr(); + os::print_open_file_descriptors(st); + st->cr(); // STEP("printing CPU info") diff --git a/test/jdk/sun/tools/jcmd/TestJcmdSanity.java b/test/jdk/sun/tools/jcmd/TestJcmdSanity.java index 92f73597ce68..559b6b7d5a4b 100644 --- a/test/jdk/sun/tools/jcmd/TestJcmdSanity.java +++ b/test/jdk/sun/tools/jcmd/TestJcmdSanity.java @@ -33,7 +33,6 @@ import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.Platform; -import jdk.test.lib.Utils; /* * @test @@ -182,5 +181,10 @@ private static void testJcmdPidVMinfo() throws Exception { output.shouldNotContain("*** Handler was modified!"); output.shouldNotContain("*** Expected: "); // e.g. *** Expected: javaSignalHandler in ... } + + // Should find file descriptor counting on Mac and Linux + if (Platform.isLinux() || Platform.isOSX()) { + output.shouldMatch("Open File Descriptors: \\d+"); + } } } From 3a109f49feb19f313632be6a2aa24ba7d9b7269b Mon Sep 17 00:00:00 2001 From: David Holmes Date: Tue, 17 Mar 2026 07:18:40 +0000 Subject: [PATCH 72/97] 8380236: macOS build is broken by JDK-8359706 Reviewed-by: kbarrett --- src/hotspot/os/bsd/os_bsd.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 207968a7a591..a4d9a2197a54 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -2613,7 +2613,7 @@ void os::Bsd::print_open_file_descriptors(outputStream* st, char* buf, size_t bu pid_t my_pid; // ensure the scratch buffer is big enough for at least one FD info struct - assert(buflen >= sizeof(struct proc_fdinfo)); + precond(buflen >= sizeof(struct proc_fdinfo)); kern_return_t kres = pid_for_task(mach_task_self(), &my_pid); if (kres != KERN_SUCCESS) { st->print_cr("Open File Descriptors: unknown"); @@ -2639,4 +2639,4 @@ void os::Bsd::print_open_file_descriptors(outputStream* st, char* buf, size_t bu #else st->print_cr("Open File Descriptors: unknown"); #endif -} \ No newline at end of file +} From 0b17e0093d35972db7c228098d991a684ab6f07f Mon Sep 17 00:00:00 2001 From: Harshit Dhiman Date: Tue, 17 Mar 2026 09:25:00 +0000 Subject: [PATCH 73/97] 8347396: Efficient TypeFunc creations Reviewed-by: dlong, vlivanov, adinn --- .../share/gc/shared/c2/barrierSetC2.cpp | 12 +++++- .../share/gc/shared/c2/barrierSetC2.hpp | 6 +++ .../shenandoah/c2/shenandoahBarrierSetC2.cpp | 40 ++++++++++++++++--- .../shenandoah/c2/shenandoahBarrierSetC2.hpp | 9 +++++ src/hotspot/share/opto/type.cpp | 8 ++++ 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp index 53577bad1d8e..ad7fc31dc091 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp @@ -813,7 +813,10 @@ Node* BarrierSetC2::obj_allocate(PhaseMacroExpand* macro, Node* mem, Node* toobi return old_tlab_top; } -static const TypeFunc* clone_type() { +const TypeFunc* BarrierSetC2::_clone_type_Type = nullptr; + +void BarrierSetC2::make_clone_type() { + assert(BarrierSetC2::_clone_type_Type == nullptr, "should be"); // Create input type (domain) int argcnt = NOT_LP64(3) LP64_ONLY(4); const Type** const domain_fields = TypeTuple::fields(argcnt); @@ -829,7 +832,12 @@ static const TypeFunc* clone_type() { const Type** const range_fields = TypeTuple::fields(0); const TypeTuple* const range = TypeTuple::make(TypeFunc::Parms + 0, range_fields); - return TypeFunc::make(domain, range); + BarrierSetC2::_clone_type_Type = TypeFunc::make(domain, range); +} + +inline const TypeFunc* BarrierSetC2::clone_type() { + assert(BarrierSetC2::_clone_type_Type != nullptr, "should be initialized"); + return BarrierSetC2::_clone_type_Type; } #define XTOP LP64_ONLY(COMMA phase->top()) diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp index 7b9cb985cff6..a486a88c48fb 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp @@ -270,6 +270,9 @@ class BarrierStubC2 : public ArenaObj { // various GC barrier sets inherit from the BarrierSetC2 class to sprinkle // barriers into the accesses. class BarrierSetC2: public CHeapObj { +private: + static const TypeFunc* _clone_type_Type; + protected: virtual void resolve_address(C2Access& access) const; virtual Node* store_at_resolved(C2Access& access, C2AccessValue& val) const; @@ -379,6 +382,9 @@ class BarrierSetC2: public CHeapObj { static int arraycopy_payload_base_offset(bool is_array); + static void make_clone_type(); + static const TypeFunc* clone_type(); + #ifndef PRODUCT virtual void dump_barrier_data(const MachNode* mach, outputStream* st) const { st->print("%x", mach->barrier_data()); diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index fdfde866cd72..1947c75314e0 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -519,7 +519,33 @@ void ShenandoahBarrierSetC2::post_barrier(GraphKit* kit, #undef __ -const TypeFunc* ShenandoahBarrierSetC2::write_barrier_pre_Type() { +const TypeFunc* ShenandoahBarrierSetC2::_write_barrier_pre_Type = nullptr; +const TypeFunc* ShenandoahBarrierSetC2::_clone_barrier_Type = nullptr; +const TypeFunc* ShenandoahBarrierSetC2::_load_reference_barrier_Type = nullptr; + +inline const TypeFunc* ShenandoahBarrierSetC2::write_barrier_pre_Type() { + assert(ShenandoahBarrierSetC2::_write_barrier_pre_Type != nullptr, "should be initialized"); + return ShenandoahBarrierSetC2::_write_barrier_pre_Type; +} + +inline const TypeFunc* ShenandoahBarrierSetC2::clone_barrier_Type() { + assert(ShenandoahBarrierSetC2::_clone_barrier_Type != nullptr, "should be initialized"); + return ShenandoahBarrierSetC2::_clone_barrier_Type; +} + +const TypeFunc* ShenandoahBarrierSetC2::load_reference_barrier_Type() { + assert(ShenandoahBarrierSetC2::_load_reference_barrier_Type != nullptr, "should be initialized"); + return ShenandoahBarrierSetC2::_load_reference_barrier_Type; +} + +void ShenandoahBarrierSetC2::init() { + ShenandoahBarrierSetC2::make_write_barrier_pre_Type(); + ShenandoahBarrierSetC2::make_clone_barrier_Type(); + ShenandoahBarrierSetC2::make_load_reference_barrier_Type(); +} + +void ShenandoahBarrierSetC2::make_write_barrier_pre_Type() { + assert(ShenandoahBarrierSetC2::_write_barrier_pre_Type == nullptr, "should be"); const Type **fields = TypeTuple::fields(1); fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // original field value const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1, fields); @@ -528,10 +554,11 @@ const TypeFunc* ShenandoahBarrierSetC2::write_barrier_pre_Type() { fields = TypeTuple::fields(0); const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0, fields); - return TypeFunc::make(domain, range); + ShenandoahBarrierSetC2::_write_barrier_pre_Type = TypeFunc::make(domain, range); } -const TypeFunc* ShenandoahBarrierSetC2::clone_barrier_Type() { +void ShenandoahBarrierSetC2::make_clone_barrier_Type() { + assert(ShenandoahBarrierSetC2::_clone_barrier_Type == nullptr, "should be"); const Type **fields = TypeTuple::fields(1); fields[TypeFunc::Parms+0] = TypeOopPtr::NOTNULL; // src oop const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1, fields); @@ -540,10 +567,11 @@ const TypeFunc* ShenandoahBarrierSetC2::clone_barrier_Type() { fields = TypeTuple::fields(0); const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0, fields); - return TypeFunc::make(domain, range); + ShenandoahBarrierSetC2::_clone_barrier_Type = TypeFunc::make(domain, range); } -const TypeFunc* ShenandoahBarrierSetC2::load_reference_barrier_Type() { +void ShenandoahBarrierSetC2::make_load_reference_barrier_Type() { + assert(ShenandoahBarrierSetC2::_load_reference_barrier_Type == nullptr, "should be"); const Type **fields = TypeTuple::fields(2); fields[TypeFunc::Parms+0] = TypeOopPtr::BOTTOM; // original field value fields[TypeFunc::Parms+1] = TypeRawPtr::BOTTOM; // original load address @@ -555,7 +583,7 @@ const TypeFunc* ShenandoahBarrierSetC2::load_reference_barrier_Type() { fields[TypeFunc::Parms+0] = TypeOopPtr::BOTTOM; const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+1, fields); - return TypeFunc::make(domain, range); + ShenandoahBarrierSetC2::_load_reference_barrier_Type = TypeFunc::make(domain, range); } Node* ShenandoahBarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& val) const { diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp index dd9e9bcc1a5f..108eaa0998bf 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp @@ -82,6 +82,13 @@ class ShenandoahBarrierSetC2 : public BarrierSetC2 { static bool clone_needs_barrier(Node* src, PhaseGVN& gvn); + static const TypeFunc* _write_barrier_pre_Type; + static const TypeFunc* _clone_barrier_Type; + static const TypeFunc* _load_reference_barrier_Type; + static void make_write_barrier_pre_Type(); + static void make_clone_barrier_Type(); + static void make_load_reference_barrier_Type(); + protected: virtual Node* load_at_resolved(C2Access& access, const Type* val_type) const; virtual Node* store_at_resolved(C2Access& access, C2AccessValue& val) const; @@ -106,6 +113,8 @@ class ShenandoahBarrierSetC2 : public BarrierSetC2 { static const TypeFunc* write_barrier_pre_Type(); static const TypeFunc* clone_barrier_Type(); static const TypeFunc* load_reference_barrier_Type(); + static void init(); + virtual bool has_load_barrier_nodes() const { return true; } // This is the entry-point for the backend to perform accesses through the Access API. diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 1a0872ee0e69..aab2ea3cd3b2 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -49,6 +49,9 @@ #include "utilities/ostream.hpp" #include "utilities/powerOfTwo.hpp" #include "utilities/stringUtils.hpp" +#if INCLUDE_SHENANDOAHGC +#include "gc/shenandoah/c2/shenandoahBarrierSetC2.hpp" +#endif // INCLUDE_SHENANDOAHGC // Portions of code courtesy of Clifford Click @@ -732,6 +735,11 @@ void Type::Initialize_shared(Compile* current) { mreg2type[Op_VecY] = TypeVect::VECTY; mreg2type[Op_VecZ] = TypeVect::VECTZ; +#if INCLUDE_SHENANDOAHGC + ShenandoahBarrierSetC2::init(); +#endif //INCLUDE_SHENANDOAHGC + + BarrierSetC2::make_clone_type(); LockNode::initialize_lock_Type(); ArrayCopyNode::initialize_arraycopy_Type(); OptoRuntime::initialize_types(); From da85cafd796655c13e75e8bf49159386605285d5 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Tue, 17 Mar 2026 16:11:38 +0000 Subject: [PATCH 74/97] 8380198: Convert java/util/prefs/PrefsSpiTest.java to JUnit Reviewed-by: jlu --- test/jdk/java/util/prefs/PrefsSpiTest.java | 39 +++++++++++----------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/test/jdk/java/util/prefs/PrefsSpiTest.java b/test/jdk/java/util/prefs/PrefsSpiTest.java index 0369c60cae23..08fff81a5527 100644 --- a/test/jdk/java/util/prefs/PrefsSpiTest.java +++ b/test/jdk/java/util/prefs/PrefsSpiTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ * @library /test/lib * @build jdk.test.lib.util.JarUtils jdk.test.lib.process.* * PrefsSpi StubPreferencesFactory StubPreferences - * @run testng PrefsSpiTest + * @run junit PrefsSpiTest */ import java.io.File; @@ -36,16 +36,13 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; import jdk.test.lib.JDKToolFinder; import jdk.test.lib.Utils; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.util.JarUtils; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static java.nio.file.StandardOpenOption.CREATE; import static java.util.Arrays.asList; @@ -53,14 +50,19 @@ import static jdk.test.lib.Utils.TEST_CLASSES; import static jdk.test.lib.Utils.TEST_CLASS_PATH; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + public class PrefsSpiTest { private static final Path SPIJAR = Path.of("extDir", "PrefsSpi.jar"); private static final String SPIJAR_CP = TEST_CLASS_PATH + File.pathSeparator + SPIJAR.toString(); - @BeforeClass - public void initialize() throws Exception { + @BeforeAll + public static void initialize() throws Exception { Path xdir = Path.of("jarDir"); Path config = xdir.resolve("META-INF/services/java.util.prefs.PreferencesFactory"); @@ -77,19 +79,18 @@ public void initialize() throws Exception { JarUtils.createJarFile(SPIJAR, xdir); } - @DataProvider - public Object[][] testCases() { - return new Object[][]{ - // CLI options, runtime arguments - {List.of("-cp", SPIJAR_CP, - "-Djava.util.prefs.PreferencesFactory=StubPreferencesFactory"), - "StubPreferences"}, - {List.of("-cp", TEST_CLASS_PATH), "java.util.prefs.*"}, - {List.of("-cp", SPIJAR_CP), "StubPreferences"} - }; + public static Stream testCases() { + return Stream.of + (// CLI options, runtime arguments + Arguments.of(List.of("-cp", SPIJAR_CP, + "-Djava.util.prefs.PreferencesFactory=StubPreferencesFactory"), + "StubPreferences"), + Arguments.of(List.of("-cp", TEST_CLASS_PATH), "java.util.prefs.*"), + Arguments.of(List.of("-cp", SPIJAR_CP), "StubPreferences")); } - @Test(dataProvider = "testCases") + @ParameterizedTest + @MethodSource("testCases") public void testProvider(List opts, String pattern) throws Throwable { List args = new ArrayList<>(); args.add(JDKToolFinder.getJDKTool("java")); From 773c375f1813afb0acf6bd1731eac39d5a43d0b4 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Tue, 17 Mar 2026 16:12:02 +0000 Subject: [PATCH 75/97] 8380221: Change jdk/nio/Basic.java to use JUnit Reviewed-by: jlu, alanb --- test/jdk/jdk/nio/Basic.java | 81 +++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/test/jdk/jdk/nio/Basic.java b/test/jdk/jdk/nio/Basic.java index 02fcfbc4a4d5..68f8f00e3090 100644 --- a/test/jdk/jdk/nio/Basic.java +++ b/test/jdk/jdk/nio/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ * @test * @bug 8198372 * @modules jdk.net java.base/sun.nio.ch:+open - * @run testng Basic + * @run junit Basic * @summary Basic tests for jdk.nio.Channels */ @@ -48,10 +48,14 @@ import sun.nio.ch.IOUtil; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; -@Test public class Basic { /** @@ -119,6 +123,7 @@ public void implReleaseChannel(SelectableChannel sc) { /** * Basic test of channel registered with Selector */ + @Test public void testSelect() throws IOException { Selector sel = Selector.open(); try (Connection connection = Connection.open()) { @@ -131,7 +136,7 @@ public void testSelect() throws IOException { ch.configureBlocking(false); SelectionKey key = ch.register(sel, SelectionKey.OP_READ); int n = sel.selectNow(); - assertTrue(n == 0); + assertEquals(0, n); // write bytes to other end of connection SocketChannel peer = connection.channel2(); @@ -141,7 +146,7 @@ public void testSelect() throws IOException { // channel should be selected n = sel.select(); - assertTrue(n == 1); + assertEquals(1, n); assertTrue(sel.selectedKeys().contains(key)); assertTrue(key.isReadable()); assertFalse(key.isWritable()); @@ -150,7 +155,7 @@ public void testSelect() throws IOException { // change interest set for writing, channel should be selected key.interestOps(SelectionKey.OP_WRITE); n = sel.select(); - assertTrue(n == 1); + assertEquals(1, n); assertTrue(sel.selectedKeys().contains(key)); assertTrue(key.isWritable()); assertFalse(key.isReadable()); @@ -159,7 +164,7 @@ public void testSelect() throws IOException { // change interest set for reading + writing, channel should be selected key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); n = sel.select(); - assertTrue(n == 1); + assertEquals(1, n); assertTrue(sel.selectedKeys().contains(key)); assertTrue(key.isWritable()); assertTrue(key.isReadable()); @@ -168,7 +173,7 @@ public void testSelect() throws IOException { // change interest set to 0 to deregister, channel should not be selected key.interestOps(0); n = sel.selectNow(); - assertTrue(n == 0); + assertEquals(0, n); } finally { sel.close(); @@ -178,6 +183,7 @@ public void testSelect() throws IOException { /** * Test that the SelectableChannelCloser implCloseChannel method is invoked. */ + @Test public void testImplCloseChannel() throws IOException { try (Connection connection = Connection.open()) { FileDescriptor fd = getFD(connection.channel1()); @@ -189,11 +195,11 @@ public void testImplCloseChannel() throws IOException { ch.close(); // implCloseChannel should been invoked once - assertTrue(closer.closeCount == 1); - assertTrue(closer.invokedToClose == ch); + assertEquals(1, closer.closeCount); + assertSame(ch, closer.invokedToClose); // implReleaseChannel should not have been invoked - assertTrue(closer.releaseCount == 0); + assertEquals(0, closer.releaseCount); } } } @@ -201,6 +207,7 @@ public void testImplCloseChannel() throws IOException { /** * Test that the SelectableChannelCloser implReleaseChannel method is invoked. */ + @Test public void testImplReleaseChannel() throws IOException { Selector sel = Selector.open(); try (Connection connection = Connection.open()) { @@ -217,50 +224,53 @@ public void testImplReleaseChannel() throws IOException { ch.close(); // implCloseChannel should have been invoked - assertTrue(closer.closeCount == 1); - assertTrue(closer.invokedToClose == ch); + assertEquals(1, closer.closeCount); + assertSame(ch, closer.invokedToClose); // implReleaseChannel should not have been invoked - assertTrue(closer.releaseCount == 0); + assertEquals(0, closer.releaseCount); // flush the selector sel.selectNow(); // implReleaseChannel should have been invoked - assertTrue(closer.releaseCount == 1); - assertTrue(closer.invokedToRelease == ch); + assertEquals(1, closer.releaseCount); + assertSame(ch, closer.invokedToRelease); } finally { sel.close(); } } - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testInvalidFileDescriptor() throws IOException { FileDescriptor fd = IOUtil.newFD(-1); - Channels.readWriteSelectableChannel(fd, new SelectableChannelCloser() { - @Override - public void implCloseChannel(SelectableChannel sc) { } - @Override - public void implReleaseChannel(SelectableChannel sc) { } - }); + assertThrows + (IllegalArgumentException.class, + () -> Channels.readWriteSelectableChannel(fd, new SelectableChannelCloser() { + @Override + public void implCloseChannel(SelectableChannel sc) { } + @Override + public void implReleaseChannel(SelectableChannel sc) { }})); } - @Test(expectedExceptions = NullPointerException.class) + @Test public void testNullFileDescriptor() throws IOException { - Channels.readWriteSelectableChannel(null, new SelectableChannelCloser() { - @Override - public void implCloseChannel(SelectableChannel sc) { } - @Override - public void implReleaseChannel(SelectableChannel sc) { } - }); + assertThrows + (NullPointerException.class, + () -> Channels.readWriteSelectableChannel(null, new SelectableChannelCloser() { + @Override + public void implCloseChannel(SelectableChannel sc) { } + @Override + public void implReleaseChannel(SelectableChannel sc) { }})); } - @Test(expectedExceptions = NullPointerException.class) + @Test public void testNullCloser() throws IOException { try (Connection connection = Connection.open()) { FileDescriptor fd = getFD(connection.channel1()); - Channels.readWriteSelectableChannel(fd, null); + assertThrows(NullPointerException.class, + () -> Channels.readWriteSelectableChannel(fd, null)); } } @@ -271,7 +281,8 @@ private static FileDescriptor getFD(SocketChannel sc) { f.setAccessible(true); return (FileDescriptor) f.get(sc); } catch (Exception e) { - throw new Error(e); + fail(e); + return null; // appease compiler } } } From 9394749f9e788fc538a7876e338e536aa296b70c Mon Sep 17 00:00:00 2001 From: Leo Korinth Date: Tue, 17 Mar 2026 16:16:56 +0000 Subject: [PATCH 76/97] 8377769: Only use large pages sizes that have any pages configured Reviewed-by: ayang, stefank --- src/hotspot/os/linux/hugepages.cpp | 26 +++++++++++++++++++++++--- src/hotspot/os/linux/hugepages.hpp | 6 +++++- src/hotspot/os/linux/os_linux.cpp | 10 +++++----- src/hotspot/share/runtime/os.hpp | 2 +- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/hotspot/os/linux/hugepages.cpp b/src/hotspot/os/linux/hugepages.cpp index 5472c093d3f3..a78f355c1792 100644 --- a/src/hotspot/os/linux/hugepages.cpp +++ b/src/hotspot/os/linux/hugepages.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2024, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -35,13 +35,18 @@ #include ExplicitHugePageSupport::ExplicitHugePageSupport() : - _initialized(false), _pagesizes(), _default_hugepage_size(SIZE_MAX), _inconsistent(false) {} + _initialized(false), _pagesizes(), _pre_allocated_pagesizes(), _default_hugepage_size(SIZE_MAX), _inconsistent(false) {} os::PageSizes ExplicitHugePageSupport::pagesizes() const { assert(_initialized, "Not initialized"); return _pagesizes; } +os::PageSizes ExplicitHugePageSupport::pre_allocated_pagesizes() const { + assert(_initialized, "Not initialized"); + return _pre_allocated_pagesizes; +} + size_t ExplicitHugePageSupport::default_hugepage_size() const { assert(_initialized, "Not initialized"); return _default_hugepage_size; @@ -129,6 +134,20 @@ static os::PageSizes scan_hugepages() { return pagesizes; } +static os::PageSizes filter_pre_allocated_hugepages(os::PageSizes pagesizes) { + os::PageSizes pre_allocated{}; + char filename[PATH_MAX]; + for (size_t ps = pagesizes.smallest(); ps != 0; ps = pagesizes.next_larger(ps)) { + os::snprintf_checked(filename, sizeof(filename), "%s/hugepages-%zukB/nr_hugepages", sys_hugepages, ps / K); + size_t pages; + bool read_success = read_number_file(filename, &pages); + if (read_success && pages > 0) { + pre_allocated.add(ps); + } + } + return pre_allocated; +} + void ExplicitHugePageSupport::print_on(outputStream* os) { if (_initialized) { os->print_cr("Explicit hugepage support:"); @@ -148,13 +167,14 @@ void ExplicitHugePageSupport::scan_os() { _default_hugepage_size = scan_default_hugepagesize(); if (_default_hugepage_size > 0) { _pagesizes = scan_hugepages(); + _pre_allocated_pagesizes = filter_pre_allocated_hugepages(_pagesizes); // See https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt: /proc/meminfo should match // /sys/kernel/mm/hugepages/hugepages-xxxx. However, we may run on a broken kernel (e.g. on WSL) // that only exposes /proc/meminfo but not /sys/kernel/mm/hugepages. In that case, we are not // sure about the state of hugepage support by the kernel, so we won't use explicit hugepages. if (!_pagesizes.contains(_default_hugepage_size)) { log_info(pagesize)("Unexpected configuration: default pagesize (%zu) " - "has no associated directory in /sys/kernel/mm/hugepages..", _default_hugepage_size); + "has no associated directory in /sys/kernel/mm/hugepages.", _default_hugepage_size); _inconsistent = true; } } diff --git a/src/hotspot/os/linux/hugepages.hpp b/src/hotspot/os/linux/hugepages.hpp index efd27c55fd60..5c190feae975 100644 --- a/src/hotspot/os/linux/hugepages.hpp +++ b/src/hotspot/os/linux/hugepages.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2024, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -47,6 +47,9 @@ class ExplicitHugePageSupport { // in /sys/kernel/mm/hugepages/hugepage-xxx) os::PageSizes _pagesizes; + // Above pages filtered for where the contents of file nr_hugepages was larger than zero + os::PageSizes _pre_allocated_pagesizes; + // Contains the default hugepage. The "default hugepage size" is the one that // - is marked in /proc/meminfo as "Hugepagesize" // - is the size one gets when using mmap(MAP_HUGETLB) when omitting size specifiers like MAP_HUGE_SHIFT) @@ -61,6 +64,7 @@ class ExplicitHugePageSupport { void scan_os(); os::PageSizes pagesizes() const; + os::PageSizes pre_allocated_pagesizes() const; size_t default_hugepage_size() const; void print_on(outputStream* os); diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 5e911ce0f44b..7bde0ae08c9c 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -3819,7 +3819,7 @@ static int hugetlbfs_page_size_flag(size_t page_size) { static bool hugetlbfs_sanity_check(size_t page_size) { const os::PageSizes page_sizes = HugePages::explicit_hugepage_info().pagesizes(); - assert(page_sizes.contains(page_size), "Invalid page sizes passed"); + assert(page_sizes.contains(page_size), "Invalid page sizes passed (%zu)", page_size); // Include the page size flag to ensure we sanity check the correct page size. int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB | hugetlbfs_page_size_flag(page_size); @@ -4066,10 +4066,10 @@ void os::Linux::large_page_init() { _large_page_size = large_page_size; - // Populate _page_sizes with large page sizes less than or equal to - // _large_page_size. - for (size_t page_size = _large_page_size; page_size != 0; - page_size = all_large_pages.next_smaller(page_size)) { + // Populate _page_sizes with _large_page_size (default large page size) even if not pre-allocated. + // Then, populate _page_sizes with all smaller large page sizes that have been pre-allocated. + os::PageSizes pre_allocated = HugePages::explicit_hugepage_info().pre_allocated_pagesizes(); + for (size_t page_size = _large_page_size; page_size != 0; page_size = pre_allocated.next_smaller(page_size)) { _page_sizes.add(page_size); } } diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index d0544cb5b514..76041f256a9e 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -454,7 +454,7 @@ class os: AllStatic { static size_t align_down_vm_page_size(size_t size) { return align_down(size, os::vm_page_size()); } // The set of page sizes which the VM is allowed to use (may be a subset of - // the page sizes actually available on the platform). + // the page sizes actually available on the platform). static const PageSizes& page_sizes() { return _page_sizes; } // Returns the page size to use for a region of memory. From ee90f00b3b38b7cf4da340deb48f04bdaee22710 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Tue, 17 Mar 2026 17:21:16 +0000 Subject: [PATCH 77/97] 8376822: UseCompactObjectHeaders: fill Klass alignment gaps in AOT cache Reviewed-by: jsjolen, asmehra --- src/hotspot/share/cds/aotMapLogger.cpp | 52 +++-- src/hotspot/share/cds/aotMapLogger.hpp | 7 +- src/hotspot/share/cds/archiveBuilder.cpp | 35 +--- src/hotspot/share/cds/archiveUtils.cpp | 185 +++++++++++++++++- src/hotspot/share/cds/archiveUtils.hpp | 18 ++ src/hotspot/share/cds/dumpAllocStats.cpp | 14 +- src/hotspot/share/cds/dumpAllocStats.hpp | 11 +- src/hotspot/share/utilities/rbTree.hpp | 7 +- .../jtreg/runtime/cds/MetaspaceAllocGaps.java | 73 +++++++ 9 files changed, 324 insertions(+), 78 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/cds/MetaspaceAllocGaps.java diff --git a/src/hotspot/share/cds/aotMapLogger.cpp b/src/hotspot/share/cds/aotMapLogger.cpp index fa769aee1bfc..98336ff9b1f5 100644 --- a/src/hotspot/share/cds/aotMapLogger.cpp +++ b/src/hotspot/share/cds/aotMapLogger.cpp @@ -98,8 +98,8 @@ void AOTMapLogger::dumptime_log(ArchiveBuilder* builder, FileMapInfo* mapinfo, DumpRegion* rw_region = &builder->_rw_region; DumpRegion* ro_region = &builder->_ro_region; - dumptime_log_metaspace_region("rw region", rw_region, &builder->_rw_src_objs); - dumptime_log_metaspace_region("ro region", ro_region, &builder->_ro_src_objs); + dumptime_log_metaspace_region("rw region", rw_region, &builder->_rw_src_objs, &builder->_ro_src_objs); + dumptime_log_metaspace_region("ro region", ro_region, &builder->_rw_src_objs, &builder->_ro_src_objs); address bitmap_end = address(bitmap + bitmap_size_in_bytes); log_region_range("bitmap", address(bitmap), bitmap_end, nullptr); @@ -122,17 +122,6 @@ void AOTMapLogger::dumptime_log(ArchiveBuilder* builder, FileMapInfo* mapinfo, class AOTMapLogger::RuntimeGatherArchivedMetaspaceObjs : public UniqueMetaspaceClosure { GrowableArrayCHeap _objs; - static int compare_objs_by_addr(ArchivedObjInfo* a, ArchivedObjInfo* b) { - intx diff = a->_src_addr - b->_src_addr; - if (diff < 0) { - return -1; - } else if (diff == 0) { - return 0; - } else { - return 1; - } - } - public: GrowableArrayCHeap* objs() { return &_objs; } @@ -152,7 +141,7 @@ class AOTMapLogger::RuntimeGatherArchivedMetaspaceObjs : public UniqueMetaspaceC void finish() { UniqueMetaspaceClosure::finish(); - _objs.sort(compare_objs_by_addr); + _objs.sort(compare_by_address); } }; // AOTMapLogger::RuntimeGatherArchivedMetaspaceObjs @@ -203,24 +192,47 @@ void AOTMapLogger::runtime_log(FileMapInfo* mapinfo, GrowableArrayCHeapbase()); address region_top = address(region->top()); log_region_range(name, region_base, region_top, region_base + _buffer_to_requested_delta); if (log_is_enabled(Debug, aot, map)) { GrowableArrayCHeap objs; - for (int i = 0; i < src_objs->objs()->length(); i++) { - ArchiveBuilder::SourceObjInfo* src_info = src_objs->at(i); + // With -XX:+UseCompactObjectHeaders, it's possible for small objects (including some from + // ro_objs) to be allocated in the gaps in the RW region. + collect_metaspace_objs(&objs, region_base, region_top, rw_objs); + collect_metaspace_objs(&objs, region_base, region_top, ro_objs); + objs.sort(compare_by_address); + log_metaspace_objects_impl(address(region->base()), address(region->end()), &objs, 0, objs.length()); + } +} + +void AOTMapLogger::collect_metaspace_objs(GrowableArrayCHeap* objs, + address region_base, address region_top , + const ArchiveBuilder::SourceObjList* src_objs) { + for (int i = 0; i < src_objs->objs()->length(); i++) { + ArchiveBuilder::SourceObjInfo* src_info = src_objs->at(i); + address buf_addr = src_info->buffered_addr(); + if (region_base <= buf_addr && buf_addr < region_top) { ArchivedObjInfo info; info._src_addr = src_info->source_addr(); - info._buffered_addr = src_info->buffered_addr(); + info._buffered_addr = buf_addr; info._requested_addr = info._buffered_addr + _buffer_to_requested_delta; info._bytes = src_info->size_in_bytes(); info._type = src_info->type(); - objs.append(info); + objs->append(info); } + } +} - log_metaspace_objects_impl(address(region->base()), address(region->end()), &objs, 0, objs.length()); +int AOTMapLogger::compare_by_address(ArchivedObjInfo* a, ArchivedObjInfo* b) { + if (a->_buffered_addr < b->_buffered_addr) { + return -1; + } else if (a->_buffered_addr > b->_buffered_addr) { + return 1; + } else { + return 0; } } diff --git a/src/hotspot/share/cds/aotMapLogger.hpp b/src/hotspot/share/cds/aotMapLogger.hpp index f495ed97f40e..0a89f1e5012c 100644 --- a/src/hotspot/share/cds/aotMapLogger.hpp +++ b/src/hotspot/share/cds/aotMapLogger.hpp @@ -127,7 +127,12 @@ class AOTMapLogger : AllStatic { static void runtime_log(FileMapInfo* mapinfo, GrowableArrayCHeap* objs); static void runtime_log_metaspace_regions(FileMapInfo* mapinfo, GrowableArrayCHeap* objs); static void dumptime_log_metaspace_region(const char* name, DumpRegion* region, - const ArchiveBuilder::SourceObjList* src_objs); + const ArchiveBuilder::SourceObjList* rw_objs, + const ArchiveBuilder::SourceObjList* ro_objs); + static void collect_metaspace_objs(GrowableArrayCHeap* objs, + address region_base, address region_top , + const ArchiveBuilder::SourceObjList* src_objs); + static int compare_by_address(ArchivedObjInfo* a, ArchivedObjInfo* b); // Common code for dumptime/runtime static void log_file_header(FileMapInfo* mapinfo); diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index 0ea5d6c6ecbd..b2d6600e44f2 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -627,6 +627,7 @@ void ArchiveBuilder::dump_ro_metadata() { start_dump_region(&_ro_region); make_shallow_copies(&_ro_region, &_ro_src_objs); RegeneratedClasses::record_regenerated_objects(); + DumpRegion::report_gaps(&_alloc_stats); } void ArchiveBuilder::make_shallow_copies(DumpRegion *dump_region, @@ -639,33 +640,10 @@ void ArchiveBuilder::make_shallow_copies(DumpRegion *dump_region, void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* src_info) { address src = src_info->source_addr(); - int bytes = src_info->size_in_bytes(); // word-aligned - size_t alignment = SharedSpaceObjectAlignment; // alignment for the dest pointer - - char* oldtop = dump_region->top(); - if (src_info->type() == MetaspaceClosureType::ClassType) { - // Allocate space for a pointer directly in front of the future InstanceKlass, so - // we can do a quick lookup from InstanceKlass* -> RunTimeClassInfo* - // without building another hashtable. See RunTimeClassInfo::get_for() - // in systemDictionaryShared.cpp. - Klass* klass = (Klass*)src; - if (klass->is_instance_klass()) { - SystemDictionaryShared::validate_before_archiving(InstanceKlass::cast(klass)); - dump_region->allocate(sizeof(address)); - } -#ifdef _LP64 - // More strict alignments needed for UseCompressedClassPointers - if (UseCompressedClassPointers) { - alignment = nth_bit(ArchiveBuilder::precomputed_narrow_klass_shift()); - } -#endif - } else if (src_info->type() == MetaspaceClosureType::SymbolType) { - // Symbols may be allocated by using AllocateHeap, so their sizes - // may be less than size_in_bytes() indicates. - bytes = ((Symbol*)src)->byte_size(); - } + int bytes = src_info->size_in_bytes(); + char* dest = dump_region->allocate_metaspace_obj(bytes, src, src_info->type(), + src_info->read_only(), &_alloc_stats); - char* dest = dump_region->allocate(bytes, alignment); memcpy(dest, src, bytes); // Update the hash of buffered sorted symbols for static dump so that the symbols have deterministic contents @@ -692,11 +670,6 @@ void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* s log_trace(aot)("Copy: " PTR_FORMAT " ==> " PTR_FORMAT " %d", p2i(src), p2i(dest), bytes); src_info->set_buffered_addr((address)dest); - - char* newtop = dump_region->top(); - _alloc_stats.record(src_info->type(), int(newtop - oldtop), src_info->read_only()); - - DEBUG_ONLY(_alloc_stats.verify((int)dump_region->used(), src_info->read_only())); } // This is used by code that hand-assembles data structures, such as the LambdaProxyClassKey, that are diff --git a/src/hotspot/share/cds/archiveUtils.cpp b/src/hotspot/share/cds/archiveUtils.cpp index ea9bde8eb8de..f79b1e134e87 100644 --- a/src/hotspot/share/cds/archiveUtils.cpp +++ b/src/hotspot/share/cds/archiveUtils.cpp @@ -30,6 +30,7 @@ #include "cds/cdsConfig.hpp" #include "cds/classListParser.hpp" #include "cds/classListWriter.hpp" +#include "cds/dumpAllocStats.hpp" #include "cds/dynamicArchive.hpp" #include "cds/filemap.hpp" #include "cds/heapShared.hpp" @@ -46,6 +47,7 @@ #include "utilities/debug.hpp" #include "utilities/formatBuffer.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/rbTree.inline.hpp" #include "utilities/spinYield.hpp" CHeapBitMap* ArchivePtrMarker::_ptrmap = nullptr; @@ -116,13 +118,17 @@ void ArchivePtrMarker::mark_pointer(address* ptr_loc) { if (ptr_base() <= ptr_loc && ptr_loc < ptr_end()) { address value = *ptr_loc; - // We don't want any pointer that points to very bottom of the archive, otherwise when - // AOTMetaspace::default_base_address()==0, we can't distinguish between a pointer - // to nothing (null) vs a pointer to an objects that happens to be at the very bottom - // of the archive. - assert(value != (address)ptr_base(), "don't point to the bottom of the archive"); - if (value != nullptr) { + // We don't want any pointer that points to very bottom of the AOT metaspace, otherwise + // when AOTMetaspace::default_base_address()==0, we can't distinguish between a pointer + // to nothing (null) vs a pointer to an objects that happens to be at the very bottom + // of the AOT metaspace. + // + // This should never happen because the protection zone prevents any valid objects from + // being allocated at the bottom of the AOT metaspace. + assert(AOTMetaspace::protection_zone_size() > 0, "must be"); + assert(ArchiveBuilder::current()->any_to_offset(value) > 0, "cannot point to bottom of AOT metaspace"); + assert(uintx(ptr_loc) % sizeof(intptr_t) == 0, "pointers must be stored in aligned addresses"); size_t idx = ptr_loc - ptr_base(); if (_ptrmap->size() <= idx) { @@ -130,7 +136,6 @@ void ArchivePtrMarker::mark_pointer(address* ptr_loc) { } assert(idx < _ptrmap->size(), "must be"); _ptrmap->set_bit(idx); - //tty->print_cr("Marking pointer [" PTR_FORMAT "] -> " PTR_FORMAT " @ %5zu", p2i(ptr_loc), p2i(*ptr_loc), idx); } } } @@ -144,7 +149,6 @@ void ArchivePtrMarker::clear_pointer(address* ptr_loc) { size_t idx = ptr_loc - ptr_base(); assert(idx < _ptrmap->size(), "cannot clear pointers that have not been marked"); _ptrmap->clear_bit(idx); - //tty->print_cr("Clearing pointer [" PTR_FORMAT "] -> " PTR_FORMAT " @ %5zu", p2i(ptr_loc), p2i(*ptr_loc), idx); } class ArchivePtrBitmapCleaner: public BitMapClosure { @@ -249,16 +253,179 @@ void DumpRegion::commit_to(char* newtop) { which, commit, _vs->actual_committed_size(), _vs->high()); } +// Basic allocation. Any alignment gaps will be wasted. char* DumpRegion::allocate(size_t num_bytes, size_t alignment) { // Always align to at least minimum alignment alignment = MAX2(SharedSpaceObjectAlignment, alignment); char* p = (char*)align_up(_top, alignment); - char* newtop = p + align_up(num_bytes, (size_t)SharedSpaceObjectAlignment); + char* newtop = p + align_up(num_bytes, SharedSpaceObjectAlignment); expand_top_to(newtop); memset(p, 0, newtop - p); return p; } +class DumpRegion::AllocGap { + size_t _gap_bytes; // size of this gap in bytes + char* _gap_bottom; // must be SharedSpaceObjectAlignment aligned +public: + size_t gap_bytes() const { return _gap_bytes; } + char* gap_bottom() const { return _gap_bottom; } + + AllocGap(size_t bytes, char* bottom) : _gap_bytes(bytes), _gap_bottom(bottom) { + precond(is_aligned(gap_bytes(), SharedSpaceObjectAlignment)); + precond(is_aligned(gap_bottom(), SharedSpaceObjectAlignment)); + } +}; + +struct DumpRegion::AllocGapCmp { + static RBTreeOrdering cmp(AllocGap a, AllocGap b) { + RBTreeOrdering order = rbtree_primitive_cmp(a.gap_bytes(), b.gap_bytes()); + if (order == RBTreeOrdering::EQ) { + order = rbtree_primitive_cmp(a.gap_bottom(), b.gap_bottom()); + } + return order; + } +}; + +struct Empty {}; +using AllocGapNode = RBNode; + +class DumpRegion::AllocGapTree : public RBTreeCHeap { +public: + size_t add_gap(char* gap_bottom, char* gap_top) { + precond(gap_bottom < gap_top); + size_t gap_bytes = pointer_delta(gap_top, gap_bottom, 1); + precond(gap_bytes > 0); + + _total_gap_bytes += gap_bytes; + + AllocGap gap(gap_bytes, gap_bottom); // constructor checks alignment + AllocGapNode* node = allocate_node(gap, Empty{}); + insert(gap, node); + + log_trace(aot, alloc)("adding a gap of %zu bytes @ %p (total = %zu) in %zu blocks", gap_bytes, gap_bottom, _total_gap_bytes, size()); + return gap_bytes; + } + + char* allocate_from_gap(size_t num_bytes) { + // The gaps are sorted in ascending order of their sizes. When two gaps have the same + // size, the one with a lower gap_bottom comes first. + // + // Find the first gap that's big enough, with the lowest gap_bottom. + AllocGap target(num_bytes, nullptr); + AllocGapNode* node = closest_ge(target); + if (node == nullptr) { + return nullptr; // Didn't find any usable gap. + } + + size_t gap_bytes = node->key().gap_bytes(); + char* gap_bottom = node->key().gap_bottom(); + char* result = gap_bottom; + precond(is_aligned(result, SharedSpaceObjectAlignment)); + + remove(node); + + precond(_total_gap_bytes >= num_bytes); + _total_gap_bytes -= num_bytes; + _total_gap_bytes_used += num_bytes; + _total_gap_allocs++; + DEBUG_ONLY(node = nullptr); // Don't use it anymore! + + precond(gap_bytes >= num_bytes); + if (gap_bytes > num_bytes) { + gap_bytes -= num_bytes; + gap_bottom += num_bytes; + + AllocGap gap(gap_bytes, gap_bottom); // constructor checks alignment + AllocGapNode* new_node = allocate_node(gap, Empty{}); + insert(gap, new_node); + } + log_trace(aot, alloc)("%zu bytes @ %p in a gap of %zu bytes (used gaps %zu times, remain gap = %zu bytes in %zu blocks)", + num_bytes, result, gap_bytes, _total_gap_allocs, _total_gap_bytes, size()); + return result; + } +}; + +size_t DumpRegion::_total_gap_bytes = 0; +size_t DumpRegion::_total_gap_bytes_used = 0; +size_t DumpRegion::_total_gap_allocs = 0; +DumpRegion::AllocGapTree DumpRegion::_gap_tree; + +// Alignment gaps happen only for the RW space. Collect the gaps into the _gap_tree so they can be +// used for future small object allocation. +char* DumpRegion::allocate_metaspace_obj(size_t num_bytes, address src, MetaspaceClosureType type, bool read_only, DumpAllocStats* stats) { + num_bytes = align_up(num_bytes, SharedSpaceObjectAlignment); + size_t alignment = SharedSpaceObjectAlignment; // alignment for the dest pointer + bool is_class = (type == MetaspaceClosureType::ClassType); + bool is_instance_class = is_class && ((Klass*)src)->is_instance_klass(); + +#ifdef _LP64 + // More strict alignments needed for UseCompressedClassPointers + if (is_class && UseCompressedClassPointers) { + size_t klass_alignment = checked_cast(nth_bit(ArchiveBuilder::precomputed_narrow_klass_shift())); + alignment = MAX2(alignment, klass_alignment); + precond(is_aligned(alignment, SharedSpaceObjectAlignment)); + } +#endif + + if (alignment == SharedSpaceObjectAlignment && type != MetaspaceClosureType::SymbolType) { + // The addresses of Symbols must be in the same order as they are in ArchiveBuilder::SourceObjList. + // If we put them in gaps, their order will change. + // + // We have enough small objects that all gaps are usually filled. + char* p = _gap_tree.allocate_from_gap(num_bytes); + if (p != nullptr) { + // Already memset to 0 when adding the gap + stats->record(type, checked_cast(num_bytes), /*read_only=*/false); // all gaps are from RW space (for classes) + return p; + } + } + + // Reserve space for a pointer directly in front of the buffered InstanceKlass, so + // we can do a quick lookup from InstanceKlass* -> RunTimeClassInfo* + // without building another hashtable. See RunTimeClassInfo::get_for() + // in systemDictionaryShared.cpp. + const size_t RuntimeClassInfoPtrSize = is_instance_class ? sizeof(address) : 0; + + if (is_class && !is_aligned(top() + RuntimeClassInfoPtrSize, alignment)) { + // We need to add a gap to align the buffered Klass. Save the gap for future small allocations. + assert(read_only == false, "only gaps in RW region are reusable"); + char* gap_bottom = top(); + char* gap_top = align_up(gap_bottom + RuntimeClassInfoPtrSize, alignment) - RuntimeClassInfoPtrSize; + size_t gap_bytes = _gap_tree.add_gap(gap_bottom, gap_top); + allocate(gap_bytes); + } + + char* oldtop = top(); + if (is_instance_class) { + SystemDictionaryShared::validate_before_archiving((InstanceKlass*)src); + allocate(RuntimeClassInfoPtrSize); + } + + precond(is_aligned(top(), alignment)); + char* result = allocate(num_bytes); + log_trace(aot, alloc)("%zu bytes @ %p", num_bytes, result); + stats->record(type, pointer_delta_as_int(top(), oldtop), read_only); // includes RuntimeClassInfoPtrSize for classes + + return result; +} + +// Usually we have no gaps left. +void DumpRegion::report_gaps(DumpAllocStats* stats) { + _gap_tree.visit_in_order([&](const AllocGapNode* node) { + stats->record_gap(checked_cast(node->key().gap_bytes())); + return true; + }); + if (_gap_tree.size() > 0) { + log_warning(aot)("Unexpected %zu gaps (%zu bytes) for Klass alignment", + _gap_tree.size(), _total_gap_bytes); + } + if (_total_gap_allocs > 0) { + log_info(aot)("Allocated %zu objects of %zu bytes in gaps (remain = %zu bytes)", + _total_gap_allocs, _total_gap_bytes_used, _total_gap_bytes); + } +} + void DumpRegion::append_intptr_t(intptr_t n, bool need_to_mark) { assert(is_aligned(_top, sizeof(intptr_t)), "bad alignment"); intptr_t *p = (intptr_t*)_top; diff --git a/src/hotspot/share/cds/archiveUtils.hpp b/src/hotspot/share/cds/archiveUtils.hpp index e5d1efa5eaba..84ad8e6bdf3f 100644 --- a/src/hotspot/share/cds/archiveUtils.hpp +++ b/src/hotspot/share/cds/archiveUtils.hpp @@ -28,7 +28,9 @@ #include "cds/cds_globals.hpp" #include "cds/serializeClosure.hpp" #include "logging/log.hpp" +#include "memory/allocation.hpp" #include "memory/metaspace.hpp" +#include "memory/metaspaceClosureType.hpp" #include "memory/virtualspace.hpp" #include "runtime/nonJavaThread.hpp" #include "runtime/semaphore.hpp" @@ -37,6 +39,7 @@ #include "utilities/macros.hpp" class BootstrapInfo; +class DumpAllocStats; class ReservedSpace; class VirtualSpace; @@ -159,6 +162,18 @@ class DumpRegion { void commit_to(char* newtop); +public: + // Allocation gaps (due to Klass alignment) + class AllocGapTree; + class AllocGap; + struct AllocGapCmp; + +private: + static AllocGapTree _gap_tree; + static size_t _total_gap_bytes; + static size_t _total_gap_bytes_used; + static size_t _total_gap_allocs; + public: DumpRegion(const char* name) : _name(name), _base(nullptr), _top(nullptr), _end(nullptr), @@ -167,6 +182,7 @@ class DumpRegion { char* expand_top_to(char* newtop); char* allocate(size_t num_bytes, size_t alignment = 0); + char* allocate_metaspace_obj(size_t num_bytes, address src, MetaspaceClosureType type, bool read_only, DumpAllocStats* stats); void append_intptr_t(intptr_t n, bool need_to_mark = false) NOT_CDS_RETURN; @@ -191,6 +207,8 @@ class DumpRegion { bool contains(char* p) { return base() <= p && p < top(); } + + static void report_gaps(DumpAllocStats* stats); }; // Closure for serializing initialization data out to a data area to be diff --git a/src/hotspot/share/cds/dumpAllocStats.cpp b/src/hotspot/share/cds/dumpAllocStats.cpp index 5f3245661030..ddd4bac60867 100644 --- a/src/hotspot/share/cds/dumpAllocStats.cpp +++ b/src/hotspot/share/cds/dumpAllocStats.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 @@ -129,15 +129,3 @@ void DumpAllocStats::print_stats(int ro_all, int rw_all) { _bytes [RW][MethodTrainingDataType]); } - -#ifdef ASSERT -void DumpAllocStats::verify(int expected_byte_size, bool read_only) const { - int bytes = 0; - const int what = (int)(read_only ? RO : RW); - for (int type = 0; type < int(_number_of_types); type ++) { - bytes += _bytes[what][type]; - } - assert(bytes == expected_byte_size, "counter mismatch (%s: %d vs %d)", - (read_only ? "RO" : "RW"), bytes, expected_byte_size); -} -#endif // ASSERT diff --git a/src/hotspot/share/cds/dumpAllocStats.hpp b/src/hotspot/share/cds/dumpAllocStats.hpp index 4553f0f6a013..4daef9195a6b 100644 --- a/src/hotspot/share/cds/dumpAllocStats.hpp +++ b/src/hotspot/share/cds/dumpAllocStats.hpp @@ -41,6 +41,7 @@ class DumpAllocStats : public StackObj { f(StringHashentry) \ f(StringBucket) \ f(CppVTables) \ + f(Gap) \ f(Other) #define DUMPED_TYPE_DECLARE(name) name ## Type, @@ -111,12 +112,19 @@ class DumpAllocStats : public StackObj { _bytes [which][t] += byte_size; } + void record_gap(int byte_size) { + _counts[RW][GapType] += 1; + _bytes [RW][GapType] += byte_size; + } + void record_other_type(int byte_size, bool read_only) { int which = (read_only) ? RO : RW; + _counts[which][OtherType] += 1; _bytes [which][OtherType] += byte_size; } void record_cpp_vtables(int byte_size) { + _counts[RW][CppVTablesType] += 1; _bytes[RW][CppVTablesType] += byte_size; } @@ -145,9 +153,6 @@ class DumpAllocStats : public StackObj { } void print_stats(int ro_all, int rw_all); - - DEBUG_ONLY(void verify(int expected_byte_size, bool read_only) const); - }; #endif // SHARE_CDS_DUMPALLOCSTATS_HPP diff --git a/src/hotspot/share/utilities/rbTree.hpp b/src/hotspot/share/utilities/rbTree.hpp index c522d7874664..9c04ccbe9ab0 100644 --- a/src/hotspot/share/utilities/rbTree.hpp +++ b/src/hotspot/share/utilities/rbTree.hpp @@ -429,7 +429,12 @@ class RBTreeResourceAreaAllocator { void free(void* ptr); }; - +template +RBTreeOrdering rbtree_primitive_cmp(T a, T b) { // handy function + if (a < b) return RBTreeOrdering::LT; + if (a > b) return RBTreeOrdering::GT; + return RBTreeOrdering::EQ; +} template using RBTreeCHeap = RBTree>; diff --git a/test/hotspot/jtreg/runtime/cds/MetaspaceAllocGaps.java b/test/hotspot/jtreg/runtime/cds/MetaspaceAllocGaps.java new file mode 100644 index 000000000000..65459263e36d --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/MetaspaceAllocGaps.java @@ -0,0 +1,73 @@ +/* + * 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 + * @requires vm.cds + * @requires vm.flagless + * @requires vm.bits == 64 + * @bug 8376822 + * @summary Allocation gaps in the RW region caused by -XX:+UseCompactObjectHeaders should be reused + * @library /test/lib + * @build MetaspaceAllocGaps + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello.jar Hello + * @run driver MetaspaceAllocGaps + */ + +import jdk.test.lib.cds.SimpleCDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.OutputAnalyzer; + +public class MetaspaceAllocGaps { + public static void main(String[] args) throws Exception { + String appJar = ClassFileInstaller.getJarPath("hello.jar"); + for (int i = 0; i < 2; i++) { + String compressedOops = "-XX:" + (i == 0 ? "-" : "+") + "UseCompressedOops"; + SimpleCDSAppTester.of("MetaspaceAllocGaps" + i) + .addVmArgs("-Xlog:aot=debug,aot+alloc=trace", + "-XX:+UseCompactObjectHeaders") + .classpath(appJar) + .appCommandLine("Hello") + .setTrainingChecker((OutputAnalyzer out) -> { + // Typically all gaps should be filled. If not, we probably have a regression in C++ class ArchiveUtils. + // + // [0.422s][debug][aot ] Detailed metadata info (excluding heap region): + // [...] + // [0.422s][debug][aot ] Gap : 0 0 0.0 | 0 0 0.0 | 0 0 0.0 <<< look for this pattern + out.shouldMatch("Allocated [1-9][0-9]+ objects of [1-9][0-9]+ bytes in gaps .remain = 0 bytes") + .shouldMatch("debug.* Gap .*0[.]0.*0[.]0.*0[.]0") + .shouldNotMatch("Unexpected .* gaps .* for Klass alignment"); + }) + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldContain("HelloWorld"); + }) + .runAOTWorkflow(); + } + } +} + +class Hello { + public static void main(String[] args) { + System.out.println("HelloWorld"); + } +} From 00c1f4b6248e116c8839b1fb19ce20182711667a Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Tue, 17 Mar 2026 20:33:44 +0000 Subject: [PATCH 78/97] 8377512: AOT cache creation fails with invalid native pointer Reviewed-by: kvn, jrose, liach --- .../share/cds/aotConstantPoolResolver.cpp | 55 ++++++++++++++++++- .../share/cds/aotConstantPoolResolver.hpp | 7 ++- src/hotspot/share/cds/aotMappedHeapWriter.cpp | 10 +++- src/hotspot/share/oops/cpCache.cpp | 26 +++++++-- .../cds/appcds/aotCache/ExcludedClasses.java | 48 +++++++++++++++- 5 files changed, 132 insertions(+), 14 deletions(-) diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp index 931459409554..f1a704d4beed 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.cpp @@ -81,6 +81,7 @@ bool AOTConstantPoolResolver::is_resolution_deterministic(ConstantPool* cp, int bool AOTConstantPoolResolver::is_class_resolution_deterministic(InstanceKlass* cp_holder, Klass* resolved_class) { assert(!is_in_archivebuilder_buffer(cp_holder), "sanity"); assert(!is_in_archivebuilder_buffer(resolved_class), "sanity"); + assert_at_safepoint(); // try_add_candidate() is called below and requires to be at safepoint. if (resolved_class->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(resolved_class); @@ -346,7 +347,15 @@ void AOTConstantPoolResolver::maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m break; case Bytecodes::_invokehandle: - InterpreterRuntime::cds_resolve_invokehandle(raw_index, cp, CHECK); + if (CDSConfig::is_dumping_method_handles()) { + ResolvedMethodEntry* method_entry = cp->resolved_method_entry_at(raw_index); + int cp_index = method_entry->constant_pool_index(); + Symbol* sig = cp->uncached_signature_ref_at(cp_index); + Klass* k; + if (check_methodtype_signature(cp(), sig, &k, true)) { + InterpreterRuntime::cds_resolve_invokehandle(raw_index, cp, CHECK); + } + } break; default: @@ -400,7 +409,7 @@ void AOTConstantPoolResolver::preresolve_indy_cp_entries(JavaThread* current, In // Check the MethodType signatures used by parameters to the indy BSMs. Make sure we don't // use types that have been excluded, or else we might end up creating MethodTypes that cannot be stored // in the AOT cache. -bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret) { +bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret, bool is_invokehandle) { ResourceMark rm; for (SignatureStream ss(sig); !ss.is_done(); ss.next()) { if (ss.is_reference()) { @@ -413,11 +422,18 @@ bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbo if (SystemDictionaryShared::should_be_excluded(k)) { if (log_is_enabled(Warning, aot, resolve)) { ResourceMark rm; - log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is excluded", k->external_name()); + log_warning(aot, resolve)("Cannot aot-resolve %s because %s is excluded", + is_invokehandle ? "invokehandle" : "Lambda proxy", + k->external_name()); } return false; } + // cp->pool_holder() must be able to resolve k in production run + precond(CDSConfig::is_dumping_aot_linked_classes()); + precond(SystemDictionaryShared::is_builtin_loader(cp->pool_holder()->class_loader_data())); + precond(SystemDictionaryShared::is_builtin_loader(k->class_loader_data())); + if (ss.at_return_type() && return_type_ret != nullptr) { *return_type_ret = k; } @@ -475,11 +491,44 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_methodhandle_arg(Constant return false; } + // klass and sigature of the method (no need to check the method name) Symbol* sig = cp->method_handle_signature_ref_at(mh_index); + Symbol* klass_name = cp->klass_name_at(cp->method_handle_klass_index_at(mh_index)); + if (log_is_enabled(Debug, aot, resolve)) { ResourceMark rm; log_debug(aot, resolve)("Checking MethodType of MethodHandle for LambdaMetafactory BSM arg %d: %s", arg_i, sig->as_C_string()); } + + { + Klass* k = find_loaded_class(Thread::current(), cp->pool_holder()->class_loader(), klass_name); + if (k == nullptr) { + // Dumping AOT cache: all classes should have been loaded by FinalImageRecipes::load_all_classes(). k must have + // been a class that was excluded when FinalImageRecipes recorded all classes at the end of the training run. + // + // Dumping static CDS archive: all classes in the classlist have already been loaded, before we resolve + // constants. k must have been a class that was excluded when the classlist was written + // at the end of the training run. + if (log_is_enabled(Warning, aot, resolve)) { + ResourceMark rm; + log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is not loaded", klass_name->as_C_string()); + } + return false; + } + if (SystemDictionaryShared::should_be_excluded(k)) { + if (log_is_enabled(Warning, aot, resolve)) { + ResourceMark rm; + log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is excluded", k->external_name()); + } + return false; + } + + // cp->pool_holder() must be able to resolve k in production run + precond(CDSConfig::is_dumping_aot_linked_classes()); + precond(SystemDictionaryShared::is_builtin_loader(cp->pool_holder()->class_loader_data())); + precond(SystemDictionaryShared::is_builtin_loader(k->class_loader_data())); + } + return check_methodtype_signature(cp, sig); } diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.hpp b/src/hotspot/share/cds/aotConstantPoolResolver.hpp index e49d9d1ad0be..ecf2ac27061e 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.hpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,7 +74,10 @@ class AOTConstantPoolResolver : AllStatic { static void maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m, Bytecodes::Code bc, int raw_index, GrowableArray* resolve_fmi_list, TRAPS); - static bool check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret = nullptr); +public: + static bool check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret = nullptr, bool is_invokehandle = false); + +private: static bool check_lambda_metafactory_signature(ConstantPool* cp, Symbol* sig); static bool check_lambda_metafactory_methodtype_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i); static bool check_lambda_metafactory_methodhandle_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i); diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.cpp b/src/hotspot/share/cds/aotMappedHeapWriter.cpp index 2e21a1d15d8b..3456c8459387 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.cpp @@ -898,8 +898,14 @@ void AOTMappedHeapWriter::compute_ptrmap(AOTMappedHeapInfo* heap_info) { native_ptr = RegeneratedClasses::get_regenerated_object(native_ptr); } - guarantee(ArchiveBuilder::current()->has_been_archived((address)native_ptr), - "Metadata %p should have been archived", native_ptr); + if (!ArchiveBuilder::current()->has_been_archived((address)native_ptr)) { + ResourceMark rm; + LogStreamHandle(Error, aot) log; + log.print("Marking native pointer for oop %p (type = %s, offset = %d)", + cast_from_oop(src_obj), src_obj->klass()->external_name(), field_offset); + src_obj->print_on(&log); + fatal("Metadata %p should have been archived", native_ptr); + } address buffered_native_ptr = ArchiveBuilder::current()->get_buffered_addr((address)native_ptr); address requested_native_ptr = ArchiveBuilder::current()->to_requested(buffered_native_ptr); diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index 34d7aa10299a..edb5f6714c03 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -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 @@ -568,6 +568,8 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv return false; } + int cp_index = method_entry->constant_pool_index(); + if (!method_entry->is_resolved(Bytecodes::_invokevirtual)) { if (method_entry->method() == nullptr) { rejection_reason = "(method entry is not resolved)"; @@ -577,9 +579,24 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv rejection_reason = "(corresponding stub is generated on demand during method resolution)"; return false; // FIXME: corresponding stub is generated on demand during method resolution (see LinkResolver::resolve_static_call). } - if (method_entry->is_resolved(Bytecodes::_invokehandle) && !CDSConfig::is_dumping_method_handles()) { - rejection_reason = "(not dumping method handles)"; - return false; + if (method_entry->is_resolved(Bytecodes::_invokehandle)) { + if (!CDSConfig::is_dumping_method_handles()) { + rejection_reason = "(not dumping method handles)"; + return false; + } + + Symbol* sig = constant_pool()->uncached_signature_ref_at(cp_index); + Klass* k; + if (!AOTConstantPoolResolver::check_methodtype_signature(constant_pool(), sig, &k, true)) { + // invokehandles that were resolved in the training run should have been filtered in + // AOTConstantPoolResolver::maybe_resolve_fmi_ref so we shouldn't come to here. + // + // If we come here it's because the AOT assembly phase has executed an invokehandle + // that uses an excluded type like jdk.jfr.Event. This should not happen because the + // AOT assembly phase should execute only a very limited set of Java code. + ResourceMark rm; + fatal("AOT assembly phase must not resolve any invokehandles whose signatures include an excluded type"); + } } if (method_entry->method()->is_method_handle_intrinsic() && !CDSConfig::is_dumping_method_handles()) { rejection_reason = "(not dumping intrinsic method handles)"; @@ -587,7 +604,6 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv } } - int cp_index = method_entry->constant_pool_index(); assert(src_cp->tag_at(cp_index).is_method() || src_cp->tag_at(cp_index).is_interface_method(), "sanity"); if (!AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java index 9a9524eb2f1a..728428afef2e 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.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 @@ -32,6 +32,7 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar * TestApp * TestApp$Foo + * TestApp$Foo$A * TestApp$Foo$Bar * TestApp$Foo$ShouldBeExcluded * TestApp$Foo$ShouldBeExcludedChild @@ -43,6 +44,8 @@ */ import java.io.File; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Array; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; @@ -165,6 +168,8 @@ static int hotSpot() { long start = System.currentTimeMillis(); while (System.currentTimeMillis() - start < 1000) { lambdaHotSpot(); + lambdaHotSpot2(); + invokeHandleHotSpot(); s.hotSpot2(); b.hotSpot3(); Taz.hotSpot4(); @@ -207,12 +212,52 @@ static void lambdaHotSpot() { } } + interface A { + Object get(); + } + + // Lambdas that refer to excluded classes should not be AOT-resolved + static void lambdaHotSpot2() { + long start = System.currentTimeMillis(); + A a = ShouldBeExcluded::new; + while (System.currentTimeMillis() - start < 20) { + Object obj = (ShouldBeExcluded)a.get(); + } + } + + static void invokeHandleHotSpot() { + try { + invokeHandleHotSpotImpl(); + } catch (Throwable t) { + throw new RuntimeException("Unexpected", t); + } + } + + static void invokeHandleHotSpotImpl() throws Throwable { + MethodHandle constructorHandle = + MethodHandles.lookup().unreflectConstructor(ShouldBeExcluded.class.getConstructor()); + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < 20) { + // The JVM rewrites this to: + // invokehandle + // + // The AOT cache must not contain a java.lang.invoke.MethodType that refers to the + // ShouldBeExcluded class. + ShouldBeExcluded o = (ShouldBeExcluded)constructorHandle.invoke(); + if (o.getClass() != ShouldBeExcluded.class) { + throw new RuntimeException("Unexpected object: " + o); + } + } + } + static void doit(Runnable r) { r.run(); } // All subclasses of jdk.jfr.Event are excluded from the CDS archive. static class ShouldBeExcluded extends jdk.jfr.Event { + public ShouldBeExcluded() {} + int f = (int)(System.currentTimeMillis()) + 123; int m() { return f + 456; @@ -275,4 +320,3 @@ static void hotSpot4() { } } } - From 8378d289104a60faa0c9ee048dc299fa7ea2c06e Mon Sep 17 00:00:00 2001 From: David Holmes Date: Tue, 17 Mar 2026 20:43:22 +0000 Subject: [PATCH 79/97] 8379481: Recent JNI methods are missing DTrace probes Reviewed-by: kbarrett, iklam, jwaters --- src/hotspot/os/posix/dtrace/hotspot_jni.d | 4 +++- src/hotspot/share/prims/jni.cpp | 15 ++++++++++----- src/hotspot/share/utilities/dtrace_disabled.hpp | 16 +++++++++------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/hotspot/os/posix/dtrace/hotspot_jni.d b/src/hotspot/os/posix/dtrace/hotspot_jni.d index c5676921b374..1937769dcb26 100644 --- a/src/hotspot/os/posix/dtrace/hotspot_jni.d +++ b/src/hotspot/os/posix/dtrace/hotspot_jni.d @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -366,6 +366,8 @@ provider hotspot_jni { probe IsInstanceOf__return(uintptr_t); probe IsSameObject__entry(void*, void*, void*); probe IsSameObject__return(uintptr_t); + probe IsVirtualThread__entry(void*, void*); + probe IsVirtualThread__return(uintptr_t); probe MonitorEnter__entry(void*, void*); probe MonitorEnter__return(uint32_t); probe MonitorExit__entry(void*, void*); diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp index 2297ce9b7907..85207fddf29e 100644 --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2024 Red Hat, Inc. * Copyright (c) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -3129,16 +3129,21 @@ JNI_END JNI_ENTRY(jobject, jni_GetModule(JNIEnv* env, jclass clazz)) - return Modules::get_module(clazz, THREAD); + HOTSPOT_JNI_GETMODULE_ENTRY(env, clazz); + jobject ret = Modules::get_module(clazz, THREAD); + HOTSPOT_JNI_GETMODULE_RETURN(ret); + return ret; JNI_END JNI_ENTRY(jboolean, jni_IsVirtualThread(JNIEnv* env, jobject obj)) + HOTSPOT_JNI_ISVIRTUALTHREAD_ENTRY(env, obj); + jboolean ret = JNI_FALSE; oop thread_obj = JNIHandles::resolve_external_guard(obj); if (thread_obj != nullptr && thread_obj->is_a(vmClasses::BaseVirtualThread_klass())) { - return JNI_TRUE; - } else { - return JNI_FALSE; + ret = JNI_TRUE; } + HOTSPOT_JNI_ISVIRTUALTHREAD_RETURN(ret); + return ret; JNI_END diff --git a/src/hotspot/share/utilities/dtrace_disabled.hpp b/src/hotspot/share/utilities/dtrace_disabled.hpp index 6cbd79326ac1..99d7cbb2f75e 100644 --- a/src/hotspot/share/utilities/dtrace_disabled.hpp +++ b/src/hotspot/share/utilities/dtrace_disabled.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, 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 @@ -683,6 +683,10 @@ #define HOTSPOT_JNI_GETMETHODID_ENTRY_ENABLED() 0 #define HOTSPOT_JNI_GETMETHODID_RETURN(arg0) #define HOTSPOT_JNI_GETMETHODID_RETURN_ENABLED() 0 +#define HOTSPOT_JNI_GETMODULE_ENTRY(arg0, arg1) +#define HOTSPOT_JNI_GETMODULE_ENTRY_ENABLED() 0 +#define HOTSPOT_JNI_GETMODULE_RETURN(arg0) +#define HOTSPOT_JNI_GETMODULE_RETURN_ENABLED() 0 #define HOTSPOT_JNI_GETOBJECTARRAYELEMENT_ENTRY(arg0, arg1, arg2) #define HOTSPOT_JNI_GETOBJECTARRAYELEMENT_ENTRY_ENABLED() 0 #define HOTSPOT_JNI_GETOBJECTARRAYELEMENT_RETURN(arg0) @@ -811,6 +815,10 @@ #define HOTSPOT_JNI_ISSAMEOBJECT_ENTRY_ENABLED() 0 #define HOTSPOT_JNI_ISSAMEOBJECT_RETURN(arg0) #define HOTSPOT_JNI_ISSAMEOBJECT_RETURN_ENABLED() 0 +#define HOTSPOT_JNI_ISVIRTUALTHREAD_ENTRY(arg0, arg1) +#define HOTSPOT_JNI_ISVIRTUALTHREAD_ENTRY_ENABLED() 0 +#define HOTSPOT_JNI_ISVIRTUALTHREAD_RETURN(arg0) +#define HOTSPOT_JNI_ISVIRTUALTHREAD_RETURN_ENABLED() 0 #define HOTSPOT_JNI_MONITORENTER_ENTRY(arg0, arg1) #define HOTSPOT_JNI_MONITORENTER_ENTRY_ENABLED() 0 #define HOTSPOT_JNI_MONITORENTER_RETURN(arg0) @@ -1080,12 +1088,6 @@ #define HOTSPOT_JNI_UNREGISTERNATIVES_RETURN(arg0) #define HOTSPOT_JNI_UNREGISTERNATIVES_RETURN_ENABLED() 0 -/* Modules */ -#define HOTSPOT_JNI_GETMODULE_ENTRY(arg0, arg1) -#define HOTSPOT_JNI_GETMODULE_ENTRY_ENABLED() 0 -#define HOTSPOT_JNI_GETMODULE_RETURN(arg0) -#define HOTSPOT_JNI_GETMODULE_RETURN_ENABLED() - #else /* !defined(DTRACE_ENABLED) */ #error This file should only be included when dtrace is not enabled #endif /* !defined(DTRACE_ENABLED) */ From 50f81c5d7c72c3085b076f897bd6608a4c016013 Mon Sep 17 00:00:00 2001 From: David Beaumont Date: Tue, 17 Mar 2026 21:43:58 +0000 Subject: [PATCH 80/97] 8379798: Refactor remaining tests in javax/xml/jaxp/functional to JUnit Reviewed-by: joehw --- .../dom/ptests/AbstractCharacterDataTest.java | 87 +++++----- .../org/w3c/dom/ptests/AttrTest.java | 27 ++-- .../org/w3c/dom/ptests/CommentTest.java | 17 +- .../org/w3c/dom/ptests/DocumentTest.java | 71 ++++---- .../org/w3c/dom/ptests/DocumentTypeTest.java | 39 +++-- .../w3c/dom/ptests/DomImplementationTest.java | 41 ++--- .../org/w3c/dom/ptests/ElementTest.java | 102 ++++++------ .../org/w3c/dom/ptests/EntityChildTest.java | 23 ++- .../org/w3c/dom/ptests/NamedNodeMapTest.java | 28 ++-- .../org/w3c/dom/ptests/NodeListTest.java | 23 ++- .../org/w3c/dom/ptests/NodeTest.java | 80 ++++++---- .../org/w3c/dom/ptests/NotationTest.java | 23 ++- .../functional/org/w3c/dom/ptests/PITest.java | 18 +-- .../org/w3c/dom/ptests/TextTest.java | 24 ++- .../org/w3c/dom/ptests/TypeInfoTest.java | 130 +++++++-------- .../org/xml/sax/ptests/AttrImplTest.java | 59 +++---- .../org/xml/sax/ptests/AttributesNSTest.java | 25 +-- .../org/xml/sax/ptests/AttributesTest.java | 25 +-- .../xml/sax/ptests/ContentHandlerTest.java | 47 +++--- .../xml/sax/ptests/DefaultHandlerTest.java | 52 +++--- .../org/xml/sax/ptests/EHFatalTest.java | 55 +++---- .../org/xml/sax/ptests/NSSupportTest.java | 29 ++-- .../org/xml/sax/ptests/NSTableTest.java | 12 +- .../org/xml/sax/ptests/ParserAdapterTest.java | 42 +++-- .../org/xml/sax/ptests/ResolverTest.java | 44 +++-- .../org/xml/sax/ptests/XMLFilterCBTest.java | 54 +++---- .../org/xml/sax/ptests/XMLFilterTest.java | 62 +++---- .../xml/sax/ptests/XMLReaderAdapterTest.java | 29 ++-- .../xml/sax/ptests/XMLReaderFactoryTest.java | 37 +++-- .../xml/sax/ptests/XMLReaderNSTableTest.java | 54 ++++--- .../org/xml/sax/ptests/XMLReaderTest.java | 56 +++---- .../jaxp/functional/test/astro/AstroTest.java | 44 ++--- .../functional/test/astro/DocumentLSTest.java | 52 +++--- .../test/astro/NamespaceContextTest.java | 32 ++-- .../functional/test/astro/SAX201Test.java | 10 +- .../test/astro/SchemaValidationTest.java | 27 ++-- .../functional/test/astro/XPathAPITest.java | 94 ++++++----- .../test/auctionportal/AuctionController.java | 151 ++++++++++-------- .../auctionportal/AuctionItemRepository.java | 127 ++++++++------- .../{movies.xml.data => movies-utf16.xml} | 0 .../functional/test/gaptest/Bug4511326.java | 9 +- .../functional/test/gaptest/Bug4512806.java | 23 ++- .../functional/test/gaptest/Bug4515047.java | 8 +- .../functional/test/gaptest/Bug4515660.java | 52 +++--- .../functional/test/gaptest/Bug4848653.java | 26 +-- .../functional/test/gaptest/Bug4858685.java | 38 ++--- .../libs/org/w3c/dom/ptests/DOMTestUtil.java | 15 +- .../libs/org/xml/sax/ptests/SAXTestConst.java | 18 ++- .../jaxp/libs/test/astro/AstroConstants.java | 13 +- .../libs/test/astro/DOMFilterFactoryImpl.java | 5 +- .../astro/DOML3InputSourceFactoryImpl.java | 34 ++-- .../test/astro/InputSourceFactoryImpl.java | 7 +- .../libs/test/astro/SAXFilterFactoryImpl.java | 28 ++-- .../test/astro/StreamFilterFactoryImpl.java | 5 +- .../astro/TemplatesFilterFactoryImpl.java | 8 +- .../test/auctionportal/HiBidConstants.java | 14 +- .../jaxp/libs/test/gaptest/GapTestConst.java | 15 +- 57 files changed, 1140 insertions(+), 1130 deletions(-) rename test/jaxp/javax/xml/jaxp/functional/test/auctionportal/content/{movies.xml.data => movies-utf16.xml} (100%) diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AbstractCharacterDataTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AbstractCharacterDataTest.java index c1b3381cb98a..b46829de8e18 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AbstractCharacterDataTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AbstractCharacterDataTest.java @@ -22,27 +22,26 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; -import static org.w3c.dom.DOMException.INDEX_SIZE_ERR; -import static org.w3c.dom.ptests.DOMTestUtil.DOMEXCEPTION_EXPECTED; - -import java.io.IOException; - -import javax.xml.parsers.ParserConfigurationException; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.CharacterData; import org.w3c.dom.DOMException; import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import static org.w3c.dom.DOMException.INDEX_SIZE_ERR; +import static org.w3c.dom.ptests.DOMTestUtil.DOMEXCEPTION_EXPECTED; + /* * @summary common test for the CharacterData Interface */ public abstract class AbstractCharacterDataTest { - @DataProvider(name = "data-for-length") - public Object[][] getDataForTestLength() { + public static Object[][] getDataForTestLength() { return new Object[][] { { "", 0 }, { "test", 4 } }; @@ -52,11 +51,11 @@ public Object[][] getDataForTestLength() { * Verify getLength method works as the spec, for an empty string, should * return zero */ - @Test(dataProvider = "data-for-length") + @ParameterizedTest + @MethodSource("getDataForTestLength") public void testGetLength(String text, int length) throws Exception { CharacterData cd = createCharacterData(text); - assertEquals(cd.getLength(), length); - + assertEquals(length, cd.getLength()); } /* @@ -66,12 +65,11 @@ public void testGetLength(String text, int length) throws Exception { public void testAppendData() throws Exception { CharacterData cd = createCharacterData("DOM"); cd.appendData("2"); - assertEquals(cd.getData(), "DOM2"); + assertEquals("DOM2", cd.getData()); } - @DataProvider(name = "data-for-delete") - public Object[][] getDataForTestDelete() { + public static Object[][] getDataForTestDelete() { return new Object[][] { { "DOM", 2, 1, "DO" }, { "DOM", 0, 2, "M" }, @@ -81,15 +79,15 @@ public Object[][] getDataForTestDelete() { /* * Verify deleteData method works as the spec. */ - @Test(dataProvider = "data-for-delete") + @ParameterizedTest + @MethodSource("getDataForTestDelete") public void testDeleteData(String text, int offset, int count, String result) throws Exception { CharacterData cd = createCharacterData(text); cd.deleteData(offset, count); assertEquals(cd.getData(), result); } - @DataProvider(name = "data-for-replace") - public Object[][] getDataForTestReplace() { + public static Object[][] getDataForTestReplace() { return new Object[][] { { "DOM", 0, 3, "SAX", "SAX" }, { "DOM", 1, 1, "AA", "DAAM" }, @@ -100,15 +98,15 @@ public Object[][] getDataForTestReplace() { /* * Verify replaceData method works as the spec. */ - @Test(dataProvider = "data-for-replace") + @ParameterizedTest + @MethodSource("getDataForTestReplace") public void testReplaceData(String text, int offset, int count, String arg, String result) throws Exception { CharacterData cd = createCharacterData(text); cd.replaceData(offset, count, arg); assertEquals(cd.getData(), result); } - @DataProvider(name = "data-for-replace-neg") - public Object[][] getDataForTestReplaceNeg() { + public static Object[][] getDataForTestReplaceNeg() { return new Object[][] { { "DOM", -1, 3, "SAX" }, //offset if neg { "DOM", 0, -1, "SAX" }, //count is neg @@ -119,19 +117,19 @@ public Object[][] getDataForTestReplaceNeg() { * Test for replaceData method: verifies that DOMException with * INDEX_SIZE_ERR is thrown if offset or count is out of the bound. */ - @Test(dataProvider = "data-for-replace-neg") + @ParameterizedTest + @MethodSource("getDataForTestReplaceNeg") public void testReplaceDataNeg(String text, int offset, int count, String arg) throws Exception { CharacterData cd = createCharacterData(text); try { cd.replaceData(offset, count, arg); fail(DOMEXCEPTION_EXPECTED); } catch (DOMException e) { - assertEquals(e.code, INDEX_SIZE_ERR); + assertEquals(INDEX_SIZE_ERR, e.code); } } - @DataProvider(name = "data-for-insert") - public Object[][] getDataForTestInsert() { + public static Object[][] getDataForTestInsert() { return new Object[][] { { "DOM", 0, "SAX", "SAXDOM" }, { "DOM", 3, "SAX", "DOMSAX" } }; @@ -140,15 +138,15 @@ public Object[][] getDataForTestInsert() { /* * Verify insertData method works as the spec. */ - @Test(dataProvider = "data-for-insert") + @ParameterizedTest + @MethodSource("getDataForTestInsert") public void testInsertData(String text, int offset, String arg, String result) throws Exception { CharacterData cd = createCharacterData(text); cd.insertData(offset, arg); assertEquals(cd.getData(), result); } - @DataProvider(name = "data-for-insert-neg") - public Object[][] getDataForTestInsertNeg() { + public static Object[][] getDataForTestInsertNeg() { return new Object[][] { { "DOM", -1 }, //offset is neg { "DOM", 4 } };//offset is greater than length @@ -158,14 +156,15 @@ public Object[][] getDataForTestInsertNeg() { * Test for insertData method: verifies that DOMException with * INDEX_SIZE_ERR is thrown if offset is out of the bound. */ - @Test(dataProvider = "data-for-insert-neg") + @ParameterizedTest + @MethodSource("getDataForTestInsertNeg") public void testInsertDataNeg(String text, int offset) throws Exception { CharacterData cd = createCharacterData(text); try { cd.insertData(offset, "TEST"); fail(DOMEXCEPTION_EXPECTED); } catch (DOMException e) { - assertEquals(e.code, INDEX_SIZE_ERR); + assertEquals(INDEX_SIZE_ERR, e.code); } } @@ -176,11 +175,10 @@ public void testInsertDataNeg(String text, int offset) throws Exception { public void testSetData() throws Exception { CharacterData cd = createCharacterData("DOM"); cd.setData("SAX"); - assertEquals(cd.getData(), "SAX"); + assertEquals("SAX", cd.getData()); } - @DataProvider(name = "data-for-substring") - public Object[][] getDataForTestSubstring() { + public static Object[][] getDataForTestSubstring() { return new Object[][] { { "DOM Level 2", 0, 3, "DOM" }, { "DOM", 0, 3, "DOM" }, @@ -190,15 +188,14 @@ public Object[][] getDataForTestSubstring() { /* * Verify substringData method works as the spec. */ - @Test(dataProvider = "data-for-substring") + @ParameterizedTest + @MethodSource("getDataForTestSubstring") public void testSubstringData(String text, int offset, int count, String result) throws Exception { CharacterData cd = createCharacterData(text); - String retStr = cd.substringData(offset, count); - assertEquals(retStr, result); + assertEquals(result, cd.substringData(offset, count)); } - @DataProvider(name = "data-for-substring-neg") - public Object[][] getDataForTestSubstringNeg() { + public static Object[][] getDataForTestSubstringNeg() { return new Object[][] { { "DOM Level 2", -1, 3 }, //offset is neg { "DOM", 0, -1 }, //count is neg @@ -209,14 +206,15 @@ public Object[][] getDataForTestSubstringNeg() { * Test for substringData method: verifies that DOMException with * INDEX_SIZE_ERR is thrown if offset or count is out of the bound. */ - @Test(dataProvider = "data-for-substring-neg") + @ParameterizedTest + @MethodSource("getDataForTestSubstringNeg") public void testSubstringDataNeg(String text, int offset, int count) throws Exception { CharacterData cd = createCharacterData(text); try { cd.substringData(offset, count); fail(DOMEXCEPTION_EXPECTED); } catch (DOMException e) { - assertEquals(e.code, INDEX_SIZE_ERR); + assertEquals(INDEX_SIZE_ERR, e.code); } } @@ -225,5 +223,4 @@ public void testSubstringDataNeg(String text, int offset, int count) throws Exce * Return a concrete CharacterData instance. */ abstract protected CharacterData createCharacterData(String text) throws IOException, SAXException, ParserConfigurationException; - } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AttrTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AttrTest.java index bd7dfb10eab2..08ba9b810555 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AttrTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/AttrTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,23 +23,23 @@ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.w3c.dom.ptests.DOMTestUtil.createDOM; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.w3c.dom.ptests.DOMTestUtil.createDOM; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.AttrTest + * @run junit/othervm org.w3c.dom.ptests.AttrTest * @summary Test for the Attr Interface */ public class AttrTest { @@ -51,12 +51,12 @@ public void testGetName() throws Exception { Document document = createDOM("Attr01.xml"); //test a new created Attr Attr attr = document.createAttribute("newAttribute"); - assertEquals(attr.getName(), "newAttribute"); + assertEquals("newAttribute", attr.getName()); //test a Attr loaded from xml file Element elemNode = (Element) document.getElementsByTagName("book").item(1); Attr attr2 = (Attr) elemNode.getAttributes().item(0); - assertEquals(attr2.getName(), "category1"); + assertEquals("category1", attr2.getName()); } /* @@ -71,7 +71,7 @@ public void testGetOwnerElement() throws Exception { NamedNodeMap nnMap = elemNode.getAttributes(); for (int i = 0; i < nnMap.getLength(); i++) { Attr attr = (Attr) nnMap.item(i); - assertEquals(attr.getOwnerElement().getNodeName(), "book"); + assertEquals("book", attr.getOwnerElement().getNodeName()); } //test an Attr without owner node @@ -143,8 +143,7 @@ public void testSetValue() throws Exception { Document document = createDOM("Attr01.xml"); Attr attr = document.createAttribute("newAttribute"); attr.setValue("newVal"); - assertEquals(attr.getValue(), "newVal"); - + assertEquals("newVal", attr.getValue()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/CommentTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/CommentTest.java index c3cff46c714c..2f0664a81397 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/CommentTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/CommentTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,25 +22,24 @@ */ package org.w3c.dom.ptests; -import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; - -import java.io.IOException; - -import javax.xml.parsers.ParserConfigurationException; - import org.w3c.dom.CharacterData; import org.w3c.dom.Document; import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; + +import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; + /* * @test * @library /javax/xml/jaxp/libs /javax/xml/jaxp/functional - * @run testng/othervm org.w3c.dom.ptests.CommentTest + * @run junit/othervm org.w3c.dom.ptests.CommentTest * @summary Test for Comment implementation returned by Document.createComment(String) */ public class CommentTest extends AbstractCharacterDataTest { @Override - protected CharacterData createCharacterData(String text) throws IOException, SAXException, ParserConfigurationException { + protected CharacterData createCharacterData(String text) throws ParserConfigurationException { Document document = createNewDocument(); return document.createComment(text); } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTest.java index 3ebf28afaa76..b7b5c2669ea6 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,34 +22,35 @@ */ package org.w3c.dom.ptests; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Attr; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + import static javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI; import static javax.xml.XMLConstants.XML_NS_URI; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +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.w3c.dom.DOMException.NAMESPACE_ERR; import static org.w3c.dom.ptests.DOMTestUtil.DOMEXCEPTION_EXPECTED; import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.Attr; -import org.w3c.dom.DOMException; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; - /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.DocumentTest + * @run junit/othervm org.w3c.dom.ptests.DocumentTest * @summary Test createAttributeNS, getElementsByTagNameNS and createElementNS method of Document */ public class DocumentTest { - @DataProvider(name = "invalid-nsuri") - public Object[][] getInvalidNamespaceURI() { + public static Object[][] getInvalidNamespaceURI() { return new Object[][] { { " ", "xml:novel" }, //blank { "hello", "xml:novel" }, //unqualified @@ -61,14 +62,14 @@ public Object[][] getInvalidNamespaceURI() { * Test for createAttributeNS method: verifies that DOMException is thrown * if reserved prefixes are used with an arbitrary namespace name. */ - @Test(dataProvider = "invalid-nsuri", expectedExceptions = DOMException.class) + @ParameterizedTest + @MethodSource("getInvalidNamespaceURI") public void testCreateAttributeNSNeg(String namespaceURI, String name) throws Exception { Document document = createDOMWithNS("DocumentTest01.xml"); - document.createAttributeNS(namespaceURI, name); + assertThrows(DOMException.class, () -> document.createAttributeNS(namespaceURI, name)); } - @DataProvider(name = "valid-nsuri") - public Object[][] getValidNamespaceURI() { + public static Object[][] getValidNamespaceURI() { return new Object[][] { { XML_NS_URI, "xml:novel" }, { XMLNS_ATTRIBUTE_NS_URI, "xmlns:novel" }, @@ -79,16 +80,16 @@ public Object[][] getValidNamespaceURI() { /* * Verify the Attr from createAttributeNS. */ - @Test(dataProvider = "valid-nsuri") + @ParameterizedTest + @MethodSource("getValidNamespaceURI") public void testCreateAttributeNS(String namespaceURI, String name) throws Exception { Document document = createDOMWithNS("DocumentTest01.xml"); Attr attr = document.createAttributeNS(namespaceURI, name); - assertEquals(attr.getNamespaceURI(), namespaceURI); - assertEquals(attr.getName(), name); + assertEquals(namespaceURI, attr.getNamespaceURI()); + assertEquals(name, attr.getName()); } - @DataProvider(name = "elementName") - public Object[][] getElementName() { + public static Object[][] getElementName() { return new Object[][] { { "author", 1 }, { "b:author", 0 } }; @@ -97,25 +98,27 @@ public Object[][] getElementName() { /* * Verify the NodeList from getElementsByTagNameNS. */ - @Test(dataProvider = "elementName") - public void testGetElementsByTagNameNS(String localName, int number) throws Exception { + @ParameterizedTest + @MethodSource("getElementName") + public void testGetElementsByTagNameNS(String localName, int expectedLength) throws Exception { Document document = createDOMWithNS("DocumentTest01.xml"); NodeList nodeList = document.getElementsByTagNameNS("urn:BooksAreUs.org:BookInfo", localName); - assertEquals(nodeList.getLength(), number); + assertEquals(expectedLength, nodeList.getLength()); } /* * Test for createElementNS method: verifies that DOMException is thrown * if reserved prefixes are used with an arbitrary namespace name. */ - @Test(dataProvider = "invalid-nsuri") + @ParameterizedTest + @MethodSource("getInvalidNamespaceURI") public void testCreateElementNSNeg(String namespaceURI, String name) throws Exception { Document document = createDOMWithNS("DocumentTest01.xml"); try { document.createElementNS(namespaceURI, name); fail(DOMEXCEPTION_EXPECTED); } catch (DOMException e) { - assertEquals(e.code, NAMESPACE_ERR); + assertEquals(NAMESPACE_ERR, e.code); } } @@ -129,9 +132,9 @@ public void testCreateElementNS() throws Exception { final String localName = "novel"; Document document = createDOMWithNS("DocumentTest01.xml"); Element element = document.createElementNS(nsURI, name); - assertEquals(element.getNamespaceURI(), nsURI); - assertEquals(element.getNodeName(), name); - assertEquals(element.getLocalName(), localName); + assertEquals(nsURI, element.getNamespaceURI()); + assertEquals(name, element.getNodeName()); + assertEquals(localName, element.getLocalName()); } /* @@ -166,9 +169,9 @@ public void testAddNewElement() throws Exception { /* * Test createElement with unqualified xml name. */ - @Test(expectedExceptions = DOMException.class) + @Test public void testCreateElementNeg() throws Exception { Document doc = createNewDocument(); - doc.createElement("!nc$%^*(!"); + assertThrows(DOMException.class, () -> doc.createElement("!nc$%^*(!")); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTypeTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTypeTest.java index b5265b825b03..448e358c97ab 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTypeTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DocumentTypeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,18 +23,18 @@ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.w3c.dom.ptests.DOMTestUtil.createDOM; - -import org.testng.Assert; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.DocumentType; import org.w3c.dom.NamedNodeMap; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.w3c.dom.ptests.DOMTestUtil.createDOM; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.DocumentTypeTest + * @run junit/othervm org.w3c.dom.ptests.DocumentTypeTest * @summary Test DocumentType */ public class DocumentTypeTest { @@ -48,10 +48,10 @@ public void testGetEntities() throws Exception { NamedNodeMap namedNodeMap = documentType.getEntities(); // should return both external and internal. Parameter entities are not // contained. Duplicates are discarded. - assertEquals(namedNodeMap.getLength(), 3); - assertEquals(namedNodeMap.item(0).getNodeName(), "author"); - assertEquals(namedNodeMap.item(1).getNodeName(), "test"); - assertEquals(namedNodeMap.item(2).getNodeName(), "writer"); + assertEquals(3, namedNodeMap.getLength()); + assertEquals("author", namedNodeMap.item(0).getNodeName()); + assertEquals("test", namedNodeMap.item(1).getNodeName()); + assertEquals("writer", namedNodeMap.item(2).getNodeName()); } /* @@ -61,12 +61,11 @@ public void testGetEntities() throws Exception { public void testGetNotations() throws Exception { DocumentType documentType = createDOM("DocumentType03.xml").getDoctype(); NamedNodeMap nm = documentType.getNotations(); - assertEquals(nm.getLength(), 2); // should return 2 because the notation - // name is repeated and - // it considers only the first - // occurence - assertEquals(nm.item(0).getNodeName(), "gs"); - assertEquals(nm.item(1).getNodeName(), "name"); + // should return 2 because the notation name is repeated, + // and it considers only the first occurrence + assertEquals(2, nm.getLength()); + assertEquals("gs", nm.item(0).getNodeName()); + assertEquals("name", nm.item(1).getNodeName()); } /* @@ -75,7 +74,7 @@ public void testGetNotations() throws Exception { @Test public void testGetName() throws Exception { DocumentType documentType = createDOM("DocumentType03.xml").getDoctype(); - assertEquals(documentType.getName(), "note"); + assertEquals("note", documentType.getName()); } /* @@ -84,8 +83,8 @@ public void testGetName() throws Exception { @Test public void testGetSystemId() throws Exception { DocumentType documentType = createDOM("DocumentType05.xml").getDoctype(); - assertEquals(documentType.getSystemId(), "DocumentBuilderImpl02.dtd"); - Assert.assertNull(documentType.getPublicId()); + assertEquals("DocumentBuilderImpl02.dtd", documentType.getSystemId()); + assertNull(documentType.getPublicId()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DomImplementationTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DomImplementationTest.java index 8e8191aa2a3f..251279ced4f9 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DomImplementationTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/DomImplementationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,21 +22,22 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; - -import javax.xml.parsers.ParserConfigurationException; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.DocumentType; +import javax.xml.parsers.ParserConfigurationException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.DomImplementationTest + * @run junit/othervm org.w3c.dom.ptests.DomImplementationTest * @summary Test DomImplementation API */ public class DomImplementationTest { @@ -50,8 +51,8 @@ public void testCreateDocument() throws ParserConfigurationException { final String name = "document:localName"; DOMImplementation domImpl = getDOMImplementation(); Document document = domImpl.createDocument(nsURI, name, null); - assertEquals(document.getDocumentElement().getNamespaceURI(), nsURI); - assertEquals(document.getDocumentElement().getNodeName(), name); + assertEquals(nsURI, document.getDocumentElement().getNamespaceURI()); + assertEquals(name, document.getDocumentElement().getNodeName()); } /* @@ -85,8 +86,7 @@ public void testCreateDocumentType02() throws ParserConfigurationException { verifyDocumentType(document.getDoctype(), name, publicId, systemId); } - @DataProvider(name = "feature-supported") - public Object[][] getFeatureSupportedList() throws ParserConfigurationException { + public static Object[][] getFeatureSupportedList() throws ParserConfigurationException { DOMImplementation impl = getDOMImplementation(); return new Object[][] { { impl, "XML", "2.0", true }, @@ -109,20 +109,21 @@ public Object[][] getFeatureSupportedList() throws ParserConfigurationException /* * Verify DOMImplementation for feature supporting. */ - @Test(dataProvider = "feature-supported") + @ParameterizedTest + @MethodSource("getFeatureSupportedList") public void testHasFeature(DOMImplementation impl, String feature, String version, boolean isSupported) { - assertEquals(impl.hasFeature(feature,version), isSupported); + assertEquals(isSupported, impl.hasFeature(feature, version)); } - private DOMImplementation getDOMImplementation() throws ParserConfigurationException { + private static DOMImplementation getDOMImplementation() throws ParserConfigurationException { return createNewDocument().getImplementation(); } - private void verifyDocumentType(DocumentType documentType, String name, String publicId, String systemId) { - assertEquals(documentType.getPublicId(), publicId); - assertEquals(documentType.getSystemId(), systemId); - assertEquals(documentType.getName(), name); + private static void verifyDocumentType(DocumentType documentType, String name, String publicId, String systemId) { + assertEquals(publicId, documentType.getPublicId()); + assertEquals(systemId, documentType.getSystemId()); + assertEquals(name, documentType.getName()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/ElementTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/ElementTest.java index b538f7d17380..b861e086b3e1 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/ElementTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/ElementTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,23 +22,9 @@ */ package org.w3c.dom.ptests; -import static javax.xml.XMLConstants.XML_NS_URI; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; -import static org.w3c.dom.DOMException.INUSE_ATTRIBUTE_ERR; -import static org.w3c.dom.ptests.DOMTestUtil.DOMEXCEPTION_EXPECTED; -import static org.w3c.dom.ptests.DOMTestUtil.createDOM; -import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; -import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; - -import java.io.StringReader; - -import javax.xml.parsers.DocumentBuilderFactory; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.Attr; import org.w3c.dom.DOMException; import org.w3c.dom.Document; @@ -47,10 +33,25 @@ import org.w3c.dom.NodeList; import org.xml.sax.InputSource; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.StringReader; + +import static javax.xml.XMLConstants.XML_NS_URI; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.w3c.dom.DOMException.INUSE_ATTRIBUTE_ERR; +import static org.w3c.dom.ptests.DOMTestUtil.DOMEXCEPTION_EXPECTED; +import static org.w3c.dom.ptests.DOMTestUtil.createDOM; +import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; +import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.ElementTest + * @run junit/othervm org.w3c.dom.ptests.ElementTest * @summary Test for the methods of Element Interface */ public class ElementTest { @@ -59,7 +60,7 @@ public void testGetAttributeNS() throws Exception { Document document = createDOMWithNS("ElementSample01.xml"); Element elemNode = (Element) document.getElementsByTagName("book").item(0); String s = elemNode.getAttributeNS("urn:BooksAreUs.org:BookInfo", "category"); - assertEquals(s, "research"); + assertEquals("research", s); } @Test @@ -67,7 +68,7 @@ public void testGetAttributeNodeNS() throws Exception { Document document = createDOMWithNS("ElementSample01.xml"); Element elemNode = (Element) document.getElementsByTagName("book").item(0); Attr attr = elemNode.getAttributeNodeNS("urn:BooksAreUs.org:BookInfo", "category"); - assertEquals(attr.getValue(), "research"); + assertEquals("research", attr.getValue()); } @@ -80,11 +81,11 @@ public void testRemoveAttributeNode() throws Exception { Document document = createDOMWithNS("ElementSample01.xml"); Element elemNode = (Element) document.getElementsByTagName("book").item(1); Attr attr = elemNode.getAttributeNode("category1"); - assertEquals(attr.getValue(), "research"); + assertEquals("research", attr.getValue()); - assertEquals(elemNode.getTagName(), "book"); + assertEquals("book", elemNode.getTagName()); elemNode.removeAttributeNode(attr); - assertEquals(elemNode.getAttribute("category1"), ""); + assertEquals("", elemNode.getAttribute("category1")); } /* @@ -112,8 +113,8 @@ public void testGetChild() throws Exception { elemNode.normalize(); Node firstChild = elemNode.getFirstChild(); Node lastChild = elemNode.getLastChild(); - assertEquals(firstChild.getNodeValue(), "fjfjf"); - assertEquals(lastChild.getNodeValue(), "fjfjf"); + assertEquals("fjfjf", firstChild.getNodeValue()); + assertEquals("fjfjf", lastChild.getNodeValue()); } /* @@ -129,32 +130,32 @@ public void testSetAttributeNode() throws Exception { myAttr.setValue(attrValue); assertNull(elemNode.setAttributeNode(myAttr)); - assertEquals(elemNode.getAttribute(attrName), attrValue); + assertEquals(attrValue, elemNode.getAttribute(attrName)); } - @DataProvider(name = "attribute") - public Object[][] getAttributeData() { + public static Object[][] getAttributeData() { return new Object[][] { { "thisisname", "thisisitsvalue" }, { "style", "font-Family" } }; } - @Test(dataProvider = "attribute") + @ParameterizedTest + @MethodSource("getAttributeData") public void testSetAttribute(String name, String value) throws Exception { Document document = createDOM("ElementSample02.xml"); Element elemNode = document.createElement("pricetag2"); elemNode.setAttribute(name, value); - assertEquals(elemNode.getAttribute(name), value); + assertEquals(value, elemNode.getAttribute(name)); } /* * Negative test for setAttribute, null is not a valid name. */ - @Test(expectedExceptions = DOMException.class) + @Test public void testSetAttributeNeg() throws Exception { Document document = createDOM("ElementSample02.xml"); Element elemNode = document.createElement("pricetag2"); - elemNode.setAttribute(null, null); + assertThrows(DOMException.class, () -> elemNode.setAttribute(null, null)); } /* @@ -182,7 +183,7 @@ public void testDuplicateAttributeNode() throws Exception { element3.setAttributeNode(attr); fail(DOMEXCEPTION_EXPECTED); } catch (DOMException doe) { - assertEquals(doe.code, INUSE_ATTRIBUTE_ERR); + assertEquals(INUSE_ATTRIBUTE_ERR, doe.code); } } @@ -201,8 +202,7 @@ public void testNamespaceAware() throws Exception { assertNull(nl.item(0)); } - @DataProvider(name = "nsattribute") - public Object[][] getNSAttributeData() { + public static Object[][] getNSAttributeData() { return new Object[][] { { "h:html", "html", "attrValue" }, { "b:style", "style", "attrValue" } }; @@ -211,14 +211,15 @@ public Object[][] getNSAttributeData() { /* * setAttributeNodeNS and verify it with getAttributeNS. */ - @Test(dataProvider = "nsattribute") + @ParameterizedTest + @MethodSource("getNSAttributeData") public void testSetAttributeNodeNS(String qualifiedName, String localName, String value) throws Exception { Document document = createDOM("ElementSample03.xml"); Element elemNode = document.createElement("pricetag2"); Attr myAttr = document.createAttributeNS(XML_NS_URI, qualifiedName); myAttr.setValue(value); assertNull(elemNode.setAttributeNodeNS(myAttr)); - assertEquals(elemNode.getAttributeNS(XML_NS_URI, localName), value); + assertEquals(value, elemNode.getAttributeNS(XML_NS_URI, localName)); } @Test @@ -233,22 +234,23 @@ public void testHasAttributeNS() throws Exception { @Test public void testToString() throws Exception { final String xml = - "" - + "" - + "" - + " \n" - + " " - + " " - + ""; + """ + \ + \ + \ + + \ + \ + """; Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(xml))); Element root = doc.getDocumentElement(); - assertEquals(root.toString(), "[datacenterlist: null]"); + assertEquals("[datacenterlist: null]", root.toString()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/EntityChildTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/EntityChildTest.java index 65fe0e046ab2..447100f45c99 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/EntityChildTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/EntityChildTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,23 +22,22 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.w3c.dom.ptests.DOMTestUtil.XML_DIR; - -import java.io.File; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import java.io.File; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.w3c.dom.ptests.DOMTestUtil.XML_DIR; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.EntityChildTest + * @run junit/othervm org.w3c.dom.ptests.EntityChildTest * @summary Test DOM Parser: parsing an xml file that contains external entities. */ public class EntityChildTest { @@ -54,7 +53,7 @@ public void test() throws Exception { Element root = document.getDocumentElement(); NodeList n = root.getElementsByTagName("table"); NodeList nl = n.item(0).getChildNodes(); - assertEquals(n.getLength(), 1); - assertEquals(nl.getLength(), 3); + assertEquals(1, n.getLength()); + assertEquals(3, nl.getLength()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NamedNodeMapTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NamedNodeMapTest.java index 86cd3aa7bbbf..5cf6bc2387cf 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NamedNodeMapTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NamedNodeMapTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,21 +22,21 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; -import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.NamedNodeMapTest + * @run junit/othervm org.w3c.dom.ptests.NamedNodeMapTest * @summary Test for the methods of NamedNodeMap Interface */ public class NamedNodeMapTest { @@ -61,9 +61,9 @@ public void testSetNamedItemNS() throws Exception { // setting to a new Value attr.setValue("newValue"); Node replacedAttr = namedNodeMap.setNamedItemNS(attr); // return the replaced attr - assertEquals(replacedAttr.getNodeValue(), "font-family"); + assertEquals("font-family", replacedAttr.getNodeValue()); Node updatedAttr = namedNodeMap.getNamedItemNS(nsURI, "style"); - assertEquals(updatedAttr.getNodeValue(), "newValue"); + assertEquals("newValue", updatedAttr.getNodeValue()); // creating a non existing attribute node @@ -75,7 +75,7 @@ public void testSetNamedItemNS() throws Exception { // checking if the node could be accessed // using the getNamedItemNS method Node newAttr = namedNodeMap.getNamedItemNS(nsURI, "newNode"); - assertEquals(newAttr.getNodeValue(), "newValue"); + assertEquals("newValue", newAttr.getNodeValue()); } /* @@ -89,7 +89,7 @@ public void testGetNamedItemNS() throws Exception { Node n = nodeList.item(7); NamedNodeMap namedNodeMap = n.getAttributes(); Node node = namedNodeMap.getNamedItemNS("urn:BooksAreUs.org:BookInfo", "aaa"); - assertEquals(node.getNodeValue(), "value"); + assertEquals("value", node.getNodeValue()); } @@ -107,14 +107,14 @@ public void testSetNamedItem() throws Exception { NamedNodeMap namedNodeMap = n.getAttributes(); Attr attr = document.createAttribute("name"); Node replacedAttr = namedNodeMap.setNamedItem(attr); - assertEquals(replacedAttr.getNodeValue(), "attributeValue"); + assertEquals("attributeValue", replacedAttr.getNodeValue()); Node updatedAttrNode = namedNodeMap.getNamedItem("name"); - assertEquals(updatedAttrNode.getNodeValue(), ""); + assertEquals("", updatedAttrNode.getNodeValue()); Attr newAttr = document.createAttribute("nonExistingName"); assertNull(namedNodeMap.setNamedItem(newAttr)); Node newAttrNode = namedNodeMap.getNamedItem("nonExistingName"); - assertEquals(newAttrNode.getNodeValue(), ""); + assertEquals("", newAttrNode.getNodeValue()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeListTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeListTest.java index 740c28279f6f..e04b24bd9ae4 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeListTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeListTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,31 +22,31 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.w3c.dom.ptests.DOMTestUtil.createDOM; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.w3c.dom.ptests.DOMTestUtil.createDOM; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.NodeListTest + * @run junit/othervm org.w3c.dom.ptests.NodeListTest * @summary Verifies a bug found in jaxp1.0.1 and 1.1FCS. After going out of * bound, the last element of a NodeList returns null. The bug has been fixed * in jaxp 1.1.1 build. */ public class NodeListTest { - @DataProvider(name = "xml") - public Object[][] getTestData() { + public static Object[][] getTestData() { return new Object[][] { { "nodelist.xml", "document" }, { "Node01.xml", "body" } }; } - @Test(dataProvider = "xml") + @ParameterizedTest + @MethodSource("getTestData") public void lastItemTest(String xmlFileName, String nodeName) throws Exception { Document document = createDOM(xmlFileName); @@ -56,8 +56,7 @@ public void lastItemTest(String xmlFileName, String nodeName) throws Exception { Element elem1 = (Element) nl.item(n - 1); nl.item(n); Element elem3 = (Element) nl.item(n - 1); - assertEquals(elem3, elem1); - + assertEquals(elem1, elem3); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeTest.java index 8df0808f9d4f..828503d0e0b3 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NodeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,19 +22,16 @@ */ package org.w3c.dom.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotEquals; -import static org.testng.Assert.assertTrue; -import static org.w3c.dom.ptests.DOMTestUtil.GOLDEN_DIR; -import static org.w3c.dom.ptests.DOMTestUtil.createDOM; -import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; -import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; - -import java.io.File; -import java.util.PropertyPermission; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentFragment; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; @@ -42,25 +39,29 @@ import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.DOMException; -import org.w3c.dom.Document; -import org.w3c.dom.DocumentFragment; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.w3c.dom.ptests.DOMTestUtil.GOLDEN_DIR; +import static org.w3c.dom.ptests.DOMTestUtil.createDOM; +import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; +import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.NodeTest + * @run junit/othervm org.w3c.dom.ptests.NodeTest * @summary Test Node interface */ public class NodeTest { - @DataProvider(name = "feature-supported") - public Object[][] getFeatureSupportedList() throws Exception { + public static Object[][] getFeatureSupportedList() throws Exception { Document document = createDOMWithNS("Node01.xml"); Node node = document.getElementsByTagName("body").item(0); return new Object[][] { @@ -81,9 +82,10 @@ public Object[][] getFeatureSupportedList() throws Exception { /* * Verify Node for feature supporting. */ - @Test(dataProvider = "feature-supported") + @ParameterizedTest + @MethodSource("getFeatureSupportedList") public void testHasFeature(Node node, String feature, String version, boolean supported) { - assertEquals(node.isSupported(feature, version), supported); + assertEquals(supported, node.isSupported(feature, version)); } /* @@ -98,7 +100,7 @@ public void testNormalize() throws Exception { Node node = document.getElementsByTagName("title").item(0); node.appendChild(document.createTextNode("test")); root.normalize(); - assertEquals(node.getChildNodes().item(0).getNodeValue(), "Typographytest"); + assertEquals("Typographytest", node.getChildNodes().item(0).getNodeValue()); } /* @@ -154,10 +156,10 @@ public void testInsertBefore() throws Exception { Element element = (Element) document.getElementsByTagName("sender").item(0); parentElement.insertBefore(createTestDocumentFragment(document), element); - String outputfile = USER_DIR + "InsertBefore.out"; + String outputfile = "InsertBefore.out"; String goldfile = GOLDEN_DIR + "InsertBeforeGF.out"; outputXml(document, outputfile); - assertTrue(compareWithGold(goldfile, outputfile)); + assertLinesMatch(goldfile, outputfile); } @@ -172,10 +174,10 @@ public void testReplaceChild() throws Exception { Element element = (Element) document.getElementsByTagName("sender").item(0); parentElement.replaceChild(createTestDocumentFragment(document), element); - String outputfile = USER_DIR + "ReplaceChild3.out"; + String outputfile = "ReplaceChild3.out"; String goldfile = GOLDEN_DIR + "ReplaceChild3GF.out"; outputXml(document, outputfile); - assertTrue(compareWithGold(goldfile, outputfile)); + assertLinesMatch(goldfile, outputfile); } /* @@ -183,14 +185,16 @@ public void testReplaceChild() throws Exception { * with a node which was created from a different document than the one * which is trying to use this method. It should throw a DOMException. */ - @Test(expectedExceptions = DOMException.class) + @Test public void testReplaceChildNeg() throws Exception { Document document = createDOM("Node04.xml"); Document doc2 = createNewDocument(); Element parentElement = (Element) document.getElementsByTagName("to").item(0); Element element = (Element) document.getElementsByTagName("sender").item(0); - parentElement.replaceChild(createTestDocumentFragment(doc2), element); + assertThrows( + DOMException.class, () -> + parentElement.replaceChild(createTestDocumentFragment(doc2), element)); } private DocumentFragment createTestDocumentFragment(Document document) { @@ -207,4 +211,10 @@ private void outputXml(Document document, String outputFileName) throws Transfor StreamResult streamResult = new StreamResult(new File(outputFileName)); transformer.transform(domSource, streamResult); } + + private static void assertLinesMatch(String goldenFile, String actual) throws IOException { + Assertions.assertLinesMatch( + Files.readAllLines(Path.of(goldenFile)), + Files.readAllLines(Path.of(actual))); + } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NotationTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NotationTest.java index 301df8e51c33..60630deb4773 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NotationTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/NotationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,23 +22,22 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.w3c.dom.ptests.DOMTestUtil.createDOM; - -import java.io.IOException; - -import javax.xml.parsers.ParserConfigurationException; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Notation; import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.w3c.dom.ptests.DOMTestUtil.createDOM; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.NotationTest + * @run junit/othervm org.w3c.dom.ptests.NotationTest * @summary Test for Notation interface */ public class NotationTest { @@ -47,7 +46,7 @@ public class NotationTest { */ @Test public void testGetSystemId() throws Exception { - assertEquals(findNotation("gs").getSystemId(), "http://who.knows.where/"); + assertEquals("http://who.knows.where/", findNotation("gs").getSystemId()); } /* @@ -55,7 +54,7 @@ public void testGetSystemId() throws Exception { */ @Test public void testGetPublicId() throws Exception { - assertEquals(findNotation("pubname").getPublicId(), "pubId"); + assertEquals("pubId", findNotation("pubname").getPublicId()); } //find notation in Notation01.xml diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/PITest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/PITest.java index ea6a9072002e..0dbff540c976 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/PITest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/PITest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,17 +22,17 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.ProcessingInstruction; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.PITest + * @run junit/othervm org.w3c.dom.ptests.PITest * @summary Test for the methods of Processing Instruction */ public class PITest { @@ -43,11 +43,11 @@ public class PITest { public void test() throws Exception { Document document = createDOMWithNS("PITest01.xml"); ProcessingInstruction pi = document.createProcessingInstruction("PI", "processing"); - assertEquals(pi.getData(), "processing"); - assertEquals(pi.getTarget(), "PI"); + assertEquals("processing", pi.getData()); + assertEquals("PI", pi.getTarget()); pi.setData("newProcessing"); - assertEquals(pi.getData(), "newProcessing"); + assertEquals("newProcessing", pi.getData()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TextTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TextTest.java index e763eda5c35d..507801d1a2d7 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TextTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TextTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,15 +22,7 @@ */ package org.w3c.dom.ptests; -import static org.testng.Assert.assertEquals; -import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; -import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; - -import java.io.IOException; - -import javax.xml.parsers.ParserConfigurationException; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.CharacterData; import org.w3c.dom.Document; import org.w3c.dom.Node; @@ -38,10 +30,17 @@ import org.w3c.dom.Text; import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.w3c.dom.ptests.DOMTestUtil.createDOMWithNS; +import static org.w3c.dom.ptests.DOMTestUtil.createNewDocument; + /* * @test * @library /javax/xml/jaxp/libs /javax/xml/jaxp/functional - * @run testng/othervm org.w3c.dom.ptests.TextTest + * @run junit/othervm org.w3c.dom.ptests.TextTest * @summary Test for Text implementation returned by Document.createTextNode(String) */ public class TextTest extends AbstractCharacterDataTest { @@ -60,8 +59,7 @@ public void testSplitText() throws Exception { textNode.splitText(0); int increased = node.getChildNodes().getLength() - rawChildNum; - assertEquals(increased, 1); - + assertEquals(1, increased); } @Override diff --git a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TypeInfoTest.java b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TypeInfoTest.java index 1013d5f4bafa..d101b45d5f17 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TypeInfoTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/w3c/dom/ptests/TypeInfoTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,24 +22,23 @@ */ package org.w3c.dom.ptests; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static org.testng.Assert.assertEquals; - -import java.io.StringReader; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.TypeInfo; import org.xml.sax.InputSource; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.StringReader; + +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static org.junit.jupiter.api.Assertions.assertEquals; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.w3c.dom.ptests.TypeInfoTest + * @run junit/othervm org.w3c.dom.ptests.TypeInfoTest * @summary Test getTypeName and getTypeNamespace methods of TypeInfo interface */ public class TypeInfoTest { @@ -48,11 +47,14 @@ public class TypeInfoTest { */ @Test public void test() throws Exception { - TypeInfo typeInfo = getTypeOfRoot(SCHEMA_INSTANCE, "\n" + "\n"); - - assertEquals(typeInfo.getTypeName(), "Test"); - assertEquals(typeInfo.getTypeNamespace(), "testNS"); - + TypeInfo typeInfo = getTypeOfRoot(SCHEMA_INSTANCE, + """ + + + """); + + assertEquals("Test", typeInfo.getTypeName()); + assertEquals("testNS", typeInfo.getTypeNamespace()); } private TypeInfo getTypeOfRoot(String schemaText, String docText) throws Exception { @@ -87,53 +89,55 @@ private Element getRoot(String schemaText, String docText) throws Exception { * Schema instance */ private static final String SCHEMA_INSTANCE = - "\n" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "\n" - + " \n" - + "\n" - + " \n" - + "\n" - + " \n" - + " \n" - + " \n" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "\n" - + "\n"; + """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """; } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttrImplTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttrImplTest.java index b43012a6197f..2af17996158c 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttrImplTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttrImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,19 +22,20 @@ */ package org.xml.sax.ptests; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.helpers.AttributesImpl; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + /** * Class containing the test cases for AttributesImpl API. */ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.AttrImplTest + * @run junit/othervm org.xml.sax.ptests.AttrImplTest */ public class AttrImplTest { private static final String CAR_URI = "http://www.cars.com/xml"; @@ -66,8 +67,8 @@ public void testcase01() { attr.addAttribute(CAR_URI, CAR_LOCALNAME, CAR_QNAME, CAR_TYPE, CAR_VALUE); attr.addAttribute(JEEP_URI, JEEP_LOCALNAME, JEEP_QNAME, JEEP_TYPE, JEEP_VALUE); - assertEquals(attr.getIndex(CAR_QNAME), 0); - assertEquals(attr.getIndex(JEEP_QNAME), 1); + assertEquals(0, attr.getIndex(CAR_QNAME)); + assertEquals(1, attr.getIndex(JEEP_QNAME)); } /** @@ -79,7 +80,7 @@ public void testcase02() { attr.addAttribute(CAR_URI, CAR_LOCALNAME, CAR_QNAME, CAR_TYPE, CAR_VALUE); attr.addAttribute(JEEP_URI, JEEP_LOCALNAME, JEEP_QNAME, JEEP_TYPE, JEEP_VALUE); - assertEquals(attr.getIndex(JEEP_URI, JEEP_LOCALNAME), 1); + assertEquals(1, attr.getIndex(JEEP_URI, JEEP_LOCALNAME)); } /** @@ -88,7 +89,7 @@ public void testcase02() { @Test public void testcase03() { AttributesImpl attr = new AttributesImpl(); - assertEquals(attr.getIndex(JEEP_URI, "whl"), -1); + assertEquals(-1, attr.getIndex(JEEP_URI, "whl")); } /** @@ -100,8 +101,8 @@ public void testcase04() { attr.addAttribute(CAR_URI, CAR_LOCALNAME, CAR_QNAME, CAR_TYPE, CAR_VALUE); attr.addAttribute(JEEP_URI, JEEP_LOCALNAME, JEEP_QNAME, JEEP_TYPE, JEEP_VALUE); - assertEquals(attr.getType(1), JEEP_TYPE); - assertEquals(attr.getType(JEEP_QNAME), JEEP_TYPE); + assertEquals(JEEP_TYPE, attr.getType(1)); + assertEquals(JEEP_TYPE, attr.getType(JEEP_QNAME)); } /** @@ -113,9 +114,9 @@ public void testcase05() { attr.addAttribute(CAR_URI, CAR_LOCALNAME, CAR_QNAME, CAR_TYPE, CAR_VALUE); attr.addAttribute(JEEP_URI, JEEP_LOCALNAME, JEEP_QNAME, JEEP_TYPE, JEEP_VALUE); - assertEquals(attr.getValue(1), JEEP_VALUE); - assertEquals(attr.getValue(attr.getQName(1)), JEEP_VALUE); - assertEquals(attr.getValue(attr.getURI(1), attr.getLocalName(1)), JEEP_VALUE); + assertEquals(JEEP_VALUE, attr.getValue(1)); + assertEquals(JEEP_VALUE, attr.getValue(attr.getQName(1))); + assertEquals(JEEP_VALUE, attr.getValue(attr.getURI(1), attr.getLocalName(1))); } /** @@ -129,11 +130,11 @@ public void testcase06() { attr.addAttribute(JEEP_URI, JEEP_LOCALNAME, JEEP_QNAME, JEEP_TYPE, JEEP_VALUE); attr.setAttribute(1, "www.megginson.com", "author", "meg", "s", "SAX2"); - assertEquals(attr.getLocalName(1), "author"); - assertEquals(attr.getQName(1), "meg"); - assertEquals(attr.getType(1), "s"); - assertEquals(attr.getType("meg"), "s"); - assertEquals(attr.getURI(1), "www.megginson.com"); + assertEquals("author", attr.getLocalName(1)); + assertEquals("meg", attr.getQName(1)); + assertEquals("s", attr.getType(1)); + assertEquals("s", attr.getType("meg")); + assertEquals("www.megginson.com", attr.getURI(1)); } /** @@ -152,11 +153,11 @@ public void testcase07() { attr.setValue(1, "SAX01"); attr.setURI(1, "www.megginson.com/sax/sax01"); - assertEquals(attr.getLocalName(1), "speclead"); - assertEquals(attr.getQName(1), "megi"); - assertEquals(attr.getType(1), "sax"); - assertEquals(attr.getType("megi"), "sax"); - assertEquals(attr.getURI(1), "www.megginson.com/sax/sax01"); + assertEquals("speclead", attr.getLocalName(1)); + assertEquals("megi", attr.getQName(1)); + assertEquals("sax", attr.getType(1)); + assertEquals("sax", attr.getType("megi")); + assertEquals("www.megginson.com/sax/sax01", attr.getURI(1)); } /** @@ -165,11 +166,11 @@ public void testcase07() { @Test public void testcase08() { AttributesImpl attr = new AttributesImpl(); - assertEquals(attr.getLength(), 0); + assertEquals(0, attr.getLength()); attr.addAttribute(CAR_URI, CAR_LOCALNAME, CAR_QNAME, CAR_TYPE, CAR_VALUE); attr.addAttribute(JEEP_URI, JEEP_LOCALNAME, JEEP_QNAME, JEEP_TYPE, JEEP_VALUE); - assertEquals(attr.getLength(), 2); + assertEquals(2, attr.getLength()); } /** @@ -189,13 +190,13 @@ public void testcase09() { * Javadoc says java.lang.ArrayIndexOutOfBoundsException is thrown When the * supplied index does not point to an attribute in the list. */ - @Test(expectedExceptions = ArrayIndexOutOfBoundsException.class) + @Test public void testcase10() { AttributesImpl attr = new AttributesImpl(); attr.addAttribute(CAR_URI, CAR_LOCALNAME, CAR_QNAME, CAR_TYPE, CAR_VALUE); attr.addAttribute(JEEP_URI, JEEP_LOCALNAME, JEEP_QNAME, JEEP_TYPE, JEEP_VALUE); attr.removeAttribute(1); - attr.removeAttribute(1); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> attr.removeAttribute(1)); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesNSTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesNSTest.java index 5e7db17c746e..687ea92c9c9f 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesNSTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesNSTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,18 +22,17 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.File; +import org.junit.jupiter.api.Test; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; /** * This tests the Attributes interface. Here the startElement() callback of @@ -45,7 +44,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.AttributesNSTest + * @run junit/othervm org.xml.sax.ptests.AttributesNSTest */ public class AttributesNSTest { /** @@ -55,7 +54,7 @@ public class AttributesNSTest { */ @Test public void testcase01() throws Exception { - String outputFile = USER_DIR + "AttributesNS.out"; + String outputFile = "AttributesNS.out"; String goldFile = GOLDEN_DIR + "AttributesNSGF.out"; String xmlFile = XML_DIR + "namespace1.xml"; SAXParserFactory spf = SAXParserFactory.newInstance(); @@ -68,6 +67,8 @@ public void testcase01() throws Exception { MyAttrCHandler myAttrCHandler = new MyAttrCHandler(outputFile); saxParser.parse(new File(xmlFile), myAttrCHandler); myAttrCHandler.flushAndClose(); - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesTest.java index 6ebdf0700608..e37d0b93e5d6 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/AttributesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,18 +22,17 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.File; +import org.junit.jupiter.api.Test; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; /** * This tests the Attributes interface. Here the startElement() callback of @@ -46,7 +45,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.AttributesTest + * @run junit/othervm org.xml.sax.ptests.AttributesTest */ public class AttributesTest { /** @@ -57,7 +56,7 @@ public class AttributesTest { */ @Test public void testcase01() throws Exception { - String outputFile = USER_DIR + "Attributes.out"; + String outputFile = "Attributes.out"; String goldFile = GOLDEN_DIR + "AttributesGF.out"; String xmlFile = XML_DIR + "family.xml"; @@ -70,6 +69,8 @@ public void testcase01() throws Exception { MyAttrCHandler myAttrCHandler = new MyAttrCHandler(outputFile); saxParser.parse(new File(xmlFile), myAttrCHandler); myAttrCHandler.flushAndClose(); - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ContentHandlerTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ContentHandlerTest.java index b450caba7aa9..0c06cd27b7e2 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ContentHandlerTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ContentHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,20 +22,7 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.BufferedWriter; -import java.io.FileInputStream; -import java.io.FileWriter; -import java.io.IOException; - -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.Locator; @@ -43,6 +30,18 @@ import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLFilterImpl; +import javax.xml.parsers.SAXParserFactory; +import java.io.BufferedWriter; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; + /** * Class registers a content event handler to XMLReader. Content event handler * transverses XML and print all visited node when XMLreader parses XML. Test @@ -51,7 +50,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.ContentHandlerTest + * @run junit/othervm org.xml.sax.ptests.ContentHandlerTest */ public class ContentHandlerTest { /** @@ -61,7 +60,7 @@ public class ContentHandlerTest { */ @Test public void testcase01() throws Exception { - String outputFile = USER_DIR + "Content.out"; + String outputFile = "Content.out"; String goldFile = GOLDEN_DIR + "ContentGF.out"; String xmlFile = XML_DIR + "namespace1.xml"; @@ -73,7 +72,9 @@ public void testcase01() throws Exception { xmlReader.setContentHandler(cHandler); xmlReader.parse(new InputSource(instream)); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } @@ -112,7 +113,6 @@ public MyContentHandler(String outputFileName) throws SAXException { /** * Write characters tag along with content of characters when meet * characters event. - * @throws IOException error happen when writing file. */ @Override public void characters(char[] ch, int start, int length) throws SAXException { @@ -123,7 +123,6 @@ public void characters(char[] ch, int start, int length) throws SAXException { /** * Write endDocument tag then flush the content and close the file when meet * endDocument event. - * @throws IOException error happen when writing file or closing file. */ @Override public void endDocument() throws SAXException { @@ -139,7 +138,6 @@ public void endDocument() throws SAXException { /** * Write endElement tag with namespaceURI, localName, qName to the file when * meet endElement event. - * @throws IOException error happen when writing file. */ @Override public void endElement(String namespaceURI,String localName,String qName) throws SAXException{ @@ -150,7 +148,6 @@ public void endElement(String namespaceURI,String localName,String qName) throws /** * Write endPrefixMapping tag along with prefix to the file when meet * endPrefixMapping event. - * @throws IOException error happen when writing file. */ @Override public void endPrefixMapping(String prefix) throws SAXException { @@ -160,7 +157,6 @@ public void endPrefixMapping(String prefix) throws SAXException { /** * Write ignorableWhitespace tag along with white spaces when meet * ignorableWhitespace event. - * @throws IOException error happen when writing file. */ @Override public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { @@ -172,7 +168,6 @@ public void ignorableWhitespace(char[] ch, int start, int length) throws SAXExce /** * Write processingInstruction tag along with target name and target data * when meet processingInstruction event. - * @throws IOException error happen when writing file. */ @Override public void processingInstruction(String target, String data) throws SAXException { @@ -196,7 +191,6 @@ public void setDocumentLocator(Locator locator) { /** * Write skippedEntity tag along with entity name when meet skippedEntity * event. - * @throws IOException error happen when writing file. */ @Override public void skippedEntity(String name) throws SAXException { @@ -205,7 +199,6 @@ public void skippedEntity(String name) throws SAXException { /** * Write startDocument tag when meet startDocument event. - * @throws IOException error happen when writing file. */ @Override public void startDocument() throws SAXException { @@ -215,7 +208,6 @@ public void startDocument() throws SAXException { /** * Write startElement tag along with namespaceURI, localName, qName, number * of attributes and line number when meet startElement event. - * @throws IOException error happen when writing file. */ @Override public void startElement(String namespaceURI, String localName, @@ -229,7 +221,6 @@ public void startElement(String namespaceURI, String localName, /** * Write startPrefixMapping tag along with prefix and uri when meet * startPrefixMapping event. - * @throws IOException error happen when writing file. */ @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/DefaultHandlerTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/DefaultHandlerTest.java index f65a290cb2cb..548cb6b4e43f 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/DefaultHandlerTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/DefaultHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,26 +22,25 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; +import org.junit.jupiter.api.Test; +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; -import org.xml.sax.Attributes; -import org.xml.sax.Locator; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; -import org.xml.sax.helpers.DefaultHandler; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; /** * XMLReader parse XML with default handler that transverses XML and @@ -50,7 +49,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.DefaultHandlerTest + * @run junit/othervm org.xml.sax.ptests.DefaultHandlerTest */ public class DefaultHandlerTest { /** @@ -60,7 +59,7 @@ public class DefaultHandlerTest { */ @Test public void testDefaultHandler() throws Exception { - String outputFile = USER_DIR + "DefaultHandler.out"; + String outputFile = "DefaultHandler.out"; String goldFile = GOLDEN_DIR + "DefaultHandlerGF.out"; String xmlFile = XML_DIR + "namespace1.xml"; @@ -75,9 +74,9 @@ public void testDefaultHandler() throws Exception { if (File.separatorChar == '\\') newAbsolutePath = Absolutepath.replace('\\', '/'); saxparser.parse("file:///" + newAbsolutePath, handler); - - assertTrue(compareWithGold(goldFile, outputFile)); - + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } @@ -108,7 +107,6 @@ class MyDefaultHandler extends DefaultHandler { /** * Write characters tag along with content of characters when meet * characters event. - * @throws IOException error happen when writing file. */ @Override public void characters(char[] ch, int start, int length) throws SAXException { @@ -118,7 +116,6 @@ public void characters(char[] ch, int start, int length) throws SAXException { /** * Write endDocument tag then flush the content and close the file when meet * endDocument event. - * @throws IOException error happen when writing file or closing file. */ @Override public void endDocument() throws SAXException { @@ -134,7 +131,6 @@ public void endDocument() throws SAXException { /** * Write endElement tag with namespaceURI, localName, qName to the file when * meet endElement event. - * @throws IOException error happen when writing file. */ @Override public void endElement(String namespaceURI,String localName,String qName) throws SAXException{ @@ -145,7 +141,6 @@ public void endElement(String namespaceURI,String localName,String qName) throws /** * Write endPrefixMapping tag along with prefix to the file when meet * endPrefixMapping event. - * @throws IOException error happen when writing file. */ @Override public void endPrefixMapping(String prefix) throws SAXException { @@ -155,7 +150,6 @@ public void endPrefixMapping(String prefix) throws SAXException { /** * Write error tag along with exception to the file when meet recoverable * error event. - * @throws IOException error happen when writing file. */ @Override public void error(SAXParseException e) throws SAXException { @@ -165,7 +159,6 @@ public void error(SAXParseException e) throws SAXException { /** * Write fatalError tag along with exception to the file when meet * unrecoverable error event. - * @throws IOException error happen when writing file. */ @Override public void fatalError(SAXParseException e) throws SAXException { @@ -174,7 +167,6 @@ public void fatalError(SAXParseException e) throws SAXException { /** * Write warning tag along with exception to the file when meet warning event. - * @throws IOException error happen when writing file. */ @Override public void warning(SAXParseException e) throws SAXException { @@ -184,7 +176,6 @@ public void warning(SAXParseException e) throws SAXException { /** * Write ignorableWhitespace tag along with white spaces when meet * ignorableWhitespace event. - * @throws IOException error happen when writing file. */ @Override public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { @@ -196,7 +187,6 @@ public void ignorableWhitespace(char[] ch, int start, int length) throws SAXExce /** * Write processingInstruction tag along with target name and target data * when meet processingInstruction event. - * @throws IOException error happen when writing file. */ @Override public void processingInstruction(String target, String data) throws SAXException { @@ -216,7 +206,6 @@ public void setDocumentLocator(Locator locator) { /** * Write skippedEntity tag along with entity name when meet skippedEntity * event. - * @throws IOException error happen when writing file. */ @Override public void skippedEntity(String name) throws SAXException { @@ -225,7 +214,6 @@ public void skippedEntity(String name) throws SAXException { /** * Write startDocument tag when meet startDocument event. - * @throws IOException error happen when writing file. */ @Override public void startDocument() throws SAXException { @@ -235,7 +223,6 @@ public void startDocument() throws SAXException { /** * Write startElement tag along with namespaceURI, localName, qName, number * of attributes and line number when meet startElement event. - * @throws IOException error happen when writing file. */ @Override public void startElement(String namespaceURI, String localName, @@ -248,7 +235,6 @@ public void startElement(String namespaceURI, String localName, /** * Write startPrefixMapping tag along with prefix and uri when meet * startPrefixMapping event. - * @throws IOException error happen when writing file. */ @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/EHFatalTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/EHFatalTest.java index 507e9d283842..578ca881e48b 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/EHFatalTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/EHFatalTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,27 +22,26 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; +import org.junit.jupiter.api.Test; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLFilterImpl; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.XMLFilterImpl; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; /** * ErrorHandler unit test. Set a ErrorHandle to XMLReader. Capture fatal error @@ -51,34 +50,31 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.EHFatalTest + * @run junit/othervm org.xml.sax.ptests.EHFatalTest */ public class EHFatalTest { /** * Error Handler to capture all error events to output file. Verifies the * output file is same as golden file. - * - * @throws Exception If any errors occur. */ @Test public void testEHFatal() throws Exception { - String outputFile = USER_DIR + "EHFatal.out"; + String outputFile = "EHFatal.out"; String goldFile = GOLDEN_DIR + "EHFatalGF.out"; String xmlFile = XML_DIR + "invalid.xml"; - try(MyErrorHandler eHandler = new MyErrorHandler(outputFile); - FileInputStream instream = new FileInputStream(xmlFile)) { - SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); - XMLReader xmlReader = saxParser.getXMLReader(); + SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); + XMLReader xmlReader = saxParser.getXMLReader(); + try (MyErrorHandler eHandler = new MyErrorHandler(outputFile); + FileInputStream instream = new FileInputStream(xmlFile)) { xmlReader.setErrorHandler(eHandler); InputSource is = new InputSource(instream); - xmlReader.parse(is); - fail("Parse should throw SAXException"); - } catch (SAXException expected) { - // This is expected. + assertThrows(SAXException.class, () -> xmlReader.parse(is)); } // Need close the output file before we compare it with golden file. - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } @@ -109,7 +105,6 @@ class MyErrorHandler extends XMLFilterImpl implements AutoCloseable { /** * Write fatalError tag along with exception to the file when meet * unrecoverable error event. - * @throws IOException error happen when writing file. */ @Override public void fatalError(SAXParseException e) throws SAXException { diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSSupportTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSSupportTest.java index 4ee4bf510a8e..988bc3c92c91 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSSupportTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSSupportTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,13 +22,14 @@ */ package org.xml.sax.ptests; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; +import org.junit.jupiter.api.Test; +import org.xml.sax.helpers.NamespaceSupport; import java.util.Enumeration; -import org.testng.annotations.Test; -import org.xml.sax.helpers.NamespaceSupport; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; /** * Unit test cases for NamespaceSupport API @@ -36,7 +37,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.NSSupportTest + * @run junit/othervm org.xml.sax.ptests.NSSupportTest */ public class NSSupportTest { /** @@ -70,14 +71,14 @@ public void testcase01() { support.declarePrefix(EMPTY_PREFIX, W3_URI); support.declarePrefix(DC_PREFIX, PURL_URI); - Enumeration e = support.getDeclaredPrefixes(); + Enumeration e = support.getDeclaredPrefixes(); int i = 0; - while(e.hasMoreElements()) { - prefixes[i++] = e.nextElement().toString(); + while (e.hasMoreElements()) { + prefixes[i++] = e.nextElement(); } support.popContext(); - assertEquals(prefixes, new String[]{EMPTY_PREFIX, DC_PREFIX}); + assertArrayEquals(new String[] { EMPTY_PREFIX, DC_PREFIX }, prefixes); } /** @@ -92,7 +93,7 @@ public void testcase02() { support.declarePrefix(DC_PREFIX, PURL_URI); parts = support.processName("dc:title", parts, false); support.popContext(); - assertEquals(parts, new String[]{PURL_URI, "title", "dc:title"}); + assertArrayEquals(new String[] { PURL_URI, "title", "dc:title" }, parts); } /** @@ -106,7 +107,7 @@ public void testcase03() { support.declarePrefix(EMPTY_PREFIX, W3_URI); parts = support.processName("a", parts, false); support.popContext(); - assertEquals(parts, new String[]{W3_URI, "a", "a"}); + assertArrayEquals(new String[] { W3_URI, "a", "a" }, parts); } @@ -121,8 +122,8 @@ public void testcase04() { support.declarePrefix(EMPTY_PREFIX, W3_URI); support.declarePrefix(DC_PREFIX, PURL_URI); - assertEquals(support.getURI(EMPTY_PREFIX), W3_URI); - assertEquals(support.getURI(DC_PREFIX), PURL_URI); + assertEquals(W3_URI, support.getURI(EMPTY_PREFIX)); + assertEquals(PURL_URI, support.getURI(DC_PREFIX)); support.popContext(); assertNull(support.getURI(EMPTY_PREFIX)); assertNull(support.getURI(DC_PREFIX)); diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSTableTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSTableTest.java index 81da8f25b88b..eda3dc89fede 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSTableTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/NSTableTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,14 +22,14 @@ */ package org.xml.sax.ptests; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; +import org.junit.jupiter.api.Test; +import org.xml.sax.XMLReader; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; -import org.testng.annotations.Test; -import org.xml.sax.XMLReader; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Class containing the test cases for Namespace Table defined at @@ -38,7 +38,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.NSTableTest + * @run junit/othervm org.xml.sax.ptests.NSTableTest */ public class NSTableTest { private static final String NAMESPACES = diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ParserAdapterTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ParserAdapterTest.java index b4a59180bace..4aaefefaa318 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ParserAdapterTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ParserAdapterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,16 +22,7 @@ */ package org.xml.sax.ptests; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.FileInputStream; - -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -41,6 +32,15 @@ import org.xml.sax.helpers.XMLFilterImpl; import org.xml.sax.helpers.XMLReaderAdapter; +import javax.xml.parsers.SAXParserFactory; +import java.io.FileInputStream; + +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.assertTrue; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; + /** * Unit test cases for ParserAdapter API. By default the only features recognized @@ -49,7 +49,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.ParserAdapterTest + * @run junit/othervm org.xml.sax.ptests.ParserAdapterTest */ public class ParserAdapterTest { /** @@ -177,9 +177,9 @@ public void getFeature02() throws Exception { * * @exception Exception If any errors occur. */ - @Test(expectedExceptions = SAXNotRecognizedException.class) + @Test public void getFeature03() throws Exception { - parserAdapter.getFeature("no-meaning-feature"); + assertThrows(SAXNotRecognizedException.class, () -> parserAdapter.getFeature("no-meaning-feature")); } /** @@ -228,31 +228,25 @@ public void setFeature04() throws Exception { /** * NPE expected when parsing a null object by ParserAdapter. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void parse01() throws Exception { - parserAdapter.parse((InputSource)null); + assertThrows(NullPointerException.class, () -> parserAdapter.parse((InputSource) null)); } /** * SAXException expected when parsing a wrong-formatter XML with ParserAdapter. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class) + @Test public void parse02() throws Exception { try(FileInputStream fis = new FileInputStream(XML_DIR + "invalid.xml")) { InputSource is = new InputSource(fis); - parserAdapter.parse(is); + assertThrows(SAXException.class, () -> parserAdapter.parse(is)); } } /** * Parse a well-formatter XML with ParserAdapter. - * - * @throws Exception If any errors occur. */ @Test public void parse03() throws Exception { diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ResolverTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ResolverTest.java index a7463edb53c8..483e8b87c1d6 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ResolverTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/ResolverTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,13 +22,14 @@ */ package org.xml.sax.ptests; -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; +import org.junit.jupiter.api.Test; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLFilterImpl; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileWriter; @@ -36,16 +37,13 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.XMLFilterImpl; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; /** * Entity resolver should be invoked in XML parse. This test verifies parsing @@ -54,24 +52,22 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.ResolverTest + * @run junit/othervm org.xml.sax.ptests.ResolverTest */ -@Test public class ResolverTest { /** * Unit test for entityResolver setter. - * - * @throws Exception If any errors occur. */ + @Test public void testResolver() throws Exception { - String outputFile = USER_DIR + "EntityResolver.out"; + String outputFile = "EntityResolver.out"; String goldFile = GOLDEN_DIR + "EntityResolverGF.out"; String xmlFile = XML_DIR + "publish.xml"; Files.copy(Paths.get(XML_DIR + "publishers.dtd"), - Paths.get(USER_DIR + "publishers.dtd"), REPLACE_EXISTING); + Paths.get("publishers.dtd"), REPLACE_EXISTING); Files.copy(Paths.get(XML_DIR + "familytree.dtd"), - Paths.get(USER_DIR + "familytree.dtd"), REPLACE_EXISTING); + Paths.get("familytree.dtd"), REPLACE_EXISTING); try(FileInputStream instream = new FileInputStream(xmlFile); MyEntityResolver eResolver = new MyEntityResolver(outputFile)) { @@ -81,7 +77,9 @@ public void testResolver() throws Exception { InputSource is = new InputSource(instream); xmlReader.parse(is); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterCBTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterCBTest.java index 4090b6b0733d..377cd02ac7d5 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterCBTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterCBTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,20 +22,7 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.BufferedWriter; -import java.io.FileInputStream; -import java.io.FileWriter; -import java.io.IOException; - -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.Locator; @@ -44,6 +31,18 @@ import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLFilterImpl; +import javax.xml.parsers.SAXParserFactory; +import java.io.BufferedWriter; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; + /** * Set parent of XMLFilter to XMLReader. Parsing on XML file will invoke XMLFilter * to write to output file. Test verifies output is same as the golden file. @@ -51,17 +50,15 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.XMLFilterCBTest + * @run junit/othervm org.xml.sax.ptests.XMLFilterCBTest */ -@Test public class XMLFilterCBTest { /** * Test XMLFilter working with XML reader. - * - * @throws Exception If any errors occur. */ + @Test public void testXMLFilterCB() throws Exception { - String outputFile = USER_DIR + "XMLFilter.out"; + String outputFile = "XMLFilter.out"; String goldFile = GOLDEN_DIR + "XMLFilterGF.out"; String xmlFile = XML_DIR + "namespace1.xml"; @@ -74,7 +71,9 @@ public void testXMLFilterCB() throws Exception { myXmlFilter.parse(new InputSource(fis)); } // Need close the output file before we compare it with golden file. - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldFile)), + Files.readAllLines(Path.of(outputFile))); } } @@ -103,7 +102,6 @@ class MyXMLFilter extends XMLFilterImpl implements AutoCloseable { /** * Write characters tag along with content of characters when meet * characters event. - * @throws IOException error happen when writing file. */ @Override public void characters(char[] ch, int start, int length) throws SAXException { @@ -114,7 +112,6 @@ public void characters(char[] ch, int start, int length) throws SAXException { /** * Write endDocument tag then flush the content and close the file when meet * endDocument event. - * @throws IOException error happen when writing file or closing file. */ @Override public void endDocument() throws SAXException { @@ -130,7 +127,6 @@ public void endDocument() throws SAXException { /** * Write endElement tag with namespaceURI, localName, qName to the file when * meet endElement event. - * @throws IOException error happen when writing file. */ @Override public void endElement(String namespaceURI,String localName,String qName) @@ -142,7 +138,6 @@ public void endElement(String namespaceURI,String localName,String qName) /** * Write endPrefixMapping tag along with prefix to the file when meet * endPrefixMapping event. - * @throws IOException error happen when writing file. */ @Override public void endPrefixMapping(String prefix) throws SAXException { @@ -152,7 +147,6 @@ public void endPrefixMapping(String prefix) throws SAXException { /** * Write error tag along with exception to the file when meet recoverable * error event. - * @throws IOException error happen when writing file. */ @Override public void error(SAXParseException e) throws SAXException { @@ -162,7 +156,6 @@ public void error(SAXParseException e) throws SAXException { /** * Write fatalError tag along with exception to the file when meet * unrecoverable error event. - * @throws IOException error happen when writing file. */ @Override public void fatalError(SAXParseException e) throws SAXException { @@ -171,7 +164,6 @@ public void fatalError(SAXParseException e) throws SAXException { /** * Write warning tag along with exception to the file when meet warning event. - * @throws IOException error happen when writing file. */ @Override public void warning(SAXParseException e) throws SAXException { @@ -181,7 +173,6 @@ public void warning(SAXParseException e) throws SAXException { /** * Write ignorableWhitespace tag along with white spaces when meet * ignorableWhitespace event. - * @throws IOException error happen when writing file. */ @Override public void ignorableWhitespace(char[] ch, int start, int length) @@ -194,7 +185,6 @@ public void ignorableWhitespace(char[] ch, int start, int length) /** * Write processingInstruction tag along with target name and target data * when meet processingInstruction event. - * @throws IOException error happen when writing file. */ @Override public void processingInstruction(String target, String data) @@ -218,7 +208,6 @@ public void setDocumentLocator(Locator locator) { /** * Write skippedEntity tag along with entity name when meet skippedEntity * event. - * @throws IOException error happen when writing file. */ @Override public void skippedEntity(String name) throws SAXException { @@ -227,7 +216,6 @@ public void skippedEntity(String name) throws SAXException { /** * Write startDocument tag when meet startDocument event. - * @throws IOException error happen when writing file. */ @Override public void startDocument() throws SAXException { @@ -237,7 +225,6 @@ public void startDocument() throws SAXException { /** * Write startElement tag along with namespaceURI, localName, qName, number * of attributes and line number when meet startElement event. - * @throws IOException error happen when writing file. */ @Override public void startElement(String namespaceURI, String localName, @@ -250,7 +237,6 @@ public void startElement(String namespaceURI, String localName, /** * Write startPrefixMapping tag along with prefix and uri when meet * startPrefixMapping event. - * @throws IOException error happen when writing file. */ @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterTest.java index 7ba882a9d1d1..ee62b896b332 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLFilterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,28 +22,28 @@ */ package org.xml.sax.ptests; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.FileInputStream; - -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.InputSource; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLFilterImpl; +import javax.xml.parsers.SAXParserFactory; +import java.io.FileInputStream; + +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.assertTrue; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; + /** * Unit test for XMLFilter. */ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.XMLFilterTest + * @run junit/othervm org.xml.sax.ptests.XMLFilterTest */ public class XMLFilterTest { /** @@ -132,8 +132,6 @@ public void errorHandler02() { /** * By default true is expected get namespaces feature. - * - * @throws Exception If any errors occur. */ @Test public void getFeature01() throws Exception { @@ -148,8 +146,6 @@ public void getFeature01() throws Exception { /** * By default false is expected get namespaces-prefix feature. - * - * @throws Exception If any errors occur. */ @Test public void getFeature02() throws Exception { @@ -163,19 +159,15 @@ public void getFeature02() throws Exception { /** * SAXNotRecognizedException is expected when get a feature by an invalid * feature name. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXNotRecognizedException.class) + @Test public void getFeature03() throws Exception { - new XMLFilterImpl().getFeature("no-meaning-feature"); + assertThrows(SAXNotRecognizedException.class, () -> new XMLFilterImpl().getFeature("no-meaning-feature")); } /** * Set namespaces feature to a value to XMLFilter. it's expected same when * obtain it again. - * - * @throws Exception If any errors occur. */ @Test public void setFeature01() throws Exception { @@ -193,8 +185,6 @@ public void setFeature01() throws Exception { /** * Set namespaces-prefix feature to a value to XMLFilter. it's expected same * when obtain it again. - * - * @throws Exception If any errors occur. */ @Test public void setFeature02() throws Exception { @@ -211,35 +201,31 @@ public void setFeature02() throws Exception { /** * NullPointerException is expected when parse a null InputSource. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void parse01() throws Exception { - new XMLFilterImpl().parse((InputSource)null); + assertThrows(NullPointerException.class, () -> new XMLFilterImpl().parse((InputSource) null)); } /** * SAXException is expected when parsing a invalid formatted XML file. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void parse02() throws Exception { - try(FileInputStream fis = new FileInputStream(XML_DIR + "invalid.xml")) { - new XMLFilterImpl().parse(new InputSource(fis)); + try (FileInputStream fis = new FileInputStream(XML_DIR + "invalid.xml")) { + InputSource input = new InputSource(fis); + assertThrows(NullPointerException.class, () -> new XMLFilterImpl().parse(input)); } } /** * No exception when parse a normal XML file. - * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void parse03() throws Exception { - try(FileInputStream fis = new FileInputStream(XML_DIR + "correct2.xml")) { - new XMLFilterImpl().parse(new InputSource(fis)); + try (FileInputStream fis = new FileInputStream(XML_DIR + "correct2.xml")) { + InputSource input = new InputSource(fis); + assertThrows(NullPointerException.class, () -> new XMLFilterImpl().parse(input)); } } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderAdapterTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderAdapterTest.java index 40462fcf076f..65dbaba99714 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderAdapterTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderAdapterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,28 +22,27 @@ */ package org.xml.sax.ptests; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.FileInputStream; - -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.HandlerBase; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderAdapter; +import javax.xml.parsers.SAXParserFactory; +import java.io.FileInputStream; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; + /** * Class containing the test cases for XMLReaderAdapter API */ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.XMLReaderAdapterTest + * @run junit/othervm org.xml.sax.ptests.XMLReaderAdapterTest */ public class XMLReaderAdapterTest { /** @@ -64,21 +63,17 @@ public void constructor01() throws SAXException { /** * To test the constructor that uses XMLReader. - * - * @throws Exception If any errors occur. */ @Test public void constructor02() throws Exception { XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); - assertNotNull(new XMLReaderAdapter(xmlReader)); + new XMLReaderAdapter(xmlReader); } /** * To test the parse method. The specification says that this method * will throw an exception if the embedded XMLReader does not support * the http://xml.org/sax/features/namespace-prefixes property. - * - * @throws Exception If any errors occur. */ @Test public void nsfeature01() throws Exception { @@ -93,8 +88,6 @@ public void nsfeature01() throws Exception { * To test the parse method. The specification says that this method * will throw an exception if the embedded XMLReader does not support * the http://xml.org/sax/features/namespace-prefixes property. - * - * @throws Exception If any errors occur. */ @Test public void parse01() throws Exception { diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderFactoryTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderFactoryTest.java index a109a27bce40..c8272d186eac 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderFactoryTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,21 +22,21 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; - -import static org.testng.Assert.assertNotNull; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.SAXException; import org.xml.sax.helpers.XMLReaderFactory; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + /** * Unit test for XMLReaderFactory.createXMLReader API. */ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.XMLReaderFactoryTest + * @run junit/othervm org.xml.sax.ptests.XMLReaderFactoryTest */ public class XMLReaderFactoryTest { /** @@ -56,20 +56,25 @@ public void createReader01() throws SAXException { */ @Test public void createReader02() throws SAXException { - setSystemProperty("org.xml.sax.driver", - "com.sun.org.apache.xerces.internal.parsers.SAXParser"); - assertNotNull(XMLReaderFactory. - createXMLReader("com.sun.org.apache.xerces.internal.parsers.SAXParser")); + System.setProperty("org.xml.sax.driver", + "com.sun.org.apache.xerces.internal.parsers.SAXParser"); + try { + assertNotNull(XMLReaderFactory.createXMLReader( + "com.sun.org.apache.xerces.internal.parsers.SAXParser")); + } finally { + System.clearProperty("org.xml.sax.driver"); + } } /** * SAXException expected when create XMLReader with an invalid driver name. * @throws org.xml.sax.SAXException expected Exception */ - @Test(expectedExceptions = SAXException.class, - expectedExceptionsMessageRegExp = - "SAX2 driver class org.apache.crimson.parser.ABCD not found") - public void createReader03() throws SAXException{ - XMLReaderFactory.createXMLReader("org.apache.crimson.parser.ABCD"); + @Test + public void createReader03() throws SAXException { + SAXException e = assertThrows( + SAXException.class, + () -> XMLReaderFactory.createXMLReader("org.apache.crimson.parser.ABCD")); + assertEquals("SAX2 driver class org.apache.crimson.parser.ABCD not found", e.getMessage()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderNSTableTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderNSTableTest.java index 9874866c7eca..2e7d0bc548f4 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderNSTableTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderNSTableTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,20 +22,20 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.FileInputStream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; -import org.testng.annotations.Test; -import org.xml.sax.InputSource; -import org.xml.sax.XMLReader; +import static org.xml.sax.ptests.SAXTestConst.GOLDEN_DIR; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; /** This class contains the testcases to test XMLReader with regard to * Namespace Table defined at @@ -44,9 +44,8 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.XMLReaderNSTableTest + * @run junit/othervm org.xml.sax.ptests.XMLReaderNSTableTest */ -@Test public class XMLReaderNSTableTest { /** * XML file that used to be parsed. @@ -62,11 +61,10 @@ public class XMLReaderNSTableTest { * namespace processing is enabled. namespace-prefix is also is enabled. * So it is a True-True combination. * The test is to test XMLReader with these conditions. - * - * @throws Exception If any errors occur. */ + @Test public void testWithTrueTrue() throws Exception { - String outputFile = USER_DIR + "XRNSTableTT.out"; + String outputFile = "XRNSTableTT.out"; String goldFile = GOLDEN_DIR + "NSTableTTGF.out"; SAXParserFactory spf = SAXParserFactory.newInstance(); @@ -79,18 +77,17 @@ public void testWithTrueTrue() throws Exception { xmlReader.setContentHandler(handler); xmlReader.parse(new InputSource(fis)); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch(goldFile, outputFile); } /** * Namespace processing is enabled. Hence namespace-prefix is * expected to be automatically off. So it is a True-False combination. * The test is to test XMLReader with these conditions. - * - * @throws Exception If any errors occur. */ + @Test public void testWithTrueFalse() throws Exception { - String outputFile = USER_DIR + "XRNSTableTF.out"; + String outputFile = "XRNSTableTF.out"; String goldFile = GOLDEN_DIR + "NSTableTFGF.out"; SAXParserFactory spf = SAXParserFactory.newInstance(); @@ -103,18 +100,17 @@ public void testWithTrueFalse() throws Exception { xmlReader.setContentHandler(handler); xmlReader.parse(new InputSource(fis)); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch(goldFile, outputFile); } /** * namespace processing is not enabled. Hence namespace-prefix is * expected to be automaically on. So it is a False-True combination. * The test is to test XMLReader with these conditions. - * - * @throws Exception If any errors occur. */ - public void testWithFalseTrue()throws Exception { - String outputFile = USER_DIR + "XRNSTableFT.out"; + @Test + public void testWithFalseTrue() throws Exception { + String outputFile = "XRNSTableFT.out"; String goldFile = GOLDEN_DIR + "NSTableFTGF.out"; SAXParserFactory spf = SAXParserFactory.newInstance(); @@ -125,6 +121,12 @@ public void testWithFalseTrue()throws Exception { xmlReader.setContentHandler(handler); xmlReader.parse(new InputSource(fis)); } - assertTrue(compareWithGold(goldFile, outputFile)); + assertLinesMatch(goldFile, outputFile); + } + + private static void assertLinesMatch(String goldenFile, String actual) throws IOException { + Assertions.assertLinesMatch( + Files.readAllLines(Path.of(goldenFile)), + Files.readAllLines(Path.of(actual))); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderTest.java b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderTest.java index 497df48f7e46..f726e19922c1 100644 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/ptests/XMLReaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,18 +22,7 @@ */ package org.xml.sax.ptests; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.xml.sax.ptests.SAXTestConst.XML_DIR; - -import java.io.FileInputStream; - -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; @@ -43,13 +32,24 @@ import org.xml.sax.ext.LexicalHandler; import org.xml.sax.helpers.XMLFilterImpl; +import javax.xml.parsers.SAXParserFactory; +import java.io.FileInputStream; + +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.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.xml.sax.ptests.SAXTestConst.XML_DIR; + /** * Class containing the test cases for SAXParser API */ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm org.xml.sax.ptests.XMLReaderTest + * @run junit/othervm org.xml.sax.ptests.XMLReaderTest */ public class XMLReaderTest { @@ -309,11 +309,11 @@ public void featureEPE02() throws Exception { * * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXNotRecognizedException.class) + @Test public void featureNE01() throws Exception { SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(true); - spf.newSAXParser().getXMLReader().getFeature("no-meaning-feature"); + assertThrows(SAXNotRecognizedException.class, () -> spf.newSAXParser().getXMLReader().getFeature("no-meaning-feature")); } /** @@ -328,7 +328,7 @@ public void entity01() throws Exception { XMLReader xmlReader = spf.newSAXParser().getXMLReader(); XMLFilterImpl xmlFilter = new XMLFilterImpl(); xmlReader.setEntityResolver(xmlFilter); - assertEquals(xmlReader.getEntityResolver(), xmlFilter); + assertEquals(xmlFilter, xmlReader.getEntityResolver()); } /** @@ -355,7 +355,7 @@ public void dtdhandler01() throws Exception { XMLReader xmlReader = spf.newSAXParser().getXMLReader(); XMLFilterImpl xmlFilter = new XMLFilterImpl(); xmlReader.setDTDHandler(xmlFilter); - assertEquals(xmlReader.getDTDHandler(), xmlFilter); + assertEquals(xmlFilter, xmlReader.getDTDHandler()); } /** @@ -382,7 +382,7 @@ public void contenthandler01() throws Exception { XMLReader xmlReader = spf.newSAXParser().getXMLReader(); XMLFilterImpl xmlFilter = new XMLFilterImpl(); xmlReader.setContentHandler(xmlFilter); - assertEquals(xmlReader.getContentHandler(), xmlFilter); + assertEquals(xmlFilter, xmlReader.getContentHandler()); } /** @@ -429,11 +429,11 @@ public void errorhandler02() throws Exception { * * @throws Exception If any errors occur. */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void parse01() throws Exception { SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(true); - spf.newSAXParser().getXMLReader().parse((InputSource) null); + assertThrows(NullPointerException.class, () -> spf.newSAXParser().getXMLReader().parse((InputSource) null)); } /** @@ -441,12 +441,12 @@ public void parse01() throws Exception { * * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXException.class) + @Test public void parse02() throws Exception { try (FileInputStream fis = new FileInputStream(XML_DIR + "invalid.xml")) { SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(true); - spf.newSAXParser().getXMLReader().parse(new InputSource(fis)); + assertThrows(SAXException.class, () -> spf.newSAXParser().getXMLReader().parse(new InputSource(fis))); } } @@ -470,11 +470,11 @@ public void parse03() throws Exception { * * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXNotSupportedException.class) + @Test public void xrProperty01() throws Exception { SAXParserFactory spf = SAXParserFactory.newInstance(); XMLReader xmlReader = spf.newSAXParser().getXMLReader(); - xmlReader.getProperty(XML_STRING); + assertThrows(SAXNotSupportedException.class, () -> xmlReader.getProperty(XML_STRING)); } /** @@ -483,11 +483,11 @@ public void xrProperty01() throws Exception { * * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXNotSupportedException.class) + @Test public void xrProperty02() throws Exception { SAXParserFactory spf = SAXParserFactory.newInstance(); XMLReader xmlReader = spf.newSAXParser().getXMLReader(); - assertNull(xmlReader.getProperty(DOM_NODE)); + assertThrows(SAXNotSupportedException.class, () -> xmlReader.getProperty(DOM_NODE)); } /** @@ -623,7 +623,7 @@ class MyDeclHandler implements DeclHandler { * @param eName The name of the associated element. * @param aName The name of the attribute. * @param type A string representing the attribute type. - * @param mode A string representing the attribute defaulting mode + * @param valueDefault A string representing the attribute defaulting mode * ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if none of these applies. * @param value A string representing the attribute's default value, or null * if there is none. diff --git a/test/jaxp/javax/xml/jaxp/functional/test/astro/AstroTest.java b/test/jaxp/javax/xml/jaxp/functional/test/astro/AstroTest.java index a42fcd9d3d72..ae7b88c5d34b 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/astro/AstroTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/astro/AstroTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,26 +23,25 @@ package test.astro; -import static java.lang.String.valueOf; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareWithGold; -import static org.testng.Assert.assertTrue; -import static test.astro.AstroConstants.ASTROCAT; -import static test.astro.AstroConstants.GOLDEN_DIR; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import javax.xml.transform.sax.TransformerHandler; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; -import javax.xml.transform.sax.TransformerHandler; - -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static java.lang.String.valueOf; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; +import static test.astro.AstroConstants.ASTROCAT; +import static test.astro.AstroConstants.GOLDEN_DIR; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.astro.AstroTest + * @run junit/othervm test.astro.AstroTest * @summary run astro application, test xslt * * There are vast amounts of textual astronomical data, typically user is @@ -67,10 +66,11 @@ * AstroProcessor to test different JAXP classes and features. * */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class AstroTest { private FiltersAndGolden[] data; - @BeforeClass + @BeforeAll public void setup() throws Exception { data = new FiltersAndGolden[4]; data[0] = new FiltersAndGolden(getGoldenFileName(1), astro -> astro.getRAFilter(0.106, 0.108)); @@ -82,8 +82,7 @@ public void setup() throws Exception { /* * Provide permutations of InputSourceFactory and FilterFactory for test. */ - @DataProvider(name = "factories") - public Object[][] getQueryFactories() { + public static Object[][] getQueryFactories() { return new Object[][] { { StreamFilterFactoryImpl.class, InputSourceFactoryImpl.class }, { SAXFilterFactoryImpl.class, InputSourceFactoryImpl.class }, @@ -92,7 +91,8 @@ public Object[][] getQueryFactories() { { StreamFilterFactoryImpl.class, DOML3InputSourceFactoryImpl.class } }; } - @Test(dataProvider = "factories") + @ParameterizedTest + @MethodSource("getQueryFactories") public void test(Class fFactClass, Class isFactClass) throws Exception { System.out.println(fFactClass.getName() +" : " + isFactClass.getName()); AstroProcessor astro = new AstroProcessor(fFactClass, ASTROCAT, isFactClass); @@ -108,10 +108,12 @@ private void runProcess(AstroProcessor astro, String processNum, String goldenFi for (int i = 0; i < filterCreators.length; i++) filters[i] = filterCreators[i].createFilter(astro); - String outputfile = Files.createTempFile(Paths.get(USER_DIR), "query" + processNum + ".out.", null).toString(); + String outputfile = Files.createTempFile(Paths.get("."), "query" + processNum + ".out.", null).toString(); System.out.println("output file: " + outputfile); astro.process(outputfile, filters); - assertTrue(compareWithGold(goldenFileName, outputfile)); + assertLinesMatch( + Files.readAllLines(Path.of(goldenFileName)), + Files.readAllLines(Path.of(outputfile))); } private String getGoldenFileName(int num) { @@ -124,8 +126,8 @@ private interface FilterCreator { } private static class FiltersAndGolden { - private FilterCreator[] filters; - private String goldenFileName; + private final FilterCreator[] filters; + private final String goldenFileName; FiltersAndGolden(String goldenFileName, FilterCreator... filters) { this.filters = filters; diff --git a/test/jaxp/javax/xml/jaxp/functional/test/astro/DocumentLSTest.java b/test/jaxp/javax/xml/jaxp/functional/test/astro/DocumentLSTest.java index d337ee1bbad9..4082e0603d69 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/astro/DocumentLSTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/astro/DocumentLSTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,26 +22,7 @@ */ package test.astro; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.w3c.dom.ls.DOMImplementationLS.MODE_SYNCHRONOUS; -import static test.astro.AstroConstants.ASTROCAT; - -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Writer; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.ls.DOMImplementationLS; @@ -50,10 +31,27 @@ import org.w3c.dom.ls.LSParser; import org.w3c.dom.ls.LSSerializer; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Writer; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.w3c.dom.ls.DOMImplementationLS.MODE_SYNCHRONOUS; +import static test.astro.AstroConstants.ASTROCAT; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.astro.DocumentLSTest + * @run junit/othervm test.astro.DocumentLSTest * @summary org.w3c.dom.ls tests */ public class DocumentLSTest { @@ -84,11 +82,11 @@ public void testLSInputParsingByteStream() throws Exception { assertTrue(src.getCertifiedText()); src.setCertifiedText(origCertified); // set back to orig - src.setSystemId(filenameToURL(ASTROCAT)); + src.setSystemId(Path.of(ASTROCAT).toUri().toASCIIString()); Document doc = domParser.parse(src); Element result = doc.getDocumentElement(); - assertEquals(result.getTagName(), "stardb"); + assertEquals("stardb", result.getTagName()); } } @@ -106,12 +104,12 @@ public void testLSInputParsingString() throws Exception { domSerializer.getDomConfig().setParameter("xml-declaration", Boolean.FALSE); LSInput src = impl.createLSInput(); src.setStringData(xml); - assertEquals(src.getStringData(), xml); + assertEquals(xml, src.getStringData()); Document doc = domParser.parse(src); String result = domSerializer.writeToString(doc); - assertEquals(result, "runDocumentLS_Q6"); + assertEquals("runDocumentLS_Q6", result); } /* @@ -128,7 +126,7 @@ public void testLSOutput() throws Exception { impl = (DOMImplementationLS) db.getDOMImplementation(); LSSerializer domSerializer = impl.createLSSerializer(); MyDOMOutput mydomoutput = new MyDOMOutput(); - try (OutputStream os = new FileOutputStream(USER_DIR + "test.out")) { + try (OutputStream os = new FileOutputStream("test.out")) { mydomoutput.setByteStream(os); mydomoutput.setEncoding("UTF-8"); assertTrue(domSerializer.write(doc, mydomoutput)); diff --git a/test/jaxp/javax/xml/jaxp/functional/test/astro/NamespaceContextTest.java b/test/jaxp/javax/xml/jaxp/functional/test/astro/NamespaceContextTest.java index 75b7880a1cae..7e5000a440ef 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/astro/NamespaceContextTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/astro/NamespaceContextTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,18 +22,18 @@ */ package test.astro; -import static javax.xml.XMLConstants.DEFAULT_NS_PREFIX; -import static javax.xml.XMLConstants.NULL_NS_URI; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.Test; import javax.xml.namespace.QName; -import org.testng.annotations.Test; +import static javax.xml.XMLConstants.DEFAULT_NS_PREFIX; +import static javax.xml.XMLConstants.NULL_NS_URI; +import static org.junit.jupiter.api.Assertions.assertEquals; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.astro.NamespaceContextTest + * @run junit/othervm test.astro.NamespaceContextTest * @summary javax.xml.namespace.QName tests */ public class NamespaceContextTest { @@ -47,9 +47,9 @@ public class NamespaceContextTest { @Test public void testQNameConstructor() { QName qname = new QName(NS_URI, LOCAL_PART, PREFIX); - assertEquals(qname.getNamespaceURI(), NS_URI); - assertEquals(qname.getLocalPart(), LOCAL_PART); - assertEquals(qname.getPrefix(), PREFIX); + assertEquals(NS_URI, qname.getNamespaceURI()); + assertEquals(LOCAL_PART, qname.getLocalPart()); + assertEquals(PREFIX, qname.getPrefix()); } /* @@ -59,9 +59,9 @@ public void testQNameConstructor() { @Test public void testDefaultFields() { QName qname = new QName(LOCAL_PART); // just the local part specified - assertEquals(qname.getNamespaceURI(), NULL_NS_URI); - assertEquals(qname.getLocalPart(), LOCAL_PART); - assertEquals(qname.getPrefix(), DEFAULT_NS_PREFIX); + assertEquals(NULL_NS_URI, qname.getNamespaceURI()); + assertEquals(LOCAL_PART, qname.getLocalPart()); + assertEquals(DEFAULT_NS_PREFIX, qname.getPrefix()); } /* @@ -71,9 +71,9 @@ public void testDefaultFields() { @Test public void testDefaultPrefix() { QName qname = new QName(NS_URI, LOCAL_PART); // no pref - assertEquals(qname.getNamespaceURI(), NS_URI); - assertEquals(qname.getLocalPart(), LOCAL_PART); - assertEquals(qname.getPrefix(), DEFAULT_NS_PREFIX); + assertEquals(NS_URI, qname.getNamespaceURI()); + assertEquals(LOCAL_PART, qname.getLocalPart()); + assertEquals(DEFAULT_NS_PREFIX, qname.getPrefix()); } /* @@ -83,6 +83,6 @@ public void testDefaultPrefix() { @Test public void testQNameString() { QName qname = new QName(NS_URI, LOCAL_PART, PREFIX); - assertEquals(QName.valueOf(qname.toString()), qname); + assertEquals(qname, QName.valueOf(qname.toString())); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/test/astro/SAX201Test.java b/test/jaxp/javax/xml/jaxp/functional/test/astro/SAX201Test.java index 6cdbd93ce828..3384d04161ab 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/astro/SAX201Test.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/astro/SAX201Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,15 +22,15 @@ */ package test.astro; -import javax.xml.parsers.SAXParserFactory; - -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.XMLReader; +import javax.xml.parsers.SAXParserFactory; + /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.astro.SAX201Test + * @run junit/othervm test.astro.SAX201Test * @summary verify SAX 2.0.1 allows to use null in setters */ public class SAX201Test { diff --git a/test/jaxp/javax/xml/jaxp/functional/test/astro/SchemaValidationTest.java b/test/jaxp/javax/xml/jaxp/functional/test/astro/SchemaValidationTest.java index 782831e60b86..d2e5740f1f82 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/astro/SchemaValidationTest.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/astro/SchemaValidationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,25 +22,25 @@ */ package test.astro; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static test.astro.AstroConstants.ASTROCAT; -import static test.astro.AstroConstants.JAXP_SCHEMA_LANGUAGE; -import static test.astro.AstroConstants.JAXP_SCHEMA_SOURCE; - -import java.io.File; +import org.junit.jupiter.api.Test; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import java.io.File; -import org.testng.annotations.Test; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static test.astro.AstroConstants.ASTROCAT; +import static test.astro.AstroConstants.JAXP_SCHEMA_LANGUAGE; +import static test.astro.AstroConstants.JAXP_SCHEMA_SOURCE; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.astro.SchemaValidationTest + * @run junit/othervm test.astro.SchemaValidationTest * @summary test parser sets schema related properties to do validation */ public class SchemaValidationTest { @@ -59,11 +59,10 @@ public void testSchemaValidation() throws Exception { * Test SAXException shall be thrown if schemaSource is set but * schemaLanguage is not set. */ - @Test(expectedExceptions = SAXException.class) + @Test public void testSchemaValidationNeg() throws Exception { SAXParser sp = getValidatingParser(); - sp.setProperty(JAXP_SCHEMA_SOURCE, "catalog.xsd"); - sp.parse(new File(ASTROCAT), new DefaultHandler()); + assertThrows(SAXException.class, () -> sp.setProperty(JAXP_SCHEMA_SOURCE, "catalog.xsd")); } private SAXParser getValidatingParser() throws ParserConfigurationException, SAXException { diff --git a/test/jaxp/javax/xml/jaxp/functional/test/astro/XPathAPITest.java b/test/jaxp/javax/xml/jaxp/functional/test/astro/XPathAPITest.java index 04bab01fa757..b305a0604596 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/astro/XPathAPITest.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/astro/XPathAPITest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,18 +22,17 @@ */ package test.astro; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static javax.xml.xpath.XPathConstants.DOM_OBJECT_MODEL; -import static javax.xml.xpath.XPathConstants.NODESET; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static org.testng.Assert.assertEquals; -import static test.astro.AstroConstants.ASTROCAT; -import static test.astro.AstroConstants.JAXP_SCHEMA_LANGUAGE; -import static test.astro.AstroConstants.JAXP_SCHEMA_SOURCE; - -import java.net.MalformedURLException; -import java.util.ArrayList; -import java.util.Iterator; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; import javax.xml.namespace.NamespaceContext; import javax.xml.namespace.QName; @@ -44,21 +43,26 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathVariableResolver; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Iterator; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.xpath.XPathConstants.DOM_OBJECT_MODEL; +import static javax.xml.xpath.XPathConstants.NODESET; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static test.astro.AstroConstants.ASTROCAT; +import static test.astro.AstroConstants.JAXP_SCHEMA_LANGUAGE; +import static test.astro.AstroConstants.JAXP_SCHEMA_SOURCE; /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.astro.XPathAPITest + * @run junit/othervm test.astro.XPathAPITest * @summary test XPath API */ -@Test(singleThreaded = true) +@Execution(ExecutionMode.SAME_THREAD) +@TestInstance(Lifecycle.PER_CLASS) public class XPathAPITest { private static final String STARDB_STAR_3_CONSTELLATION = "//astro:stardb/astro:star[3]/astro:constellation"; private static final String STARDB_STAR = "//astro:stardb/astro:star"; @@ -66,7 +70,7 @@ public class XPathAPITest { private XPathFactory xpf; private NamespaceContext nsContext; - @BeforeClass + @BeforeAll public void setup() throws Exception { DocumentBuilderFactory df = DocumentBuilderFactory.newInstance(); df.setNamespaceAware(true); @@ -81,45 +85,49 @@ public void setup() throws Exception { nsContext = new MyNamespaceContext(); } - @DataProvider(name = "nodelist-evaluator") - public Object[][] getNodeListEvaluator() throws MalformedURLException { - return new Object[][] { { (XPathEvaluator) expression -> getXPath().evaluate(expression, doc.getDocumentElement(), NODESET) }, - { (XPathEvaluator) expression -> getXPath().evaluate(expression, createXMLInputSource(), NODESET) }, - { (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(doc.getDocumentElement(), NODESET) }, - { (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(createXMLInputSource(), NODESET) } }; + public Object[] getNodeListEvaluator() { + return new Object[] { + (XPathEvaluator) expression -> getXPath().evaluate(expression, doc.getDocumentElement(), NODESET), + (XPathEvaluator) expression -> getXPath().evaluate(expression, createXMLInputSource(), NODESET), + (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(doc.getDocumentElement(), NODESET), + (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(createXMLInputSource(), NODESET), + }; } /* * Test xpath expression evaluations method that returns type indicated by * QName */ - @Test(dataProvider = "nodelist-evaluator") + @ParameterizedTest + @MethodSource("getNodeListEvaluator") public void testEvaluateNodeList(XPathEvaluator evaluator) throws Exception { NodeList o = (NodeList) evaluator.evaluate(STARDB_STAR); - assertEquals(o.getLength(), 10); + assertEquals(10, o.getLength()); } - @DataProvider(name = "string-evaluator") - public Object[][] getStringEvaluator() throws MalformedURLException { - return new Object[][] { { (XPathEvaluator) expression -> getXPath().evaluate(expression, doc.getDocumentElement()) }, - { (XPathEvaluator) expression -> getXPath().evaluate(expression, createXMLInputSource()) }, - { (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(doc.getDocumentElement()) }, - { (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(createXMLInputSource()) } }; + public Object[] getStringEvaluator() { + return new Object[] { + (XPathEvaluator) expression -> getXPath().evaluate(expression, doc.getDocumentElement()), + (XPathEvaluator) expression -> getXPath().evaluate(expression, createXMLInputSource()), + (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(doc.getDocumentElement()), + (XPathEvaluator) expression -> getXPathExpression(expression).evaluate(createXMLInputSource()), + }; } /* * Test xpath expression evaluations method that returns String */ - @Test(dataProvider = "string-evaluator") + @ParameterizedTest + @MethodSource("getStringEvaluator") public void testEvaluateString(XPathEvaluator evaluator) throws Exception { - assertEquals(evaluator.evaluate(STARDB_STAR_3_CONSTELLATION), "Psc"); + assertEquals("Psc", evaluator.evaluate(STARDB_STAR_3_CONSTELLATION)); } @Test public void testXPathVariableResolver() throws Exception { XPath xpath = getXPath(); xpath.setXPathVariableResolver(new MyXPathVariableResolver()); - assertEquals(xpath.evaluate("//astro:stardb/astro:star[astro:hr=$id]/astro:constellation", doc.getDocumentElement()), "Peg"); + assertEquals("Peg", xpath.evaluate("//astro:stardb/astro:star[astro:hr=$id]/astro:constellation", doc.getDocumentElement())); } @@ -142,15 +150,15 @@ public String getPrefix(String nsURI) { return "http://www.astro.com/astro".equals(nsURI) ? "astro" : ""; } - public Iterator getPrefixes(String nsURI) { - ArrayList list = new ArrayList(); + public Iterator getPrefixes(String nsURI) { + ArrayList list = new ArrayList<>(); list.add("astro"); return list.iterator(); } } @FunctionalInterface - private interface XPathEvaluator { + public interface XPathEvaluator { Object evaluate(String expression) throws XPathExpressionException; } @@ -165,6 +173,6 @@ private XPathExpression getXPathExpression(String expression) throws XPathExpres } private InputSource createXMLInputSource() { - return new InputSource(filenameToURL(ASTROCAT)); + return new InputSource(Path.of(ASTROCAT).toUri().toASCIIString()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionController.java b/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionController.java index b2a66629e394..a3cc438f3941 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionController.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,22 +22,18 @@ */ package test.auctionportal; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static jaxp.library.JAXPTestUtilities.bomStream; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import static test.auctionportal.HiBidConstants.JAXP_SCHEMA_LANGUAGE; -import static test.auctionportal.HiBidConstants.JAXP_SCHEMA_SOURCE; -import static test.auctionportal.HiBidConstants.PORTAL_ACCOUNT_NS; -import static test.auctionportal.HiBidConstants.XML_DIR; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.math.BigInteger; -import java.nio.file.Paths; -import java.util.GregorianCalendar; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.w3c.dom.Attr; +import org.w3c.dom.DOMConfiguration; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.w3c.dom.TypeInfo; +import org.w3c.dom.bootstrap.DOMImplementationRegistry; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSSerializer; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.DatatypeFactory; @@ -51,17 +47,28 @@ import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.GregorianCalendar; -import org.testng.annotations.Test; -import org.w3c.dom.Attr; -import org.w3c.dom.DOMConfiguration; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import org.w3c.dom.TypeInfo; -import org.w3c.dom.bootstrap.DOMImplementationRegistry; -import org.w3c.dom.ls.DOMImplementationLS; -import org.w3c.dom.ls.LSSerializer; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static test.auctionportal.HiBidConstants.JAXP_SCHEMA_LANGUAGE; +import static test.auctionportal.HiBidConstants.JAXP_SCHEMA_SOURCE; +import static test.auctionportal.HiBidConstants.PORTAL_ACCOUNT_NS; +import static test.auctionportal.HiBidConstants.XML_DIR; /** * This is the user controller class for the Auction portal HiBid.com. @@ -69,14 +76,13 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.auctionportal.AuctionController + * @run junit/othervm test.auctionportal.AuctionController */ public class AuctionController { /** * Check for DOMErrorHandler handling DOMError. Before fix of bug 4890927 * DOMConfiguration.setParameter("well-formed",true) throws an exception. * - * @throws Exception If any errors occur. */ @Test public void testCreateNewItem2Sell() throws Exception { @@ -99,7 +105,6 @@ public void testCreateNewItem2Sell() throws Exception { * Check for DOMErrorHandler handling DOMError. Before fix of bug 4896132 * test throws DOM Level 1 node error. * - * @throws Exception If any errors occur. */ @Test public void testCreateNewItem2SellRetry() throws Exception { @@ -131,7 +136,6 @@ public void testCreateNewItem2SellRetry() throws Exception { * Check if setting the attribute to be of type ID works. This will affect * the Attr.isID method according to the spec. * - * @throws Exception If any errors occur. */ @Test public void testCreateID() throws Exception { @@ -152,7 +156,6 @@ public void testCreateID() throws Exception { /** * Check the user data on the node. * - * @throws Exception If any errors occur. */ @Test public void testCheckingUserData() throws Exception { @@ -164,45 +167,44 @@ public void testCheckingUserData() throws Exception { DocumentBuilder docBuilder = dbf.newDocumentBuilder(); Document document = docBuilder.parse(xmlFile); - Element account = (Element)document.getElementsByTagNameNS(PORTAL_ACCOUNT_NS, "Account").item(0); - assertEquals(account.getNodeName(), "acc:Account"); + Element account = (Element) document.getElementsByTagNameNS(PORTAL_ACCOUNT_NS, "Account").item(0); + assertEquals("acc:Account", account.getNodeName()); Element firstName = (Element) document.getElementsByTagNameNS(PORTAL_ACCOUNT_NS, "FirstName").item(0); - assertEquals(firstName.getNodeName(), "FirstName"); + assertEquals("FirstName", firstName.getNodeName()); Document doc1 = docBuilder.newDocument(); Element someName = doc1.createElement("newelem"); someName.setUserData("mykey", "dd", - (operation, key, data, src, dst) -> { - System.err.println("In UserDataHandler" + key); - System.out.println("In UserDataHandler"); - }); - Element impAccount = (Element)document.importNode(someName, true); - assertEquals(impAccount.getNodeName(), "newelem"); + (operation, key, data, src, dst) -> { + System.err.println("In UserDataHandler" + key); + System.out.println("In UserDataHandler"); + }); + Element impAccount = (Element) document.importNode(someName, true); + assertEquals("newelem", impAccount.getNodeName()); document.normalizeDocument(); String data = (someName.getUserData("mykey")).toString(); - assertEquals(data, "dd"); + assertEquals("dd", data); } /** * Check the UTF-16 XMLEncoding xml file. * - * @throws Exception If any errors occur. - * @see movies.xml + * @see movies-utf16.xml */ - @Test - public void testCheckingEncoding() throws Exception { - // Note since movies.xml is UTF-16 encoding. We're not using stanard XML - // file suffix. - String xmlFile = XML_DIR + "movies.xml.data"; + @ParameterizedTest + @EnumSource(value=ByteOrder.class) + public void testCheckingEncoding(ByteOrder byteOrder) throws Exception { + String xmlFile = XML_DIR + "movies-utf16.xml"; - try (InputStream source = bomStream("UTF-16", xmlFile)) { + // File is stored as UTF-8, but declares itself as UTF-16 for testing. + try (InputStream source = utf16Stream(xmlFile, byteOrder)) { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); Document document = dbf.newDocumentBuilder().parse(source); - assertEquals(document.getXmlEncoding(), "UTF-16"); - assertEquals(document.getXmlStandalone(), true); + assertEquals("UTF-16", document.getXmlEncoding()); + assertTrue(document.getXmlStandalone()); } } @@ -210,7 +212,6 @@ public void testCheckingEncoding() throws Exception { * Check validation API features. A schema which is including in Bug 4909119 * used to be testing for the functionalities. * - * @throws Exception If any errors occur. * @see userDetails.xsd */ @Test @@ -244,7 +245,6 @@ public void testGetOwnerInfo() throws Exception { /** * Check grammar caching with imported schemas. * - * @throws Exception If any errors occur. * @see coins.xsd * @see coinsImportMe.xsd */ @@ -279,7 +279,6 @@ public void testGetOwnerItemList() throws Exception { * parsing using the SAXParser. SCHEMA_SOURCE attribute is using for this * test. * - * @throws Exception If any errors occur. * @see coins.xsd * @see coinsImportMe.xsd */ @@ -304,7 +303,6 @@ public void testGetOwnerItemList1() throws Exception { /** * Check usage of javax.xml.datatype.Duration class. * - * @throws Exception If any errors occur. */ @Test public void testGetItemDuration() throws Exception { @@ -326,18 +324,17 @@ public void testGetItemDuration() throws Exception { Duration sellDuration = DatatypeFactory.newInstance().newDuration(childList.item(0).getNodeValue()); assertFalse(sellDuration.isShorterThan(duration)); assertFalse(sellDuration.isLongerThan(duration)); - assertEquals(sellDuration.getField(DatatypeConstants.DAYS), BigInteger.valueOf(365)); - assertEquals(sellDuration.normalizeWith(new GregorianCalendar(1999, 2, 22)), duration); + assertEquals(BigInteger.valueOf(365), sellDuration.getField(DatatypeConstants.DAYS)); + assertEquals(duration, sellDuration.normalizeWith(new GregorianCalendar(1999, 2, 22))); Duration myDuration = sellDuration.add(duration); - assertEquals(myDuration.normalizeWith(new GregorianCalendar(2003, 2, 22)), - DatatypeFactory.newInstance().newDuration("P730D")); + assertEquals(DatatypeFactory.newInstance().newDuration("P730D"), + myDuration.normalizeWith(new GregorianCalendar(2003, 2, 22))); } /** * Check usage of TypeInfo interface introduced in DOM L3. * - * @throws Exception If any errors occur. */ @Test public void testGetTypeInfo() throws Exception { @@ -354,12 +351,36 @@ public void testGetTypeInfo() throws Exception { Document document = docBuilder.parse(xmlFile); Element userId = (Element)document.getElementsByTagNameNS(PORTAL_ACCOUNT_NS, "UserID").item(0); TypeInfo typeInfo = userId.getSchemaTypeInfo(); - assertTrue(typeInfo.getTypeName().equals("nonNegativeInteger")); - assertTrue(typeInfo.getTypeNamespace().equals(W3C_XML_SCHEMA_NS_URI)); + assertEquals("nonNegativeInteger", typeInfo.getTypeName()); + assertEquals(W3C_XML_SCHEMA_NS_URI, typeInfo.getTypeNamespace()); Element role = (Element)document.getElementsByTagNameNS(PORTAL_ACCOUNT_NS, "Role").item(0); TypeInfo roletypeInfo = role.getSchemaTypeInfo(); - assertTrue(roletypeInfo.getTypeName().equals("BuyOrSell")); - assertTrue(roletypeInfo.getTypeNamespace().equals(PORTAL_ACCOUNT_NS)); + assertEquals("BuyOrSell", roletypeInfo.getTypeName()); + assertEquals(PORTAL_ACCOUNT_NS, roletypeInfo.getTypeNamespace()); + } + + /** Convert file contents to a given character set with BOM marker. */ + public static InputStream utf16Stream(String file, ByteOrder byteOrder) + throws IOException { + Charset charset; + byte[] head; + switch (byteOrder) { + case BIG_ENDIAN: + charset = StandardCharsets.UTF_16BE; + head = new byte[] { (byte) 0xFE, (byte) 0xFF }; + break; + case LITTLE_ENDIAN: + charset = StandardCharsets.UTF_16LE; + head = new byte[] { (byte) 0xFF, (byte) 0xFE }; + break; + default: + throw new AssertionError("Unsupported byte order: " + byteOrder); + } + byte[] content = Files.readString(Paths.get(file)).getBytes(charset); + ByteBuffer bb = ByteBuffer.allocate(head.length + content.length); + bb.put(head); + bb.put(content); + return new ByteArrayInputStream(bb.array()); } } diff --git a/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionItemRepository.java b/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionItemRepository.java index 854cb7d1ad45..dca34a73d824 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionItemRepository.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/AuctionItemRepository.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,37 +22,37 @@ */ package test.auctionportal; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; - -import static javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING; -import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static jaxp.library.JAXPTestUtilities.compareDocumentWithGold; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import static test.auctionportal.HiBidConstants.GOLDEN_DIR; -import static test.auctionportal.HiBidConstants.JAXP_SCHEMA_LANGUAGE; -import static test.auctionportal.HiBidConstants.JAXP_SCHEMA_SOURCE; -import static test.auctionportal.HiBidConstants.SP_ENTITY_EXPANSION_LIMIT; -import static test.auctionportal.HiBidConstants.SP_MAX_OCCUR_LIMIT; -import static test.auctionportal.HiBidConstants.XML_DIR; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Paths; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.xml.sax.SAXParseException; +import static javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static test.auctionportal.HiBidConstants.GOLDEN_DIR; +import static test.auctionportal.HiBidConstants.JAXP_SCHEMA_LANGUAGE; +import static test.auctionportal.HiBidConstants.JAXP_SCHEMA_SOURCE; +import static test.auctionportal.HiBidConstants.SP_ENTITY_EXPANSION_LIMIT; +import static test.auctionportal.HiBidConstants.SP_MAX_OCCUR_LIMIT; +import static test.auctionportal.HiBidConstants.XML_DIR; /** * This is a test class for the Auction portal HiBid.com. @@ -60,7 +60,7 @@ /* * @test * @library /javax/xml/jaxp/libs - * @run testng/othervm test.auctionportal.AuctionItemRepository + * @run junit/othervm test.auctionportal.AuctionItemRepository */ public class AuctionItemRepository { /** @@ -79,7 +79,6 @@ public class AuctionItemRepository { * not. Previous system property was changed to jdk.xml.entityExpansionLimit * see http://docs.oracle.com/javase/tutorial/jaxp/limits/limits.html. * - * @throws Exception If any errors occur. */ @Test public void testEntityExpansionSAXPos() throws Exception { @@ -88,7 +87,7 @@ public void testEntityExpansionSAXPos() throws Exception { // implementation limits. factory.setFeature(FEATURE_SECURE_PROCESSING, true); // Set entityExpansionLimit as 2 should expect fatalError - setSystemProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(128000)); + System.setProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(128000)); SAXParser parser = factory.newSAXParser(); MyErrorHandler fatalHandler = new MyErrorHandler(); @@ -101,27 +100,25 @@ public void testEntityExpansionSAXPos() throws Exception { * not. Previous system property was changed to jdk.xml.entityExpansionLimit * see http://docs.oracle.com/javase/tutorial/jaxp/limits/limits.html. * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXParseException.class) + @Test public void testEntityExpansionSAXNeg() throws Exception { SAXParserFactory factory = SAXParserFactory.newInstance(); // Secure processing will limit XML processing to conform to // implementation limits. factory.setFeature(FEATURE_SECURE_PROCESSING, true); // Set entityExpansionLimit as 2 should expect SAXParseException. - setSystemProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(2)); + System.setProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(2)); SAXParser parser = factory.newSAXParser(); MyErrorHandler fatalHandler = new MyErrorHandler(); - parser.parse(new File(ENTITY_XML), fatalHandler); + assertThrows(SAXParseException.class, () -> parser.parse(new File(ENTITY_XML), fatalHandler)); } /** * Testing set MaxOccursLimit to 10000 in the secure processing enabled for * SAXParserFactory. * - * @throws Exception If any errors occur. */ @Test public void testMaxOccurLimitPos() throws Exception { @@ -130,7 +127,7 @@ public void testMaxOccurLimitPos() throws Exception { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setValidating(true); factory.setFeature(FEATURE_SECURE_PROCESSING, true); - setSystemProperty(SP_MAX_OCCUR_LIMIT, String.valueOf(10000)); + System.setProperty(SP_MAX_OCCUR_LIMIT, String.valueOf(10000)); SAXParser parser = factory.newSAXParser(); parser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA_NS_URI); parser.setProperty(JAXP_SCHEMA_SOURCE, new File(schema_file)); @@ -145,13 +142,12 @@ public void testMaxOccurLimitPos() throws Exception { * Use a DocumentBuilder to create a DOM object and see if Secure Processing * feature affects the entity expansion. * - * @throws Exception If any errors occur. */ @Test public void testEntityExpansionDOMPos() throws Exception { DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance(); dfactory.setFeature(FEATURE_SECURE_PROCESSING, true); - setSystemProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(10000)); + System.setProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(10000)); DocumentBuilder dBuilder = dfactory.newDocumentBuilder(); MyErrorHandler eh = new MyErrorHandler(); dBuilder.setErrorHandler(eh); @@ -164,27 +160,25 @@ public void testEntityExpansionDOMPos() throws Exception { * Processing feature and entityExpansionLimit value affects output. * Negative test that when entityExpansionLimit is too small. * - * @throws Exception If any errors occur. */ - @Test(expectedExceptions = SAXParseException.class) + @Test public void testEntityExpansionDOMNeg() throws Exception { DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance(); dfactory.setFeature(FEATURE_SECURE_PROCESSING, true); - setSystemProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(2)); + System.setProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(2)); DocumentBuilder dBuilder = dfactory.newDocumentBuilder(); MyErrorHandler eh = new MyErrorHandler(); dBuilder.setErrorHandler(eh); - dBuilder.parse(ENTITY_XML); + assertThrows(SAXParseException.class, () -> dBuilder.parse(ENTITY_XML)); } /** * Test xi:include with a SAXParserFactory. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXIncludeSAXPos() throws Exception { - String resultFile = USER_DIR + "doc_xinclude.out"; + String resultFile = "doc_xinclude.out"; String goldFile = GOLDEN_DIR + "doc_xincludeGold.xml"; String xmlFile = XML_DIR + "doc_xinclude.xml"; @@ -203,11 +197,10 @@ public void testXIncludeSAXPos() throws Exception { * Test the simple case of including a document using xi:include using a * DocumentBuilder. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXIncludeDOMPos() throws Exception { - String resultFile = USER_DIR + "doc_xincludeDOM.out"; + String resultFile = "doc_xincludeDOM.out"; String goldFile = GOLDEN_DIR + "doc_xincludeGold.xml"; String xmlFile = XML_DIR + "doc_xinclude.xml"; try (FileOutputStream fos = new FileOutputStream(resultFile)) { @@ -226,11 +219,10 @@ public void testXIncludeDOMPos() throws Exception { * Test the simple case of including a document using xi:include within a * xi:fallback using a DocumentBuilder. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXIncludeFallbackDOMPos() throws Exception { - String resultFile = USER_DIR + "doc_fallbackDOM.out"; + String resultFile = "doc_fallbackDOM.out"; String goldFile = GOLDEN_DIR + "doc_fallbackGold.xml"; String xmlFile = XML_DIR + "doc_fallback.xml"; try (FileOutputStream fos = new FileOutputStream(resultFile)) { @@ -250,11 +242,10 @@ public void testXIncludeFallbackDOMPos() throws Exception { * Test for xi:fallback where the fall back text is parsed as text. This * test uses a nested xi:include for the fallback test. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXIncludeFallbackTextPos() throws Exception { - String resultFile = USER_DIR + "doc_fallback_text.out"; + String resultFile = "doc_fallback_text.out"; String goldFile = GOLDEN_DIR + "doc_fallback_textGold.xml"; String xmlFile = XML_DIR + "doc_fallback_text.xml"; try (FileOutputStream fos = new FileOutputStream(resultFile)) { @@ -273,11 +264,10 @@ public void testXIncludeFallbackTextPos() throws Exception { /** * Test the XPointer element() framework with XInclude. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXpointerElementPos() throws Exception { - String resultFile = USER_DIR + "doc_xpointer_element.out"; + String resultFile = "doc_xpointer_element.out"; String goldFile = GOLDEN_DIR + "doc_xpointerGold.xml"; String xmlFile = XML_DIR + "doc_xpointer_element.xml"; try (FileOutputStream fos = new FileOutputStream(resultFile)) { @@ -297,11 +287,10 @@ public void testXpointerElementPos() throws Exception { /** * Test the XPointer framework with a SAX object. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXPointerPos() throws Exception { - String resultFile = USER_DIR + "doc_xpointer.out"; + String resultFile = "doc_xpointer.out"; String goldFile = GOLDEN_DIR + "doc_xpointerGold.xml"; String xmlFile = XML_DIR + "doc_xpointer.xml"; @@ -320,11 +309,10 @@ public void testXPointerPos() throws Exception { * Test if xi:include may reference the doc containing the include if the * parse type is text. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXIncludeLoopPos() throws Exception { - String resultFile = USER_DIR + "doc_xinc_loops.out"; + String resultFile = "doc_xinc_loops.out"; String goldFile = GOLDEN_DIR + "doc_xinc_loopGold.xml"; String xmlFile = XML_DIR + "doc_xinc_loops.xml"; @@ -347,11 +335,10 @@ public void testXIncludeLoopPos() throws Exception { * Test if two non nested xi:include elements can include the same document * with an xi:include statement. * - * @throws Exception If any errors occur. */ - @Test(groups = {"readWriteLocalFiles"}) + @Test public void testXIncludeNestedPos() throws Exception { - String resultFile = USER_DIR + "schedule.out"; + String resultFile = "schedule.out"; String goldFile = GOLDEN_DIR + "scheduleGold.xml"; String xmlFile = XML_DIR + "schedule.xml"; @@ -367,4 +354,20 @@ public void testXIncludeNestedPos() throws Exception { } assertTrue(compareDocumentWithGold(goldFile, resultFile)); } + + public static boolean compareDocumentWithGold(String goldfile, String resultFile) + throws ParserConfigurationException, SAXException, IOException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setCoalescing(true); + factory.setIgnoringElementContentWhitespace(true); + factory.setIgnoringComments(true); + DocumentBuilder db = factory.newDocumentBuilder(); + + Document goldD = db.parse(Paths.get(goldfile).toFile()); + goldD.normalizeDocument(); + Document resultD = db.parse(Paths.get(resultFile).toFile()); + resultD.normalizeDocument(); + return goldD.isEqualNode(resultD); + } } diff --git a/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/content/movies.xml.data b/test/jaxp/javax/xml/jaxp/functional/test/auctionportal/content/movies-utf16.xml similarity index 100% rename from test/jaxp/javax/xml/jaxp/functional/test/auctionportal/content/movies.xml.data rename to test/jaxp/javax/xml/jaxp/functional/test/auctionportal/content/movies-utf16.xml diff --git a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4511326.java b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4511326.java index a8bac488b8ec..a9bb95e20a87 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4511326.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4511326.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,19 +23,18 @@ package test.gaptest; -import java.io.StringReader; +import org.junit.jupiter.api.Test; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamSource; - -import org.testng.annotations.Test; +import java.io.StringReader; /* * @test * @bug 4511326 * @library /javax/xml/jaxp/libs - * @run testng/othervm test.gaptest.Bug4511326 + * @run junit/othervm test.gaptest.Bug4511326 * @summary In forwards-compatible mode the attribute isn't ignored */ public class Bug4511326 { diff --git a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4512806.java b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4512806.java index d27186bcf6dd..2e7b05eaaacc 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4512806.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4512806.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,24 +23,23 @@ package test.gaptest; -import static javax.xml.transform.OutputKeys.ENCODING; -import static javax.xml.transform.OutputKeys.INDENT; -import static org.testng.Assert.assertEquals; - -import java.io.StringReader; +import org.junit.jupiter.api.Test; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamSource; +import java.io.StringReader; -import org.testng.annotations.Test; +import static javax.xml.transform.OutputKeys.ENCODING; +import static javax.xml.transform.OutputKeys.INDENT; +import static org.junit.jupiter.api.Assertions.assertEquals; /* * @test * @bug 4512806 * @library /javax/xml/jaxp/libs - * @run testng/othervm test.gaptest.Bug4512806 + * @run junit/othervm test.gaptest.Bug4512806 * @summary test transformer.setOutputProperties(null) */ public class Bug4512806 { @@ -57,13 +56,13 @@ public void testProperty() throws TransformerConfigurationException { transformer.setOutputProperty(INDENT, "no"); transformer.setOutputProperty(ENCODING, "UTF-16"); - assertEquals(printPropertyValue(INDENT), "indent=no"); - assertEquals(printPropertyValue(ENCODING), "encoding=UTF-16"); + assertEquals("indent=no", printPropertyValue(INDENT)); + assertEquals("encoding=UTF-16", printPropertyValue(ENCODING)); transformer.setOutputProperties(null); - assertEquals(printPropertyValue(INDENT), "indent=yes"); - assertEquals(printPropertyValue(ENCODING), "encoding=UTF-8"); + assertEquals("indent=yes", printPropertyValue(INDENT)); + assertEquals("encoding=UTF-8", printPropertyValue(ENCODING)); } diff --git a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515047.java b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515047.java index 5fc68a28a6b6..8466c9cff799 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515047.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515047.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,8 @@ package test.gaptest; +import org.junit.jupiter.api.Test; + import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; @@ -31,13 +33,11 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; -import org.testng.annotations.Test; - /* * @test * @bug 4515047 * @library /javax/xml/jaxp/libs - * @run testng/othervm test.gaptest.Bug4515047 + * @run junit/othervm test.gaptest.Bug4515047 * @summary test transform an empty dom source */ public class Bug4515047 { diff --git a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515660.java b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515660.java index 014765f89d85..303db79f68bd 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515660.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4515660.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,14 +23,15 @@ package test.gaptest; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; -import static jaxp.library.JAXPTestUtilities.clearSystemProperty; - -import static org.testng.Assert.assertTrue; - -import java.io.IOException; -import java.io.StringReader; -import java.io.StringWriter; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.XMLFilterImpl; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; @@ -41,32 +42,31 @@ import javax.xml.transform.sax.SAXSource; import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.stream.StreamResult; +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.XMLFilterImpl; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @bug 4515660 * @library /javax/xml/jaxp/libs - * @run testng/othervm test.gaptest.Bug4515660 + * @run junit/othervm test.gaptest.Bug4515660 * @summary verify property org.xml.sax.driver is used by SAXTransformerFactory */ -@Test(singleThreaded = true) +@Execution(ExecutionMode.SAME_THREAD) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class Bug4515660 { - @BeforeClass + @BeforeAll public void setSaxDrier() { - setSystemProperty("org.xml.sax.driver", ReaderStub.class.getName()); + System.setProperty("org.xml.sax.driver", ReaderStub.class.getName()); } - @AfterClass + @AfterAll public void clearSaxDrier() { - clearSystemProperty("org.xml.sax.driver"); + System.clearProperty("org.xml.sax.driver"); } @Test @@ -88,9 +88,13 @@ public void testTransformer() throws TransformerException { @Test public void testSAXTransformerFactory() throws TransformerConfigurationException { - final String xsl = "\n" + "\n" - + " Hello World!\n" + "\n"; - + final String xsl = + """ + + + Hello World! + + """; ReaderStub.used = false; TransformerFactory transFactory = TransformerFactory.newInstance(); diff --git a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4848653.java b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4848653.java index b8fe9e068140..edf2ef2d1c1a 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4848653.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4848653.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,28 +22,27 @@ */ package test.gaptest; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static test.gaptest.GapTestConst.XML_DIR; - -import java.io.IOException; +import org.junit.jupiter.api.Test; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.XMLReader; import javax.xml.XMLConstants; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import java.io.IOException; +import java.nio.file.Path; -import org.testng.annotations.Test; -import org.xml.sax.ErrorHandler; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; -import org.xml.sax.XMLReader; +import static test.gaptest.GapTestConst.XML_DIR; /* * @test * @bug 4848653 * @library /javax/xml/jaxp/libs - * @run testng/othervm test.gaptest.Bug4848653 + * @run junit/othervm test.gaptest.Bug4848653 * @summary Verify JAXP schemaLanguage property is ignored if setValidating(false) */ public class Bug4848653 { @@ -56,7 +55,8 @@ public void test() throws IOException, SAXException, ParserConfigurationExceptio parser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaLanguage", XMLConstants.W3C_XML_SCHEMA_NS_URI); String filename = XML_DIR + "Bug4848653.xml"; - InputSource is = new InputSource(filenameToURL(filename)); + String uri = Path.of(filename).toUri().toASCIIString(); + InputSource is = new InputSource(uri); XMLReader xmlReader = parser.getXMLReader(); xmlReader.setErrorHandler(new MyErrorHandler()); xmlReader.parse(is); diff --git a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4858685.java b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4858685.java index 6ebcae5940d8..7667d6ad140d 100644 --- a/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4858685.java +++ b/test/jaxp/javax/xml/jaxp/functional/test/gaptest/Bug4858685.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,43 +22,43 @@ */ package test.gaptest; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static org.testng.Assert.assertEquals; -import static test.gaptest.GapTestConst.GOLDEN_DIR; -import static test.gaptest.GapTestConst.XML_DIR; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; +import org.junit.jupiter.api.Test; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.stream.StreamSource; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; -import org.testng.annotations.Test; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static test.gaptest.GapTestConst.GOLDEN_DIR; +import static test.gaptest.GapTestConst.XML_DIR; /* * @test * @bug 4858685 4894410 * @library /javax/xml/jaxp/libs - * @run testng/othervm test.gaptest.Bug4858685 + * @run junit/othervm test.gaptest.Bug4858685 * @summary test transforming text node */ public class Bug4858685 { @Test public void test() throws TransformerException, IOException { - String uri = XML_DIR + "certificate.xml"; + String filename = XML_DIR + "certificate.xml"; TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); // use URI as a StreamSource - StreamSource streamSource = new StreamSource(filenameToURL(uri)); + String uri = Path.of(filename).toUri().toASCIIString(); + StreamSource streamSource = new StreamSource(uri); DOMResult domResult = new DOMResult(); @@ -66,12 +66,8 @@ public void test() throws TransformerException, IOException { transformer.transform(streamSource, domResult); // dump DOM in a human readable form - String gotString = DOMDump.dumpDom(domResult.getNode()); - String goldenString = new String(Files.readAllBytes(Paths.get(GOLDEN_DIR + "Bug4858685.txt"))); - - assertEquals(gotString, goldenString); - + assertEquals(goldenString, DOMDump.dumpDom(domResult.getNode())); } /** diff --git a/test/jaxp/javax/xml/jaxp/libs/org/w3c/dom/ptests/DOMTestUtil.java b/test/jaxp/javax/xml/jaxp/libs/org/w3c/dom/ptests/DOMTestUtil.java index 6b32aa36e741..c42396061353 100644 --- a/test/jaxp/javax/xml/jaxp/libs/org/w3c/dom/ptests/DOMTestUtil.java +++ b/test/jaxp/javax/xml/jaxp/libs/org/w3c/dom/ptests/DOMTestUtil.java @@ -22,11 +22,9 @@ */ package org.w3c.dom.ptests; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; - import java.io.File; import java.io.IOException; +import java.nio.file.Path; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -39,15 +37,22 @@ * This class defines the path constant and common method */ public class DOMTestUtil { + private static final Path TEST_SRC = Path.of(System.getProperty("test.src")).toAbsolutePath(); + + private static String forwardSlashDir(Path p) { + // Convention in these tests is to include trailing '/' in directory strings. + return p.toString().replace(File.separatorChar, '/') + '/'; + } + /* * XML source file directory. */ - public static final String XML_DIR = getPathByClassName(DOMTestUtil.class, ".." + FILE_SEP + "xmlfiles"); + public static final String XML_DIR = forwardSlashDir(TEST_SRC.resolveSibling("xmlfiles")); /* * Golden validation files directory. */ - public static final String GOLDEN_DIR = getPathByClassName(DOMTestUtil.class, ".." + FILE_SEP + "xmlfiles" + FILE_SEP + "out"); + public static final String GOLDEN_DIR = XML_DIR + "out/"; /* * Error Message for DOMException being expected. diff --git a/test/jaxp/javax/xml/jaxp/libs/org/xml/sax/ptests/SAXTestConst.java b/test/jaxp/javax/xml/jaxp/libs/org/xml/sax/ptests/SAXTestConst.java index 6798d7879dc3..ea35a484cdf4 100644 --- a/test/jaxp/javax/xml/jaxp/libs/org/xml/sax/ptests/SAXTestConst.java +++ b/test/jaxp/javax/xml/jaxp/libs/org/xml/sax/ptests/SAXTestConst.java @@ -22,8 +22,8 @@ */ package org.xml.sax.ptests; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; +import java.io.File; +import java.nio.file.Path; /** * This is the Base test class provide basic support for JAXP SAX functional @@ -31,16 +31,20 @@ * has their own TestBase class. */ public class SAXTestConst { + private static final Path TEST_SRC = Path.of(System.getProperty("test.src")).toAbsolutePath(); + + private static String forwardSlashDir(Path p) { + // Convention in these tests is to include trailing '/' in directory strings. + return p.toString().replace(File.separatorChar, '/') + '/'; + } + /** * XML source file directory. */ - public static final String XML_DIR = getPathByClassName(SAXTestConst.class, - ".." + FILE_SEP + "xmlfiles"); - + public static final String XML_DIR = forwardSlashDir(TEST_SRC.resolveSibling("xmlfiles")); /** * Golden validation files directory. */ - public static final String GOLDEN_DIR = getPathByClassName(SAXTestConst.class, - ".." + FILE_SEP + "xmlfiles" + FILE_SEP + "out"); + public static final String GOLDEN_DIR = XML_DIR + "out/"; } diff --git a/test/jaxp/javax/xml/jaxp/libs/test/astro/AstroConstants.java b/test/jaxp/javax/xml/jaxp/libs/test/astro/AstroConstants.java index b3f2cb6e5c6e..9645832c8968 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/astro/AstroConstants.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/astro/AstroConstants.java @@ -23,10 +23,14 @@ package test.astro; +import java.nio.file.Path; + import static java.io.File.separator; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; public class AstroConstants { + private static final Path XML_FILES = + Path.of(System.getProperty("test.src")).resolve("xmlfiles").toAbsolutePath(); + // Query parameters : public static final double RA_MIN = 0.0; // hours @@ -36,7 +40,7 @@ public class AstroConstants { // Stylesheet source paths: - public static final String XSLPATH = getPathByClassName(AstroConstants.class, "xmlfiles" + separator + "xsl"); + public static final String XSLPATH = XML_FILES.resolve("xsl").toString() + separator; public static final String RAXSL = XSLPATH + "ra.xsl"; public static final String DECXSL = XSLPATH + "dec.xsl"; public static final String RADECXSL = XSLPATH + "radec.xsl"; @@ -51,10 +55,9 @@ public class AstroConstants { // Catalog references - public static final String ASTROCAT = getPathByClassName(AstroConstants.class, "xmlfiles") + "catalog.xml"; - + public static final String ASTROCAT = XML_FILES.resolve("catalog.xml").toString(); - public static final String GOLDEN_DIR = getPathByClassName(AstroConstants.class, "xmlfiles" + separator + "gold"); + public static final String GOLDEN_DIR = XML_FILES.resolve("gold").toString() + separator; public static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; public static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource"; } diff --git a/test/jaxp/javax/xml/jaxp/libs/test/astro/DOMFilterFactoryImpl.java b/test/jaxp/javax/xml/jaxp/libs/test/astro/DOMFilterFactoryImpl.java index e232fb61fcc6..32c346738469 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/astro/DOMFilterFactoryImpl.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/astro/DOMFilterFactoryImpl.java @@ -22,12 +22,12 @@ */ package test.astro; -import static jaxp.library.JAXPTestUtilities.filenameToURL; import static test.astro.AstroConstants.DECXSL; import static test.astro.AstroConstants.RAXSL; import static test.astro.AstroConstants.STYPEXSL; import java.io.IOException; +import java.nio.file.Path; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -75,6 +75,7 @@ protected String getStellarXsl() { private Document getStylesheetDOM(String xslfilename) throws SAXException, IOException, ParserConfigurationException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); - return dbf.newDocumentBuilder().parse(filenameToURL(xslfilename)); + String xslUri = Path.of(xslfilename).toUri().toASCIIString(); + return dbf.newDocumentBuilder().parse(xslUri); } } diff --git a/test/jaxp/javax/xml/jaxp/libs/test/astro/DOML3InputSourceFactoryImpl.java b/test/jaxp/javax/xml/jaxp/libs/test/astro/DOML3InputSourceFactoryImpl.java index 2f4170b21ce2..6fde37c79abb 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/astro/DOML3InputSourceFactoryImpl.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/astro/DOML3InputSourceFactoryImpl.java @@ -22,22 +22,6 @@ */ package test.astro; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static jaxp.library.JAXPTestUtilities.USER_DIR; -import static org.w3c.dom.ls.DOMImplementationLS.MODE_SYNCHRONOUS; -import static org.w3c.dom.traversal.NodeFilter.SHOW_ELEMENT; - -import java.io.ByteArrayInputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.file.Files; -import java.nio.file.Paths; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - import org.w3c.dom.DOMConfiguration; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -50,6 +34,20 @@ import org.w3c.dom.ls.LSSerializerFilter; import org.xml.sax.InputSource; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.ByteArrayInputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.w3c.dom.ls.DOMImplementationLS.MODE_SYNCHRONOUS; +import static org.w3c.dom.traversal.NodeFilter.SHOW_ELEMENT; + /* * A specialized implementation of an Input Source factory that utilizes * DOM Level 3 implementations to build a Document (DOM) from the @@ -72,7 +70,7 @@ public InputSource newInputSource(String filename) throws Exception { Document doc = null; LSInput src = impl.createLSInput(); // register the input file with the input source... - String systemId = filenameToURL(filename); + String systemId = Path.of(filename).toUri().toASCIIString(); src.setSystemId(systemId); try (Reader reader = new FileReader(filename)) { src.setCharacterStream(reader); @@ -82,7 +80,7 @@ public InputSource newInputSource(String filename) throws Exception { // Use DOM L3 LSSerializer (previously called a DOMWriter) // to serialize the xml doc DOM to a file stream. - String tmpCatalog = Files.createTempFile(Paths.get(USER_DIR), "catalog.xml", null).toString(); + String tmpCatalog = Files.createTempFile(Paths.get("."), "catalog.xml", null).toString(); LSSerializer domserializer = impl.createLSSerializer(); domserializer.setFilter(new MyDOMWriterFilter()); diff --git a/test/jaxp/javax/xml/jaxp/libs/test/astro/InputSourceFactoryImpl.java b/test/jaxp/javax/xml/jaxp/libs/test/astro/InputSourceFactoryImpl.java index a2c5b8319c82..d5ccdbf70e63 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/astro/InputSourceFactoryImpl.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/astro/InputSourceFactoryImpl.java @@ -22,10 +22,10 @@ */ package test.astro; -import static jaxp.library.JAXPTestUtilities.filenameToURL; - import org.xml.sax.InputSource; +import java.nio.file.Path; + /* * Default implementation of a input source factory. This is the most * straight forward way to create a sax input source and set it's @@ -38,7 +38,8 @@ public InputSourceFactoryImpl() { public InputSource newInputSource(String filename) { InputSource catSrc = new InputSource(filename); - catSrc.setSystemId(filenameToURL(filename)); + String uri = Path.of(filename).toUri().toASCIIString(); + catSrc.setSystemId(uri); return catSrc; } } diff --git a/test/jaxp/javax/xml/jaxp/libs/test/astro/SAXFilterFactoryImpl.java b/test/jaxp/javax/xml/jaxp/libs/test/astro/SAXFilterFactoryImpl.java index 4ef5d3cc2597..403c0a70460b 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/astro/SAXFilterFactoryImpl.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/astro/SAXFilterFactoryImpl.java @@ -22,22 +22,22 @@ */ package test.astro; -import static jaxp.library.JAXPTestUtilities.filenameToURL; -import static test.astro.AstroConstants.DECENTXSL; -import static test.astro.AstroConstants.DECXSL; -import static test.astro.AstroConstants.RAENTXSL; -import static test.astro.AstroConstants.STYPEXSL; -import static test.astro.AstroConstants.TOPTEMPLXSL; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.Source; import javax.xml.transform.sax.SAXSource; +import java.nio.file.Path; -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; +import static test.astro.AstroConstants.DECENTXSL; +import static test.astro.AstroConstants.DECXSL; +import static test.astro.AstroConstants.RAENTXSL; +import static test.astro.AstroConstants.STYPEXSL; +import static test.astro.AstroConstants.TOPTEMPLXSL; /* * Implementation of the filter factory interface that utilizes SAX @@ -51,7 +51,7 @@ * */ public class SAXFilterFactoryImpl extends SourceFilterFactory { - private EntityResolver entityResolver; + private final EntityResolver entityResolver; public SAXFilterFactoryImpl() { super(); @@ -60,7 +60,8 @@ public SAXFilterFactoryImpl() { @Override protected Source getSource(String xslFileName) throws SAXException, ParserConfigurationException { - SAXSource saxsource = new SAXSource(new InputSource(filenameToURL(xslFileName))); + String xslUri = Path.of(xslFileName).toUri().toASCIIString(); + SAXSource saxsource = new SAXSource(new InputSource(xslUri)); saxsource.setXMLReader(getXMLReader()); return saxsource; } @@ -97,7 +98,8 @@ private static class SAXFilterFactoryEntityResolver implements EntityResolver { public InputSource resolveEntity(String publicid, String sysId) { if (sysId.equals("http://astro.com/stylesheets/toptemplate")) { InputSource retval = new InputSource(TOPTEMPLXSL); - retval.setSystemId(filenameToURL(TOPTEMPLXSL)); + String xslUri = Path.of(TOPTEMPLXSL).toUri().toASCIIString(); + retval.setSystemId(xslUri); return retval; } else { return null; // use default behavior diff --git a/test/jaxp/javax/xml/jaxp/libs/test/astro/StreamFilterFactoryImpl.java b/test/jaxp/javax/xml/jaxp/libs/test/astro/StreamFilterFactoryImpl.java index 0978161312ec..38e864f68972 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/astro/StreamFilterFactoryImpl.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/astro/StreamFilterFactoryImpl.java @@ -22,7 +22,6 @@ */ package test.astro; -import static jaxp.library.JAXPTestUtilities.filenameToURL; import static test.astro.AstroConstants.DECXSL; import static test.astro.AstroConstants.RADECXSL; import static test.astro.AstroConstants.RAXSL; @@ -30,11 +29,13 @@ import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; +import java.nio.file.Path; public class StreamFilterFactoryImpl extends SourceFilterFactory { @Override protected Source getSource(String xslFileName) { - return new StreamSource(filenameToURL(xslFileName)); + String xslUri = Path.of(xslFileName).toUri().toASCIIString(); + return new StreamSource(xslUri); } @Override diff --git a/test/jaxp/javax/xml/jaxp/libs/test/astro/TemplatesFilterFactoryImpl.java b/test/jaxp/javax/xml/jaxp/libs/test/astro/TemplatesFilterFactoryImpl.java index 09a71b1a5345..6047d0c5ae87 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/astro/TemplatesFilterFactoryImpl.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/astro/TemplatesFilterFactoryImpl.java @@ -22,13 +22,13 @@ */ package test.astro; -import static jaxp.library.JAXPTestUtilities.filenameToURL; import static test.astro.AstroConstants.DECXSL; import static test.astro.AstroConstants.RAURIXSL; import static test.astro.AstroConstants.STYPEXSL; import static test.astro.AstroConstants.TOPTEMPLINCXSL; import java.io.IOException; +import java.nio.file.Path; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; @@ -94,7 +94,8 @@ protected TransformerHandler getTransformerHandler(String xslFileName) throws SA // create the stylesheet input source InputSource xslSrc = new InputSource(xslFileName); - xslSrc.setSystemId(filenameToURL(xslFileName)); + String xslUri = Path.of(xslFileName).toUri().toASCIIString(); + xslSrc.setSystemId(xslUri); // hook up the templates handler as the xsl content handler xmlreader.setContentHandler(templatesHandler); // call parse on the xsl input source @@ -113,7 +114,8 @@ private static class TemplatesFilterFactoryURIResolver implements URIResolver { public Source resolve(String href, String base) throws TransformerException { if ("http://astro.com/stylesheets/topleveltemplate".equals(href)) { StreamSource ss = new StreamSource(TOPTEMPLINCXSL); - ss.setSystemId(filenameToURL(TOPTEMPLINCXSL)); + String xslUri = Path.of(TOPTEMPLINCXSL).toUri().toASCIIString(); + ss.setSystemId(xslUri); return ss; } else { return null; diff --git a/test/jaxp/javax/xml/jaxp/libs/test/auctionportal/HiBidConstants.java b/test/jaxp/javax/xml/jaxp/libs/test/auctionportal/HiBidConstants.java index 5c812c52b042..7852540a308c 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/auctionportal/HiBidConstants.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/auctionportal/HiBidConstants.java @@ -22,21 +22,29 @@ */ package test.auctionportal; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; +import java.io.File; +import java.nio.file.Path; /** * This is the Base test class provide basic support for Auction portal test. */ public class HiBidConstants { + private static final Path TEST_SRC = Path.of(System.getProperty("test.src")).toAbsolutePath(); + + private static String forwardSlashDir(Path p) { + // Convention in these tests is to include trailing '/' in directory strings. + return p.toString().replace(File.separatorChar, '/') + '/'; + } + /** * XML source file directory. */ - public static final String XML_DIR = getPathByClassName(HiBidConstants.class, "content"); + public static final String XML_DIR = forwardSlashDir(TEST_SRC.resolve("content")); /** * Golden validation files directory. */ - public static final String GOLDEN_DIR = getPathByClassName(HiBidConstants.class, "golden"); + public static final String GOLDEN_DIR = forwardSlashDir(TEST_SRC.resolve("golden")); /** * Name space for account operation. diff --git a/test/jaxp/javax/xml/jaxp/libs/test/gaptest/GapTestConst.java b/test/jaxp/javax/xml/jaxp/libs/test/gaptest/GapTestConst.java index 237d0ac6ed27..648a6c2be101 100644 --- a/test/jaxp/javax/xml/jaxp/libs/test/gaptest/GapTestConst.java +++ b/test/jaxp/javax/xml/jaxp/libs/test/gaptest/GapTestConst.java @@ -22,20 +22,27 @@ */ package test.gaptest; -import static jaxp.library.JAXPTestUtilities.FILE_SEP; -import static jaxp.library.JAXPTestUtilities.getPathByClassName; +import java.io.File; +import java.nio.file.Path; /** * This class defines the path constant */ public class GapTestConst { + private static final Path TEST_SRC = Path.of(System.getProperty("test.src")).toAbsolutePath(); + + private static String forwardSlashDir(Path p) { + // Convention in these tests is to include trailing '/' in directory strings. + return p.toString().replace(File.separatorChar, '/') + '/'; + } + /** * XML source file directory. */ - public static final String XML_DIR = getPathByClassName(GapTestConst.class, "xmlfiles"); + public static final String XML_DIR = forwardSlashDir(TEST_SRC.resolve("xmlfiles")); /** * Golden validation files directory. */ - public static final String GOLDEN_DIR = getPathByClassName(GapTestConst.class, "xmlfiles" + FILE_SEP + "out"); + public static final String GOLDEN_DIR = XML_DIR + "out/"; } From abb9c33cedac730753dd3aaebd101532a1aa70a1 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Wed, 18 Mar 2026 01:14:16 +0000 Subject: [PATCH 81/97] 8380125: Formatting issue for few swing classes Reviewed-by: honkar, azvegint --- .../share/classes/javax/swing/AbstractButton.java | 4 ++-- .../javax/swing/ActionPropertyChangeListener.java | 2 +- .../share/classes/javax/swing/AncestorNotifier.java | 2 +- .../share/classes/javax/swing/ArrayTable.java | 6 +++--- .../share/classes/javax/swing/JComboBox.java | 4 ++-- .../share/classes/javax/swing/JList.java | 2 +- .../share/classes/javax/swing/JPopupMenu.java | 6 +++--- .../share/classes/javax/swing/JTextField.java | 4 ++-- .../share/classes/javax/swing/JTree.java | 2 +- .../share/classes/javax/swing/KeyboardManager.java | 2 +- .../share/classes/javax/swing/PopupFactory.java | 2 +- .../share/classes/javax/swing/SwingUtilities.java | 2 +- .../share/classes/javax/swing/UIDefaults.java | 4 ++-- .../javax/swing/plaf/basic/BasicComboPopup.java | 4 ++-- .../classes/javax/swing/plaf/basic/BasicListUI.java | 2 +- .../javax/swing/plaf/basic/BasicMenuBarUI.java | 10 +++++----- .../javax/swing/plaf/basic/BasicPopupMenuUI.java | 2 +- .../javax/swing/plaf/basic/BasicSpinnerUI.java | 2 +- .../javax/swing/plaf/basic/BasicSplitPaneUI.java | 12 ++++++------ .../javax/swing/plaf/basic/BasicTabbedPaneUI.java | 4 ++-- .../javax/swing/plaf/basic/BasicToolBarUI.java | 4 ++-- .../classes/javax/swing/plaf/basic/BasicTreeUI.java | 2 +- .../swing/plaf/nimbus/AbstractRegionPainter.java | 2 +- .../javax/swing/plaf/nimbus/NimbusLookAndFeel.java | 2 +- .../javax/swing/plaf/nimbus/SynthPainterImpl.java | 6 +++--- .../javax/swing/plaf/synth/SynthComboBoxUI.java | 2 +- .../javax/swing/plaf/synth/SynthScrollPaneUI.java | 2 +- .../classes/javax/swing/text/JTextComponent.java | 2 +- .../share/classes/javax/swing/text/TextAction.java | 4 ++-- .../javax/swing/tree/DefaultTreeCellEditor.java | 2 +- .../share/classes/sun/swing/SwingUtilities2.java | 6 +++--- 31 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/java.desktop/share/classes/javax/swing/AbstractButton.java b/src/java.desktop/share/classes/javax/swing/AbstractButton.java index d4112b5ed04f..a1961acce290 100644 --- a/src/java.desktop/share/classes/javax/swing/AbstractButton.java +++ b/src/java.desktop/share/classes/javax/swing/AbstractButton.java @@ -1070,13 +1070,13 @@ public void setAction(Action a) { Action oldValue = getAction(); if (action==null || !action.equals(a)) { action = a; - if (oldValue!=null) { + if (oldValue != null) { removeActionListener(oldValue); oldValue.removePropertyChangeListener(actionPropertyChangeListener); actionPropertyChangeListener = null; } configurePropertiesFromAction(action); - if (action!=null) { + if (action != null) { // Don't add if it is already a listener if (!isListener(ActionListener.class, action)) { addActionListener(action); diff --git a/src/java.desktop/share/classes/javax/swing/ActionPropertyChangeListener.java b/src/java.desktop/share/classes/javax/swing/ActionPropertyChangeListener.java index db12b6766b8b..19b9152a1b56 100644 --- a/src/java.desktop/share/classes/javax/swing/ActionPropertyChangeListener.java +++ b/src/java.desktop/share/classes/javax/swing/ActionPropertyChangeListener.java @@ -111,7 +111,7 @@ private void setTarget(T c) { while ((r = (OwnedWeakReference)queue.poll()) != null) { ActionPropertyChangeListener oldPCL = r.getOwner(); Action oldAction = oldPCL.getAction(); - if (oldAction!=null) { + if (oldAction != null) { oldAction.removePropertyChangeListener(oldPCL); } } diff --git a/src/java.desktop/share/classes/javax/swing/AncestorNotifier.java b/src/java.desktop/share/classes/javax/swing/AncestorNotifier.java index f3cfdd9d0ba9..a3ed463d2d56 100644 --- a/src/java.desktop/share/classes/javax/swing/AncestorNotifier.java +++ b/src/java.desktop/share/classes/javax/swing/AncestorNotifier.java @@ -213,7 +213,7 @@ public void componentHidden(ComponentEvent e) { public void propertyChange(PropertyChangeEvent evt) { String s = evt.getPropertyName(); - if (s!=null && (s.equals("parent") || s.equals("ancestor"))) { + if (s != null && (s.equals("parent") || s.equals("ancestor"))) { JComponent component = (JComponent)evt.getSource(); if (evt.getNewValue() != null) { diff --git a/src/java.desktop/share/classes/javax/swing/ArrayTable.java b/src/java.desktop/share/classes/javax/swing/ArrayTable.java index 7c38f9fb5770..282bd3454ca4 100644 --- a/src/java.desktop/share/classes/javax/swing/ArrayTable.java +++ b/src/java.desktop/share/classes/javax/swing/ArrayTable.java @@ -145,7 +145,7 @@ public void put(Object key, Object value){ */ public Object get(Object key) { Object value = null; - if (table !=null) { + if (table != null) { if (isArray()) { Object[] array = (Object[])table; for (int i = 0; i keyMap = containerMap.get(topContainer); - if (keyMap!=null) { + if (keyMap != null) { Vector v = (Vector)keyMap.get(JMenuBar.class); if (v != null) { v.removeElement(mb); diff --git a/src/java.desktop/share/classes/javax/swing/PopupFactory.java b/src/java.desktop/share/classes/javax/swing/PopupFactory.java index 208177fc55e8..7245820c289a 100644 --- a/src/java.desktop/share/classes/javax/swing/PopupFactory.java +++ b/src/java.desktop/share/classes/javax/swing/PopupFactory.java @@ -925,7 +925,7 @@ public void show() { add to that, otherwise add to the window. */ while (!(parent instanceof Window) && - (parent!=null)) { + (parent != null)) { parent = parent.getParent(); } diff --git a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java index afe1c444c318..ae5faf64f3bd 100644 --- a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java +++ b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java @@ -506,7 +506,7 @@ public static Window windowForComponent(Component c) { public static boolean isDescendingFrom(Component a,Component b) { if(a == b) return true; - for(Container p = a.getParent();p!=null;p=p.getParent()) + for(Container p = a.getParent(); p != null; p = p.getParent()) if(p == b) return true; return false; diff --git a/src/java.desktop/share/classes/javax/swing/UIDefaults.java b/src/java.desktop/share/classes/javax/swing/UIDefaults.java index d59a46dba326..67a739360f9a 100644 --- a/src/java.desktop/share/classes/javax/swing/UIDefaults.java +++ b/src/java.desktop/share/classes/javax/swing/UIDefaults.java @@ -1169,7 +1169,7 @@ public Object createValue(final UIDefaults table) { */ private Class[] getClassArray(Object[] args) { Class[] types = null; - if (args!=null) { + if (args != null) { types = new Class[args.length]; for (int i = 0; i< args.length; i++) { /* PENDING(ges): At present only the primitive types @@ -1199,7 +1199,7 @@ private Class[] getClassArray(Object[] args) { private String printArgs(Object[] array) { String s = "{"; - if (array !=null) { + if (array != null) { for (int i = 0 ; i < array.length-1; i++) { s = s.concat(array[i] + ","); } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java index 38798d4c94fe..42791772c2de 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java @@ -1070,11 +1070,11 @@ else if (propertyName == "componentOrientation") { ComponentOrientation o =(ComponentOrientation)e.getNewValue(); JList list = getList(); - if (list!=null && list.getComponentOrientation()!=o) { + if (list != null && list.getComponentOrientation()!=o) { list.setComponentOrientation(o); } - if (scroller!=null && scroller.getComponentOrientation()!=o) { + if (scroller != null && scroller.getComponentOrientation()!=o) { scroller.setComponentOrientation(o); } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java index 0a4aa03dce9d..ab5fdb12c5aa 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java @@ -876,7 +876,7 @@ protected void installDefaults() } Long l = (Long)UIManager.get("List.timeFactor"); - timeFactor = (l!=null) ? l.longValue() : 1000L; + timeFactor = (l != null) ? l.longValue() : 1000L; updateIsFileList(); } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuBarUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuBarUI.java index 2079aafd3a2e..b7310b56aefa 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuBarUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuBarUI.java @@ -125,7 +125,7 @@ protected void installListeners() { for (int i = 0; i < menuBar.getMenuCount(); i++) { JMenu menu = menuBar.getMenu(i); - if (menu!=null) + if (menu != null) menu.getModel().addChangeListener(changeListener); } menuBar.addContainerListener(containerListener); @@ -167,7 +167,7 @@ public void uninstallUI(JComponent c) { * Uninstalls default properties. */ protected void uninstallDefaults() { - if (menuBar!=null) { + if (menuBar != null) { LookAndFeel.uninstallBorder(menuBar); } } @@ -180,7 +180,7 @@ protected void uninstallListeners() { for (int i = 0; i < menuBar.getMenuCount(); i++) { JMenu menu = menuBar.getMenu(i); - if (menu !=null) + if (menu != null) menu.getModel().removeChangeListener(changeListener); } @@ -240,7 +240,7 @@ public void stateChanged(ChangeEvent e) { int i,c; for(i=0,c = menuBar.getMenuCount() ; i < c ; i++) { JMenu menu = menuBar.getMenu(i); - if(menu !=null && menu.isSelected()) { + if(menu != null && menu.isSelected()) { menuBar.getSelectionModel().setSelectedIndex(i); break; } @@ -277,7 +277,7 @@ public void actionPerformed(ActionEvent e) { MenuElement[] me; MenuElement[] subElements; JMenu menu = menuBar.getMenu(0); - if (menu!=null) { + if (menu != null) { me = new MenuElement[3]; me[0] = (MenuElement) menuBar; me[1] = (MenuElement) menu; diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java index 8df27d3e3cb6..b72a2d8a1403 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java @@ -908,7 +908,7 @@ public void eventDispatched(AWTEvent ev) { } boolean isInPopup(Component src) { - for (Component c=src; c!=null; c=c.getParent()) { + for (Component c=src; c != null; c=c.getParent()) { if (c instanceof Window) { break; } else if (c instanceof JPopupMenu) { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSpinnerUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSpinnerUI.java index 2b8713436974..7080fa3aac1e 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSpinnerUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSpinnerUI.java @@ -688,7 +688,7 @@ public void actionPerformed(ActionEvent e) { arrowButton = (JButton)e.getSource(); } } else { - if (arrowButton!=null && !arrowButton.getModel().isPressed() + if (arrowButton != null && !arrowButton.getModel().isPressed() && autoRepeatTimer.isRunning()) { autoRepeatTimer.stop(); spinner = null; diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneUI.java index 270181f46002..1d8c471e9ec2 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneUI.java @@ -2273,7 +2273,7 @@ else if (key == START_RESIZE) { JSplitPane parentSplitPane = (JSplitPane)SwingUtilities.getAncestorOfClass( JSplitPane.class, splitPane); - if (parentSplitPane!=null) { + if (parentSplitPane != null) { parentSplitPane.requestFocus(); } } @@ -2307,7 +2307,7 @@ private void moveFocus(JSplitPane splitPane, int direction) { } while (splitPane.isAncestorOf(focusOn) && !focusFrom.contains(focusOn)); } - if ( focusOn!=null && !splitPane.isAncestorOf(focusOn) ) { + if ( focusOn != null && !splitPane.isAncestorOf(focusOn) ) { focusOn.requestFocus(); } } @@ -2323,7 +2323,7 @@ private void toggleFocus(JSplitPane splitPane) { if (focusOn != null) { // don't change the focus if the new focused component belongs // to the same splitpane and the same side - if ( focus!=null && + if ( focus != null && ( (SwingUtilities.isDescendingFrom(focus, left) && SwingUtilities.isDescendingFrom(focusOn, left)) || (SwingUtilities.isDescendingFrom(focus, right) && @@ -2338,15 +2338,15 @@ private Component getNextSide(JSplitPane splitPane, Component focus) { Component left = splitPane.getLeftComponent(); Component right = splitPane.getRightComponent(); Component next; - if (focus!=null && SwingUtilities.isDescendingFrom(focus, left) && - right!=null) { + if (focus != null && SwingUtilities.isDescendingFrom(focus, left) && + right != null) { next = getFirstAvailableComponent(right); if (next != null) { return next; } } JSplitPane parentSplitPane = (JSplitPane)SwingUtilities.getAncestorOfClass(JSplitPane.class, splitPane); - if (parentSplitPane!=null) { + if (parentSplitPane != null) { // focus next side of the parent split pane next = getNextSide(parentSplitPane, focus); } else { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java index 842e8892c763..6e0d0101b9cc 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java @@ -528,7 +528,7 @@ protected void uninstallListeners() { } tabPane.removeContainerListener(getHandler()); - if (htmlViews!=null) { + if (htmlViews != null) { htmlViews.removeAllElements(); htmlViews = null; } @@ -4090,7 +4090,7 @@ private void updateHtmlViews(int index, boolean inserted) { setHtmlView(v, inserted, index); } } else { // Not HTML - if (htmlViews!=null) { // Add placeholder + if (htmlViews != null) { // Add placeholder setHtmlView(null, inserted, index); } // else nada! } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolBarUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolBarUI.java index 41ab6137976f..1bcd4a9be8dd 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolBarUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolBarUI.java @@ -980,7 +980,7 @@ public void setOrientation(int orientation) { toolBar.setOrientation( orientation ); - if (dragWindow !=null) + if (dragWindow != null) dragWindow.setOrientation(orientation); } @@ -1616,7 +1616,7 @@ public void setOrientation(int o) { this.orientation = o; Dimension size = getSize(); setSize(new Dimension(size.height, size.width)); - if (offset!=null) { + if (offset != null) { if( BasicGraphicsUtils.isLeftToRight(toolBar) ) { setOffset(new Point(offset.y, offset.x)); } else if( o == JToolBar.HORIZONTAL ) { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java index 28beaee69296..096fe7cc5f7f 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java @@ -943,7 +943,7 @@ protected void installDefaults() { lineTypeDashed = UIManager.getBoolean("Tree.lineTypeDashed"); Long l = (Long)UIManager.get("Tree.timeFactor"); - timeFactor = (l!=null) ? l.longValue() : 1000L; + timeFactor = (l != null) ? l.longValue() : 1000L; Object showsRootHandles = UIManager.get("Tree.showsRootHandles"); if (showsRootHandles != null) { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java index 1bdff19fa2dc..cfcc014940af 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java @@ -660,7 +660,7 @@ private void paintWith9SquareCaching(Graphics2D g, PaintContext ctx, ImageScalingHelper.paint(g, 0, 0, w, h, img, insets, dstInsets, ImageScalingHelper.PaintType.PAINT9_STRETCH, ImageScalingHelper.PAINT_ALL); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - oldScalingHints!=null?oldScalingHints:RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + oldScalingHints != null ? oldScalingHints:RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); } else { // render directly paint0(g, c, w, h, extendedCacheKeys); diff --git a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java index c9340a62368c..05fa3bbc9b6e 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java @@ -531,7 +531,7 @@ private NimbusProperty(String prefix, String state, String suffix) { public Object createValue(UIDefaults table) { Object obj = null; // check specified state - if (state!=null){ + if (state != null){ obj = uiDefaults.get(prefix+"["+state+"]."+suffix); } // check enabled state diff --git a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java index ca19a74b6ac0..1842073588bf 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java @@ -63,13 +63,13 @@ private void paint(Painter p, SynthContext ctx, Graphics g, int x, int y if (p != null) { if (g instanceof Graphics2D){ Graphics2D gfx = (Graphics2D)g; - if (transform!=null){ + if (transform != null) { gfx.transform(transform); } gfx.translate(x, y); p.paint(gfx, ctx.getComponent(), w, h); gfx.translate(-x, -y); - if (transform!=null){ + if (transform != null){ try { gfx.transform(transform.createInverse()); } catch (NoninvertibleTransformException e) { @@ -85,7 +85,7 @@ private void paint(Painter p, SynthContext ctx, Graphics g, int x, int y BufferedImage img = new BufferedImage(w,h, BufferedImage.TYPE_INT_ARGB); Graphics2D gfx = img.createGraphics(); - if (transform!=null){ + if (transform != null){ gfx.transform(transform); } p.paint(gfx, ctx.getComponent(), w, h); diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java index 77ca2a1fc05c..ad10e70a8373 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java @@ -814,7 +814,7 @@ public void focusLost(FocusEvent e) { public void propertyChange(PropertyChangeEvent evt) { ComboBoxEditor newEditor = comboBox.getEditor(); if (editor != newEditor){ - if (editorComponent!=null){ + if (editorComponent != null) { editorComponent.removeFocusListener(this); } editor = newEditor; diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthScrollPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthScrollPaneUI.java index 96541d51f671..0c3a17fbdbba 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthScrollPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthScrollPaneUI.java @@ -225,7 +225,7 @@ private SynthContext getContext(JComponent c, int state) { private int getComponentState(JComponent c) { int baseState = SynthLookAndFeel.getComponentState(c); - if (viewportViewFocusHandler!=null && viewportViewHasFocus){ + if (viewportViewFocusHandler != null && viewportViewHasFocus) { baseState = baseState | FOCUSED; } return baseState; diff --git a/src/java.desktop/share/classes/javax/swing/text/JTextComponent.java b/src/java.desktop/share/classes/javax/swing/text/JTextComponent.java index 59cee1e12ee1..f81ba9d66c2d 100644 --- a/src/java.desktop/share/classes/javax/swing/text/JTextComponent.java +++ b/src/java.desktop/share/classes/javax/swing/text/JTextComponent.java @@ -1181,7 +1181,7 @@ public static void loadKeymap(Keymap map, KeyBinding[] bindings, Action[] action Hashtable h = new Hashtable(); for (Action a : actions) { String value = (String)a.getValue(Action.NAME); - h.put((value!=null ? value:""), a); + h.put((value != null ? value : ""), a); } for (KeyBinding binding : bindings) { Action a = h.get(binding.actionName); diff --git a/src/java.desktop/share/classes/javax/swing/text/TextAction.java b/src/java.desktop/share/classes/javax/swing/text/TextAction.java index 5c11c994ab20..d05f7e24a6b6 100644 --- a/src/java.desktop/share/classes/javax/swing/text/TextAction.java +++ b/src/java.desktop/share/classes/javax/swing/text/TextAction.java @@ -107,11 +107,11 @@ public static final Action[] augmentList(Action[] list1, Action[] list2) { Hashtable h = new Hashtable(); for (Action a : list1) { String value = (String)a.getValue(Action.NAME); - h.put((value!=null ? value:""), a); + h.put((value != null ? value : ""), a); } for (Action a : list2) { String value = (String)a.getValue(Action.NAME); - h.put((value!=null ? value:""), a); + h.put((value != null ? value : ""), a); } Action[] actions = new Action[h.size()]; int index = 0; diff --git a/src/java.desktop/share/classes/javax/swing/tree/DefaultTreeCellEditor.java b/src/java.desktop/share/classes/javax/swing/tree/DefaultTreeCellEditor.java index 4662c95c01fc..3f3721dd30aa 100644 --- a/src/java.desktop/share/classes/javax/swing/tree/DefaultTreeCellEditor.java +++ b/src/java.desktop/share/classes/javax/swing/tree/DefaultTreeCellEditor.java @@ -259,7 +259,7 @@ public boolean isCellEditable(EventObject event) { ((MouseEvent)event).getY()); editable = (lastPath != null && path != null && lastPath.equals(path)); - if (path!=null) { + if (path != null) { lastRow = tree.getRowForPath(path); Object value = path.getLastPathComponent(); boolean isSelected = tree.isRowSelected(lastRow); diff --git a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java index 28b93ed1e2a0..399ef3e531d5 100644 --- a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java +++ b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java @@ -1646,17 +1646,17 @@ public static Component compositeRequestFocus(Component component) { if (container.isFocusCycleRoot()) { FocusTraversalPolicy policy = container.getFocusTraversalPolicy(); Component comp = policy.getDefaultComponent(container); - if (comp!=null) { + if (comp != null) { comp.requestFocus(FocusEvent.Cause.TRAVERSAL); return comp; } } Container rootAncestor = container.getFocusCycleRootAncestor(); - if (rootAncestor!=null) { + if (rootAncestor != null) { FocusTraversalPolicy policy = rootAncestor.getFocusTraversalPolicy(); Component comp = policy.getComponentAfter(rootAncestor, container); - if (comp!=null && SwingUtilities.isDescendingFrom(comp, container)) { + if (comp != null && SwingUtilities.isDescendingFrom(comp, container)) { comp.requestFocus(FocusEvent.Cause.TRAVERSAL); return comp; } From d52e5bd0357a074f74757f7a8256ed14a2e0eaee Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Wed, 18 Mar 2026 03:36:00 +0000 Subject: [PATCH 82/97] 8378727: [macOS] Missing dispatch_release for semaphores in CDesktopPeer Reviewed-by: honkar, kizune, azvegint, dmarkov --- .../macosx/native/libawt_lwawt/awt/CDesktopPeer.m | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m index 460749c363dd..faacef5adea0 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m @@ -70,6 +70,7 @@ dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)); // 1 second timeout // Asynchronous call to openURL + dispatch_retain(semaphore); [[NSWorkspace sharedWorkspace] openURLs:urls withApplicationAtURL:appURI configuration:configuration @@ -78,9 +79,11 @@ status = (OSStatus) error.code; } dispatch_semaphore_signal(semaphore); + dispatch_release(semaphore); }]; dispatch_semaphore_wait(semaphore, timeout); + dispatch_release(semaphore); JNI_COCOA_EXIT(env); return status; @@ -146,6 +149,7 @@ dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)); // 1 second timeout // Asynchronous call - openURLs:withApplicationAtURL + dispatch_retain(semaphore); [[NSWorkspace sharedWorkspace] openURLs:urls withApplicationAtURL:appURI configuration:configuration @@ -154,9 +158,11 @@ status = (OSStatus) error.code; } dispatch_semaphore_signal(semaphore); + dispatch_release(semaphore); }]; dispatch_semaphore_wait(semaphore, timeout); + dispatch_release(semaphore); [urlToOpen release]; JNI_COCOA_EXIT(env); From 31de288c0c67448487eabd46348bef97a1a60846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galder=20Zamarre=C3=B1o?= Date: Wed, 18 Mar 2026 06:49:20 +0000 Subject: [PATCH 83/97] 8367399: C2 SuperWord: add IR rules for MemorySegment cases from JDK-8329077 Reviewed-by: fyang, epeter --- .../TestCompatibleUseDefTypeSize.java | 218 +++++++++--------- 1 file changed, 103 insertions(+), 115 deletions(-) diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java index 873533aaba84..e6edda9085a0 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,13 +25,15 @@ package compiler.loopopts.superword; import compiler.lib.ir_framework.*; +import compiler.lib.verify.Verify; import jdk.test.lib.Utils; import jdk.test.whitebox.WhiteBox; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; import java.lang.reflect.Array; import java.util.Map; import java.util.HashMap; import java.util.Random; -import java.nio.ByteOrder; /* * @test @@ -61,6 +64,8 @@ public class TestCompatibleUseDefTypeSize { float[] bF; double[] aD; double[] bD; + MemorySegment aMSF; + MemorySegment aMSD; // List of tests Map tests = new HashMap(); @@ -92,6 +97,8 @@ public TestCompatibleUseDefTypeSize() { bF = generateF(); aD = generateD(); bD = generateD(); + aMSF = generateMemorySegmentF(); + aMSD = generateMemorySegmentD(); // Add all tests to list tests.put("test0", () -> { return test0(aB.clone(), bC.clone()); }); @@ -122,6 +129,10 @@ public TestCompatibleUseDefTypeSize() { tests.put("testLongToShort", () -> { return testLongToShort(aL.clone(), bS.clone()); }); tests.put("testLongToChar", () -> { return testLongToChar(aL.clone(), bC.clone()); }); tests.put("testLongToInt", () -> { return testLongToInt(aL.clone(), bI.clone()); }); + tests.put("testFloatToIntMemorySegment", () -> { return testFloatToIntMemorySegment(copyF(aMSF), bF.clone()); }); + tests.put("testDoubleToLongMemorySegment", () -> { return testDoubleToLongMemorySegment(copyD(aMSD), bD.clone()); }); + tests.put("testIntToFloatMemorySegment", () -> { return testIntToFloatMemorySegment(copyF(aMSF), bF.clone()); }); + tests.put("testLongToDoubleMemorySegment", () -> { return testLongToDoubleMemorySegment(copyD(aMSD), bD.clone()); }); // Compute gold value for all test methods before compilation for (Map.Entry entry : tests.entrySet()) { @@ -160,7 +171,11 @@ public TestCompatibleUseDefTypeSize() { "testLongToByte", "testLongToShort", "testLongToChar", - "testLongToInt"}) + "testLongToInt", + "testFloatToIntMemorySegment", + "testDoubleToLongMemorySegment", + "testIntToFloatMemorySegment", + "testLongToDoubleMemorySegment"}) public void runTests() { for (Map.Entry entry : tests.entrySet()) { String name = entry.getKey(); @@ -170,7 +185,7 @@ public void runTests() { // Compute new result Object[] result = test.run(); // Compare gold and new result - verify(name, gold, result); + Verify.checkEQ(gold, result); } } @@ -230,119 +245,32 @@ static double[] generateD() { return a; } - static void verify(String name, Object[] gold, Object[] result) { - if (gold.length != result.length) { - throw new RuntimeException("verify " + name + ": not the same number of outputs: gold.length = " + - gold.length + ", result.length = " + result.length); - } - for (int i = 0; i < gold.length; i++) { - Object g = gold[i]; - Object r = result[i]; - if (g.getClass() != r.getClass() || !g.getClass().isArray() || !r.getClass().isArray()) { - throw new RuntimeException("verify " + name + ": must both be array of same type:" + - " gold[" + i + "].getClass() = " + g.getClass().getSimpleName() + - " result[" + i + "].getClass() = " + r.getClass().getSimpleName()); - } - if (g == r) { - throw new RuntimeException("verify " + name + ": should be two separate arrays (with identical content):" + - " gold[" + i + "] == result[" + i + "]"); - } - if (Array.getLength(g) != Array.getLength(r)) { - throw new RuntimeException("verify " + name + ": arrays must have same length:" + - " gold[" + i + "].length = " + Array.getLength(g) + - " result[" + i + "].length = " + Array.getLength(r)); - } - Class c = g.getClass().getComponentType(); - if (c == byte.class) { - verifyB(name, i, (byte[])g, (byte[])r); - } else if (c == short.class) { - verifyS(name, i, (short[])g, (short[])r); - } else if (c == char.class) { - verifyC(name, i, (char[])g, (char[])r); - } else if (c == int.class) { - verifyI(name, i, (int[])g, (int[])r); - } else if (c == long.class) { - verifyL(name, i, (long[])g, (long[])r); - } else if (c == float.class) { - verifyF(name, i, (float[])g, (float[])r); - } else if (c == double.class) { - verifyD(name, i, (double[])g, (double[])r); - } else { - throw new RuntimeException("verify " + name + ": array type not supported for verify:" + - " gold[" + i + "].getClass() = " + g.getClass().getSimpleName() + - " result[" + i + "].getClass() = " + r.getClass().getSimpleName()); - } - } - } - - static void verifyB(String name, int i, byte[] g, byte[] r) { - for (int j = 0; j < g.length; j++) { - if (g[j] != r[j]) { - throw new RuntimeException("verify " + name + ": arrays must have same content:" + - " gold[" + i + "][" + j + "] = " + g[j] + - " result[" + i + "][" + j + "] = " + r[j]); - } - } - } - - static void verifyS(String name, int i, short[] g, short[] r) { - for (int j = 0; j < g.length; j++) { - if (g[j] != r[j]) { - throw new RuntimeException("verify " + name + ": arrays must have same content:" + - " gold[" + i + "][" + j + "] = " + g[j] + - " result[" + i + "][" + j + "] = " + r[j]); - } - } - } - - static void verifyC(String name, int i, char[] g, char[] r) { - for (int j = 0; j < g.length; j++) { - if (g[j] != r[j]) { - throw new RuntimeException("verify " + name + ": arrays must have same content:" + - " gold[" + i + "][" + j + "] = " + g[j] + - " result[" + i + "][" + j + "] = " + r[j]); - } - } - } - - static void verifyI(String name, int i, int[] g, int[] r) { - for (int j = 0; j < g.length; j++) { - if (g[j] != r[j]) { - throw new RuntimeException("verify " + name + ": arrays must have same content:" + - " gold[" + i + "][" + j + "] = " + g[j] + - " result[" + i + "][" + j + "] = " + r[j]); - } - } - } - - static void verifyL(String name, int i, long[] g, long[] r) { - for (int j = 0; j < g.length; j++) { - if (g[j] != r[j]) { - throw new RuntimeException("verify " + name + ": arrays must have same content:" + - " gold[" + i + "][" + j + "] = " + g[j] + - " result[" + i + "][" + j + "] = " + r[j]); - } - } - } - - static void verifyF(String name, int i, float[] g, float[] r) { - for (int j = 0; j < g.length; j++) { - if (Float.floatToIntBits(g[j]) != Float.floatToIntBits(r[j])) { - throw new RuntimeException("verify " + name + ": arrays must have same content:" + - " gold[" + i + "][" + j + "] = " + g[j] + - " result[" + i + "][" + j + "] = " + r[j]); - } - } - } - - static void verifyD(String name, int i, double[] g, double[] r) { - for (int j = 0; j < g.length; j++) { - if (Double.doubleToLongBits(g[j]) != Double.doubleToLongBits(r[j])) { - throw new RuntimeException("verify " + name + ": arrays must have same content:" + - " gold[" + i + "][" + j + "] = " + g[j] + - " result[" + i + "][" + j + "] = " + r[j]); - } + static MemorySegment generateMemorySegmentF() { + MemorySegment a = MemorySegment.ofArray(new float[RANGE]); + for (int i = 0; i < (int) a.byteSize(); i += 8) { + a.set(ValueLayout.JAVA_LONG_UNALIGNED, i, RANDOM.nextLong()); } + return a; + } + + MemorySegment copyF(MemorySegment src) { + MemorySegment dst = generateMemorySegmentF(); + MemorySegment.copy(src, 0, dst, 0, src.byteSize()); + return dst; + } + + static MemorySegment generateMemorySegmentD() { + MemorySegment a = MemorySegment.ofArray(new double[RANGE]); + for (int i = 0; i < (int) a.byteSize(); i += 8) { + a.set(ValueLayout.JAVA_LONG_UNALIGNED, i, RANDOM.nextLong()); + } + return a; + } + + MemorySegment copyD(MemorySegment src) { + MemorySegment dst = generateMemorySegmentD(); + MemorySegment.copy(src, 0, dst, 0, src.byteSize()); + return dst; } @Test @@ -707,4 +635,64 @@ public Object[] testIntToLong(int[] ints, long[] res) { return new Object[] { ints, res }; } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", "> 0", + IRNode.STORE_VECTOR, "> 0", + IRNode.VECTOR_REINTERPRET, "> 0"}, + applyIf = {"AlignVector", "false"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) + static Object[] testFloatToIntMemorySegment(MemorySegment a, float[] b) { + for (int i = 0; i < RANGE; i++) { + a.set(ValueLayout.JAVA_FLOAT_UNALIGNED, 4L * i, b[i]); + } + + return new Object[]{ a, b }; + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", "> 0", + IRNode.STORE_VECTOR, "> 0", + IRNode.VECTOR_REINTERPRET, "> 0"}, + applyIf = {"AlignVector", "false"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) + static Object[] testDoubleToLongMemorySegment(MemorySegment a, double[] b) { + for (int i = 0; i < RANGE; i++) { + a.set(ValueLayout.JAVA_DOUBLE_UNALIGNED, 8L * i, b[i]); + } + + return new Object[]{ a, b }; + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.STORE_VECTOR, "> 0", + IRNode.VECTOR_REINTERPRET, "> 0"}, + applyIf = {"AlignVector", "false"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) + static Object[] testIntToFloatMemorySegment(MemorySegment a, float[] b) { + for (int i = 0; i < RANGE; i++) { + b[i] = a.get(ValueLayout.JAVA_FLOAT_UNALIGNED, 4L * i); + } + + return new Object[]{ a, b }; + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_L, "> 0", + IRNode.STORE_VECTOR, "> 0", + IRNode.VECTOR_REINTERPRET, "> 0"}, + applyIf = {"AlignVector", "false"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) + static Object[] testLongToDoubleMemorySegment(MemorySegment a, double[] b) { + for (int i = 0; i < RANGE; i++) { + b[i] = a.get(ValueLayout.JAVA_DOUBLE_UNALIGNED, 8L * i); + } + + return new Object[]{ a, b }; + } } From d8f19bf961f1f25a029943b6390f8b9c95f31304 Mon Sep 17 00:00:00 2001 From: Dusan Balek Date: Wed, 18 Mar 2026 07:04:25 +0000 Subject: [PATCH 84/97] 8347418: ConsoleIOContext.countTrailintBackslashes causes NullPointerException Reviewed-by: jlahoda --- .../jshell/tool/ConsoleIOContext.java | 2 + .../jshell/ConcurrentHistoryLoadingTest.java | 80 +++++++++++++++++++ .../langtools/jdk/jshell/ReplToolTesting.java | 14 ++-- .../jdk/jshell/TerminalNoExecTest.java | 8 +- 4 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 test/langtools/jdk/jshell/ConcurrentHistoryLoadingTest.java diff --git a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java index 32664504d209..eba81b4be075 100644 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java @@ -49,6 +49,7 @@ import java.util.ListIterator; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutorService; @@ -202,6 +203,7 @@ public int readBuffered(byte[] b) throws IOException { .filter(key -> key.startsWith(HISTORY_LINE_PREFIX)) .sorted() .map(key -> repl.prefs.get(key)) + .filter(Objects::nonNull) .forEach(loadHistory::add); for (ListIterator it = loadHistory.listIterator(); it.hasNext(); ) { diff --git a/test/langtools/jdk/jshell/ConcurrentHistoryLoadingTest.java b/test/langtools/jdk/jshell/ConcurrentHistoryLoadingTest.java new file mode 100644 index 000000000000..926dbbf50b4e --- /dev/null +++ b/test/langtools/jdk/jshell/ConcurrentHistoryLoadingTest.java @@ -0,0 +1,80 @@ +/* + * 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.ByteArrayInputStream; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.prefs.Preferences; + +import jdk.jshell.tool.JavaShellToolBuilder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/* + * @test + * @bug 8347418 + * @summary Verify that loading of JShell history doesn't lead to a + * NullPointerException when the Preferences are modified concurrently. + * @run junit ConcurrentHistoryLoadingTest + */ +public class ConcurrentHistoryLoadingTest { + + private static final String HISTORY_LINE_PREFIX = "HISTORY_LINE_"; + + @Test + public void testConcurrentHistoryLoading() throws Throwable { + AtomicBoolean removeOnAccess = new AtomicBoolean(); + Preferences testPrefs = new ReplToolTesting.MemoryPreferences() { + @Override + protected String getSpi(String key) { + String result = super.getSpi(key); + if (key.startsWith(HISTORY_LINE_PREFIX) && removeOnAccess.getAndSet(false)) { + for (String key2Remote : keysSpi()) { + remove(key2Remote); + } + } + return result; + } + }; + StringBuilder input = new StringBuilder(); + int max = 10; + for (int j = 0; j < max; j++) { + input.append("int x").append(j).append(" = 42\n"); + } + JavaShellToolBuilder + .builder() + .persistence(testPrefs) + .in(new ByteArrayInputStream(input.toString().getBytes()), null) + .start(); + Assertions.assertEquals(10, Arrays.stream(testPrefs.keys()) + .filter(key -> key.startsWith(HISTORY_LINE_PREFIX)) + .count()); + removeOnAccess.set(true); + JavaShellToolBuilder + .builder() + .persistence(testPrefs) + .in(new ByteArrayInputStream(input.toString().getBytes()), null) + .start(); + + } +} diff --git a/test/langtools/jdk/jshell/ReplToolTesting.java b/test/langtools/jdk/jshell/ReplToolTesting.java index 429a0a7ce028..2dabf29e1f92 100644 --- a/test/langtools/jdk/jshell/ReplToolTesting.java +++ b/test/langtools/jdk/jshell/ReplToolTesting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -914,7 +914,7 @@ public synchronized void write(byte b[], int off, int len) { } } - public static final class MemoryPreferences extends AbstractPreferences { + public static class MemoryPreferences extends AbstractPreferences { private final Map values = new HashMap<>(); private final Map nodes = new HashMap<>(); @@ -943,17 +943,17 @@ protected void removeSpi(String key) { } @Override - protected void removeNodeSpi() throws BackingStoreException { + protected void removeNodeSpi() { ((MemoryPreferences) parent()).nodes.remove(name()); } @Override - protected String[] keysSpi() throws BackingStoreException { + protected String[] keysSpi() { return values.keySet().toArray(new String[0]); } @Override - protected String[] childrenNamesSpi() throws BackingStoreException { + protected String[] childrenNamesSpi() { return nodes.keySet().toArray(new String[0]); } @@ -963,11 +963,11 @@ protected AbstractPreferences childSpi(String name) { } @Override - protected void syncSpi() throws BackingStoreException { + protected void syncSpi() { } @Override - protected void flushSpi() throws BackingStoreException { + protected void flushSpi() { } } diff --git a/test/langtools/jdk/jshell/TerminalNoExecTest.java b/test/langtools/jdk/jshell/TerminalNoExecTest.java index 3d76157fd26f..d7cd20046afa 100644 --- a/test/langtools/jdk/jshell/TerminalNoExecTest.java +++ b/test/langtools/jdk/jshell/TerminalNoExecTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.concurrent.TimeUnit; +import java.util.HashMap; import java.util.concurrent.atomic.AtomicBoolean; import jdk.jfr.consumer.RecordingStream; import jdk.jshell.tool.JavaShellToolBuilder; @@ -58,7 +58,9 @@ public static void main(String... args) throws Exception { spawnedNewProcess.set(true); }); rs.startAsync(); - JavaShellToolBuilder.builder().run("--execution=local", "--no-startup"); + JavaShellToolBuilder.builder() + .persistence(new HashMap<>()) + .run("--execution=local", "--no-startup"); rs.stop(); } if (spawnedNewProcess.get()) { From 706fbb3044ef56cad14cf4e413b24042a3ea3f13 Mon Sep 17 00:00:00 2001 From: Volkan Yazici Date: Wed, 18 Mar 2026 08:25:29 +0000 Subject: [PATCH 85/97] 8378431: Move input validation checks to Java for java.lang.StringUTF16 intrinsics Reviewed-by: dfenacci, rgiulietti, rriggs --- src/hotspot/share/classfile/vmIntrinsics.hpp | 4 +- src/hotspot/share/opto/library_call.cpp | 214 ++++++++---------- src/hotspot/share/opto/library_call.hpp | 4 +- .../share/classes/java/lang/StringUTF16.java | 112 ++++++--- .../TestStringIntrinsicRangeChecks.java | 10 +- 5 files changed, 198 insertions(+), 146 deletions(-) diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 67817682ced9..e84acd622843 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -368,10 +368,10 @@ class methodHandle; do_intrinsic(_inflateStringB, java_lang_StringLatin1, inflate_name, inflateB_signature, F_S) \ do_signature(inflateB_signature, "([BI[BII)V") \ do_intrinsic(_toBytesStringU, java_lang_StringUTF16, toBytes_name, toBytesU_signature, F_S) \ - do_name( toBytes_name, "toBytes") \ + do_name( toBytes_name, "toBytes0") \ do_signature(toBytesU_signature, "([CII)[B") \ do_intrinsic(_getCharsStringU, java_lang_StringUTF16, getCharsU_name, getCharsU_signature, F_S) \ - do_name( getCharsU_name, "getChars") \ + do_name( getCharsU_name, "getChars0") \ do_signature(getCharsU_signature, "([BII[CI)V") \ do_intrinsic(_getCharStringU, java_lang_StringUTF16, getChar_name, getCharStringU_signature, F_S) \ do_signature(getCharStringU_signature, "([BI)C") \ diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 501e35ae5cb9..ffc798019b43 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -843,6 +843,22 @@ void LibraryCallKit::set_result(RegionNode* region, PhiNode* value) { assert(value->type()->basic_type() == result()->bottom_type()->basic_type(), "sanity"); } +RegionNode* LibraryCallKit::create_bailout() { + RegionNode* bailout = new RegionNode(1); + record_for_igvn(bailout); + return bailout; +} + +bool LibraryCallKit::check_bailout(RegionNode* bailout) { + if (bailout->req() > 1) { + bailout = _gvn.transform(bailout)->as_Region(); + Node* frame = _gvn.transform(new ParmNode(C->start(), TypeFunc::FramePtr)); + Node* halt = _gvn.transform(new HaltNode(bailout, frame, "unexpected guard failure in intrinsic")); + C->root()->add_req(halt); + } + return stopped(); +} + //------------------------------generate_guard--------------------------- // Helper function for generating guarded fast-slow graph structures. // The given 'test', if true, guards a slow path. If the test fails @@ -951,36 +967,19 @@ void LibraryCallKit::generate_string_range_check(Node* array, Node* offset, Node* count, bool char_count, - bool halt_on_oob) { + RegionNode* region) { if (stopped()) { return; // already stopped } - RegionNode* bailout = new RegionNode(1); - record_for_igvn(bailout); if (char_count) { // Convert char count to byte count count = _gvn.transform(new LShiftINode(count, intcon(1))); } - // Offset and count must not be negative - generate_negative_guard(offset, bailout, nullptr, halt_on_oob); - generate_negative_guard(count, bailout, nullptr, halt_on_oob); + generate_negative_guard(offset, region, nullptr, true); + generate_negative_guard(count, region, nullptr, true); // Offset + count must not exceed length of array - generate_limit_guard(offset, count, load_array_length(array), bailout, halt_on_oob); - - if (bailout->req() > 1) { - if (halt_on_oob) { - bailout = _gvn.transform(bailout)->as_Region(); - Node* frame = _gvn.transform(new ParmNode(C->start(), TypeFunc::FramePtr)); - Node* halt = _gvn.transform(new HaltNode(bailout, frame, "unexpected guard failure in intrinsic")); - C->root()->add_req(halt); - } else { - PreserveJVMState pjvms(this); - set_control(_gvn.transform(bailout)); - uncommon_trap(Deoptimization::Reason_intrinsic, - Deoptimization::Action_maybe_recompile); - } - } + generate_limit_guard(offset, count, load_array_length(array), region, true); } Node* LibraryCallKit::current_thread_helper(Node*& tls_output, ByteSize handle_offset, @@ -1139,10 +1138,6 @@ bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) { //------------------------------inline_countPositives------------------------------ // int java.lang.StringCoding#countPositives0(byte[] ba, int off, int len) bool LibraryCallKit::inline_countPositives() { - if (too_many_traps(Deoptimization::Reason_intrinsic)) { - return false; - } - assert(callee()->signature()->size() == 3, "countPositives has 3 parameters"); // no receiver since it is static method Node* ba = argument(0); @@ -1150,8 +1145,9 @@ bool LibraryCallKit::inline_countPositives() { Node* len = argument(2); ba = must_be_not_null(ba, true); - generate_string_range_check(ba, offset, len, false, true); - if (stopped()) { + RegionNode* bailout = create_bailout(); + generate_string_range_check(ba, offset, len, false, bailout); + if (check_bailout(bailout)) { return true; } @@ -1283,9 +1279,6 @@ bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) { //-----------------------------inline_string_indexOfI----------------------- bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) { - if (too_many_traps(Deoptimization::Reason_intrinsic)) { - return false; - } if (!Matcher::match_rule_supported(Op_StrIndexOf)) { return false; } @@ -1307,9 +1300,10 @@ bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) { Node* tgt_start = array_element_address(tgt, intcon(0), T_BYTE); // Range checks - generate_string_range_check(src, src_offset, src_count, ae != StrIntrinsicNode::LL, true); - generate_string_range_check(tgt, intcon(0), tgt_count, ae == StrIntrinsicNode::UU, true); - if (stopped()) { + RegionNode* bailout = create_bailout(); + generate_string_range_check(src, src_offset, src_count, ae != StrIntrinsicNode::LL, bailout); + generate_string_range_check(tgt, intcon(0), tgt_count, ae == StrIntrinsicNode::UU, bailout); + if (check_bailout(bailout)) { return true; } @@ -1404,7 +1398,11 @@ bool LibraryCallKit::inline_string_indexOfChar(StrIntrinsicNode::ArgEnc ae) { Node* src_count = _gvn.transform(new SubINode(max, from_index)); // Range checks - generate_string_range_check(src, src_offset, src_count, ae == StrIntrinsicNode::U, true); + RegionNode* bailout = create_bailout(); + generate_string_range_check(src, src_offset, src_count, ae == StrIntrinsicNode::U, bailout); + if (check_bailout(bailout)) { + return true; + } // Check for int_ch >= 0 Node* int_ch_cmp = _gvn.transform(new CmpINode(int_ch, intcon(0))); @@ -1454,9 +1452,6 @@ bool LibraryCallKit::inline_string_indexOfChar(StrIntrinsicNode::ArgEnc ae) { // void StringLatin1.inflate0(byte[] src, int srcOff, char[] dst, int dstOff, int len) // void StringLatin1.inflate0(byte[] src, int srcOff, byte[] dst, int dstOff, int len) bool LibraryCallKit::inline_string_copy(bool compress) { - if (too_many_traps(Deoptimization::Reason_intrinsic)) { - return false; - } int nargs = 5; // 2 oops, 3 ints assert(callee()->signature()->size() == nargs, "string copy has 5 arguments"); @@ -1495,9 +1490,10 @@ bool LibraryCallKit::inline_string_copy(bool compress) { } // Range checks - generate_string_range_check(src, src_offset, length, convert_src, true); - generate_string_range_check(dst, dst_offset, length, convert_dst, true); - if (stopped()) { + RegionNode* bailout = create_bailout(); + generate_string_range_check(src, src_offset, length, convert_src, bailout); + generate_string_range_check(dst, dst_offset, length, convert_dst, bailout); + if (check_bailout(bailout)) { return true; } @@ -1545,12 +1541,10 @@ bool LibraryCallKit::inline_string_copy(bool compress) { #endif //_LP64 //------------------------inline_string_toBytesU-------------------------- -// public static byte[] StringUTF16.toBytes(char[] value, int off, int len) +// public static byte[] StringUTF16.toBytes0(char[] value, int off, int len) bool LibraryCallKit::inline_string_toBytesU() { - if (too_many_traps(Deoptimization::Reason_intrinsic)) { - return false; - } // Get the arguments. + assert(callee()->signature()->size() == 3, "character array encoder requires 3 arguments"); Node* value = argument(0); Node* offset = argument(1); Node* length = argument(2); @@ -1558,30 +1552,18 @@ bool LibraryCallKit::inline_string_toBytesU() { Node* newcopy = nullptr; // Set the original stack and the reexecute bit for the interpreter to reexecute - // the bytecode that invokes StringUTF16.toBytes() if deoptimization happens. + // the bytecode that invokes StringUTF16.toBytes0() if deoptimization happens. { PreserveReexecuteState preexecs(this); jvms()->set_should_reexecute(true); - // Check if a null path was taken unconditionally. - value = null_check(value); - - RegionNode* bailout = new RegionNode(1); - record_for_igvn(bailout); - - // Range checks - generate_negative_guard(offset, bailout); - generate_negative_guard(length, bailout); - generate_limit_guard(offset, length, load_array_length(value), bailout); + value = must_be_not_null(value, true); + RegionNode* bailout = create_bailout(); + generate_negative_guard(offset, bailout, nullptr, true); + generate_negative_guard(length, bailout, nullptr, true); + generate_limit_guard(offset, length, load_array_length(value), bailout, true); // Make sure that resulting byte[] length does not overflow Integer.MAX_VALUE - generate_limit_guard(length, intcon(0), intcon(max_jint/2), bailout); - - if (bailout->req() > 1) { - PreserveJVMState pjvms(this); - set_control(_gvn.transform(bailout)); - uncommon_trap(Deoptimization::Reason_intrinsic, - Deoptimization::Action_maybe_recompile); - } - if (stopped()) { + generate_limit_guard(length, intcon(0), intcon(max_jint/2), bailout, true); + if (check_bailout(bailout)) { return true; } @@ -1640,12 +1622,9 @@ bool LibraryCallKit::inline_string_toBytesU() { } //------------------------inline_string_getCharsU-------------------------- -// public void StringUTF16.getChars(byte[] src, int srcBegin, int srcEnd, char dst[], int dstBegin) +// public void StringUTF16.getChars0(byte[] src, int srcBegin, int srcEnd, char dst[], int dstBegin) bool LibraryCallKit::inline_string_getCharsU() { - if (too_many_traps(Deoptimization::Reason_intrinsic)) { - return false; - } - + assert(callee()->signature()->size() == 5, "StringUTF16.getChars0() has 5 arguments"); // Get the arguments. Node* src = argument(0); Node* src_begin = argument(1); @@ -1658,8 +1637,8 @@ bool LibraryCallKit::inline_string_getCharsU() { AllocateArrayNode* alloc = tightly_coupled_allocation(dst); // Check if a null path was taken unconditionally. - src = null_check(src); - dst = null_check(dst); + src = must_be_not_null(src, true); + dst = must_be_not_null(dst, true); if (stopped()) { return true; } @@ -1669,51 +1648,50 @@ bool LibraryCallKit::inline_string_getCharsU() { src_begin = _gvn.transform(new LShiftINode(src_begin, intcon(1))); // Range checks - generate_string_range_check(src, src_begin, length, true); - generate_string_range_check(dst, dst_begin, length, false); - if (stopped()) { + RegionNode* bailout = create_bailout(); + generate_string_range_check(src, src_begin, length, true, bailout); + generate_string_range_check(dst, dst_begin, length, false, bailout); + if (check_bailout(bailout)) { return true; } - if (!stopped()) { - // Calculate starting addresses. - Node* src_start = array_element_address(src, src_begin, T_BYTE); - Node* dst_start = array_element_address(dst, dst_begin, T_CHAR); + // Calculate starting addresses. + Node* src_start = array_element_address(src, src_begin, T_BYTE); + Node* dst_start = array_element_address(dst, dst_begin, T_CHAR); - // Check if array addresses are aligned to HeapWordSize - const TypeInt* tsrc = gvn().type(src_begin)->is_int(); - const TypeInt* tdst = gvn().type(dst_begin)->is_int(); - bool aligned = tsrc->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_BYTE) + tsrc->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0) && - tdst->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_CHAR) + tdst->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0); + // Check if array addresses are aligned to HeapWordSize + const TypeInt* tsrc = gvn().type(src_begin)->is_int(); + const TypeInt* tdst = gvn().type(dst_begin)->is_int(); + bool aligned = tsrc->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_BYTE) + tsrc->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0) && + tdst->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_CHAR) + tdst->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0); - // Figure out which arraycopy runtime method to call (disjoint, uninitialized). - const char* copyfunc_name = "arraycopy"; - address copyfunc_addr = StubRoutines::select_arraycopy_function(T_CHAR, aligned, true, copyfunc_name, true); - Node* call = make_runtime_call(RC_LEAF|RC_NO_FP, - OptoRuntime::fast_arraycopy_Type(), - copyfunc_addr, copyfunc_name, TypeRawPtr::BOTTOM, - src_start, dst_start, ConvI2X(length) XTOP); - // Do not let reads from the cloned object float above the arraycopy. - if (alloc != nullptr) { - if (alloc->maybe_set_complete(&_gvn)) { - // "You break it, you buy it." - InitializeNode* init = alloc->initialization(); - assert(init->is_complete(), "we just did this"); - init->set_complete_with_arraycopy(); - assert(dst->is_CheckCastPP(), "sanity"); - assert(dst->in(0)->in(0) == init, "dest pinned"); - } - // Do not let stores that initialize this object be reordered with - // a subsequent store that would make this object accessible by - // other threads. - // Record what AllocateNode this StoreStore protects so that - // escape analysis can go from the MemBarStoreStoreNode to the - // AllocateNode and eliminate the MemBarStoreStoreNode if possible - // based on the escape status of the AllocateNode. - insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out_or_null(AllocateNode::RawAddress)); - } else { - insert_mem_bar(Op_MemBarCPUOrder); + // Figure out which arraycopy runtime method to call (disjoint, uninitialized). + const char* copyfunc_name = "arraycopy"; + address copyfunc_addr = StubRoutines::select_arraycopy_function(T_CHAR, aligned, true, copyfunc_name, true); + Node* call = make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::fast_arraycopy_Type(), + copyfunc_addr, copyfunc_name, TypeRawPtr::BOTTOM, + src_start, dst_start, ConvI2X(length) XTOP); + // Do not let reads from the cloned object float above the arraycopy. + if (alloc != nullptr) { + if (alloc->maybe_set_complete(&_gvn)) { + // "You break it, you buy it." + InitializeNode* init = alloc->initialization(); + assert(init->is_complete(), "we just did this"); + init->set_complete_with_arraycopy(); + assert(dst->is_CheckCastPP(), "sanity"); + assert(dst->in(0)->in(0) == init, "dest pinned"); } + // Do not let stores that initialize this object be reordered with + // a subsequent store that would make this object accessible by + // other threads. + // Record what AllocateNode this StoreStore protects so that + // escape analysis can go from the MemBarStoreStoreNode to the + // AllocateNode and eliminate the MemBarStoreStoreNode if possible + // based on the escape status of the AllocateNode. + insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out_or_null(AllocateNode::RawAddress)); + } else { + insert_mem_bar(Op_MemBarCPUOrder); } C->set_has_split_ifs(true); // Has chance for split-if optimization @@ -1725,9 +1703,16 @@ bool LibraryCallKit::inline_string_getCharsU() { // static void StringUTF16.putChar(byte[] val, int index, int c) // static char StringUTF16.getChar(byte[] val, int index) bool LibraryCallKit::inline_string_char_access(bool is_store) { + Node* ch; + if (is_store) { + assert(callee()->signature()->size() == 3, "StringUTF16.putChar() has 3 arguments"); + ch = argument(2); + } else { + assert(callee()->signature()->size() == 2, "StringUTF16.getChar() has 2 arguments"); + ch = nullptr; + } Node* value = argument(0); Node* index = argument(1); - Node* ch = is_store ? argument(2) : nullptr; // This intrinsic accesses byte[] array as char[] array. Computing the offsets // correctly requires matched array shapes. @@ -6185,9 +6170,10 @@ bool LibraryCallKit::inline_encodeISOArray(bool ascii) { } // Check source & target bounds - generate_string_range_check(src, src_offset, length, src_elem == T_BYTE, true); - generate_string_range_check(dst, dst_offset, length, false, true); - if (stopped()) { + RegionNode* bailout = create_bailout(); + generate_string_range_check(src, src_offset, length, src_elem == T_BYTE, bailout); + generate_string_range_check(dst, dst_offset, length, false, bailout); + if (check_bailout(bailout)) { return true; } diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index bd4b753d1cbe..9b87df645e19 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -130,6 +130,8 @@ class LibraryCallKit : public GraphKit { virtual int reexecute_sp() { return _reexecute_sp; } // Helper functions to inline natives + RegionNode* create_bailout(); + bool check_bailout(RegionNode* bailout); Node* generate_guard(Node* test, RegionNode* region, float true_prob); Node* generate_slow_guard(Node* test, RegionNode* region); Node* generate_fair_guard(Node* test, RegionNode* region); @@ -143,7 +145,7 @@ class LibraryCallKit : public GraphKit { bool with_opaque = false); void generate_string_range_check(Node* array, Node* offset, Node* length, bool char_count, - bool halt_on_oob = false); + RegionNode* region); Node* current_thread_helper(Node* &tls_output, ByteSize handle_offset, bool is_immutable); Node* generate_current_thread(Node* &tls_output); diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java index 27b9ae54a8aa..23de31a61b79 100644 --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -67,30 +67,61 @@ static byte[] newBytesFor(int len) { // Check the size of a UTF16-coded string // Throw an exception if out of range - static int newBytesLength(int len) { - if (len < 0) { + private static int newBytesLength(int len) { + checkBytesLength(len); + return len << 1; + } + + /** + * Checks if the provided length is a valid UTF-16 string byte array length. + * + * @param length a UTF-16 string byte array length + * + * @throws NegativeArraySizeException if {@code length < 0} + * @throws OutOfMemoryError if {@code length > (Integer.MAX_VALUE / 2)} + */ + private static void checkBytesLength(int length) { + if (length < 0) { throw new NegativeArraySizeException(); } - if (len >= MAX_LENGTH) { - throw new OutOfMemoryError("UTF16 String size is " + len + - ", should be less than " + MAX_LENGTH); + if (length >= MAX_LENGTH) { + throw new OutOfMemoryError("UTF16 String size is " + length + + ", should be less than " + MAX_LENGTH); } - return len << 1; } + /** + * Writes the given code point to the specified position of the provided + * UTF-16 string byte array. + *

+ * WARNING: This method does not perform any input validations. + * + * @param val a UTF-16 string byte array + * @param index the index of the character to write the code point to + * @param c a code point + */ + // vmIntrinsics::_putCharStringU @IntrinsicCandidate - // intrinsic performs no bounds checks static void putChar(byte[] val, int index, int c) { - assert index >= 0 && index < length(val) : "Trusted caller missed bounds check"; + assert val != null && index >= 0 && index < length(val) : "Trusted caller violated input constraints"; index <<= 1; val[index++] = (byte)(c >> HI_BYTE_SHIFT); val[index] = (byte)(c >> LO_BYTE_SHIFT); } + /** + * {@return the code point at the the specified position of the provided + * UTF-16 string byte array} + *

+ * WARNING: This method does not perform any input validations. + * + * @param val a UTF-16 string byte array + * @param index the index of the character to get the code point from + */ + // vmIntrinsics::_getCharStringU @IntrinsicCandidate - // intrinsic performs no bounds checks static char getChar(byte[] val, int index) { - assert index >= 0 && index < length(val) : "Trusted caller missed bounds check"; + assert val != null && index >= 0 && index < length(val) : "Trusted caller violated input constraints"; index <<= 1; return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) | ((val[index] & 0xff) << LO_BYTE_SHIFT)); @@ -173,14 +204,27 @@ static char[] toChars(byte[] value) { } /** - * {@return an encoded byte[] for the UTF16 characters in char[]} - * No checking is done on the characters, some may or may not be latin1. - * @param value a char array - * @param off an offset - * @param len a length + * {@return a UTF-16 string byte array produced by encoding the characters + * in the provided character array sub-range} + * + * @param value a character array to encode + * @param off the index of the character to start encoding from + * @param len the number of characters to encode + * + * @throws NegativeArraySizeException if {@code len < 0} + * @throws NullPointerException if {@code value} is null + * @throws OutOfMemoryError if {@code len > (Integer.MAX_VALUE / 2)} + * @throws StringIndexOutOfBoundsException if the sub-range is out of bounds */ - @IntrinsicCandidate static byte[] toBytes(char[] value, int off, int len) { + checkBytesLength(len); + String.checkBoundsOffCount(off, len, value.length); // Implicit null check on `value` + return toBytes0(value, off, len); + } + + // vmIntrinsics::_toBytesStringU + @IntrinsicCandidate + private static byte[] toBytes0(char[] value, int off, int len) { byte[] val = newBytesFor(len); for (int i = 0; i < len; i++) { putChar(val, i, value[off]); @@ -495,12 +539,28 @@ static byte[] toBytesSupplementary(int cp) { return result; } - @IntrinsicCandidate + /** + * Copies the specified sub-range of characters from a UTF-16 string byte + * array to the specified character array sub-range. + * + * @param value the source UTF-16 string byte array to copy from + * @param srcBegin the index (inclusive) of the first character in the source sub-range + * @param srcEnd the index (exclusive) of the last character in the source sub-range + * @param dst the target character array to copy to + * @param dstBegin the index (inclusive) of the first character in the target sub-range + * + * @throws NullPointerException if {@code value} or {@code dst} is null + * @throws StringIndexOutOfBoundsException if the sub-ranges are out of bounds + */ static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) { - // We need a range check here because 'getChar' has no checks - if (srcBegin < srcEnd) { - String.checkBoundsOffCount(srcBegin, srcEnd - srcBegin, length(value)); - } + checkBoundsBeginEnd(srcBegin, srcEnd, value); // Implicit null check on `value` via `checkBoundsBeginEnd()` + String.checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length); // Implicit null check on `dst` + getChars0(value, srcBegin, srcEnd, dst, dstBegin); + } + + // vmIntrinsics::_getCharsStringU + @IntrinsicCandidate + private static void getChars0(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) { for (int i = srcBegin; i < srcEnd; i++) { dst[dstBegin++] = getChar(value, i); } @@ -721,7 +781,7 @@ static int compareToCI_Latin1(byte[] value, byte[] other) { return -StringLatin1.compareToCI_UTF16(other, value); } - public static int compareToFC_Latin1(byte[] value, byte[] other) { + static int compareToFC_Latin1(byte[] value, byte[] other) { return -StringLatin1.compareToFC_UTF16(other, value); } @@ -769,7 +829,7 @@ private static int compareToFC0(byte[] value, int off, int last, byte[] other, i return 0; } - public static int compareToFC(byte[] value, byte[] other) { + static int compareToFC(byte[] value, byte[] other) { int tlast = length(value); int olast = length(other); int lim = Math.min(tlast, olast); @@ -1970,13 +2030,13 @@ static int lastIndexOfLatin1(byte[] src, int srcCount, } } - static final int MAX_LENGTH = Integer.MAX_VALUE >> 1; + private static final int MAX_LENGTH = Integer.MAX_VALUE >> 1; - static void checkIndex(int off, byte[] val) { + private static void checkIndex(int off, byte[] val) { String.checkIndex(off, length(val)); } - static void checkOffset(int off, byte[] val) { + private static void checkOffset(int off, byte[] val) { String.checkOffset(off, length(val)); } diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java index a7d2cfe7fa7f..ec99bba19d2b 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -89,12 +89,16 @@ public static void main(String[] args) throws Exception { for (int srcOff = 0; srcOff < SIZE; ++srcOff) { for (int dstOff = 0; dstOff < SIZE; ++dstOff) { for (int len = 0; len < SIZE; ++len) { + int srcEnd = srcOff + len; + int dstEnd = dstOff + len; // Check for potential overlows in source or destination array boolean srcOverflow = (srcOff + len) > SIZE; boolean srcOverflowB = (2*srcOff + 2*len) > SIZE; boolean dstOverflow = (dstOff + len) > SIZE; boolean dstOverflowB = (2*dstOff + 2*len) > SIZE; - boolean getCharsOver = (srcOff < len) && ((2*(len-1) >= SIZE) || ((dstOff + len - srcOff) > SIZE)); + boolean getCharsOver = srcOff > srcEnd || (2*srcEnd) > SIZE || // src + (2*len) > SIZE || // len + dstOff > dstEnd || dstEnd > SIZE; // dst // Check if an exception is thrown and bail out if result is inconsistent with above // assumptions (for example, an exception was not thrown although an overflow happened). check(compressByte, srcOverflowB || dstOverflow, byteArray, srcOff, SIZE, dstOff, len); @@ -102,7 +106,7 @@ public static void main(String[] args) throws Exception { check(inflateByte, srcOverflow || dstOverflowB, byteArray, srcOff, SIZE, dstOff, len); check(inflateChar, srcOverflow || dstOverflow, byteArray, srcOff, SIZE, dstOff, len); check(toBytes, srcOverflow, charArray, srcOff, len); - check(getChars, getCharsOver, byteArray, srcOff, len, SIZE, dstOff); + check(getChars, getCharsOver, byteArray, srcOff, srcEnd, SIZE, dstOff); } } } From 3a93daf189213d4c8af5d57d7af1e832401331b6 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Wed, 18 Mar 2026 09:07:14 +0000 Subject: [PATCH 86/97] 8373369: [REDO] Remove ThreadLocalAllocBuffer::_reserve_for_allocation_prefetch Reviewed-by: mdoerr, kvn, tschatzl --- src/hotspot/cpu/ppc/ppc.ad | 29 ------- .../gc/shared/threadLocalAllocBuffer.cpp | 28 +------ .../gc/shared/threadLocalAllocBuffer.hpp | 1 - src/hotspot/share/opto/macro.cpp | 3 +- src/hotspot/share/runtime/vmStructs.cpp | 1 - .../runtime/ThreadLocalAllocBuffer.java | 3 +- .../classes/sun/jvm/hotspot/runtime/VM.java | 7 -- test/hotspot/jtreg/ProblemList.txt | 3 +- .../TestAllocatePrefetchStyleLargeFlags.java | 79 +++++++++++++++++++ 9 files changed, 84 insertions(+), 70 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/TestAllocatePrefetchStyleLargeFlags.java diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 21fc12742efc..01c290f9b042 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -6327,36 +6327,8 @@ instruct loadConD_Ex(regD dst, immD src) %{ // Prefetch instructions. // Must be safe to execute with invalid address (cannot fault). -// Special prefetch versions which use the dcbz instruction. -instruct prefetch_alloc_zero(indirectMemory mem, iRegLsrc src) %{ - match(PrefetchAllocation (AddP mem src)); - predicate(AllocatePrefetchStyle == 3); - ins_cost(MEMORY_REF_COST); - - format %{ "PREFETCH $mem, 2, $src \t// Prefetch write-many with zero" %} - size(4); - ins_encode %{ - __ dcbz($src$$Register, $mem$$base$$Register); - %} - ins_pipe(pipe_class_memory); -%} - -instruct prefetch_alloc_zero_no_offset(indirectMemory mem) %{ - match(PrefetchAllocation mem); - predicate(AllocatePrefetchStyle == 3); - ins_cost(MEMORY_REF_COST); - - format %{ "PREFETCH $mem, 2 \t// Prefetch write-many with zero" %} - size(4); - ins_encode %{ - __ dcbz($mem$$base$$Register); - %} - ins_pipe(pipe_class_memory); -%} - instruct prefetch_alloc(indirectMemory mem, iRegLsrc src) %{ match(PrefetchAllocation (AddP mem src)); - predicate(AllocatePrefetchStyle != 3); ins_cost(MEMORY_REF_COST); format %{ "PREFETCH $mem, 2, $src \t// Prefetch write-many" %} @@ -6369,7 +6341,6 @@ instruct prefetch_alloc(indirectMemory mem, iRegLsrc src) %{ instruct prefetch_alloc_no_offset(indirectMemory mem) %{ match(PrefetchAllocation mem); - predicate(AllocatePrefetchStyle != 3); ins_cost(MEMORY_REF_COST); format %{ "PREFETCH $mem, 2 \t// Prefetch write-many" %} diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp index 61cf73fe04ad..4c160929f5ac 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp @@ -37,7 +37,6 @@ #include "utilities/copy.hpp" size_t ThreadLocalAllocBuffer::_max_size = 0; -int ThreadLocalAllocBuffer::_reserve_for_allocation_prefetch = 0; unsigned int ThreadLocalAllocBuffer::_target_refills = 0; ThreadLocalAllocBuffer::ThreadLocalAllocBuffer() : @@ -225,30 +224,6 @@ void ThreadLocalAllocBuffer::startup_initialization() { // abort during VM initialization. _target_refills = MAX2(_target_refills, 2U); -#ifdef COMPILER2 - // If the C2 compiler is present, extra space is needed at the end of - // TLABs, otherwise prefetching instructions generated by the C2 - // compiler will fault (due to accessing memory outside of heap). - // The amount of space is the max of the number of lines to - // prefetch for array and for instance allocations. (Extra space must be - // reserved to accommodate both types of allocations.) - // - // Only SPARC-specific BIS instructions are known to fault. (Those - // instructions are generated if AllocatePrefetchStyle==3 and - // AllocatePrefetchInstr==1). To be on the safe side, however, - // extra space is reserved for all combinations of - // AllocatePrefetchStyle and AllocatePrefetchInstr. - // - // If the C2 compiler is not present, no space is reserved. - - // +1 for rounding up to next cache line, +1 to be safe - if (CompilerConfig::is_c2_or_jvmci_compiler_enabled()) { - int lines = MAX2(AllocatePrefetchLines, AllocateInstancePrefetchLines) + 2; - _reserve_for_allocation_prefetch = (AllocatePrefetchDistance + AllocatePrefetchStepSize * lines) / - (int)HeapWordSize; - } -#endif - // During jvm startup, the main thread is initialized // before the heap is initialized. So reinitialize it now. guarantee(Thread::current()->is_Java_thread(), "tlab initialization thread not Java thread"); @@ -454,8 +429,7 @@ void ThreadLocalAllocStats::publish() { } size_t ThreadLocalAllocBuffer::end_reserve() { - size_t reserve_size = CollectedHeap::lab_alignment_reserve(); - return MAX2(reserve_size, (size_t)_reserve_for_allocation_prefetch); + return CollectedHeap::lab_alignment_reserve(); } size_t ThreadLocalAllocBuffer::estimated_used_bytes() const { diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp index 25d9bf00eac1..eb664d139613 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp @@ -57,7 +57,6 @@ class ThreadLocalAllocBuffer: public CHeapObj { 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 int _reserve_for_allocation_prefetch; // Reserve at the end of the TLAB static unsigned _target_refills; // expected number of refills between GCs unsigned _number_of_refills; diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index b9045ddcf4c7..6995cacd1b09 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -1917,8 +1917,7 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, transform_later(cache_adr); cache_adr = new CastP2XNode(needgc_false, cache_adr); transform_later(cache_adr); - // Address is aligned to execute prefetch to the beginning of cache line size - // (it is important when BIS instruction is used on SPARC as prefetch). + // Address is aligned to execute prefetch to the beginning of cache line size. Node* mask = _igvn.MakeConX(~(intptr_t)(step_size-1)); cache_adr = new AndXNode(cache_adr, mask); transform_later(cache_adr); diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 9796b990b2af..93e0ff2f3b6b 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -335,7 +335,6 @@ nonstatic_field(ThreadLocalAllocBuffer, _pf_top, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \ - static_field(ThreadLocalAllocBuffer, _reserve_for_allocation_prefetch, int) \ static_field(ThreadLocalAllocBuffer, _target_refills, unsigned) \ nonstatic_field(ThreadLocalAllocBuffer, _number_of_refills, unsigned) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste, unsigned) \ diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadLocalAllocBuffer.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadLocalAllocBuffer.java index 11f03a6003e6..683e4b679359 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadLocalAllocBuffer.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadLocalAllocBuffer.java @@ -76,10 +76,9 @@ private long alignmentReserve() { private long endReserve() { long labAlignmentReserve = VM.getVM().getLabAlignmentReserve(); - long reserveForAllocationPrefetch = VM.getVM().getReserveForAllocationPrefetch(); long heapWordSize = VM.getVM().getHeapWordSize(); - return Math.max(labAlignmentReserve, reserveForAllocationPrefetch) * heapWordSize; + return labAlignmentReserve * heapWordSize; } /** Support for iteration over heap -- not sure how this will diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java index dc27a4fc59ed..1607563150a2 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java @@ -123,7 +123,6 @@ public class VM { private int invocationEntryBCI; private ReversePtrs revPtrs; private VMRegImpl vmregImpl; - private int reserveForAllocationPrefetch; private int labAlignmentReserve; // System.getProperties from debuggee VM @@ -447,8 +446,6 @@ private VM(TypeDataBase db, JVMDebugger debugger, boolean isBigEndian) { boolType = (CIntegerType) db.lookupType("bool"); Type threadLocalAllocBuffer = db.lookupType("ThreadLocalAllocBuffer"); - CIntegerField reserveForAllocationPrefetchField = threadLocalAllocBuffer.getCIntegerField("_reserve_for_allocation_prefetch"); - reserveForAllocationPrefetch = (int)reserveForAllocationPrefetchField.getCInteger(intType); Type collectedHeap = db.lookupType("CollectedHeap"); CIntegerField labAlignmentReserveField = collectedHeap.getCIntegerField("_lab_alignment_reserve"); @@ -915,10 +912,6 @@ public String getVMInternalInfo() { return vmInternalInfo; } - public int getReserveForAllocationPrefetch() { - return reserveForAllocationPrefetch; - } - public int getLabAlignmentReserve() { return labAlignmentReserve; } diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index ba63f7752234..60d2ffa722f2 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -83,6 +83,8 @@ compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64 compiler/longcountedloops/TestLoopNestTooManyTraps.java 8376591 generic-all +compiler/unsafe/AlignmentGapAccess.java 8373487 generic-all + ############################################################################# # :hotspot_gc @@ -196,4 +198,3 @@ vmTestbase/nsk/monitoring/ThreadMXBean/findMonitorDeadlockedThreads/find006/Test # in either implementation or test code. ############################################################################# - diff --git a/test/hotspot/jtreg/compiler/c2/TestAllocatePrefetchStyleLargeFlags.java b/test/hotspot/jtreg/compiler/c2/TestAllocatePrefetchStyleLargeFlags.java new file mode 100644 index 000000000000..b455107d5f68 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestAllocatePrefetchStyleLargeFlags.java @@ -0,0 +1,79 @@ +/* + * 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 + * @summary Stress allocation prefetch with large legal AllocatePrefetch* values + * @requires vm.compiler2.enabled + * + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+UseTLAB + * -XX:AllocatePrefetchStyle=1 + * -XX:AllocatePrefetchDistance=512 + * -XX:AllocatePrefetchStepSize=512 + * -XX:AllocatePrefetchLines=64 + * -XX:AllocateInstancePrefetchLines=64 + * compiler.c2.TestAllocatePrefetchStyleLargeFlags + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+UseTLAB + * -XX:AllocatePrefetchStyle=2 + * -XX:AllocatePrefetchDistance=512 + * -XX:AllocatePrefetchStepSize=512 + * -XX:AllocatePrefetchLines=64 + * -XX:AllocateInstancePrefetchLines=64 + * compiler.c2.TestAllocatePrefetchStyleLargeFlags + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+UseTLAB + * -XX:AllocatePrefetchStyle=3 + * -XX:AllocatePrefetchDistance=512 + * -XX:AllocatePrefetchStepSize=512 + * -XX:AllocatePrefetchLines=64 + * -XX:AllocateInstancePrefetchLines=64 + * compiler.c2.TestAllocatePrefetchStyleLargeFlags + */ + +package compiler.c2; + +public class TestAllocatePrefetchStyleLargeFlags { + private static volatile Object sink; + + private static final class Payload { + private final int value; + + private Payload(int value) { + this.value = value; + } + } + + private static Object allocateInstance(int value) { + return new Payload(value); + } + + private static Object allocateArray(int value) { + return new int[value & 31]; + } + + public static void main(String[] args) { + for (int i = 0; i < 50_000; i++) { + sink = allocateInstance(i); + sink = allocateArray(i); + } + } +} From 9ef2e8dc1c993a875eb7e47525df277d96066fe1 Mon Sep 17 00:00:00 2001 From: Ivan Bereziuk Date: Wed, 18 Mar 2026 10:18:19 +0000 Subject: [PATCH 87/97] 8278102: containers/docker/TestJcmd.java failed with "RuntimeException: Could not find specified process" Reviewed-by: kevinw, sgehwolf --- test/hotspot/jtreg/ProblemList.txt | 1 - test/hotspot/jtreg/containers/docker/TestJcmd.java | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 60d2ffa722f2..5bb5bf54fba9 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -117,7 +117,6 @@ runtime/NMT/VirtualAllocCommitMerge.java 8309698 linux-s390x applications/jcstress/copy.java 8229852 linux-all -containers/docker/TestJcmd.java 8278102 linux-all containers/docker/TestJFREvents.java 8327723 linux-x64 containers/docker/TestJcmdWithSideCar.java 8341518 linux-x64 diff --git a/test/hotspot/jtreg/containers/docker/TestJcmd.java b/test/hotspot/jtreg/containers/docker/TestJcmd.java index fcd5c665f2b9..4f6fda20c867 100644 --- a/test/hotspot/jtreg/containers/docker/TestJcmd.java +++ b/test/hotspot/jtreg/containers/docker/TestJcmd.java @@ -170,14 +170,10 @@ private static Process startObservedContainer() throws Exception { opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z") .addJavaOpts("-cp", "/test-classes/") .addDockerOpts("--cap-add=SYS_PTRACE") + .addDockerOpts("--pull=never") .addDockerOpts("--name", CONTAINER_NAME) .addClassOptions("" + TIME_TO_RUN_CONTAINER_PROCESS); - if (IS_PODMAN && !ROOT_UID.equals(getId("-u"))) { - // map the current userid to the one in the target namespace - opts.addDockerOpts("--userns=keep-id"); - } - // avoid large Xmx opts.appendTestJavaOptions = false; @@ -186,7 +182,7 @@ private static Process startObservedContainer() throws Exception { return ProcessTools.startProcess("main-container-process", pb, line -> line.contains(EventGeneratorLoop.MAIN_METHOD_STARTED), - 5, TimeUnit.SECONDS); + 15, TimeUnit.SECONDS); } From e99ed13691369346386cc22df053c16f96aa6f69 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 18 Mar 2026 11:05:10 +0000 Subject: [PATCH 88/97] 8379671: C2: Fix usage of PhaseGVN::transform in some intrinsics Reviewed-by: bmaillard, dfenacci, roland --- src/hotspot/share/opto/library_call.cpp | 75 ++++++++++++------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index ffc798019b43..743612a10510 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -3006,7 +3006,7 @@ bool LibraryCallKit::inline_native_time_funcs(address funcAddr, const char* func // slow path: runtime call // } bool LibraryCallKit::inline_native_vthread_start_transition(address funcAddr, const char* funcName, bool is_final_transition) { - Node* vt_oop = _gvn.transform(must_be_not_null(argument(0), true)); // VirtualThread this argument + Node* vt_oop = must_be_not_null(argument(0), true); // VirtualThread this argument IdealKit ideal(this); Node* thread = ideal.thread(); @@ -3026,7 +3026,7 @@ bool LibraryCallKit::inline_native_vthread_start_transition(address funcAddr, co ideal.if_then(disabled, BoolTest::ne, ideal.ConI(0)); { sync_kit(ideal); - Node* is_mount = is_final_transition ? ideal.ConI(0) : _gvn.transform(argument(1)); + Node* is_mount = is_final_transition ? ideal.ConI(0) : argument(1); const TypeFunc* tf = OptoRuntime::vthread_transition_Type(); make_runtime_call(RC_NO_LEAF, tf, funcAddr, funcName, TypePtr::BOTTOM, vt_oop, is_mount); ideal.sync_kit(this); @@ -3038,7 +3038,7 @@ bool LibraryCallKit::inline_native_vthread_start_transition(address funcAddr, co } bool LibraryCallKit::inline_native_vthread_end_transition(address funcAddr, const char* funcName, bool is_first_transition) { - Node* vt_oop = _gvn.transform(must_be_not_null(argument(0), true)); // VirtualThread this argument + Node* vt_oop = must_be_not_null(argument(0), true); // VirtualThread this argument IdealKit ideal(this); Node* _notify_jvmti_addr = makecon(TypeRawPtr::make((address)MountUnmountDisabler::notify_jvmti_events_address())); @@ -3046,7 +3046,7 @@ bool LibraryCallKit::inline_native_vthread_end_transition(address funcAddr, cons ideal.if_then(_notify_jvmti, BoolTest::eq, ideal.ConI(1)); { sync_kit(ideal); - Node* is_mount = is_first_transition ? ideal.ConI(1) : _gvn.transform(argument(1)); + Node* is_mount = is_first_transition ? ideal.ConI(1) : argument(1); const TypeFunc* tf = OptoRuntime::vthread_transition_Type(); make_runtime_call(RC_NO_LEAF, tf, funcAddr, funcName, TypePtr::BOTTOM, vt_oop, is_mount); ideal.sync_kit(this); @@ -3077,7 +3077,7 @@ bool LibraryCallKit::inline_native_notify_jvmti_sync() { { // unconditionally update the is_disable_suspend bit in current JavaThread Node* thread = ideal.thread(); - Node* arg = _gvn.transform(argument(0)); // argument for notification + Node* arg = argument(0); // argument for notification Node* addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::is_disable_suspend_offset())); const TypePtr *addr_type = _gvn.type(addr)->isa_ptr(); @@ -3157,7 +3157,7 @@ bool LibraryCallKit::inline_native_classID() { ideal.set(result, _gvn.transform(new AddLNode(array_kls_trace_id, longcon(1)))); } __ else_(); { // void class case - ideal.set(result, _gvn.transform(longcon(LAST_TYPE_ID + 1))); + ideal.set(result, longcon(LAST_TYPE_ID + 1)); } __ end_if(); Node* signaled_flag_address = makecon(TypeRawPtr::make(JfrIntrinsicSupport::signal_address())); @@ -3185,9 +3185,9 @@ bool LibraryCallKit::inline_native_jvm_commit() { // TLS. Node* tls_ptr = _gvn.transform(new ThreadLocalNode()); // Jfr java buffer. - Node* java_buffer_offset = _gvn.transform(new AddPNode(top(), tls_ptr, _gvn.transform(MakeConX(in_bytes(JAVA_BUFFER_OFFSET_JFR))))); + Node* java_buffer_offset = _gvn.transform(new AddPNode(top(), tls_ptr, MakeConX(in_bytes(JAVA_BUFFER_OFFSET_JFR)))); Node* java_buffer = _gvn.transform(new LoadPNode(control(), input_memory_state, java_buffer_offset, TypePtr::BOTTOM, TypeRawPtr::NOTNULL, MemNode::unordered)); - Node* java_buffer_pos_offset = _gvn.transform(new AddPNode(top(), java_buffer, _gvn.transform(MakeConX(in_bytes(JFR_BUFFER_POS_OFFSET))))); + Node* java_buffer_pos_offset = _gvn.transform(new AddPNode(top(), java_buffer, MakeConX(in_bytes(JFR_BUFFER_POS_OFFSET)))); // Load the current value of the notified field in the JfrThreadLocal. Node* notified_offset = basic_plus_adr(top(), tls_ptr, in_bytes(NOTIFY_OFFSET_JFR)); @@ -3209,7 +3209,7 @@ bool LibraryCallKit::inline_native_jvm_commit() { // Iff notified, the return address of the commit method is the current position of the backing java buffer. This is used to reset the event writer. Node* current_pos_X = _gvn.transform(new LoadXNode(control(), input_memory_state, java_buffer_pos_offset, TypeRawPtr::NOTNULL, TypeX_X, MemNode::unordered)); // Convert the machine-word to a long. - Node* current_pos = _gvn.transform(ConvX2L(current_pos_X)); + Node* current_pos = ConvX2L(current_pos_X); // False branch, not notified. Node* not_notified = _gvn.transform(new IfFalseNode(iff_notified)); @@ -3219,7 +3219,7 @@ bool LibraryCallKit::inline_native_jvm_commit() { // Arg is the next position as a long. Node* arg = argument(0); // Convert long to machine-word. - Node* next_pos_X = _gvn.transform(ConvL2X(arg)); + Node* next_pos_X = ConvL2X(arg); // Store the next_position to the underlying jfr java buffer. store_to_memory(control(), java_buffer_pos_offset, next_pos_X, LP64_ONLY(T_LONG) NOT_LP64(T_INT), MemNode::release); @@ -3228,9 +3228,9 @@ bool LibraryCallKit::inline_native_jvm_commit() { set_all_memory(commit_memory); // Now load the flags from off the java buffer and decide if the buffer is a lease. If so, it needs to be returned post-commit. - Node* java_buffer_flags_offset = _gvn.transform(new AddPNode(top(), java_buffer, _gvn.transform(MakeConX(in_bytes(JFR_BUFFER_FLAGS_OFFSET))))); + Node* java_buffer_flags_offset = _gvn.transform(new AddPNode(top(), java_buffer, MakeConX(in_bytes(JFR_BUFFER_FLAGS_OFFSET)))); Node* flags = make_load(control(), java_buffer_flags_offset, TypeInt::UBYTE, T_BYTE, MemNode::unordered); - Node* lease_constant = _gvn.transform(_gvn.intcon(4)); + Node* lease_constant = _gvn.intcon(4); // And flags with lease constant. Node* lease = _gvn.transform(new AndINode(flags, lease_constant)); @@ -3267,7 +3267,7 @@ bool LibraryCallKit::inline_native_jvm_commit() { lease_compare_rgn->init_req(_true_path, call_return_lease_control); lease_compare_rgn->init_req(_false_path, not_lease); - lease_compare_mem->init_req(_true_path, _gvn.transform(reset_memory())); + lease_compare_mem->init_req(_true_path, reset_memory()); lease_compare_mem->init_req(_false_path, commit_memory); lease_compare_io->init_req(_true_path, i_o()); @@ -3425,10 +3425,10 @@ bool LibraryCallKit::inline_native_getEventWriter() { IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD); // Mask off the excluded information from the epoch. - Node * vthread_is_excluded = _gvn.transform(new AndINode(vthread_epoch_raw, _gvn.transform(excluded_mask))); + Node * vthread_is_excluded = _gvn.transform(new AndINode(vthread_epoch_raw, excluded_mask)); // Branch on excluded to conditionalize updating the epoch for the virtual thread. - Node* is_excluded_cmp = _gvn.transform(new CmpINode(vthread_is_excluded, _gvn.transform(excluded_mask))); + Node* is_excluded_cmp = _gvn.transform(new CmpINode(vthread_is_excluded, excluded_mask)); Node* test_not_excluded = _gvn.transform(new BoolNode(is_excluded_cmp, BoolTest::ne)); IfNode* iff_not_excluded = create_and_map_if(control(), test_not_excluded, PROB_MAX, COUNT_UNKNOWN); @@ -3440,7 +3440,7 @@ bool LibraryCallKit::inline_native_getEventWriter() { set_control(included); // Get epoch value. - Node* epoch = _gvn.transform(new AndINode(vthread_epoch_raw, _gvn.transform(epoch_mask))); + Node* epoch = _gvn.transform(new AndINode(vthread_epoch_raw, epoch_mask)); // Load the current epoch generation. The value is unsigned 16-bit, so we type it as T_CHAR. Node* epoch_generation_address = makecon(TypeRawPtr::make(JfrIntrinsicSupport::epoch_generation_address())); @@ -3478,7 +3478,7 @@ bool LibraryCallKit::inline_native_getEventWriter() { // Update control and phi nodes. epoch_compare_rgn->init_req(_true_path, call_write_checkpoint_control); epoch_compare_rgn->init_req(_false_path, epoch_is_equal); - epoch_compare_mem->init_req(_true_path, _gvn.transform(reset_memory())); + epoch_compare_mem->init_req(_true_path, reset_memory()); epoch_compare_mem->init_req(_false_path, input_memory_state); epoch_compare_io->init_req(_true_path, i_o()); epoch_compare_io->init_req(_false_path, input_io_state); @@ -3519,11 +3519,11 @@ bool LibraryCallKit::inline_native_getEventWriter() { vthread_compare_mem->init_req(_false_path, input_memory_state); vthread_compare_io->init_req(_true_path, _gvn.transform(exclude_compare_io)); vthread_compare_io->init_req(_false_path, input_io_state); - tid->init_req(_true_path, _gvn.transform(vthread_tid)); - tid->init_req(_false_path, _gvn.transform(thread_obj_tid)); - exclusion->init_req(_true_path, _gvn.transform(vthread_is_excluded)); - exclusion->init_req(_false_path, _gvn.transform(threadObj_is_excluded)); - pinVirtualThread->init_req(_true_path, _gvn.transform(continuation_support)); + tid->init_req(_true_path, vthread_tid); + tid->init_req(_false_path, thread_obj_tid); + exclusion->init_req(_true_path, vthread_is_excluded); + exclusion->init_req(_false_path, threadObj_is_excluded); + pinVirtualThread->init_req(_true_path, continuation_support); pinVirtualThread->init_req(_false_path, _gvn.intcon(0)); // Update branch state. @@ -3581,9 +3581,9 @@ bool LibraryCallKit::inline_native_getEventWriter() { // Update control and phi nodes. event_writer_tid_compare_rgn->init_req(_true_path, tid_is_not_equal); event_writer_tid_compare_rgn->init_req(_false_path, tid_is_equal); - event_writer_tid_compare_mem->init_req(_true_path, _gvn.transform(reset_memory())); + event_writer_tid_compare_mem->init_req(_true_path, reset_memory()); event_writer_tid_compare_mem->init_req(_false_path, _gvn.transform(vthread_compare_mem)); - event_writer_tid_compare_io->init_req(_true_path, _gvn.transform(i_o())); + event_writer_tid_compare_io->init_req(_true_path, i_o()); event_writer_tid_compare_io->init_req(_false_path, _gvn.transform(vthread_compare_io)); // Result of top level CFG, Memory, IO and Value. @@ -3598,14 +3598,14 @@ bool LibraryCallKit::inline_native_getEventWriter() { // Result memory. result_mem->init_req(_true_path, _gvn.transform(event_writer_tid_compare_mem)); - result_mem->init_req(_false_path, _gvn.transform(input_memory_state)); + result_mem->init_req(_false_path, input_memory_state); // Result IO. result_io->init_req(_true_path, _gvn.transform(event_writer_tid_compare_io)); - result_io->init_req(_false_path, _gvn.transform(input_io_state)); + result_io->init_req(_false_path, input_io_state); // Result value. - result_value->init_req(_true_path, _gvn.transform(event_writer)); // return event writer oop + result_value->init_req(_true_path, event_writer); // return event writer oop result_value->init_req(_false_path, null()); // return null // Set output state. @@ -3671,7 +3671,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD); // Mask off the excluded information from the epoch. - Node * const is_excluded = _gvn.transform(new AndINode(epoch_raw, _gvn.transform(excluded_mask))); + Node * const is_excluded = _gvn.transform(new AndINode(epoch_raw, excluded_mask)); // Load the tid field from the thread. Node* tid = load_field_from_object(thread, "tid", "J"); @@ -3681,7 +3681,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { Node* tid_memory = store_to_memory(control(), thread_id_offset, tid, T_LONG, MemNode::unordered, true); // Branch is_excluded to conditionalize updating the epoch . - Node* excluded_cmp = _gvn.transform(new CmpINode(is_excluded, _gvn.transform(excluded_mask))); + Node* excluded_cmp = _gvn.transform(new CmpINode(is_excluded, excluded_mask)); Node* test_excluded = _gvn.transform(new BoolNode(excluded_cmp, BoolTest::eq)); IfNode* iff_excluded = create_and_map_if(control(), test_excluded, PROB_MIN, COUNT_UNKNOWN); @@ -3696,7 +3696,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { Node* vthread_is_included = _gvn.intcon(0); // Get epoch value. - Node* epoch = _gvn.transform(new AndINode(epoch_raw, _gvn.transform(epoch_mask))); + Node* epoch = _gvn.transform(new AndINode(epoch_raw, epoch_mask)); // Store the vthread epoch to the jfr thread local. Node* vthread_epoch_offset = basic_plus_adr(top(), jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EPOCH_OFFSET_JFR)); @@ -3714,8 +3714,8 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { excluded_rgn->init_req(_false_path, included); excluded_mem->init_req(_true_path, tid_memory); excluded_mem->init_req(_false_path, included_memory); - exclusion->init_req(_true_path, _gvn.transform(vthread_is_excluded)); - exclusion->init_req(_false_path, _gvn.transform(vthread_is_included)); + exclusion->init_req(_true_path, vthread_is_excluded); + exclusion->init_req(_false_path, vthread_is_included); // Set intermediate state. set_control(_gvn.transform(excluded_rgn)); @@ -3771,7 +3771,6 @@ bool LibraryCallKit::inline_native_setCurrentThread() { Node* p = basic_plus_adr(top()/*!oop*/, thread, in_bytes(JavaThread::vthread_offset())); Node* thread_obj_handle = make_load(nullptr, p, p->bottom_type()->is_ptr(), T_OBJECT, MemNode::unordered); - thread_obj_handle = _gvn.transform(thread_obj_handle); const TypePtr *adr_type = _gvn.type(thread_obj_handle)->isa_ptr(); access_store_at(nullptr, thread_obj_handle, adr_type, arr, _gvn.type(arr), T_OBJECT, IN_NATIVE | MO_UNORDERED); @@ -3867,7 +3866,7 @@ bool LibraryCallKit::inline_native_Continuation_pinning(bool unpin) { } else { pin_count_rhs = _gvn.intcon(UINT32_MAX); } - Node* pin_count_cmp = _gvn.transform(new CmpUNode(_gvn.transform(pin_count), pin_count_rhs)); + Node* pin_count_cmp = _gvn.transform(new CmpUNode(pin_count, pin_count_rhs)); Node* test_pin_count_over_underflow = _gvn.transform(new BoolNode(pin_count_cmp, BoolTest::eq)); IfNode* iff_pin_count_over_underflow = create_and_map_if(control(), test_pin_count_over_underflow, PROB_MIN, COUNT_UNKNOWN); @@ -3904,10 +3903,10 @@ bool LibraryCallKit::inline_native_Continuation_pinning(bool unpin) { PhiNode* result_mem = new PhiNode(result_rgn, Type::MEMORY, TypePtr::BOTTOM); record_for_igvn(result_mem); - result_rgn->init_req(_true_path, _gvn.transform(valid_pin_count)); - result_rgn->init_req(_false_path, _gvn.transform(continuation_is_null)); - result_mem->init_req(_true_path, _gvn.transform(reset_memory())); - result_mem->init_req(_false_path, _gvn.transform(input_memory_state)); + result_rgn->init_req(_true_path, valid_pin_count); + result_rgn->init_req(_false_path, continuation_is_null); + result_mem->init_req(_true_path, reset_memory()); + result_mem->init_req(_false_path, input_memory_state); // Set output state. set_control(_gvn.transform(result_rgn)); From 262b31be3de42012bf9460e5deb0b43bec6a3fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Wed, 18 Mar 2026 12:33:41 +0000 Subject: [PATCH 89/97] 8359335: Template-Framework Library: Primitive Types subtyping Reviewed-by: chagedorn, epeter --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 5 ++- .../library/Expression.java | 31 +++++++++++-- .../library/Operations.java | 13 +++++- .../library/PrimitiveType.java | 20 ++++++++- .../examples/TestPrimitiveTypes.java | 20 ++++++++- .../tests/TestExpression.java | 45 ++++++++++++++++--- 6 files changed, 116 insertions(+), 18 deletions(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 875ef57c865b..33be24a03677 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -52,6 +52,7 @@ import static compiler.lib.template_framework.Template.$; import compiler.lib.template_framework.library.CodeGenerationDataNameType; import compiler.lib.template_framework.library.Expression; +import compiler.lib.template_framework.library.Expression.Nesting; import compiler.lib.template_framework.library.Operations; import compiler.lib.template_framework.library.PrimitiveType; import compiler.lib.template_framework.library.TestFrameworkClass; @@ -335,7 +336,7 @@ public static String generate(CompileFramework comp) { for (int i = 0; i < 10; i++) { // The depth determines roughly how many operations are going to be used in the expression. int depth = RANDOM.nextInt(1, 20); - Expression expression = Expression.nestRandomly(type, Operations.PRIMITIVE_OPERATIONS, depth); + Expression expression = Expression.nestRandomly(type, Operations.PRIMITIVE_OPERATIONS, depth, Nesting.EXACT); tests.add(testTemplate.asToken(expression)); } } @@ -350,7 +351,7 @@ public static String generate(CompileFramework comp) { for (int i = 0; i < 2; i++) { // The depth determines roughly how many operations are going to be used in the expression. int depth = RANDOM.nextInt(1, 20); - Expression expression = Expression.nestRandomly(type, Operations.SCALAR_NUMERIC_OPERATIONS, depth); + Expression expression = Expression.nestRandomly(type, Operations.SCALAR_NUMERIC_OPERATIONS, depth, Nesting.EXACT); tests.add(testTemplate.asToken(expression)); } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java index 43ab16af415b..37ad4debde67 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java @@ -382,6 +382,23 @@ public String toString() { return sb.toString(); } + /** + * {@link Nesting} defines the different ways of selecting {@link Expression}s + * to nest based on their types. + */ + public enum Nesting { + /** + * Only nest {@Expression}s where the argument and return types match exactly + * based on the implementation of {@link CodeGenerateionDataNameType#isSubtypeOf}. + */ + EXACT, + /** + * Only nest {@Expression}s where the return type is a subtype of the argument + * type based on the implemetation of {@link CodeGenerateionDataNameType#isSubtypeOf}. + */ + SUBTYPE + } + /** * Create a nested {@link Expression} with a specified {@code returnType} from a * set of {@code expressions}. @@ -391,11 +408,13 @@ public String toString() { * the nested {@link Expression}. * @param maxNumberOfUsedExpressions the maximal number of {@link Expression}s from the * {@code expressions} are nested. + * @param nesting control the {@link Nesting} of the sampled {@link Expression}s. * @return a new randomly nested {@link Expression}. */ public static Expression nestRandomly(CodeGenerationDataNameType returnType, List expressions, - int maxNumberOfUsedExpressions) { + int maxNumberOfUsedExpressions, + Nesting nesting) { List filtered = expressions.stream().filter(e -> e.returnType.isSubtypeOf(returnType)).toList(); if (filtered.isEmpty()) { @@ -406,7 +425,7 @@ public static Expression nestRandomly(CodeGenerationDataNameType returnType, Expression expression = filtered.get(r); for (int i = 1; i < maxNumberOfUsedExpressions; i++) { - expression = expression.nestRandomly(expressions); + expression = expression.nestRandomly(expressions, nesting); } return expression; } @@ -416,12 +435,16 @@ public static Expression nestRandomly(CodeGenerationDataNameType returnType, * {@code this} {@link Expression}, ensuring compatibility of argument and return type. * * @param nestingExpressions list of expressions we sample from for the inner {@link Expression}. + * @param nesting control the {@link Nesting} of the sampled {@link Expression}s. * @return a new nested {@link Expression}. */ - public Expression nestRandomly(List nestingExpressions) { + public Expression nestRandomly(List nestingExpressions, Nesting nesting) { int argumentIndex = RANDOM.nextInt(this.argumentTypes.size()); CodeGenerationDataNameType argumentType = this.argumentTypes.get(argumentIndex); - List filtered = nestingExpressions.stream().filter(e -> e.returnType.isSubtypeOf(argumentType)).toList(); + List filtered = nestingExpressions.stream() + .filter(e -> e.returnType.isSubtypeOf(argumentType) && + (nesting == Nesting.EXACT ? argumentType.isSubtypeOf(e.returnType) : true)) + .toList(); if (filtered.isEmpty()) { // Found no expression that has a matching returnType. diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index 1fe05cc2b6cd..2ea251cc5e52 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -129,8 +129,17 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(type, "(", type, " + ", type, ")")); ops.add(Expression.make(type, "(", type, " - ", type, ")")); ops.add(Expression.make(type, "(", type, " * ", type, ")")); - ops.add(Expression.make(type, "(", type, " / ", type, ")")); - ops.add(Expression.make(type, "(", type, " % ", type, ")")); + // Because of subtyping, we can sample an expression like `(float)((int)(3) / (int)(0))`. Floating point + // division and modulo do not throw an ArithmeticException on division by zero, integer division and modulo + // do. In the expression above, the division has an integer on both sides, so it is executed as an integer + // division and throws an ArithmeticException even though we would expect the float division not to do so. + // To prevent this issue, we provide two versions of floating point division operations: one that casts + // its operands and one that expects that an ArithmeticException might be thrown when we get unlucky when + // sampling subtypes. + ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") / (" + type.name() +")(", type, "))")); + ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") % (" + type.name() +")(", type, "))")); + ops.add(Expression.make(type, "(", type, " / ", type, ")", WITH_ARITHMETIC_EXCEPTION)); + ops.add(Expression.make(type, "(", type, " % ", type, ")", WITH_ARITHMETIC_EXCEPTION)); // Relational / Comparison Operators ops.add(Expression.make(BOOLEANS, "(", type, " == ", type, ")")); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java index c8541ac1fa6f..b20dbb28d22a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java @@ -71,7 +71,25 @@ private PrimitiveType(Kind kind) { @Override public boolean isSubtypeOf(DataName.Type other) { - return (other instanceof PrimitiveType pt) && pt.kind == kind; + // Implement other >: this according to JLS §4.10.1. + if (other instanceof PrimitiveType superType) { + if (superType.kind == Kind.BOOLEAN || kind == Kind.BOOLEAN) { + // Boolean does not have a supertype and only itself as a subtype. + return superType.kind == this.kind; + } + if (superType.kind == Kind.CHAR || kind == Kind.CHAR) { + // Char does not have a subtype, but it is itself a subtype of any primitive type with + // a larger byte size. The following is correct for the subtype relation to floats, + // since chars are 16 bits wide and floats 32 bits or more. + return superType.kind == this.kind || (superType.byteSize() > this.byteSize() && this.kind != Kind.BYTE); + } + // Due to float >: long, all integers are subtypes of floating point types. + return (superType.isFloating() && !this.isFloating()) || + // Generally, narrower types are subtypes of wider types. + (superType.isFloating() == this.isFloating() && superType.byteSize() >= this.byteSize()); + } + + return false; } @Override diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java index 6193b6aad0cb..facaefaa8f33 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java @@ -48,6 +48,7 @@ import static compiler.lib.template_framework.Template.$; import static compiler.lib.template_framework.Template.addDataName; import static compiler.lib.template_framework.DataName.Mutability.MUTABLE; +import static compiler.lib.template_framework.DataName.Mutability.MUTABLE_OR_IMMUTABLE; import compiler.lib.template_framework.library.Hooks; import compiler.lib.template_framework.library.CodeGenerationDataNameType; @@ -129,7 +130,8 @@ public static String generate() { } // Finally, test the type by creating some DataNames (variables), and sampling - // from them. There should be no cross-over between the types. + // from them. Sampling exactly should not lead to any conversion and sampling + // subtypes should only lead to widening conversions. // IMPORTANT: since we are adding the DataName via an inserted Template, we // must chose a "transparentScope", so that the DataName escapes. If we // instead chose "scope", the test would fail, because it later @@ -150,6 +152,14 @@ public static String generate() { """ )); + var assignmentTemplate = Template.make("lhsType", (PrimitiveType lhsType) -> scope( + dataNames(MUTABLE).exactOf(lhsType).sampleAndLetAs("lhs"), + dataNames(MUTABLE_OR_IMMUTABLE).subtypeOf(lhsType).sampleAndLetAs("rhs"), + """ + #lhs = #rhs; + """ + )); + var namesTemplate = Template.make(() -> scope( """ public static void test_names() { @@ -161,10 +171,16 @@ public static void test_names() { ).toList() ), """ - // Now sample: + // Sample exactly: """, Collections.nCopies(10, CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(sampleTemplate::asToken).toList() + ), + """ + // Sample subtypes: + """, + Collections.nCopies(10, + CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(assignmentTemplate::asToken).toList() ) )), """ diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java index b34538c39c16..609b09360799 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java @@ -41,6 +41,7 @@ import static compiler.lib.template_framework.Template.scope; import compiler.lib.template_framework.library.CodeGenerationDataNameType; import compiler.lib.template_framework.library.Expression; +import compiler.lib.template_framework.library.Expression.Nesting; /** * This tests the use of the {@link Expression} from the template library. This is @@ -174,45 +175,75 @@ public static void testNestRandomly() { Expression e4 = Expression.make(myTypeA1, "<", myTypeA, ">"); Expression e5 = Expression.make(myTypeA, "[", myTypeB, "]"); - Expression e1e2 = e1.nestRandomly(List.of(e2)); - Expression e1ex = e1.nestRandomly(List.of(e3, e2, e3)); - Expression e1e4 = e1.nestRandomly(List.of(e3, e4, e3)); - Expression e1ey = e1.nestRandomly(List.of(e3, e3)); + Expression e1e2 = e1.nestRandomly(List.of(e2), Nesting.SUBTYPE); + Expression e1e2Exact = e1.nestRandomly(List.of(e2), Nesting.EXACT); + Expression e1ex = e1.nestRandomly(List.of(e3, e2, e3), Nesting.SUBTYPE); + Expression e1exExact = e1.nestRandomly(List.of(e3, e2, e3), Nesting.EXACT); + Expression e1e4 = e1.nestRandomly(List.of(e3, e4, e3), Nesting.SUBTYPE); + Expression e1e4Exact = e1.nestRandomly(List.of(e3, e4, e3), Nesting.EXACT); + Expression e1ey = e1.nestRandomly(List.of(e3, e3), Nesting.SUBTYPE); + Expression e1eyExact = e1.nestRandomly(List.of(e3, e3), Nesting.EXACT); // 5-deep nesting of e1 - Expression deep1 = Expression.nestRandomly(myTypeA, List.of(e1, e3), 5); + Expression deep1 = Expression.nestRandomly(myTypeA, List.of(e1, e3), 5, Nesting.SUBTYPE); + Expression deep1Exact = Expression.nestRandomly(myTypeA, List.of(e1, e3), 5, Nesting.EXACT); // Alternating pattern - Expression deep2 = Expression.nestRandomly(myTypeA, List.of(e5, e3), 5); + Expression deep2 = Expression.nestRandomly(myTypeA, List.of(e5, e3), 5, Nesting.SUBTYPE); + Expression deep2Exact = Expression.nestRandomly(myTypeA, List.of(e5, e3), 5, Nesting.SUBTYPE); var template = Template.make(() -> scope( "xx", e1e2.toString(), "yy\n", + "xx", e1e2Exact.toString(), "yy\n", "xx", e1ex.toString(), "yy\n", + "xx", e1exExact.toString(), "yy\n", "xx", e1e4.toString(), "yy\n", + "xx", e1e4Exact.toString(), "yy\n", "xx", e1ey.toString(), "yy\n", + "xx", e1eyExact.toString(), "yy\n", "xx", deep1.toString(), "yy\n", + "xx", deep1Exact.toString(), "yy\n", "xx", deep2.toString(), "yy\n", + "xx", deep2Exact.toString(), "yy\n", "xx", e1e2.asToken(List.of("a")), "yy\n", + "xx", e1e2Exact.asToken(List.of("a")), "yy\n", "xx", e1ex.asToken(List.of("a")), "yy\n", + "xx", e1exExact.asToken(List.of("a")), "yy\n", "xx", e1e4.asToken(List.of("a")), "yy\n", + "xx", e1e4Exact.asToken(List.of("a")), "yy\n", "xx", e1ey.asToken(List.of("a")), "yy\n", + "xx", e1eyExact.asToken(List.of("a")), "yy\n", "xx", deep1.asToken(List.of("a")), "yy\n", - "xx", deep2.asToken(List.of("a")), "yy\n" + "xx", deep1Exact.asToken(List.of("a")), "yy\n", + "xx", deep2.asToken(List.of("a")), "yy\n", + "xx", deep2Exact.asToken(List.of("a")), "yy\n" )); String expected = """ xxExpression["[(", MyTypeA, ")]"]yy xxExpression["[(", MyTypeA, ")]"]yy + xxExpression["[(", MyTypeA, ")]"]yy + xxExpression["[(", MyTypeA, ")]"]yy xxExpression["[<", MyTypeA, ">]"]yy xxExpression["[", MyTypeA, "]"]yy + xxExpression["[", MyTypeA, "]"]yy + xxExpression["[", MyTypeA, "]"]yy + xxExpression["[[[[[", MyTypeA, "]]]]]"]yy xxExpression["[[[[[", MyTypeA, "]]]]]"]yy xxExpression["[{[{[", MyTypeB, "]}]}]"]yy + xxExpression["[{[{[", MyTypeB, "]}]}]"]yy + xx[(a)]yy + xx[(a)]yy xx[(a)]yy xx[(a)]yy xx[]yy xx[a]yy + xx[a]yy + xx[a]yy + xx[[[[[a]]]]]yy xx[[[[[a]]]]]yy xx[{[{[a]}]}]yy + xx[{[{[a]}]}]yy """; String code = template.render(); checkEQ(code, expected); From 08ff2bf4d0541f7b3fd7c4a9c7f386cb710510af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eirik=20Bj=C3=B8rsn=C3=B8s?= Date: Wed, 18 Mar 2026 14:12:43 +0000 Subject: [PATCH 90/97] 8376398: [TESTBUG] Testing of Unsafe native (re)allocation is sensitive to current JDK native memory use Reviewed-by: azafari --- test/hotspot/jtreg/runtime/Unsafe/AllocateMemory.java | 4 ++-- test/hotspot/jtreg/runtime/Unsafe/Reallocate.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/runtime/Unsafe/AllocateMemory.java b/test/hotspot/jtreg/runtime/Unsafe/AllocateMemory.java index 7d8d33b12257..b18bcb8cc134 100644 --- a/test/hotspot/jtreg/runtime/Unsafe/AllocateMemory.java +++ b/test/hotspot/jtreg/runtime/Unsafe/AllocateMemory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, 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 @@ -61,7 +61,7 @@ public static void main(String args[]) throws Exception { // allocateMemory() should throw an OutOfMemoryError when the underlying malloc fails, // since we start with -XX:MallocLimit try { - address = unsafe.allocateMemory(100 * 1024 * 1024); + address = unsafe.allocateMemory(101 * 1024 * 1024); throw new RuntimeException("Did not get expected OutOfMemoryError"); } catch (OutOfMemoryError e) { // Expected diff --git a/test/hotspot/jtreg/runtime/Unsafe/Reallocate.java b/test/hotspot/jtreg/runtime/Unsafe/Reallocate.java index c15b931449d7..dc0ce86fa1f5 100644 --- a/test/hotspot/jtreg/runtime/Unsafe/Reallocate.java +++ b/test/hotspot/jtreg/runtime/Unsafe/Reallocate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, 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 @@ -62,7 +62,7 @@ public static void main(String args[]) throws Exception { // Make sure we can throw an OOME when we fail to reallocate due to OOM try { - unsafe.reallocateMemory(address, 100 * 1024 * 1024); + unsafe.reallocateMemory(address, 101 * 1024 * 1024); } catch (OutOfMemoryError e) { // Expected return; From 00a77704b00eb4a4d4356695329fb0e247bb3028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Arias=20de=20Reyna=20Dom=C3=ADnguez?= Date: Wed, 18 Mar 2026 14:26:48 +0000 Subject: [PATCH 91/97] 8380292: Confusing "reverted *" messages during training Reviewed-by: adinn, asmehra --- src/hotspot/share/oops/constantPool.cpp | 2 +- src/hotspot/share/oops/cpCache.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 456333efad0d..8d817178d1a8 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -564,7 +564,7 @@ void ConstantPool::remove_resolved_klass_if_non_deterministic(int cp_index) { } LogStreamHandle(Trace, aot, resolve) log; - if (log.is_enabled()) { + if (log.is_enabled() && !CDSConfig::is_dumping_preimage_static_archive()) { ResourceMark rm; log.print("%s klass CP entry [%3d]: %s %s", (can_archive ? "archived" : "reverted"), diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index edb5f6714c03..4177e4601efe 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -450,7 +450,7 @@ void ConstantPoolCache::remove_resolved_field_entries_if_non_deterministic() { Symbol* klass_name = cp->klass_name_at(klass_cp_index); Symbol* name = cp->uncached_name_ref_at(cp_index); Symbol* signature = cp->uncached_signature_ref_at(cp_index); - if (resolved) { + if (resolved && !CDSConfig::is_dumping_preimage_static_archive()) { log.print("%s field CP entry [%3d]: %s => %s.%s:%s%s%s", (archived ? "archived" : "reverted"), cp_index, @@ -493,7 +493,7 @@ void ConstantPoolCache::remove_resolved_method_entries_if_non_deterministic() { Symbol* name = cp->uncached_name_ref_at(cp_index); Symbol* signature = cp->uncached_signature_ref_at(cp_index); LogStream ls(lt); - if (resolved) { + if (resolved && !CDSConfig::is_dumping_preimage_static_archive()) { ls.print("%s%s method CP entry [%3d]: %s %s.%s:%s", (archived ? "archived" : "reverted"), (rme->is_resolved(Bytecodes::_invokeinterface) ? " interface" : ""), @@ -539,7 +539,7 @@ void ConstantPoolCache::remove_resolved_indy_entries_if_non_deterministic() { Symbol* bsm_name = cp->uncached_name_ref_at(bsm_ref); Symbol* bsm_signature = cp->uncached_signature_ref_at(bsm_ref); Symbol* bsm_klass = cp->klass_name_at(cp->uncached_klass_ref_index_at(bsm_ref)); - if (resolved) { + if (resolved && !CDSConfig::is_dumping_preimage_static_archive()) { log.print("%s indy CP entry [%3d]: %s (%d)", (archived ? "archived" : "reverted"), cp_index, cp->pool_holder()->name()->as_C_string(), i); From 0379c0b005f4e99cb0f22ef6c43608697e805422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eirik=20Bj=C3=B8rsn=C3=B8s?= Date: Wed, 18 Mar 2026 14:36:58 +0000 Subject: [PATCH 92/97] 8379557: Further optimize URL.toExternalForm Reviewed-by: vyazici --- .../classes/java/net/URLStreamHandler.java | 35 +++++++-- test/jdk/java/net/URL/Constructor.java | 19 ++++- .../openjdk/bench/java/net/URLToString.java | 77 +++++++++++++++++++ 3 files changed, 122 insertions(+), 9 deletions(-) create mode 100644 test/micro/org/openjdk/bench/java/net/URLToString.java diff --git a/src/java.base/share/classes/java/net/URLStreamHandler.java b/src/java.base/share/classes/java/net/URLStreamHandler.java index f66902a451e6..76807d27cee9 100644 --- a/src/java.base/share/classes/java/net/URLStreamHandler.java +++ b/src/java.base/share/classes/java/net/URLStreamHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -481,14 +481,33 @@ protected boolean hostsEqual(URL u1, URL u2) { * @return a string representation of the {@code URL} argument. */ protected String toExternalForm(URL u) { - String s; + // The fast paths and branch-free concatenations in this method are here for + // a reason and should not be updated without checking performance figures. + + // Optionality, subtly different for authority + boolean emptyAuth = u.getAuthority() == null || u.getAuthority().isEmpty(); + boolean emptyPath = u.getPath() == null; + boolean emptyQuery = u.getQuery() == null; + boolean emptyRef = u.getRef() == null; + var path = emptyPath ? "" : u.getPath(); + // Fast paths for empty components + if (emptyQuery && emptyRef) { + return emptyAuth + ? (u.getProtocol() + ":" + path) + : (u.getProtocol() + "://" + u.getAuthority() + path); + } + // Prefer locals for efficient concatenation + var authSep = emptyAuth ? ":" : "://"; + var auth = emptyAuth ? "" : u.getAuthority(); + var querySep = emptyQuery ? "" : "?"; + var query = emptyQuery ? "" : u.getQuery(); + var refSep = emptyRef ? "" : "#"; + var ref = emptyRef ? "" : u.getRef(); return u.getProtocol() - + ':' - + ((s = u.getAuthority()) != null && !s.isEmpty() - ? "//" + s : "") - + ((s = u.getPath()) != null ? s : "") - + ((s = u.getQuery()) != null ? '?' + s : "") - + ((s = u.getRef()) != null ? '#' + s : ""); + + authSep + auth + + path + + querySep + query + + refSep + ref; } /** diff --git a/test/jdk/java/net/URL/Constructor.java b/test/jdk/java/net/URL/Constructor.java index 13eae40d6d44..b1d6dc91bc4e 100644 --- a/test/jdk/java/net/URL/Constructor.java +++ b/test/jdk/java/net/URL/Constructor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, 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 @@ -48,6 +48,7 @@ public static void main(String[] args) throws Exception { entries.addAll(Arrays.asList(jarURLs)); entries.addAll(Arrays.asList(normalHttpURLs)); entries.addAll(Arrays.asList(abnormalHttpURLs)); + entries.addAll(Arrays.asList(blankComponents)); if (hasFtp()) entries.addAll(Arrays.asList(ftpURLs)); URL url; @@ -252,4 +253,20 @@ static class Entry { "/dir1/entry.txt", "ftp://br:pwd@ftp.foo.com/dir1/entry.txt") }; + + static Entry[] blankComponents = new Entry[] { + new Entry(null, "http://host/path#", "http://host/path#"), + new Entry(null, "http://host/path?", "http://host/path?"), + new Entry(null, "http://host/path?#", "http://host/path?#"), + new Entry(null, "http://host/path#?", "http://host/path#?"), + new Entry(null, "file:/path#", "file:/path#"), + new Entry(null, "file:/path?", "file:/path?"), + new Entry(null, "file:/path?#", "file:/path?#"), + new Entry(null, "file:///path#", "file:/path#"), + new Entry(null, "file:///path?", "file:/path?"), + new Entry(null, "file:/path#?", "file:/path#?"), + new Entry("file:/path", "path?#", "file:/path?#"), + new Entry(null, "file:", "file:"), + new Entry(null, "file:#", "file:#") + }; } diff --git a/test/micro/org/openjdk/bench/java/net/URLToString.java b/test/micro/org/openjdk/bench/java/net/URLToString.java new file mode 100644 index 000000000000..38f6500c5732 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/net/URLToString.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.net; + +import org.openjdk.jmh.annotations.*; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.concurrent.TimeUnit; + +/** + * Tests java.net.URL.toString performance + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(value = 3) +public class URLToString { + + @Param({"false", "true"}) + boolean auth; + + @Param({"false", "true"}) + boolean query; + + @Param({"false", "true"}) + boolean ref; + + private URL url; + + @Setup() + public void setup() throws MalformedURLException { + StringBuilder sb = new StringBuilder(); + if (auth) { + sb.append("http://hostname"); + } else { + sb.append("file:"); + } + sb.append("/some/long/path/to/jar/app-1.0.jar!/org/summerframework/samples/horseclinic/HorseClinicApplication.class"); + if (query) { + sb.append("?param=value"); + } + if (ref) { + sb.append("#fragment"); + } + + url = URI.create(sb.toString()).toURL(); + } + + @Benchmark + public String urlToString() { + return url.toString(); + } +} From 766959f884be63e7f65175c6e685a10445c8a182 Mon Sep 17 00:00:00 2001 From: Ashay Rane <253344819+raneashay@users.noreply.github.com> Date: Wed, 18 Mar 2026 15:15:12 +0000 Subject: [PATCH 93/97] 8371685: C2: Add flag to disable Loop Peeling Reviewed-by: chagedorn, snatarajan, roland --- src/hotspot/share/opto/c2_globals.hpp | 9 ++ src/hotspot/share/opto/loopTransform.cpp | 22 +++++ src/hotspot/share/opto/loopnode.cpp | 8 +- .../compiler/loopopts/TestLoopPeeling.java | 37 +++++++- .../loopopts/TestLoopPeelingDisabled.java | 89 +++++++++++++++++++ 5 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestLoopPeelingDisabled.java diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 1662f808286c..66a6f2d0c3ce 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -334,6 +334,15 @@ product(bool, PartialPeelLoop, true, \ "Partial peel (rotate) loops") \ \ + product(uint, LoopPeeling, 1, DIAGNOSTIC, \ + "Control loop peeling optimization: " \ + "0 = always disable loop peeling, " \ + "1 = enable loop peeling (default), " \ + "2 = disable loop peeling as a standalone optimization but " \ + "allow it as a helper to other loop optimizations like removing " \ + "empty loops") \ + range(0, 2) \ + \ product(intx, PartialPeelNewPhiDelta, 0, \ "Additional phis that can be created by partial peeling") \ range(0, max_jint) \ diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 4e221a9a0ef0..1801841bad3e 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -524,6 +524,9 @@ bool IdealLoopTree::policy_peeling(PhaseIdealLoop *phase) { // return the estimated loop size if peeling is applicable, otherwise return // zero. No node budget is allocated. uint IdealLoopTree::estimate_peeling(PhaseIdealLoop *phase) { + if (LoopPeeling != 1) { + return 0; + } // If nodes are depleted, some transform has miscalculated its needs. assert(!phase->exceeding_node_budget(), "sanity"); @@ -775,6 +778,7 @@ void PhaseIdealLoop::peeled_dom_test_elim(IdealLoopTree* loop, Node_List& old_ne // exit // void PhaseIdealLoop::do_peeling(IdealLoopTree *loop, Node_List &old_new) { + assert(LoopPeeling != 0, "do_peeling called with loop peeling always disabled"); C->set_major_progress(); // Peeling a 'main' loop in a pre/main/post situation obfuscates the @@ -2201,6 +2205,15 @@ void PhaseIdealLoop::do_maximally_unroll(IdealLoopTree *loop, Node_List &old_new // If loop is tripping an odd number of times, peel odd iteration if ((cl->trip_count() & 1) == 1) { + if (LoopPeeling == 0) { +#ifndef PRODUCT + if (TraceLoopOpts) { + tty->print("MaxUnroll cancelled since LoopPeeling is always disabled"); + loop->dump_head(); + } +#endif + return; + } do_peeling(loop, old_new); } @@ -3243,6 +3256,15 @@ bool IdealLoopTree::do_remove_empty_loop(PhaseIdealLoop *phase) { #endif if (needs_guard) { + if (LoopPeeling == 0) { +#ifndef PRODUCT + if (TraceLoopOpts) { + tty->print("Empty loop not removed since LoopPeeling is always disabled"); + this->dump_head(); + } +#endif + return false; + } // Peel the loop to ensure there's a zero trip guard Node_List old_new; phase->do_peeling(this, old_new); diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 1523372e64cf..b2eb0c47458b 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -896,11 +896,11 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { // Loop is strip mined: use the safepoint of the outer strip mined loop OuterStripMinedLoopNode* outer_loop = head->as_CountedLoop()->outer_loop(); assert(outer_loop != nullptr, "no outer loop"); - safepoint = outer_loop->outer_safepoint(); + safepoint = LoopPeeling == 0 ? nullptr : outer_loop->outer_safepoint(); outer_loop->transform_to_counted_loop(&_igvn, this); exit_test = head->loopexit(); } else { - safepoint = find_safepoint(back_control, x, loop); + safepoint = LoopPeeling == 0 ? nullptr : find_safepoint(back_control, x, loop); } IfFalseNode* exit_branch = exit_test->false_proj(); @@ -1074,8 +1074,8 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { // Peel one iteration of the loop and use the safepoint at the end // of the peeled iteration to insert Parse Predicates. If no well // positioned safepoint peel to guarantee a safepoint in the outer - // loop. - if (safepoint != nullptr || !loop->_has_call) { + // loop. When loop peeling is disabled, skip the peeling step altogether. + if (LoopPeeling != 0 && (safepoint != nullptr || !loop->_has_call)) { old_new.clear(); do_peeling(loop, old_new); } else { diff --git a/test/hotspot/jtreg/compiler/loopopts/TestLoopPeeling.java b/test/hotspot/jtreg/compiler/loopopts/TestLoopPeeling.java index a32f3cb851aa..aab6e4f2aaa3 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestLoopPeeling.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestLoopPeeling.java @@ -23,12 +23,27 @@ /* * @test - * @bug 8078262 8177095 - * @summary Tests correct dominator information after loop peeling. + * @bug 8078262 8177095 8371685 + * @summary Tests correct dominator information after loop peeling and + * tests that LoopPeeling flag correctly disables loop peeling. * * @run main/othervm -Xcomp * -XX:CompileCommand=compileonly,compiler.loopopts.TestLoopPeeling::test* * compiler.loopopts.TestLoopPeeling + * @run main/othervm -Xcomp + * -XX:+UnlockDiagnosticVMOptions -XX:LoopPeeling=0 + * -XX:CompileCommand=compileonly,compiler.loopopts.TestLoopPeeling::test* + * compiler.loopopts.TestLoopPeeling + * @run main/othervm -Xbatch + * -XX:+UnlockDiagnosticVMOptions -XX:LoopPeeling=0 + * compiler.loopopts.TestLoopPeeling + * @run main/othervm -Xcomp + * -XX:+UnlockDiagnosticVMOptions -XX:LoopPeeling=2 + * -XX:CompileCommand=compileonly,compiler.loopopts.TestLoopPeeling::test* + * compiler.loopopts.TestLoopPeeling + * @run main/othervm -Xbatch + * -XX:+UnlockDiagnosticVMOptions -XX:LoopPeeling=2 + * compiler.loopopts.TestLoopPeeling */ package compiler.loopopts; @@ -44,6 +59,8 @@ public static void main(String args[]) { test.testArrayAccess2(0); test.testArrayAccess3(0, false); test.testArrayAllocation(0, 1); + test.testEmptyLoop(100); + test.testMaxUnrollOddTrip(); } catch (Exception e) { // Ignore exceptions } @@ -142,5 +159,19 @@ public byte[] testArrayAllocation(int index, int inc) { } return null; } -} + public void testEmptyLoop(int limit) { + for (int i = 0; i < limit; i++) { + // Empty body - candidate for empty loop removal + } + } + + public int testMaxUnrollOddTrip() { + int sum = 0; + // Trip count of 5 (odd) - maximal unroll needs to peel one iteration + for (int i = 0; i < 5; i++) { + sum += array[i]; + } + return sum; + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/TestLoopPeelingDisabled.java b/test/hotspot/jtreg/compiler/loopopts/TestLoopPeelingDisabled.java new file mode 100644 index 000000000000..e7bbe24d1def --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestLoopPeelingDisabled.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.loopopts; + +import compiler.lib.ir_framework.*; +import compiler.lib.ir_framework.driver.irmatching.IRViolationException; +import jdk.test.lib.Asserts; + +/* + * @test + * @bug 8371685 + * @requires vm.flagless & vm.debug + * @summary Verifies that the LoopPeeling flag correctly disables loop peeling + * by checking whether the "After Loop Peeling" compile phase is + * emitted. When loop peeling is disabled, no peeling should occur and + * the phase must be absent from the compilation output. + * @library /test/lib / + * @run driver compiler.loopopts.TestLoopPeelingDisabled + */ +public class TestLoopPeelingDisabled { + static int[] array = new int[100]; + + public static void main(String[] args) { + // First, run the test with loop peeling enabled, which is the default. + // The IR framework should catch if the number of counted loops does not + // match the annotations. + TestFramework.run(); + + // Then, run the same test with loop peeling disabled, which should + // elide the {BEFORE,AFTER}_LOOP_PEELING compilation phases, causing the + // test to throw an IRViolationException. We then check whether the + // exception message matches our expectation (that the loop peeling + // phase was not found). + try { + TestFramework.runWithFlags("-XX:+UnlockDiagnosticVMOptions", + "-XX:LoopPeeling=0"); + Asserts.fail("Expected IRViolationException"); + } catch (IRViolationException e) { + String info = e.getExceptionInfo(); + if (!info.contains("NO compilation output found for this phase")) { + Asserts.fail("Unexpected IR violation: " + info); + } + System.out.println("Loop peeling correctly disabled"); + } + + // Finally, run the same test with loop peeling disabled only when + // splitting iterations. Since the function being tested does not hit + // this case, we expect that the loop will be peeled, which is ensured + // by the IR annotations. + TestFramework.runWithFlags("-XX:+UnlockDiagnosticVMOptions", + "-XX:LoopPeeling=2"); + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "1"}, phase = CompilePhase.BEFORE_LOOP_PEELING) + @IR(counts = {IRNode.COUNTED_LOOP, "2"}, phase = CompilePhase.AFTER_LOOP_PEELING) + public static int test() { + int sum = 0; + + // Use an odd trip count so that `do_maximally_unroll()` tries to peel + // the odd iteration. + for (int i = 0; i < 5; i++) { + sum += array[i]; + } + + return sum; + } +} From 446fb203b2e27807f0cd2ec78447478a1de19773 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Wed, 18 Mar 2026 16:07:41 +0000 Subject: [PATCH 94/97] 8379015: Convert TraceNewOopMapGeneration to unified logging Reviewed-by: dholmes, matsaave --- src/hotspot/share/classfile/classPrinter.cpp | 3 +- src/hotspot/share/classfile/classPrinter.hpp | 3 +- .../share/interpreter/bytecodeTracer.cpp | 8 +- src/hotspot/share/logging/logTag.hpp | 3 +- src/hotspot/share/oops/generateOopMap.cpp | 111 +++++++----------- src/hotspot/share/runtime/globals.hpp | 12 -- .../runtime/logging/GenerateOopMapTest.java | 84 +++++++++++++ 7 files changed, 139 insertions(+), 85 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/logging/GenerateOopMapTest.java diff --git a/src/hotspot/share/classfile/classPrinter.cpp b/src/hotspot/share/classfile/classPrinter.cpp index 3ed0a5e98407..6cf89f7357ff 100644 --- a/src/hotspot/share/classfile/classPrinter.cpp +++ b/src/hotspot/share/classfile/classPrinter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -224,6 +224,7 @@ void ClassPrinter::print_flags_help(outputStream* os) { os->print_cr(" 0x%02x - print info for invokehandle", PRINT_METHOD_HANDLE); os->print_cr(" 0x%02x - print details of the C++ and Java objects that represent classes", PRINT_CLASS_DETAILS); os->print_cr(" 0x%02x - print details of the C++ objects that represent methods", PRINT_METHOD_DETAILS); + os->print_cr(" 0x%02x - print MethodData", PRINT_METHOD_DATA); os->cr(); } diff --git a/src/hotspot/share/classfile/classPrinter.hpp b/src/hotspot/share/classfile/classPrinter.hpp index 470e82ddc0e5..b09a1a1ef3b7 100644 --- a/src/hotspot/share/classfile/classPrinter.hpp +++ b/src/hotspot/share/classfile/classPrinter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,6 +54,7 @@ class ClassPrinter : public AllStatic { PRINT_METHOD_HANDLE = 1 << 4, // extra information for invokehandle PRINT_CLASS_DETAILS = 1 << 5, // print details of the C++ and Java objects that represent classes PRINT_METHOD_DETAILS = 1 << 6, // print details of the C++ objects that represent methods + PRINT_METHOD_DATA = 1 << 7, // print MethodData - requires MDO lock }; static bool has_mode(int flags, Mode mode) { return (flags & static_cast(mode)) != 0; diff --git a/src/hotspot/share/interpreter/bytecodeTracer.cpp b/src/hotspot/share/interpreter/bytecodeTracer.cpp index 219742189578..2610a8a2bd69 100644 --- a/src/hotspot/share/interpreter/bytecodeTracer.cpp +++ b/src/hotspot/share/interpreter/bytecodeTracer.cpp @@ -78,7 +78,7 @@ class BytecodePrinter { void print_field_or_method(int cp_index, outputStream* st); void print_dynamic(int cp_index, outputStream* st); void print_attributes(int bci, outputStream* st); - void bytecode_epilog(int bci, outputStream* st); + void print_method_data_at(int bci, outputStream* st); public: BytecodePrinter(int flags = 0) : _is_wide(false), _code(Bytecodes::_illegal), _flags(flags) {} @@ -171,7 +171,9 @@ class BytecodePrinter { } _next_pc = is_wide() ? bcp+2 : bcp+1; print_attributes(bci, st); - bytecode_epilog(bci, st); + if (ClassPrinter::has_mode(_flags, ClassPrinter::PRINT_METHOD_DATA)) { + print_method_data_at(bci, st); + } } }; @@ -598,7 +600,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { } -void BytecodePrinter::bytecode_epilog(int bci, outputStream* st) { +void BytecodePrinter::print_method_data_at(int bci, outputStream* st) { MethodData* mdo = method()->method_data(); if (mdo != nullptr) { diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index 3ad6a197d07d..20d61b542b08 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.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 @@ -90,6 +90,7 @@ class outputStream; LOG_TAG(freelist) \ LOG_TAG(gc) \ NOT_PRODUCT(LOG_TAG(generate)) \ + LOG_TAG(generateoopmap) \ LOG_TAG(handshake) \ LOG_TAG(hashtables) \ LOG_TAG(heap) \ diff --git a/src/hotspot/share/oops/generateOopMap.cpp b/src/hotspot/share/oops/generateOopMap.cpp index 5e12c57676d9..09912aeaf635 100644 --- a/src/hotspot/share/oops/generateOopMap.cpp +++ b/src/hotspot/share/oops/generateOopMap.cpp @@ -400,9 +400,7 @@ void GenerateOopMap::bb_mark_fct(GenerateOopMap *c, int bci, int *data) { if (c->is_bb_header(bci)) return; - if (TraceNewOopMapGeneration) { - tty->print_cr("Basicblock#%d begins at: %d", c->_bb_count, bci); - } + log_debug(generateoopmap)( "Basicblock#%d begins at: %d", c->_bb_count, bci); c->set_bbmark_bit(bci); c->_bb_count++; } @@ -913,13 +911,12 @@ void GenerateOopMap::do_interpretation() // iterated more than once. int i = 0; do { -#ifndef PRODUCT - if (TraceNewOopMapGeneration) { - tty->print("\n\nIteration #%d of do_interpretation loop, method:\n", i); - method()->print_name(tty); - tty->print("\n\n"); + if (log_is_enabled(Trace, generateoopmap)) { + LogStream st(Log(generateoopmap)::trace()); + st.print("Iteration #%d of do_interpretation loop, method:", i); + method()->print_name(&st); + st.print("\n\n"); } -#endif _conflict = false; _monitor_safe = true; // init_state is now called from init_basic_blocks. The length of a @@ -1084,8 +1081,7 @@ void GenerateOopMap::initialize_vars() { void GenerateOopMap::add_to_ref_init_set(int localNo) { - if (TraceNewOopMapGeneration) - tty->print_cr("Added init vars: %d", localNo); + log_debug(generateoopmap)("Added init vars: %d", localNo); // Is it already in the set? if (_init_vars->contains(localNo) ) @@ -1284,13 +1280,13 @@ void GenerateOopMap::report_monitor_mismatch(const char *msg) { void GenerateOopMap::print_states(outputStream *os, CellTypeState* vec, int num) { for (int i = 0; i < num; i++) { - vec[i].print(tty); + vec[i].print(os); } } // Print the state values at the current bytecode. -void GenerateOopMap::print_current_state(outputStream *os, - BytecodeStream *currentBC, +void GenerateOopMap::print_current_state(outputStream* os, + BytecodeStream* currentBC, bool detailed) { if (detailed) { os->print(" %4d vars = ", currentBC->bci()); @@ -1312,6 +1308,7 @@ void GenerateOopMap::print_current_state(outputStream *os, case Bytecodes::_invokestatic: case Bytecodes::_invokedynamic: case Bytecodes::_invokeinterface: { + ResourceMark rm; int idx = currentBC->has_index_u4() ? currentBC->get_index_u4() : currentBC->get_index_u2(); ConstantPool* cp = method()->constants(); int nameAndTypeIdx = cp->name_and_type_ref_index_at(idx, currentBC->code()); @@ -1342,8 +1339,9 @@ void GenerateOopMap::print_current_state(outputStream *os, // Sets the current state to be the state after executing the // current instruction, starting in the current state. void GenerateOopMap::interp1(BytecodeStream *itr) { - if (TraceNewOopMapGeneration) { - print_current_state(tty, itr, TraceNewOopMapGenerationDetailed); + if (log_is_enabled(Trace, generateoopmap)) { + LogStream st(Log(generateoopmap)::trace()); + print_current_state(&st, itr, Verbose); } // Should we report the results? Result is reported *before* the instruction at the current bci is executed. @@ -2026,9 +2024,7 @@ void GenerateOopMap::ret_jump_targets_do(BytecodeStream *bcs, jmpFct_t jmpFct, i DEBUG_ONLY(BasicBlock* target_bb = &jsr_bb[1];) assert(target_bb == get_basic_block_at(target_bci), "wrong calc. of successor basicblock"); bool alive = jsr_bb->is_alive(); - if (TraceNewOopMapGeneration) { - tty->print("pc = %d, ret -> %d alive: %s\n", bci, target_bci, alive ? "true" : "false"); - } + log_debug(generateoopmap)("pc = %d, ret -> %d alive: %s", bci, target_bci, alive ? "true" : "false"); if (alive) jmpFct(this, target_bci, data); } } @@ -2046,6 +2042,7 @@ char* GenerateOopMap::state_vec_to_string(CellTypeState* vec, int len) { return _state_vec_buf; } +#ifndef PRODUCT void GenerateOopMap::print_time() { tty->print_cr ("Accumulated oopmap times:"); tty->print_cr ("---------------------------"); @@ -2053,6 +2050,7 @@ void GenerateOopMap::print_time() { tty->print_cr (" (%3.0f bytecodes per sec) ", (double)GenerateOopMap::_total_byte_count / GenerateOopMap::_total_oopmap_time.seconds()); } +#endif // // ============ Main Entry Point =========== @@ -2062,27 +2060,16 @@ GenerateOopMap::GenerateOopMap(const methodHandle& method) { _method = method; _max_locals=0; _init_vars = nullptr; - -#ifndef PRODUCT - // If we are doing a detailed trace, include the regular trace information. - if (TraceNewOopMapGenerationDetailed) { - TraceNewOopMapGeneration = true; - } -#endif } bool GenerateOopMap::compute_map(Thread* current) { #ifndef PRODUCT - if (TimeOopMap2) { - method()->print_short_name(tty); - tty->print(" "); - } if (TimeOopMap) { _total_byte_count += method()->code_size(); + TraceTime t_all(nullptr, &_total_oopmap_time, TimeOopMap); } #endif - TraceTime t_single("oopmap time", TimeOopMap2); - TraceTime t_all(nullptr, &_total_oopmap_time, TimeOopMap); + TraceTime t_single("oopmap time", TRACETIME_LOG(Debug, generateoopmap)); // Initialize values _got_error = false; @@ -2099,16 +2086,16 @@ bool GenerateOopMap::compute_map(Thread* current) { _did_rewriting = false; _did_relocation = false; - if (TraceNewOopMapGeneration) { - tty->print("Method name: %s\n", method()->name()->as_C_string()); - if (Verbose) { - _method->print_codes(); - tty->print_cr("Exception table:"); - ExceptionTable excps(method()); - for(int i = 0; i < excps.length(); i ++) { - tty->print_cr("[%d - %d] -> %d", - excps.start_pc(i), excps.end_pc(i), excps.handler_pc(i)); - } + if (log_is_enabled(Debug, generateoopmap)) { + ResourceMark rm; + LogStream st(Log(generateoopmap)::debug()); + st.print_cr("Method name: %s\n", method()->name()->as_C_string()); + _method->print_codes_on(&st); + st.print_cr("Exception table:"); + ExceptionTable excps(method()); + for (int i = 0; i < excps.length(); i ++) { + st.print_cr("[%d - %d] -> %d", + excps.start_pc(i), excps.end_pc(i), excps.handler_pc(i)); } } @@ -2175,7 +2162,7 @@ void GenerateOopMap::verify_error(const char *format, ...) { // void GenerateOopMap::report_result() { - if (TraceNewOopMapGeneration) tty->print_cr("Report result pass"); + log_debug(generateoopmap)("Report result pass"); // We now want to report the result of the parse _report_result = true; @@ -2192,7 +2179,7 @@ void GenerateOopMap::report_result() { } void GenerateOopMap::result_for_basicblock(int bci) { - if (TraceNewOopMapGeneration) tty->print_cr("Report result pass for basicblock"); + log_debug(generateoopmap)("Report result pass for basicblock"); // We now want to report the result of the parse _report_result = true; @@ -2200,7 +2187,7 @@ void GenerateOopMap::result_for_basicblock(int bci) { // Find basicblock and report results BasicBlock* bb = get_basic_block_containing(bci); guarantee(bb != nullptr, "no basic block for bci"); - assert(bb->is_reachable(), "getting result from unreachable basicblock"); + assert(bb->is_reachable(), "getting result from unreachable basicblock %d", bci); bb->set_changed(true); interp_bb(bb); } @@ -2212,9 +2199,7 @@ void GenerateOopMap::result_for_basicblock(int bci) { void GenerateOopMap::record_refval_conflict(int varNo) { assert(varNo>=0 && varNo< _max_locals, "index out of range"); - if (TraceOopMapRewrites) { - tty->print("### Conflict detected (local no: %d)\n", varNo); - } + log_trace(generateoopmap)("### Conflict detected (local no: %d)", varNo); if (!_new_var_map) { _new_var_map = NEW_RESOURCE_ARRAY(int, _max_locals); @@ -2253,10 +2238,12 @@ void GenerateOopMap::rewrite_refval_conflicts() // Tracing flag _did_rewriting = true; - if (TraceOopMapRewrites) { - tty->print_cr("ref/value conflict for method %s - bytecodes are getting rewritten", method()->name()->as_C_string()); - method()->print(); - method()->print_codes(); + if (log_is_enabled(Trace, generateoopmap)) { + ResourceMark rm; + LogStream st(Log(generateoopmap)::trace()); + st.print_cr("ref/value conflict for method %s - bytecodes are getting rewritten", method()->name()->as_C_string()); + method()->print_on(&st); + method()->print_codes_on(&st); } assert(_new_var_map!=nullptr, "nothing to rewrite"); @@ -2266,9 +2253,7 @@ void GenerateOopMap::rewrite_refval_conflicts() if (!_got_error) { for (int k = 0; k < _max_locals && !_got_error; k++) { if (_new_var_map[k] != k) { - if (TraceOopMapRewrites) { - tty->print_cr("Rewriting: %d -> %d", k, _new_var_map[k]); - } + log_trace(generateoopmap)("Rewriting: %d -> %d", k, _new_var_map[k]); rewrite_refval_conflict(k, _new_var_map[k]); if (_got_error) return; nof_conflicts++; @@ -2319,22 +2304,16 @@ bool GenerateOopMap::rewrite_refval_conflict_inst(BytecodeStream *itr, int from, int bci = itr->bci(); if (is_aload(itr, &index) && index == from) { - if (TraceOopMapRewrites) { - tty->print_cr("Rewriting aload at bci: %d", bci); - } + log_trace(generateoopmap)("Rewriting aload at bci: %d", bci); return rewrite_load_or_store(itr, Bytecodes::_aload, Bytecodes::_aload_0, to); } if (is_astore(itr, &index) && index == from) { if (!stack_top_holds_ret_addr(bci)) { - if (TraceOopMapRewrites) { - tty->print_cr("Rewriting astore at bci: %d", bci); - } + log_trace(generateoopmap)("Rewriting astore at bci: %d", bci); return rewrite_load_or_store(itr, Bytecodes::_astore, Bytecodes::_astore_0, to); } else { - if (TraceOopMapRewrites) { - tty->print_cr("Suppress rewriting of astore at bci: %d", bci); - } + log_trace(generateoopmap)("Suppress rewriting of astore at bci: %d", bci); } } @@ -2502,9 +2481,7 @@ void GenerateOopMap::compute_ret_adr_at_TOS() { // TDT: should this be is_good_address() ? if (_stack_top > 0 && stack()[_stack_top-1].is_address()) { _ret_adr_tos->append(bcs.bci()); - if (TraceNewOopMapGeneration) { - tty->print_cr("Ret_adr TOS at bci: %d", bcs.bci()); - } + log_debug(generateoopmap)("Ret_adr TOS at bci: %d", bcs.bci()); } interp1(&bcs); } diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 559071cae685..0c627e272098 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -871,21 +871,9 @@ const int ObjectAlignmentInBytes = 8; develop(bool, VerifyDependencies, trueInDebug, \ "Exercise and verify the compilation dependency mechanism") \ \ - develop(bool, TraceNewOopMapGeneration, false, \ - "Trace OopMapGeneration") \ - \ - develop(bool, TraceNewOopMapGenerationDetailed, false, \ - "Trace OopMapGeneration: print detailed cell states") \ - \ develop(bool, TimeOopMap, false, \ "Time calls to GenerateOopMap::compute_map() in sum") \ \ - develop(bool, TimeOopMap2, false, \ - "Time calls to GenerateOopMap::compute_map() individually") \ - \ - develop(bool, TraceOopMapRewrites, false, \ - "Trace rewriting of methods during oop map generation") \ - \ develop(bool, TraceFinalizerRegistration, false, \ "Trace registration of final references") \ \ diff --git a/test/hotspot/jtreg/runtime/logging/GenerateOopMapTest.java b/test/hotspot/jtreg/runtime/logging/GenerateOopMapTest.java new file mode 100644 index 000000000000..6e114ff6b1d3 --- /dev/null +++ b/test/hotspot/jtreg/runtime/logging/GenerateOopMapTest.java @@ -0,0 +1,84 @@ +/* + * 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 GenerateOopMap + * @bug 8379015 + * @requires vm.flagless + * @modules java.base/jdk.internal.misc + * @library /test/lib + * @run driver GenerateOopMapTest + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.Platform; + +public class GenerateOopMapTest { + + static String infoPattern = "[generateoopmap]"; + static String debugPattern = "[generateoopmap] Basicblock#0 begins at:"; + static String tracePattern = "[trace][generateoopmap] 5 vars = 'r' stack = 'v' monitors = '' \tifne"; + static String traceDetailPattern = "[generateoopmap] 0 vars = ( r |slot0) invokestatic()V"; + + static void test() throws Exception { + // Don't print much with info level. + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:generateoopmap=info", + "GenerateOopMapTest", "test"); + OutputAnalyzer o = new OutputAnalyzer(pb.start()); + o.shouldNotContain(infoPattern).shouldHaveExitValue(0); + + // Prints bytecodes and BasicBlock information in debug. + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:generateoopmap=debug", + "GenerateOopMapTest", "test"); + o = new OutputAnalyzer(pb.start()); + o.shouldContain(debugPattern).shouldHaveExitValue(0); + + // Prints ref/val for each bytecode in trace. + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:generateoopmap=trace", + "GenerateOopMapTest", "test"); + o = new OutputAnalyzer(pb.start()); + o.shouldContain(tracePattern).shouldHaveExitValue(0); + + // Prints extra stuff with detailed. Not sure how useful this is but keep it for now. + if (Platform.isDebugBuild()) { + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:generateoopmap=trace", + "-XX:+Verbose", + "GenerateOopMapTest", "test"); + o = new OutputAnalyzer(pb.start()); + o.shouldContain(traceDetailPattern).shouldHaveExitValue(0); + } + }; + + public static void main(String... args) throws Exception { + System.gc(); + if (args.length == 0) { + test(); + } + } +} From 20567e82412b0ea8976a029728b400ca788178ba Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Wed, 18 Mar 2026 16:08:20 +0000 Subject: [PATCH 95/97] 8380218: Refactor test/jdk/java/nio/charset TestNG tests to JUnit Reviewed-by: bpb, naoto --- .../charset/Charset/DefaultCharsetTest.java | 50 +++++++-------- .../jdk/java/nio/charset/Charset/ForName.java | 62 ++++++++++--------- .../CoderMalfunctionErrorTest.java | 18 +++--- .../CoderMalfunctionErrorTest.java | 17 ++--- .../charset/spi/CharsetProviderBasicTest.java | 26 ++++---- test/jdk/sun/nio/cs/StreamEncoderOut.java | 25 ++++---- .../sun/nio/cs/TestUnicodeReversedBOM.java | 47 +++++++------- 7 files changed, 124 insertions(+), 121 deletions(-) diff --git a/test/jdk/java/nio/charset/Charset/DefaultCharsetTest.java b/test/jdk/java/nio/charset/Charset/DefaultCharsetTest.java index be33ecb70f2a..f3caf94775e5 100644 --- a/test/jdk/java/nio/charset/Charset/DefaultCharsetTest.java +++ b/test/jdk/java/nio/charset/Charset/DefaultCharsetTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,22 +34,19 @@ * jdk.test.lib.Platform * jdk.test.lib.process.* * Default - * @run testng DefaultCharsetTest + * @run junit DefaultCharsetTest */ -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; import java.util.Map; +import java.util.stream.Stream; -import jdk.test.lib.Platform; import jdk.test.lib.process.ProcessTools; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; public class DefaultCharsetTest { @@ -58,28 +55,27 @@ public class DefaultCharsetTest { private static final Map env = pb.environment(); private static String UNSUPPORTED = null; - @BeforeClass + @BeforeAll public static void checkSupports() throws Exception { UNSUPPORTED = runWithLocale("nonexist"); } - @DataProvider - public static Iterator locales() { - List data = new ArrayList<>(); - data.add(new String[]{"en_US", "iso-8859-1"}); - data.add(new String[]{"ja_JP.utf8", "utf-8"}); - data.add(new String[]{"tr_TR", "iso-8859-9"}); - data.add(new String[]{"C", "us-ascii"}); - data.add(new String[]{"ja_JP", "x-euc-jp-linux"}); - data.add(new String[]{"ja_JP.eucjp", "x-euc-jp-linux"}); - data.add(new String[]{"ja_JP.ujis", "x-euc-jp-linux"}); - data.add(new String[]{"ja_JP.utf8", "utf-8"}); - return data.iterator(); + public static Stream locales() { + return Stream.of( + Arguments.of("en_US", "iso-8859-1"), + Arguments.of("ja_JP.utf8", "utf-8"), + Arguments.of("tr_TR", "iso-8859-9"), + Arguments.of("C", "us-ascii"), + Arguments.of("ja_JP", "x-euc-jp-linux"), + Arguments.of("ja_JP.eucjp", "x-euc-jp-linux"), + Arguments.of("ja_JP.ujis", "x-euc-jp-linux"), + Arguments.of("ja_JP.utf8", "utf-8") + ); } - @Test(dataProvider = "locales") - public void testDefaultCharset(String locale, String expectedCharset) - throws Exception { + @ParameterizedTest + @MethodSource("locales") + public void testDefaultCharset(String locale, String expectedCharset) throws Exception { String actual = runWithLocale(locale); if (UNSUPPORTED.equals(actual)) { System.out.println(locale + ": Locale not supported, skipping..."); diff --git a/test/jdk/java/nio/charset/Charset/ForName.java b/test/jdk/java/nio/charset/Charset/ForName.java index d8a1e2f72d30..6af903a3c8a3 100644 --- a/test/jdk/java/nio/charset/Charset/ForName.java +++ b/test/jdk/java/nio/charset/Charset/ForName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,49 +25,51 @@ * @summary Unit test for forName(String, Charset) * @bug 8270490 * @modules jdk.charsets - * @run testng ForName + * @run junit ForName */ import java.nio.charset.Charset; -import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.StandardCharsets; +import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; -@Test public class ForName { - @DataProvider - Object[][] params() { - return new Object[][] { - {"UTF-8", null, StandardCharsets.UTF_8}, - {"UTF-8", StandardCharsets.US_ASCII, StandardCharsets.UTF_8}, - {"windows-31j", StandardCharsets.US_ASCII, Charset.forName("windows-31j")}, - {"foo", StandardCharsets.US_ASCII, StandardCharsets.US_ASCII}, - {"foo", null, null}, - {"\u3042", null, null}, - {"\u3042", StandardCharsets.UTF_8, StandardCharsets.UTF_8}, - }; + static Stream params() { + return Stream.of( + Arguments.of("UTF-8", null, StandardCharsets.UTF_8), + Arguments.of("UTF-8", StandardCharsets.US_ASCII, StandardCharsets.UTF_8), + Arguments.of("windows-31j", StandardCharsets.US_ASCII, Charset.forName("windows-31j")), + Arguments.of("foo", StandardCharsets.US_ASCII, StandardCharsets.US_ASCII), + Arguments.of("foo", null, null), + Arguments.of("\u3042", null, null), + Arguments.of("\u3042", StandardCharsets.UTF_8, StandardCharsets.UTF_8) + ); } - @DataProvider - Object[][] paramsIAE() { - return new Object[][] { - {null, null}, - {null, StandardCharsets.UTF_8}, - }; + static Stream paramsIAE() { + return Stream.of( + Arguments.of(null, null), + Arguments.of(null, StandardCharsets.UTF_8) + ); } - @Test(dataProvider="params") - public void testForName_2arg(String name, Charset fallback, Charset expected) throws Exception { + @ParameterizedTest + @MethodSource("params") + public void testForName_2arg(String name, Charset fallback, Charset expected) { var cs = Charset.forName(name, fallback); - assertEquals(cs, expected); + assertEquals(expected, cs); } - @Test(dataProvider="paramsIAE", expectedExceptions=IllegalArgumentException.class) - public void testForName_2arg_IAE(String name, Charset fallback) throws Exception { - Charset.forName(name, fallback); + @ParameterizedTest + @MethodSource("paramsIAE") + public void testForName_2arg_IAE(String name, Charset fallback) { + assertThrows(IllegalArgumentException.class, () -> Charset.forName(name, fallback)); } } diff --git a/test/jdk/java/nio/charset/CharsetDecoder/CoderMalfunctionErrorTest.java b/test/jdk/java/nio/charset/CharsetDecoder/CoderMalfunctionErrorTest.java index 79091071ea3b..5e830b955b69 100644 --- a/test/jdk/java/nio/charset/CharsetDecoder/CoderMalfunctionErrorTest.java +++ b/test/jdk/java/nio/charset/CharsetDecoder/CoderMalfunctionErrorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 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 @@ -23,26 +23,30 @@ /* @test * @bug 8253832 - * @run testng CoderMalfunctionErrorTest * @summary Check CoderMalfunctionError is thrown for any RuntimeException * on CharsetDecoder.decodeLoop() invocation. + * @run junit CoderMalfunctionErrorTest */ -import org.testng.annotations.Test; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.*; -@Test +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; + public class CoderMalfunctionErrorTest { - @Test (expectedExceptions = CoderMalfunctionError.class) + + @Test public void testDecodeLoop() { - new CharsetDecoder(StandardCharsets.US_ASCII, 1, 1) { + assertThrows(CoderMalfunctionError.class, + () -> new CharsetDecoder(StandardCharsets.US_ASCII, 1, 1) { @Override protected CoderResult decodeLoop(ByteBuffer byteBuffer, CharBuffer charBuffer) { throw new RuntimeException("This exception should be wrapped in CoderMalfunctionError"); } - }.decode(null, null, true); + }.decode(null, null, true)); } } diff --git a/test/jdk/java/nio/charset/CharsetEncoder/CoderMalfunctionErrorTest.java b/test/jdk/java/nio/charset/CharsetEncoder/CoderMalfunctionErrorTest.java index ffd73164d9f6..e917126d4617 100644 --- a/test/jdk/java/nio/charset/CharsetEncoder/CoderMalfunctionErrorTest.java +++ b/test/jdk/java/nio/charset/CharsetEncoder/CoderMalfunctionErrorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 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 @@ -23,26 +23,29 @@ /* @test * @bug 8253832 - * @run testng CoderMalfunctionErrorTest + * @run junit CoderMalfunctionErrorTest * @summary Check CoderMalfunctionError is thrown for any RuntimeException * on CharsetEncoder.encodeLoop() invocation. */ -import org.testng.annotations.Test; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.*; -@Test +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; + public class CoderMalfunctionErrorTest { - @Test (expectedExceptions = CoderMalfunctionError.class) + @Test public void testEncodeLoop() { - new CharsetEncoder(StandardCharsets.US_ASCII, 1, 1) { + assertThrows(CoderMalfunctionError.class, + () -> new CharsetEncoder(StandardCharsets.US_ASCII, 1, 1) { @Override protected CoderResult encodeLoop(CharBuffer charBuffer, ByteBuffer byteBuffer) { throw new RuntimeException("This exception should be wrapped in CoderMalfunctionError"); } - }.encode(null, null, true); + }.encode(null, null, true)); } } diff --git a/test/jdk/java/nio/charset/spi/CharsetProviderBasicTest.java b/test/jdk/java/nio/charset/spi/CharsetProviderBasicTest.java index abf46d81b1c8..8b0e74d4360b 100644 --- a/test/jdk/java/nio/charset/spi/CharsetProviderBasicTest.java +++ b/test/jdk/java/nio/charset/spi/CharsetProviderBasicTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,28 +35,25 @@ * jdk.test.lib.util.JarUtils * FooCharset FooProvider CharsetTest * @run driver SetupJar - * @run testng CharsetProviderBasicTest + * @run junit CharsetProviderBasicTest */ import java.io.File; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.stream.Stream; import jdk.test.lib.JDKToolFinder; import jdk.test.lib.Utils; import jdk.test.lib.process.ProcessTools; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import static java.util.Arrays.asList; public class CharsetProviderBasicTest { - private static final String TEST_SRC = System.getProperty("test.src"); - private static final List DEFAULT_CSS = List.of( "US-ASCII", "8859_1", "iso-ir-6", "UTF-16", "windows-1252", "!BAR", "cp1252" ); @@ -69,14 +66,15 @@ private static boolean checkSupports(String locale) throws Throwable { .equals(locale); } - @DataProvider - public static Iterator testCases() { - return Stream.of("", "ja_JP.eucJP", "tr_TR") - .map(locale -> new Object[]{locale, "FOO"}) - .iterator(); + public static Stream testCases() { + return Stream.of("", + "ja_JP.eucJP", + "tr_TR") + .map(locale -> Arguments.of(locale, "FOO")); } - @Test(dataProvider = "testCases") + @ParameterizedTest + @MethodSource("testCases") public void testDefaultCharset(String locale, String css) throws Throwable { if ((System.getProperty("os.name").startsWith("Windows") || !checkSupports(locale)) && (!locale.isEmpty())) { diff --git a/test/jdk/sun/nio/cs/StreamEncoderOut.java b/test/jdk/sun/nio/cs/StreamEncoderOut.java index 90a22512311b..90b4aee838d7 100644 --- a/test/jdk/sun/nio/cs/StreamEncoderOut.java +++ b/test/jdk/sun/nio/cs/StreamEncoderOut.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,10 +24,8 @@ /* @test @bug 8030179 @summary test if the charset encoder deails with surrogate correctly - * @run testng/othervm -esa StreamEncoderOut + * @run junit/othervm -esa StreamEncoderOut */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.io.OutputStream; @@ -35,9 +33,12 @@ import java.nio.charset.Charset; import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import static java.util.stream.Collectors.joining; -@Test public class StreamEncoderOut { enum Input { @@ -57,14 +58,11 @@ public String toString() { } } - @DataProvider(name = "CharsetAndString") - // [Charset, Input] - public static Object[][] makeStreamTestData() { + public static Stream makeStreamTestData() { // Cross product of supported charsets and inputs - return Charset.availableCharsets().values().stream(). - filter(Charset::canEncode). - flatMap(cs -> Stream.of(Input.values()).map(i -> new Object[]{cs, i})). - toArray(Object[][]::new); + return Charset.availableCharsets().values().stream() + .filter(Charset::canEncode) + .flatMap(cs -> Stream.of(Input.values()).map(i -> Arguments.of(cs, i))); } private static String generate(String s, int n) { @@ -79,7 +77,8 @@ public void write(byte b[], int off, int len) throws IOException {} public void write(int b) throws IOException {} }; - @Test(dataProvider = "CharsetAndString") + @ParameterizedTest + @MethodSource("makeStreamTestData") public void test(Charset cs, Input input) throws IOException { OutputStreamWriter w = new OutputStreamWriter(DEV_NULL, cs); String t = generate(input.value, 8193); diff --git a/test/jdk/sun/nio/cs/TestUnicodeReversedBOM.java b/test/jdk/sun/nio/cs/TestUnicodeReversedBOM.java index d76bf2f60347..ca0798c13e55 100644 --- a/test/jdk/sun/nio/cs/TestUnicodeReversedBOM.java +++ b/test/jdk/sun/nio/cs/TestUnicodeReversedBOM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 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,16 +26,18 @@ * @bug 8216140 * @summary Test reversed BOM (U+FFFE) in the middle of a byte buffer * passes through during decoding with UnicodeDecoder. - * @run testng TestUnicodeReversedBOM + * @run junit TestUnicodeReversedBOM */ + import java.nio.charset.*; import java.nio.*; import java.util.*; +import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -@Test public class TestUnicodeReversedBOM { private static byte[] A_REVERSED_BE = {(byte)0x0, (byte)'A', (byte)0xff, (byte)0xfe}; @@ -46,26 +48,25 @@ public class TestUnicodeReversedBOM { private static byte[] BOM_REVERSED_LE = {(byte)0xff, (byte)0xfe, (byte)0xfe, (byte)0xff}; - @DataProvider - // [(byte[])byte array, (Charset)cs] - public static Object[][] ReversedBOM() { - return new Object[][] { - {A_REVERSED_BE, StandardCharsets.UTF_16}, - {A_REVERSED_LE, StandardCharsets.UTF_16}, - {A_REVERSED_BE, StandardCharsets.UTF_16BE}, - {A_REVERSED_LE, StandardCharsets.UTF_16BE}, - {A_REVERSED_BE, StandardCharsets.UTF_16LE}, - {A_REVERSED_LE, StandardCharsets.UTF_16LE}, - {BOM_REVERSED_BE, StandardCharsets.UTF_16}, - {BOM_REVERSED_LE, StandardCharsets.UTF_16}, - {BOM_REVERSED_BE, StandardCharsets.UTF_16BE}, - {BOM_REVERSED_LE, StandardCharsets.UTF_16BE}, - {BOM_REVERSED_BE, StandardCharsets.UTF_16LE}, - {BOM_REVERSED_LE, StandardCharsets.UTF_16LE}, - }; + public static Stream ReversedBOM() { + return Stream.of( + Arguments.of(A_REVERSED_BE, StandardCharsets.UTF_16), + Arguments.of(A_REVERSED_LE, StandardCharsets.UTF_16), + Arguments.of(A_REVERSED_BE, StandardCharsets.UTF_16BE), + Arguments.of(A_REVERSED_LE, StandardCharsets.UTF_16BE), + Arguments.of(A_REVERSED_BE, StandardCharsets.UTF_16LE), + Arguments.of(A_REVERSED_LE, StandardCharsets.UTF_16LE), + Arguments.of(BOM_REVERSED_BE, StandardCharsets.UTF_16), + Arguments.of(BOM_REVERSED_LE, StandardCharsets.UTF_16), + Arguments.of(BOM_REVERSED_BE, StandardCharsets.UTF_16BE), + Arguments.of(BOM_REVERSED_LE, StandardCharsets.UTF_16BE), + Arguments.of(BOM_REVERSED_BE, StandardCharsets.UTF_16LE), + Arguments.of(BOM_REVERSED_LE, StandardCharsets.UTF_16LE) + ); } - @Test(dataProvider = "ReversedBOM") + @ParameterizedTest + @MethodSource("ReversedBOM") public void testReversedBOM(byte[] ba, Charset cs) throws CharacterCodingException { cs.newDecoder() .onMalformedInput(CodingErrorAction.REPORT) From b6de5aed2c2a5493c089b7f8493e953b13e4c2fa Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Wed, 18 Mar 2026 16:45:53 +0000 Subject: [PATCH 96/97] 8379699: H3ConnectionPoolTest::testH2H3WithTwoAltSVC fails intermittently Reviewed-by: syan, jpai --- .../http3/H3ConnectionPoolTest.java | 56 +++++++------------ 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java b/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java index 614d564005be..129566044849 100644 --- a/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java +++ b/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java @@ -25,11 +25,13 @@ * @test * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer - * jdk.test.lib.Asserts * jdk.test.lib.Utils * jdk.test.lib.net.SimpleSSLContext * @run junit/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors,http3,quic:hs * -Djdk.internal.httpclient.debug=false + * -Djdk.httpclient.keepalive.timeout.h3=480 + * -Djdk.httpclient.quic.idleTimeout=480 + * -Djdk.test.server.quic.idleTimeout=480 * H3ConnectionPoolTest */ @@ -44,7 +46,6 @@ import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.function.Supplier; import javax.net.ssl.SSLContext; @@ -60,11 +61,11 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.Http3DiscoveryMode.ANY; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; -import static jdk.test.lib.Asserts.assertEquals; -import static jdk.test.lib.Asserts.assertNotEquals; -import static jdk.test.lib.Asserts.assertTrue; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class H3ConnectionPoolTest implements HttpServerAdapters { @@ -257,7 +258,7 @@ private static void testH2H3(boolean samePort) throws Exception { HttpResponse response2 = client.send(request2, BodyHandlers.ofString()); assertEquals(HTTP_3, response2.version()); checkStatus(200, response2.statusCode()); - assertNotEquals(response2.connectionLabel().get(), response1.connectionLabel().get()); + assertNotEquals(response1.connectionLabel().get(), response2.connectionLabel().get()); // second request with HTTP3_URI_ONLY should reuse a created connection // It should reuse the advertised connection (from response2) if same @@ -295,8 +296,8 @@ private static void testH2H3(boolean samePort) throws Exception { HttpResponse response2 = client.send(request2, BodyHandlers.ofString()); assertEquals(HTTP_3, response2.version()); checkStatus(200, response2.statusCode()); - assertNotEquals(response2.connectionLabel().get(), h2resp2.connectionLabel().get()); - assertNotEquals(response2.connectionLabel().get(), response1.connectionLabel().get()); + assertNotEquals(h2resp2.connectionLabel().get(), response2.connectionLabel().get()); + assertNotEquals(response1.connectionLabel().get(), response2.connectionLabel().get()); // third request with ALT_SVC should reuse the same advertised // connection (from response2), regardless of same origin... @@ -304,16 +305,16 @@ private static void testH2H3(boolean samePort) throws Exception { HttpResponse response3 = client.send(request3, BodyHandlers.ofString()); assertEquals(HTTP_3, response3.version()); checkStatus(200, response3.statusCode()); - assertEquals(response3.connectionLabel().get(), response2.connectionLabel().get()); - assertNotEquals(response3.connectionLabel().get(), response1.connectionLabel().get()); + assertEquals(response2.connectionLabel().get(), response3.connectionLabel().get()); + assertNotEquals(response1.connectionLabel().get(), response3.connectionLabel().get()); // fourth request with HTTP_3_URI_ONLY should reuse the first connection, // and not reuse the second. HttpRequest request4 = req1Builder.copy().build(); HttpResponse response4 = client.send(request4, BodyHandlers.ofString()); assertEquals(HTTP_3, response4.version()); - assertEquals(response4.connectionLabel().get(), response1.connectionLabel().get()); - assertNotEquals(response4.connectionLabel().get(), response3.connectionLabel().get()); + assertEquals(response1.connectionLabel().get(), response4.connectionLabel().get()); + assertNotEquals(response3.connectionLabel().get(), response4.connectionLabel().get()); checkStatus(200, response1.statusCode()); } else { System.out.println("WARNING: Couldn't create HTTP/3 server on same port! Can't test all..."); @@ -329,7 +330,7 @@ private static void testH2H3(boolean samePort) throws Exception { HttpResponse response2 = client.send(request2, BodyHandlers.ofString()); assertEquals(HTTP_3, response2.version()); checkStatus(200, response2.statusCode()); - assertNotEquals(response2.connectionLabel().get(), h2resp2.connectionLabel().get()); + assertNotEquals(h2resp2.connectionLabel().get(), response2.connectionLabel().get()); // third request with ALT_SVC should reuse the same advertised // connection (from response2), regardless of same origin... @@ -337,7 +338,7 @@ private static void testH2H3(boolean samePort) throws Exception { HttpResponse response3 = client.send(request3, BodyHandlers.ofString()); assertEquals(HTTP_3, response3.version()); checkStatus(200, response3.statusCode()); - assertEquals(response3.connectionLabel().get(), response2.connectionLabel().get()); + assertEquals(response2.connectionLabel().get(), response3.connectionLabel().get()); } } finally { http3OnlyServer.stop(); @@ -417,7 +418,7 @@ private static void testH2H3Concurrent(boolean samePort) throws Exception { response2); assertEquals(HTTP_3, response2.version()); checkStatus(200, response2.statusCode()); - assertNotEquals(response2.connectionLabel().get(), c1Label); + assertNotEquals(c1Label, response2.connectionLabel().get()); if (i == 0) { c2Label = response2.connectionLabel().get(); } @@ -494,8 +495,8 @@ private static void testH2H3Concurrent(boolean samePort) throws Exception { if (i == 0) { c2Label = response2.connectionLabel().get(); } - assertNotEquals(response2.connectionLabel().get(), h2resp2.connectionLabel().get()); - assertNotEquals(response2.connectionLabel().get(), c1Label); + assertNotEquals(h2resp2.connectionLabel().get(), response2.connectionLabel().get()); + assertNotEquals(c1Label, response2.connectionLabel().get()); assertEquals(c2Label, response2.connectionLabel().orElse(null)); } var expectedLabels = Set.of(c1Label, c2Label); @@ -507,7 +508,7 @@ private static void testH2H3Concurrent(boolean samePort) throws Exception { response3); assertEquals(HTTP_3, response3.version()); checkStatus(200, response3.statusCode()); - assertNotEquals(response3.connectionLabel().get(), h2resp2.connectionLabel().get()); + assertNotEquals(h2resp2.connectionLabel().get(), response3.connectionLabel().get()); var label = response3.connectionLabel().orElse(""); assertTrue(expectedLabels.contains(label), "Unexpected label: %s not in %s" .formatted(label, expectedLabels)); @@ -526,7 +527,7 @@ private static void testH2H3Concurrent(boolean samePort) throws Exception { HttpResponse response2 = client.send(request2, BodyHandlers.ofString()); assertEquals(HTTP_3, response2.version()); checkStatus(200, response2.statusCode()); - assertNotEquals(response2.connectionLabel().get(), h2resp2.connectionLabel().get()); + assertNotEquals(h2resp2.connectionLabel().get(), response2.connectionLabel().get()); // third request with ALT_SVC should reuse the same advertised // connection (from response2), regardless of same origin... @@ -560,21 +561,4 @@ static void checkStatus(int expected, int found) throws Exception { } } - static void checkStrings(String expected, String found) throws Exception { - if (!expected.equals(found)) { - System.err.printf("Test failed: wrong string %s/%s\n", - expected, found); - throw new RuntimeException("Test failed"); - } - } - - - static T logExceptionally(String desc, Throwable t) { - System.out.println(desc + " failed: " + t); - System.err.println(desc + " failed: " + t); - if (t instanceof RuntimeException r) throw r; - if (t instanceof Error e) throw e; - throw new CompletionException(t); - } - } From 7d805e113948196ac4e45ac05e09413e63b9bb46 Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Wed, 18 Mar 2026 16:57:52 +0000 Subject: [PATCH 97/97] 8380222: Refactor test/jdk/java/lang/Character TestNG tests to JUnit Reviewed-by: jlu, bpb, liach, iris --- .../lang/Character/Latin1CaseConversion.java | 47 +++++++++---------- .../UnicodeBlock/NumberEntities.java | 17 +++---- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/test/jdk/java/lang/Character/Latin1CaseConversion.java b/test/jdk/java/lang/Character/Latin1CaseConversion.java index a176bd4b002e..436193c5f168 100644 --- a/test/jdk/java/lang/Character/Latin1CaseConversion.java +++ b/test/jdk/java/lang/Character/Latin1CaseConversion.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,17 +21,16 @@ * questions. */ -import org.testng.annotations.Test; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; /** * @test * @bug 8302877 * @summary Provides exhaustive verification of Character.toUpperCase and Character.toLowerCase * for all code points in the latin1 range 0-255. - * @run testng Latin1CaseConversion + * @run junit Latin1CaseConversion */ public class Latin1CaseConversion { @@ -44,41 +43,41 @@ public void shouldUpperCaseAndLowerCaseLatin1() { if (c < 0x41) { // Before A assertUnchanged(upper, lower, c); } else if (c <= 0x5A) { // A-Z - assertEquals(upper, c); - assertEquals(lower, c + 32); + assertEquals(c, upper); + assertEquals(c + 32, lower); } else if (c < 0x61) { // Between Z and a assertUnchanged(upper, lower, c); } else if (c <= 0x7A) { // a-z - assertEquals(upper, c - 32); - assertEquals(lower, c); + assertEquals(c - 32, upper); + assertEquals(c, lower); } else if (c < 0xB5) { // Between z and Micro Sign assertUnchanged(upper, lower, c); } else if (c == 0xB5) { // Special case for Micro Sign - assertEquals(upper, 0x39C); - assertEquals(lower, c); + assertEquals(0x39C, upper); + assertEquals(c, lower); } else if (c < 0xC0) { // Between my and A-grave assertUnchanged(upper, lower, c); } else if (c < 0xD7) { // A-grave - O with Diaeresis - assertEquals(upper, c); - assertEquals(lower, c + 32); + assertEquals(c, upper); + assertEquals(c + 32, lower); } else if (c == 0xD7) { // Multiplication assertUnchanged(upper, lower, c); } else if (c <= 0xDE) { // O with slash - Thorn - assertEquals(upper, c); - assertEquals(lower, c + 32); + assertEquals(c, upper); + assertEquals(c + 32, lower); } else if (c == 0xDF) { // Sharp s assertUnchanged(upper, lower, c); } else if (c < 0xF7) { // a-grave - divsion - assertEquals(upper, c - 32); - assertEquals(lower, c); + assertEquals(c - 32, upper); + assertEquals(c, lower); } else if (c == 0xF7) { // Division assertUnchanged(upper, lower, c); } else if (c < 0xFF) { // o with slash - thorn - assertEquals(upper, c - 32); - assertEquals(lower, c); + assertEquals(c - 32, upper); + assertEquals(c, lower); } else if (c == 0XFF) { // Special case for y with Diaeresis - assertEquals(upper, 0x178); - assertEquals(lower, c); + assertEquals(0x178, upper); + assertEquals(c, lower); } else { fail("Uncovered code point: " + Integer.toHexString(c)); } @@ -86,7 +85,7 @@ public void shouldUpperCaseAndLowerCaseLatin1() { } private static void assertUnchanged(int upper, int lower, int c) { - assertEquals(upper, c); - assertEquals(lower, c); + assertEquals(c, upper); + assertEquals(c, lower); } } diff --git a/test/jdk/java/lang/Character/UnicodeBlock/NumberEntities.java b/test/jdk/java/lang/Character/UnicodeBlock/NumberEntities.java index 30382c3cfe39..86b9fe6ea692 100644 --- a/test/jdk/java/lang/Character/UnicodeBlock/NumberEntities.java +++ b/test/jdk/java/lang/Character/UnicodeBlock/NumberEntities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, 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 @@ -28,17 +28,17 @@ * of Character.UnicodeBlock constants. Also checks the size of * Character.UnicodeScript's "aliases" map. * @modules java.base/java.lang:open - * @run testng NumberEntities + * @run junit NumberEntities */ -import static org.testng.Assert.assertEquals; -import org.testng.annotations.Test; - import java.lang.reflect.Field; import java.util.Map; -@Test +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class NumberEntities { + @Test public void test_UnicodeBlock_NumberEntities() throws Throwable { // The number of entries in Character.UnicodeBlock.map. // See src/java.base/share/classes/java/lang/Character.java @@ -46,13 +46,14 @@ public void test_UnicodeBlock_NumberEntities() throws Throwable { Field m = Character.UnicodeBlock.class.getDeclaredField("map"); n.setAccessible(true); m.setAccessible(true); - assertEquals(((Map)m.get(null)).size(), n.getInt(null)); + assertEquals(n.getInt(null), ((Map)m.get(null)).size()); } + @Test public void test_UnicodeScript_aliases() throws Throwable { // The number of entries in Character.UnicodeScript.aliases. // See src/java.base/share/classes/java/lang/Character.java Field aliases = Character.UnicodeScript.class.getDeclaredField("aliases"); aliases.setAccessible(true); - assertEquals(((Map)aliases.get(null)).size(), Character.UnicodeScript.UNKNOWN.ordinal() + 1); + assertEquals(Character.UnicodeScript.UNKNOWN.ordinal() + 1, ((Map)aliases.get(null)).size()); } }