diff --git a/.gitignore b/.gitignore index 0743489f8ec..b6b4a1a559a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ NashornProfile.txt **/JTreport/** **/JTwork/** /src/utils/LogCompilation/target/ +/src/utils/LogCompilation/logc.jar /.project/ /.settings/ /compile_commands.json diff --git a/make/PreInit.gmk b/make/PreInit.gmk index 3df44308dd9..8152587781c 100644 --- a/make/PreInit.gmk +++ b/make/PreInit.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -66,7 +66,8 @@ CALLED_SPEC_TARGETS := $(filter-out $(ALL_GLOBAL_TARGETS), $(CALLED_TARGETS)) ifeq ($(CALLED_SPEC_TARGETS), ) SKIP_SPEC := true endif -ifeq ($(findstring p, $(MAKEFLAGS))$(findstring q, $(MAKEFLAGS)), pq) +MFLAGS_SINGLE := $(filter-out --%, $(MFLAGS)) +ifeq ($(findstring p, $(MFLAGS_SINGLE))$(findstring q, $(MFLAGS_SINGLE)), pq) SKIP_SPEC := true endif diff --git a/make/autoconf/lib-bundled.m4 b/make/autoconf/lib-bundled.m4 index bc358928af0..7aa5fdf2589 100644 --- a/make/autoconf/lib-bundled.m4 +++ b/make/autoconf/lib-bundled.m4 @@ -267,7 +267,7 @@ AC_DEFUN_ONCE([LIB_SETUP_ZLIB], LIBZ_LIBS="" if test "x$USE_EXTERNAL_LIBZ" = "xfalse"; then LIBZ_CFLAGS="$LIBZ_CFLAGS -I$TOPDIR/src/java.base/share/native/libzip/zlib" - if test "x$OPENJDK_TARGET_OS" = xmacosx; then + if test "x$OPENJDK_TARGET_OS" = xmacosx -o "x$OPENJDK_TARGET_OS" = xaix -o "x$OPENJDK_TARGET_OS" = xlinux; then LIBZ_CFLAGS="$LIBZ_CFLAGS -DHAVE_UNISTD_H=1 -DHAVE_STDARG_H=1" fi else diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index a9ca91d9309..9734c6845ea 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -3403,11 +3403,13 @@ encode %{ } else if (rtype == relocInfo::metadata_type) { __ mov_metadata(dst_reg, (Metadata*)con); } else { - assert(rtype == relocInfo::none, "unexpected reloc type"); + assert(rtype == relocInfo::none || rtype == relocInfo::external_word_type, "unexpected reloc type"); + // load fake address constants using a normal move if (! __ is_valid_AArch64_address(con) || con < (address)(uintptr_t)os::vm_page_size()) { __ mov(dst_reg, con); } else { + // no reloc so just use adrp and add uint64_t offset; __ adrp(dst_reg, con, offset); __ add(dst_reg, dst_reg, offset); @@ -4535,6 +4537,18 @@ operand immP_1() interface(CONST_INTER); %} +// AOT Runtime Constants Address +operand immAOTRuntimeConstantsAddress() +%{ + // Check if the address is in the range of AOT Runtime Constants + predicate(AOTRuntimeConstants::contains((address)(n->get_ptr()))); + match(ConP); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + // Float and Double operands // Double Immediate operand immD() @@ -6898,6 +6912,20 @@ instruct loadConP1(iRegPNoSp dst, immP_1 con) ins_pipe(ialu_imm); %} +instruct loadAOTRCAddress(iRegPNoSp dst, immAOTRuntimeConstantsAddress con) +%{ + match(Set dst con); + + ins_cost(INSN_COST); + format %{ "adr $dst, $con\t# AOT Runtime Constants Address" %} + + ins_encode %{ + __ load_aotrc_address($dst$$Register, (address)$con$$constant); + %} + + ins_pipe(ialu_imm); +%} + // Load Narrow Pointer Constant instruct loadConN(iRegNNoSp dst, immN con) diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index c0621cbd5c2..30048a2079d 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -33,6 +33,7 @@ #include "c1/c1_ValueStack.hpp" #include "ci/ciArrayKlass.hpp" #include "ci/ciInstance.hpp" +#include "code/aotCodeCache.hpp" #include "code/compiledIC.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/gc_globals.hpp" @@ -532,6 +533,15 @@ void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_cod case T_LONG: { assert(patch_code == lir_patch_none, "no patching handled here"); +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + address b = c->as_pointer(); + if (AOTRuntimeConstants::contains(b)) { + __ load_aotrc_address(dest->as_register_lo(), b); + break; + } + } +#endif __ mov(dest->as_register_lo(), (intptr_t)c->as_jlong()); break; } diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp index d7884c27a2c..68291720208 100644 --- a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp @@ -23,6 +23,7 @@ */ #include "asm/macroAssembler.inline.hpp" +#include "code/aotCodeCache.hpp" #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BarrierSetAssembler.hpp" #include "gc/g1/g1BarrierSetRuntime.hpp" @@ -243,9 +244,25 @@ static void generate_post_barrier(MacroAssembler* masm, assert_different_registers(store_addr, new_val, thread, tmp1, tmp2, noreg, rscratch1); // Does store cross heap regions? - __ eor(tmp1, store_addr, new_val); // tmp1 := store address ^ new value - __ lsr(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes) - __ cbz(tmp1, done); + #if INCLUDE_CDS + // AOT code needs to load the barrier grain shift from the aot + // runtime constants area in the code cache otherwise we can compile + // it as an immediate operand + if (AOTCodeCache::is_on_for_dump()) { + address grain_shift_address = (address)AOTRuntimeConstants::grain_shift_address(); + __ eor(tmp1, store_addr, new_val); + __ lea(tmp2, ExternalAddress(grain_shift_address)); + __ ldrb(tmp2, tmp2); + __ lsrv(tmp1, tmp1, tmp2); + __ cbz(tmp1, done); + } else +#endif + { + __ eor(tmp1, store_addr, new_val); // tmp1 := store address ^ new value + __ lsr(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes) + __ cbz(tmp1, done); + } + // Crosses regions, storing null? if (new_val_may_be_null) { __ cbz(new_val, done); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 35e90d296c9..3e3e95be07e 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -5754,6 +5754,14 @@ void MacroAssembler::adrp(Register reg1, const Address &dest, uint64_t &byte_off } void MacroAssembler::load_byte_map_base(Register reg) { +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + address byte_map_base_adr = AOTRuntimeConstants::card_table_base_address(); + lea(reg, ExternalAddress(byte_map_base_adr)); + ldr(reg, Address(reg)); + return; + } +#endif CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); // Strictly speaking the card table base isn't an address at all, and it might @@ -5761,6 +5769,20 @@ void MacroAssembler::load_byte_map_base(Register reg) { mov(reg, (uint64_t)ctbs->card_table_base_const()); } +void MacroAssembler::load_aotrc_address(Register reg, address a) { +#if INCLUDE_CDS + assert(AOTRuntimeConstants::contains(a), "address out of range for data area"); + if (AOTCodeCache::is_on_for_dump()) { + // all aotrc field addresses should be registered in the AOTCodeCache address table + lea(reg, ExternalAddress(a)); + } else { + mov(reg, (uint64_t)a); + } +#else + ShouldNotReachHere(); +#endif +} + void MacroAssembler::build_frame(int framesize) { assert(framesize >= 2 * wordSize, "framesize must include space for FP/LR"); assert(framesize % (2*wordSize) == 0, "must preserve 2*wordSize alignment"); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 7b5af532ca1..fa32f3055b9 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -1476,6 +1476,9 @@ public: // Load the base of the cardtable byte map into reg. void load_byte_map_base(Register reg); + // Load a constant address in the AOT Runtime Constants area + void load_aotrc_address(Register reg, address a); + // Prolog generator routines to support switch between x86 code and // generated ARM code diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index 37ee9451405..d9be0fdcc8d 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -32,6 +32,7 @@ #include "c1/c1_ValueStack.hpp" #include "ci/ciArrayKlass.hpp" #include "ci/ciInstance.hpp" +#include "code/aotCodeCache.hpp" #include "compiler/oopMap.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/gc_globals.hpp" @@ -535,6 +536,15 @@ void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_cod case T_LONG: { assert(patch_code == lir_patch_none, "no patching handled here"); +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + address b = c->as_pointer(); + if (AOTRuntimeConstants::contains(b)) { + __ load_aotrc_address(dest->as_register_lo(), b); + break; + } + } +#endif __ movptr(dest->as_register_lo(), (intptr_t)c->as_jlong()); break; } diff --git a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp index 34de9403ccf..b20d7b5cd07 100644 --- a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp @@ -23,6 +23,7 @@ */ #include "asm/macroAssembler.inline.hpp" +#include "code/aotCodeCache.hpp" #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BarrierSetAssembler.hpp" #include "gc/g1/g1BarrierSetRuntime.hpp" @@ -268,6 +269,16 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, __ bind(done); } +#if INCLUDE_CDS +// return a register that differs from reg1, reg2, reg3 and reg4 + +static Register pick_different_reg(Register reg1, Register reg2 = noreg, Register reg3= noreg, Register reg4 = noreg) { + RegSet available = (RegSet::of(rscratch1, rscratch2, rax, rbx) + rdx - + RegSet::of(reg1, reg2, reg3, reg4)); + return *(available.begin()); +} +#endif // INCLUDE_CDS + static void generate_post_barrier(MacroAssembler* masm, const Register store_addr, const Register new_val, @@ -280,10 +291,32 @@ static void generate_post_barrier(MacroAssembler* masm, Label L_done; // Does store cross heap regions? - __ movptr(tmp1, store_addr); // tmp1 := store address - __ xorptr(tmp1, new_val); // tmp1 := store address ^ new value - __ shrptr(tmp1, G1HeapRegion::LogOfHRGrainBytes); // ((store address ^ new value) >> LogOfHRGrainBytes) == 0? - __ jccb(Assembler::equal, L_done); +#if INCLUDE_CDS + // AOT code needs to load the barrier grain shift from the aot + // runtime constants area in the code cache otherwise we can compile + // it as an immediate operand + + if (AOTCodeCache::is_on_for_dump()) { + address grain_shift_addr = AOTRuntimeConstants::grain_shift_address(); + Register save = pick_different_reg(rcx, tmp1, new_val, store_addr); + __ push(save); + __ movptr(save, store_addr); + __ xorptr(save, new_val); + __ push(rcx); + __ lea(rcx, ExternalAddress(grain_shift_addr)); + __ movl(rcx, Address(rcx, 0)); + __ shrptr(save); + __ pop(rcx); + __ pop(save); + __ jcc(Assembler::equal, L_done); + } else +#endif // INCLUDE_CDS + { + __ movptr(tmp1, store_addr); // tmp1 := store address + __ xorptr(tmp1, new_val); // tmp1 := store address ^ new value + __ shrptr(tmp1, G1HeapRegion::LogOfHRGrainBytes); // ((store address ^ new value) >> LogOfHRGrainBytes) == 0? + __ jccb(Assembler::equal, L_done); + } // Crosses regions, storing null? if (new_val_may_be_null) { diff --git a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp index 65e6b4e01fc..0ea769dd488 100644 --- a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp @@ -23,6 +23,7 @@ */ #include "asm/macroAssembler.inline.hpp" +#include "code/aotCodeCache.hpp" #include "gc/shared/barrierSet.hpp" #include "gc/shared/cardTable.hpp" #include "gc/shared/cardTableBarrierSet.hpp" @@ -111,7 +112,15 @@ void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembl __ shrptr(end, CardTable::card_shift()); __ subptr(end, addr); // end --> cards count - __ mov64(tmp, (intptr_t)ctbs->card_table_base_const()); +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + __ lea(tmp, ExternalAddress(AOTRuntimeConstants::card_table_base_address())); + __ movq(tmp, Address(tmp, 0)); + } else +#endif + { + __ mov64(tmp, (intptr_t)ctbs->card_table_base_const()); + } __ addptr(addr, tmp); __ BIND(L_loop); __ movb(Address(addr, count, Address::times_1), 0); @@ -121,7 +130,7 @@ __ BIND(L_loop); __ BIND(L_done); } -void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Address dst) { +void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Address dst, Register rscratch) { // Does a store check for the oop in register obj. The content of // register obj is destroyed afterwards. CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); @@ -136,6 +145,13 @@ void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register ob // never need to be relocated. On 64bit however the value may be too // large for a 32bit displacement. intptr_t byte_map_base = (intptr_t)ctbs->card_table_base_const(); +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + __ lea(rscratch, ExternalAddress(AOTRuntimeConstants::card_table_base_address())); + __ movq(rscratch, Address(rscratch, 0)); + card_addr = Address(rscratch, obj, Address::times_1, 0); + } else +#endif if (__ is_simm32(byte_map_base)) { card_addr = Address(noreg, obj, Address::times_1, byte_map_base); } else { @@ -174,10 +190,10 @@ void CardTableBarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorS if (needs_post_barrier) { // flatten object address if needed if (!precise || (dst.index() == noreg && dst.disp() == 0)) { - store_check(masm, dst.base(), dst); + store_check(masm, dst.base(), dst, tmp2); } else { __ lea(tmp1, dst); - store_check(masm, tmp1, dst); + store_check(masm, tmp1, dst, tmp2); } } } diff --git a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp index 0a36571c757..201c11062f2 100644 --- a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp @@ -33,7 +33,7 @@ protected: virtual void gen_write_ref_array_pre_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count) {} - void store_check(MacroAssembler* masm, Register obj, Address dst); + void store_check(MacroAssembler* masm, Register obj, Address dst, Register rscratch); virtual void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, Register tmp); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 83169df3456..b54f6adc263 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -10034,6 +10034,20 @@ void MacroAssembler::restore_legacy_gprs() { addq(rsp, 16 * wordSize); } +void MacroAssembler::load_aotrc_address(Register reg, address a) { +#if INCLUDE_CDS + assert(AOTRuntimeConstants::contains(a), "address out of range for data area"); + if (AOTCodeCache::is_on_for_dump()) { + // all aotrc field addresses should be registered in the AOTCodeCache address table + lea(reg, ExternalAddress(a)); + } else { + mov64(reg, (uint64_t)a); + } +#else + ShouldNotReachHere(); +#endif +} + void MacroAssembler::setcc(Assembler::Condition comparison, Register dst) { if (VM_Version::supports_apx_f()) { esetzucc(comparison, dst); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index eb23199ca63..5c049f710e2 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -2070,6 +2070,7 @@ public: void save_legacy_gprs(); void restore_legacy_gprs(); + void load_aotrc_address(Register reg, address a); void setcc(Assembler::Condition comparison, Register dst); }; diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index aed54fe93d4..0ffa4c2031c 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -5187,6 +5187,18 @@ operand immL_65535() interface(CONST_INTER); %} +// AOT Runtime Constants Address +operand immAOTRuntimeConstantsAddress() +%{ + // Check if the address is in the range of AOT Runtime Constants + predicate(AOTRuntimeConstants::contains((address)(n->get_ptr()))); + match(ConP); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + operand kReg() %{ constraint(ALLOC_IN_RC(vectmask_reg)); @@ -7332,6 +7344,19 @@ instruct loadD(regD dst, memory mem) ins_pipe(pipe_slow); // XXX %} +instruct loadAOTRCAddress(rRegP dst, immAOTRuntimeConstantsAddress con) +%{ + match(Set dst con); + + format %{ "leaq $dst, $con\t# AOT Runtime Constants Address" %} + + ins_encode %{ + __ load_aotrc_address($dst$$Register, (address)$con$$constant); + %} + + ins_pipe(ialu_reg_fat); +%} + // max = java.lang.Math.max(float a, float b) instruct maxF_reg_avx10_2(regF dst, regF a, regF b) %{ predicate(VM_Version::supports_avx10_2()); diff --git a/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp b/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp index a20feadcba4..93beb549366 100644 --- a/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp +++ b/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp @@ -27,22 +27,30 @@ #include "runtime/vm_version.hpp" int VM_Version::get_current_sve_vector_length() { - assert(_features & CPU_SVE, "should not call this"); + assert(VM_Version::supports_sve(), "should not call this"); ShouldNotReachHere(); return 0; } int VM_Version::set_and_get_current_sve_vector_length(int length) { - assert(_features & CPU_SVE, "should not call this"); + assert(VM_Version::supports_sve(), "should not call this"); ShouldNotReachHere(); return 0; } void VM_Version::get_os_cpu_info() { - if (IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE)) _features |= CPU_CRC32; - if (IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)) _features |= CPU_AES | CPU_SHA1 | CPU_SHA2; - if (IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE)) _features |= CPU_ASIMD; + if (IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE)) { + set_feature(CPU_CRC32); + } + if (IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)) { + set_feature(CPU_AES); + set_feature(CPU_SHA1); + set_feature(CPU_SHA2); + } + if (IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE)) { + set_feature(CPU_ASIMD); + } // No check for CPU_PMULL, CPU_SVE, CPU_SVE2 __int64 dczid_el0 = _ReadStatusReg(0x5807 /* ARM64_DCZID_EL0 */); diff --git a/src/hotspot/share/adlc/main.cpp b/src/hotspot/share/adlc/main.cpp index 4e8a96617e8..8e6ea5bbec9 100644 --- a/src/hotspot/share/adlc/main.cpp +++ b/src/hotspot/share/adlc/main.cpp @@ -213,6 +213,7 @@ int main(int argc, char *argv[]) AD.addInclude(AD._CPP_file, "adfiles", get_basename(AD._VM_file._name)); AD.addInclude(AD._CPP_file, "adfiles", get_basename(AD._HPP_file._name)); AD.addInclude(AD._CPP_file, "memory/allocation.inline.hpp"); + AD.addInclude(AD._CPP_file, "code/aotCodeCache.hpp"); AD.addInclude(AD._CPP_file, "code/codeCache.hpp"); AD.addInclude(AD._CPP_file, "code/compiledIC.hpp"); AD.addInclude(AD._CPP_file, "code/nativeInst.hpp"); @@ -257,6 +258,7 @@ int main(int argc, char *argv[]) AD.addInclude(AD._CPP_PEEPHOLE_file, "adfiles", get_basename(AD._HPP_file._name)); AD.addInclude(AD._CPP_PIPELINE_file, "adfiles", get_basename(AD._HPP_file._name)); AD.addInclude(AD._DFA_file, "adfiles", get_basename(AD._HPP_file._name)); + AD.addInclude(AD._DFA_file, "code/aotCodeCache.hpp"); AD.addInclude(AD._DFA_file, "oops/compressedOops.hpp"); AD.addInclude(AD._DFA_file, "opto/cfgnode.hpp"); // Use PROB_MAX in predicate. AD.addInclude(AD._DFA_file, "opto/intrinsicnode.hpp"); diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 29cb5d737d8..67817682ced 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -1029,7 +1029,7 @@ class methodHandle; do_intrinsic(_VectorUnaryLibOp, jdk_internal_vm_vector_VectorSupport, vector_unary_lib_op_name, vector_unary_lib_op_sig, F_S) \ do_signature(vector_unary_lib_op_sig,"(J" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljava/lang/String;" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ @@ -1040,7 +1040,7 @@ class methodHandle; do_intrinsic(_VectorBinaryLibOp, jdk_internal_vm_vector_VectorSupport, vector_binary_lib_op_name, vector_binary_lib_op_sig, F_S) \ do_signature(vector_binary_lib_op_sig,"(J" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljava/lang/String;" \ "Ljdk/internal/vm/vector/VectorSupport$VectorPayload;" \ diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index 32a53691f3f..e5f68afc51d 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -29,9 +29,11 @@ #include "cds/cds_globals.hpp" #include "cds/cdsConfig.hpp" #include "cds/heapShared.hpp" +#include "ci/ciUtilities.hpp" #include "classfile/javaAssertions.hpp" #include "code/aotCodeCache.hpp" #include "code/codeCache.hpp" +#include "gc/shared/cardTableBarrierSet.hpp" #include "gc/shared/gcConfig.hpp" #include "logging/logStream.hpp" #include "memory/memoryReserver.hpp" @@ -53,6 +55,7 @@ #endif #if INCLUDE_G1GC #include "gc/g1/g1BarrierSetRuntime.hpp" +#include "gc/g1/g1HeapRegion.hpp" #endif #if INCLUDE_SHENANDOAHGC #include "gc/shenandoah/shenandoahRuntime.hpp" @@ -258,6 +261,9 @@ void AOTCodeCache::init2() { return; } + // initialize aot runtime constants as appropriate to this runtime + AOTRuntimeConstants::initialize_from_runtime(); + // initialize the table of external routines so we can save // generated code blobs that reference them AOTCodeAddressTable* table = opened_cache->_table; @@ -1447,6 +1453,12 @@ void AOTCodeAddressTable::init_extrs() { #endif #endif // ZERO + // addresses of fields in AOT runtime constants area + address* p = AOTRuntimeConstants::field_addresses_list(); + while (*p != nullptr) { + SET_ADDRESS(_extrs, *p++); + } + _extrs_complete = true; log_debug(aot, codecache, init)("External addresses recorded"); } @@ -1729,6 +1741,11 @@ int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeB if (addr == (address)-1) { // Static call stub has jump to itself return id; } + // Check card_table_base address first since it can point to any address + BarrierSet* bs = BarrierSet::barrier_set(); + bool is_const_card_table_base = !UseG1GC && !UseShenandoahGC && bs->is_a(BarrierSet::CardTableBarrierSet); + guarantee(!is_const_card_table_base || addr != ci_card_table_address_const(), "sanity"); + // Seach for C string id = id_for_C_string(addr); if (id >= 0) { @@ -1798,6 +1815,44 @@ int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeB return id; } +AOTRuntimeConstants AOTRuntimeConstants::_aot_runtime_constants; + +void AOTRuntimeConstants::initialize_from_runtime() { + BarrierSet* bs = BarrierSet::barrier_set(); + address card_table_base = nullptr; + uint grain_shift = 0; +#if INCLUDE_G1GC + if (bs->is_a(BarrierSet::G1BarrierSet)) { + grain_shift = G1HeapRegion::LogOfHRGrainBytes; + } else +#endif +#if INCLUDE_SHENANDOAHGC + if (bs->is_a(BarrierSet::ShenandoahBarrierSet)) { + grain_shift = 0; + } else +#endif + if (bs->is_a(BarrierSet::CardTableBarrierSet)) { + CardTable::CardValue* base = ci_card_table_address_const(); + assert(base != nullptr, "unexpected byte_map_base"); + card_table_base = base; + CardTableBarrierSet* ctbs = barrier_set_cast(bs); + grain_shift = ctbs->grain_shift(); + } + _aot_runtime_constants._card_table_base = card_table_base; + _aot_runtime_constants._grain_shift = grain_shift; +} + +address AOTRuntimeConstants::_field_addresses_list[] = { + ((address)&_aot_runtime_constants._card_table_base), + ((address)&_aot_runtime_constants._grain_shift), + nullptr +}; + +address AOTRuntimeConstants::card_table_base_address() { + assert(UseSerialGC || UseParallelGC, "Only these GCs have constant card table base"); + return (address)&_aot_runtime_constants._card_table_base; +} + // This is called after initialize() but before init2() // and _cache is not set yet. void AOTCodeCache::print_on(outputStream* st) { diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index 45b4a15510d..85f8b47920f 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_CODE_AOTCODECACHE_HPP #define SHARE_CODE_AOTCODECACHE_HPP +#include "gc/shared/gc_globals.hpp" #include "runtime/stubInfo.hpp" /* @@ -422,4 +423,36 @@ public: #endif // PRODUCT }; +// code cache internal runtime constants area used by AOT code +class AOTRuntimeConstants { + friend class AOTCodeCache; + private: + address _card_table_base; + uint _grain_shift; + static address _field_addresses_list[]; + static AOTRuntimeConstants _aot_runtime_constants; + // private constructor for unique singleton + AOTRuntimeConstants() { } + // private for use by friend class AOTCodeCache + static void initialize_from_runtime(); + public: +#if INCLUDE_CDS + static bool contains(address adr) { + address base = (address)&_aot_runtime_constants; + address hi = base + sizeof(AOTRuntimeConstants); + return (base <= adr && adr < hi); + } + static address card_table_base_address(); + static address grain_shift_address() { return (address)&_aot_runtime_constants._grain_shift; } + static address* field_addresses_list() { + return _field_addresses_list; + } +#else + static bool contains(address adr) { return false; } + static address card_table_base_address() { return nullptr; } + static address grain_shift_address() { return nullptr; } + static address* field_addresses_list() { return nullptr; } +#endif +}; + #endif // SHARE_CODE_AOTCODECACHE_HPP diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.hpp index 406096acf10..c5c7058471c 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_GC_G1_G1BARRIERSET_HPP #define SHARE_GC_G1_G1BARRIERSET_HPP +#include "gc/g1/g1HeapRegion.hpp" #include "gc/g1/g1SATBMarkQueueSet.hpp" #include "gc/shared/bufferNode.hpp" #include "gc/shared/cardTable.hpp" @@ -116,6 +117,8 @@ class G1BarrierSet: public CardTableBarrierSet { virtual void print_on(outputStream* st) const; + virtual uint grain_shift() { return G1HeapRegion::LogOfHRGrainBytes; } + // Callbacks for runtime accesses. template class AccessBarrier: public CardTableBarrierSet::AccessBarrier { diff --git a/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp b/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp index 914358760aa..86b74aa5736 100644 --- a/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp +++ b/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp @@ -22,6 +22,7 @@ * */ +#include "code/aotCodeCache.hpp" #include "gc/shared/c1/cardTableBarrierSetC1.hpp" #include "gc/shared/cardTable.hpp" #include "gc/shared/cardTableBarrierSet.hpp" @@ -123,6 +124,7 @@ void CardTableBarrierSetC1::post_barrier(LIRAccess& access, LIR_Opr addr, LIR_Op assert(addr->is_register(), "must be a register at this point"); #ifdef CARDTABLEBARRIERSET_POST_BARRIER_HELPER + assert(!AOTCodeCache::is_on(), "this path is not implemented"); gen->CardTableBarrierSet_post_barrier_helper(addr, card_table_base); #else LIR_Opr tmp = gen->new_pointer_register(); @@ -135,6 +137,17 @@ void CardTableBarrierSetC1::post_barrier(LIRAccess& access, LIR_Opr addr, LIR_Op } LIR_Address* card_addr; +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + // load the card table address from the AOT Runtime Constants area + LIR_Opr byte_map_base_adr = LIR_OprFact::intptrConst(AOTRuntimeConstants::card_table_base_address()); + LIR_Opr byte_map_base_reg = gen->new_pointer_register(); + __ move(byte_map_base_adr, byte_map_base_reg); + LIR_Address* byte_map_base_indirect = new LIR_Address(byte_map_base_reg, 0, T_LONG); + __ move(byte_map_base_indirect, byte_map_base_reg); + card_addr = new LIR_Address(tmp, byte_map_base_reg, T_BYTE); + } else +#endif if (gen->can_inline_as_constant(card_table_base)) { card_addr = new LIR_Address(tmp, card_table_base->as_jint(), T_BYTE); } else { diff --git a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp index 42af77ebdf4..f7445ff254f 100644 --- a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp @@ -23,6 +23,7 @@ */ #include "ci/ciUtilities.hpp" +#include "code/aotCodeCache.hpp" #include "gc/shared/c2/cardTableBarrierSetC2.hpp" #include "gc/shared/cardTable.hpp" #include "gc/shared/cardTableBarrierSet.hpp" @@ -114,13 +115,20 @@ Node* CardTableBarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& access return result; } -Node* CardTableBarrierSetC2::byte_map_base_node(GraphKit* kit) const { +Node* CardTableBarrierSetC2::byte_map_base_node(IdealKit* kit) const { // Get base of card map +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + // load the card table address from the AOT Runtime Constants area + Node* byte_map_base_adr = kit->makecon(TypeRawPtr::make(AOTRuntimeConstants::card_table_base_address())); + return kit->load_aot_const(byte_map_base_adr, TypeRawPtr::NOTNULL); + } +#endif CardTable::CardValue* card_table_base = ci_card_table_address_const(); if (card_table_base != nullptr) { return kit->makecon(TypeRawPtr::make((address)card_table_base)); } else { - return kit->null(); + return kit->makecon(Type::get_zero_type(T_ADDRESS)); } } @@ -168,7 +176,7 @@ void CardTableBarrierSetC2::post_barrier(GraphKit* kit, Node* card_offset = __ URShiftX(cast, __ ConI(CardTable::card_shift())); // Combine card table base and card offset - Node* card_adr = __ AddP(__ top(), byte_map_base_node(kit), card_offset); + Node* card_adr = __ AddP(__ top(), byte_map_base_node(&ideal), card_offset); // Get the alias_index for raw card-mark memory int adr_type = Compile::AliasIdxRaw; diff --git a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp index 84876808f0d..8f5bae8c6dd 100644 --- a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp +++ b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp @@ -43,7 +43,7 @@ protected: Node* new_val, const Type* value_type) const; virtual Node* atomic_xchg_at_resolved(C2AtomicParseAccess& access, Node* new_val, const Type* value_type) const; - Node* byte_map_base_node(GraphKit* kit) const; + Node* byte_map_base_node(IdealKit* kit) const; public: virtual void eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const; diff --git a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp index 3a9b46d9df8..5d355318b21 100644 --- a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp +++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp @@ -103,6 +103,10 @@ public: virtual void print_on(outputStream* st) const; + // The AOT code cache manager needs to know the region grain size + // shift for some barrier sets. + virtual uint grain_shift() { return 0; } + template class AccessBarrier: public BarrierSet::AccessBarrier { typedef BarrierSet::AccessBarrier Raw; diff --git a/src/hotspot/share/gc/shared/space.cpp b/src/hotspot/share/gc/shared/space.cpp index 84ba21527fd..093fa4432e2 100644 --- a/src/hotspot/share/gc/shared/space.cpp +++ b/src/hotspot/share/gc/shared/space.cpp @@ -51,7 +51,7 @@ void ContiguousSpace::initialize(MemRegion mr, set_bottom(bottom); set_end(end); if (clear_space) { - clear(SpaceDecorator::DontMangle); + set_top(bottom); } if (ZapUnusedHeapArea) { mangle_unused_area(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp index c6e6108fda8..2be5722f7f9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp @@ -41,9 +41,9 @@ bool ShenandoahBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { return true; } - ShenandoahReentrantLock* lock = ShenandoahNMethod::lock_for_nmethod(nm); + ShenandoahNMethodLock* lock = ShenandoahNMethod::lock_for_nmethod(nm); assert(lock != nullptr, "Must be"); - ShenandoahReentrantLocker locker(lock); + ShenandoahNMethodLocker locker(lock); if (!is_armed(nm)) { // Some other thread managed to complete while we were diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp index 07d339eb32e..64e135e9a4e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp @@ -136,13 +136,13 @@ public: assert(!nm_data->is_unregistered(), "Should not see unregistered entry"); if (nm->is_unloading()) { - ShenandoahReentrantLocker locker(nm_data->lock()); + ShenandoahNMethodLocker locker(nm_data->lock()); nm->unlink(); return; } { - ShenandoahReentrantLocker locker(nm_data->lock()); + ShenandoahNMethodLocker locker(nm_data->lock()); // Heal oops if (_bs->is_armed(nm)) { @@ -154,7 +154,7 @@ public: } // Clear compiled ICs and exception caches - ShenandoahReentrantLocker locker(nm_data->ic_lock()); + ShenandoahNMethodLocker locker(nm_data->ic_lock()); nm->unload_nmethod_caches(_unloading_occurred); } }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 364279deafe..5206a0558e8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -1023,7 +1023,7 @@ public: void do_nmethod(nmethod* n) { ShenandoahNMethod* data = ShenandoahNMethod::gc_data(n); - ShenandoahReentrantLocker locker(data->lock()); + ShenandoahNMethodLocker locker(data->lock()); // Setup EvacOOM scope below reentrant lock to avoid deadlock with // nmethod_entry_barrier ShenandoahEvacOOMScope oom; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp index 91c6024d522..d55a06d5713 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp @@ -32,8 +32,8 @@ #include "gc/shenandoah/shenandoahSimpleBitMap.hpp" #include "logging/logStream.hpp" -typedef ShenandoahLock ShenandoahRebuildLock; -typedef ShenandoahLocker ShenandoahRebuildLocker; +typedef ShenandoahLock ShenandoahRebuildLock; +typedef ShenandoahLocker ShenandoahRebuildLocker; // Each ShenandoahHeapRegion is associated with a ShenandoahFreeSetPartitionId. enum class ShenandoahFreeSetPartitionId : uint8_t { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index 98d30a3481f..2c2e5533c01 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -605,7 +605,8 @@ void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t mutator_x ShenandoahOldGeneration* old_gen = old_generation(); size_t old_capacity = old_gen->max_capacity(); size_t old_usage = old_gen->used(); // includes humongous waste - size_t old_available = ((old_capacity >= old_usage)? old_capacity - old_usage: 0) + old_trashed_regions * region_size_bytes; + size_t old_currently_available = + ((old_capacity >= old_usage)? old_capacity - old_usage: 0) + old_trashed_regions * region_size_bytes; ShenandoahYoungGeneration* young_gen = young_generation(); size_t young_capacity = young_gen->max_capacity(); @@ -621,7 +622,8 @@ void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t mutator_x size_t young_reserve = (young_generation()->max_capacity() * ShenandoahEvacReserve) / 100; // If ShenandoahOldEvacPercent equals 100, max_old_reserve is limited only by mutator_xfer_limit and young_reserve - const size_t bound_on_old_reserve = ((old_available + mutator_xfer_limit + young_reserve) * ShenandoahOldEvacPercent) / 100; + const size_t bound_on_old_reserve = + ((old_currently_available + mutator_xfer_limit + young_reserve) * ShenandoahOldEvacPercent) / 100; size_t proposed_max_old = ((ShenandoahOldEvacPercent == 100)? bound_on_old_reserve: MIN2((young_reserve * ShenandoahOldEvacPercent) / (100 - ShenandoahOldEvacPercent), @@ -631,68 +633,105 @@ void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t mutator_x } // Decide how much old space we should reserve for a mixed collection - size_t reserve_for_mixed = 0; + size_t proposed_reserve_for_mixed = 0; const size_t old_fragmented_available = - old_available - (old_generation()->free_unaffiliated_regions() + old_trashed_regions) * region_size_bytes; + old_currently_available - (old_generation()->free_unaffiliated_regions() + old_trashed_regions) * region_size_bytes; if (old_fragmented_available > proposed_max_old) { - // After we've promoted regions in place, there may be an abundance of old-fragmented available memory, - // even more than the desired percentage for old reserve. We cannot transfer these fragmented regions back - // to young. Instead we make the best of the situation by using this fragmented memory for both promotions - // and evacuations. + // In this case, the old_fragmented_available is greater than the desired amount of evacuation to old. + // We'll use all of this memory to hold results of old evacuation, and we'll give back to the young generation + // any old regions that are not fragmented. + // + // This scenario may happen after we have promoted many regions in place, and each of these regions had non-zero + // unused memory, so there is now an abundance of old-fragmented available memory, even more than the desired + // percentage for old reserve. We cannot transfer these fragmented regions back to young. Instead we make the + // best of the situation by using this fragmented memory for both promotions and evacuations. + proposed_max_old = old_fragmented_available; } - size_t reserve_for_promo = old_fragmented_available; + // Otherwise: old_fragmented_available <= proposed_max_old. Do not shrink proposed_max_old from the original computation. + + // Though we initially set proposed_reserve_for_promo to equal the entirety of old fragmented available, we have the + // opportunity below to shift some of this memory into the proposed_reserve_for_mixed. + size_t proposed_reserve_for_promo = old_fragmented_available; const size_t max_old_reserve = proposed_max_old; + const size_t mixed_candidate_live_memory = old_generation()->unprocessed_collection_candidates_live_memory(); const bool doing_mixed = (mixed_candidate_live_memory > 0); if (doing_mixed) { - // We want this much memory to be unfragmented in order to reliably evacuate old. This is conservative because we - // may not evacuate the entirety of unprocessed candidates in a single mixed evacuation. + // In the ideal, all of the memory reserved for mixed evacuation would be unfragmented, but we don't enforce + // this. Note that the initial value of max_evac_need is conservative because we may not evacuate all of the + // remaining mixed evacuation candidates in a single cycle. const size_t max_evac_need = (size_t) (mixed_candidate_live_memory * ShenandoahOldEvacWaste); - assert(old_available >= old_generation()->free_unaffiliated_regions() * region_size_bytes, + assert(old_currently_available >= old_generation()->free_unaffiliated_regions() * region_size_bytes, "Unaffiliated available must be less than total available"); // We prefer to evacuate all of mixed into unfragmented memory, and will expand old in order to do so, unless // we already have too much fragmented available memory in old. - reserve_for_mixed = max_evac_need; - if (reserve_for_mixed + reserve_for_promo > max_old_reserve) { - // In this case, we'll allow old-evac to target some of the fragmented old memory. - size_t excess_reserves = (reserve_for_mixed + reserve_for_promo) - max_old_reserve; - if (reserve_for_promo > excess_reserves) { - reserve_for_promo -= excess_reserves; + proposed_reserve_for_mixed = max_evac_need; + if (proposed_reserve_for_mixed + proposed_reserve_for_promo > max_old_reserve) { + // We're trying to reserve more memory than is available. So we need to shrink our reserves. + size_t excess_reserves = (proposed_reserve_for_mixed + proposed_reserve_for_promo) - max_old_reserve; + // We need to shrink reserves by excess_reserves. We prefer to shrink by reducing promotion, giving priority to mixed + // evacuation. If the promotion reserve is larger than the amount we need to shrink by, do all the shrinkage there. + if (proposed_reserve_for_promo > excess_reserves) { + proposed_reserve_for_promo -= excess_reserves; } else { - excess_reserves -= reserve_for_promo; - reserve_for_promo = 0; - reserve_for_mixed -= excess_reserves; + // Otherwise, we'll shrink promotion reserve to zero and we'll shrink the mixed-evac reserve by the remaining excess. + excess_reserves -= proposed_reserve_for_promo; + proposed_reserve_for_promo = 0; + proposed_reserve_for_mixed -= excess_reserves; } } } + assert(proposed_reserve_for_mixed + proposed_reserve_for_promo <= max_old_reserve, + "Reserve for mixed (%zu) plus reserve for promotions (%zu) must be less than maximum old reserve (%zu)", + proposed_reserve_for_mixed, proposed_reserve_for_promo, max_old_reserve); // Decide how much additional space we should reserve for promotions from young. We give priority to mixed evacations // over promotions. const size_t promo_load = old_generation()->get_promotion_potential(); const bool doing_promotions = promo_load > 0; - if (doing_promotions) { - // We've already set aside all of the fragmented available memory within old-gen to represent old objects - // to be promoted from young generation. promo_load represents the memory that we anticipate to be promoted - // from regions that have reached tenure age. In the ideal, we will always use fragmented old-gen memory - // to hold individually promoted objects and will use unfragmented old-gen memory to represent the old-gen - // evacuation workloa. - // We're promoting and have an estimate of memory to be promoted from aged regions - assert(max_old_reserve >= (reserve_for_mixed + reserve_for_promo), "Sanity"); - const size_t available_for_additional_promotions = max_old_reserve - (reserve_for_mixed + reserve_for_promo); - size_t promo_need = (size_t)(promo_load * ShenandoahPromoEvacWaste); - if (promo_need > reserve_for_promo) { - reserve_for_promo += MIN2(promo_need - reserve_for_promo, available_for_additional_promotions); + // promo_load represents the combined total of live memory within regions that have reached tenure age. The true + // promotion potential is larger than this, because individual objects within regions that have not yet reached tenure + // age may be promotable. On the other hand, some of the objects that we intend to promote in the next GC cycle may + // die before they are next marked. In the future, the promo_load will include the total size of tenurable objects + // residing in regions that have not yet reached tenure age. + + if (doing_promotions) { + // We are always doing promotions, even when old_generation->get_promotion_potential() returns 0. As currently implemented, + // get_promotion_potential() only knows the total live memory contained within young-generation regions whose age is + // tenurable. It does not know whether that memory will still be live at the end of the next mark cycle, and it doesn't + // know how much memory is contained within objects whose individual ages are tenurable, which reside in regions with + // non-tenurable age. We use this, as adjusted by ShenandoahPromoEvacWaste, as an approximation of the total amount of + // memory to be promoted. In the near future, we expect to implement a change that will allow get_promotion_potential() + // to account also for the total memory contained within individual objects that are tenure-ready even when they do + // not reside in aged regions. This will represent a conservative over approximation of promotable memory because + // some of these objects may die before the next GC cycle executes. + + // Be careful not to ask for too much promotion reserves. We have observed jtreg test failures under which a greedy + // promotion reserve causes a humongous allocation which is awaiting a full GC to fail (specifically + // gc/TestAllocHumongousFragment.java). This happens if too much of the memory reclaimed by the full GC + // is immediately reserved so that it cannot be allocated by the waiting mutator. It's not clear that this + // particular test is representative of the needs of typical GenShen users. It is really a test of high frequency + // Full GCs under heap fragmentation stress. + + size_t promo_need = (size_t) (promo_load * ShenandoahPromoEvacWaste); + if (promo_need > proposed_reserve_for_promo) { + const size_t available_for_additional_promotions = + max_old_reserve - (proposed_reserve_for_mixed + proposed_reserve_for_promo); + if (proposed_reserve_for_promo + available_for_additional_promotions >= promo_need) { + proposed_reserve_for_promo = promo_need; + } else { + proposed_reserve_for_promo += available_for_additional_promotions; + } } - // We've already reserved all the memory required for the promo_load, and possibly more. The excess - // can be consumed by objects promoted from regions that have not yet reached tenure age. } + // else, leave proposed_reserve_for_promo as is. By default, it is initialized to represent old_fragmented_available. // This is the total old we want to reserve (initialized to the ideal reserve) - size_t old_reserve = reserve_for_mixed + reserve_for_promo; + size_t proposed_old_reserve = proposed_reserve_for_mixed + proposed_reserve_for_promo; // We now check if the old generation is running a surplus or a deficit. size_t old_region_deficit = 0; @@ -702,68 +741,70 @@ void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t mutator_x // align the mutator_xfer_limit on region size mutator_xfer_limit = mutator_region_xfer_limit * region_size_bytes; - if (old_available >= old_reserve) { + if (old_currently_available >= proposed_old_reserve) { // We are running a surplus, so the old region surplus can go to young - const size_t old_surplus = old_available - old_reserve; + const size_t old_surplus = old_currently_available - proposed_old_reserve; old_region_surplus = old_surplus / region_size_bytes; const size_t unaffiliated_old_regions = old_generation()->free_unaffiliated_regions() + old_trashed_regions; old_region_surplus = MIN2(old_region_surplus, unaffiliated_old_regions); old_generation()->set_region_balance(checked_cast(old_region_surplus)); - } else if (old_available + mutator_xfer_limit >= old_reserve) { - // Mutator's xfer limit is sufficient to satisfy our need: transfer all memory from there - size_t old_deficit = old_reserve - old_available; + old_currently_available -= old_region_surplus * region_size_bytes; + young_available += old_region_surplus * region_size_bytes; + } else if (old_currently_available + mutator_xfer_limit >= proposed_old_reserve) { + // We know that old_currently_available < proposed_old_reserve because above test failed. Expand old_currently_available. + // Mutator's xfer limit is sufficient to satisfy our need: transfer all memory from there. + size_t old_deficit = proposed_old_reserve - old_currently_available; old_region_deficit = (old_deficit + region_size_bytes - 1) / region_size_bytes; old_generation()->set_region_balance(0 - checked_cast(old_region_deficit)); + old_currently_available += old_region_deficit * region_size_bytes; + young_available -= old_region_deficit * region_size_bytes; } else { - // We'll try to xfer from both mutator excess and from young collector reserve - size_t available_reserves = old_available + young_reserve + mutator_xfer_limit; - size_t old_entitlement = (available_reserves * ShenandoahOldEvacPercent) / 100; + // We know that (old_currently_available < proposed_old_reserve) and + // (old_currently_available + mutator_xfer_limit < proposed_old_reserve) because above tests failed. + // We need to shrink proposed_old_reserves. - // Round old_entitlement down to nearest multiple of regions to be transferred to old - size_t entitled_xfer = old_entitlement - old_available; - entitled_xfer = region_size_bytes * (entitled_xfer / region_size_bytes); - size_t unaffiliated_young_regions = young_generation()->free_unaffiliated_regions(); - size_t unaffiliated_young_memory = unaffiliated_young_regions * region_size_bytes; - if (entitled_xfer > unaffiliated_young_memory) { - entitled_xfer = unaffiliated_young_memory; - } - old_entitlement = old_available + entitled_xfer; - if (old_entitlement < old_reserve) { - // There's not enough memory to satisfy our desire. Scale back our old-gen intentions. - size_t budget_overrun = old_reserve - old_entitlement;; - if (reserve_for_promo > budget_overrun) { - reserve_for_promo -= budget_overrun; - old_reserve -= budget_overrun; - } else { - budget_overrun -= reserve_for_promo; - reserve_for_promo = 0; - reserve_for_mixed = (reserve_for_mixed > budget_overrun)? reserve_for_mixed - budget_overrun: 0; - old_reserve = reserve_for_promo + reserve_for_mixed; - } - } + // We could potentially shrink young_reserves in order to further expand proposed_old_reserves. Let's not bother. The + // important thing is that we keep a total amount of memory in reserve in preparation for the next GC cycle. At + // the time we choose the next collection set, we'll have an opportunity to shift some of these young reserves + // into old reserves if that makes sense. - // Because of adjustments above, old_reserve may be smaller now than it was when we tested the branch - // condition above: "(old_available + mutator_xfer_limit >= old_reserve) - // Therefore, we do NOT know that: mutator_xfer_limit < old_reserve - old_available - - size_t old_deficit = old_reserve - old_available; - old_region_deficit = (old_deficit + region_size_bytes - 1) / region_size_bytes; - - // Shrink young_reserve to account for loan to old reserve - const size_t reserve_xfer_regions = old_region_deficit - mutator_region_xfer_limit; - young_reserve -= reserve_xfer_regions * region_size_bytes; + // Start by taking all of mutator_xfer_limit into old_currently_available. + size_t old_region_deficit = mutator_region_xfer_limit; old_generation()->set_region_balance(0 - checked_cast(old_region_deficit)); + old_currently_available += old_region_deficit * region_size_bytes; + young_available -= old_region_deficit * region_size_bytes; + + assert(old_currently_available < proposed_old_reserve, + "Old currently available (%zu) must be less than old reserve (%zu)", old_currently_available, proposed_old_reserve); + + // There's not enough memory to satisfy our desire. Scale back our old-gen intentions. We prefer to satisfy + // the budget_overrun entirely from the promotion reserve, if that is large enough. Otherwise, we'll satisfy + // the overrun from a combination of promotion and mixed-evacuation reserves. + size_t budget_overrun = proposed_old_reserve - old_currently_available; + if (proposed_reserve_for_promo > budget_overrun) { + proposed_reserve_for_promo -= budget_overrun; + // Dead code: + // proposed_old_reserve -= budget_overrun; + } else { + budget_overrun -= proposed_reserve_for_promo; + proposed_reserve_for_promo = 0; + proposed_reserve_for_mixed = (proposed_reserve_for_mixed > budget_overrun)? proposed_reserve_for_mixed - budget_overrun: 0; + // Dead code: + // Note: proposed_reserve_for_promo is 0 and proposed_reserve_for_mixed may equal 0. + // proposed_old_reserve = proposed_reserve_for_mixed; + } } - assert(old_region_deficit == 0 || old_region_surplus == 0, "Only surplus or deficit, never both"); - assert(young_reserve + reserve_for_mixed + reserve_for_promo <= old_available + young_available, + assert(old_region_deficit == 0 || old_region_surplus == 0, + "Only surplus (%zu) or deficit (%zu), never both", old_region_surplus, old_region_deficit); + assert(young_reserve + proposed_reserve_for_mixed + proposed_reserve_for_promo <= old_currently_available + young_available, "Cannot reserve more memory than is available: %zu + %zu + %zu <= %zu + %zu", - young_reserve, reserve_for_mixed, reserve_for_promo, old_available, young_available); + young_reserve, proposed_reserve_for_mixed, proposed_reserve_for_promo, old_currently_available, young_available); // deficit/surplus adjustments to generation sizes will precede rebuild young_generation()->set_evacuation_reserve(young_reserve); - old_generation()->set_evacuation_reserve(reserve_for_mixed); - old_generation()->set_promoted_reserve(reserve_for_promo); + old_generation()->set_evacuation_reserve(proposed_reserve_for_mixed); + old_generation()->set_promoted_reserve(proposed_reserve_for_promo); } void ShenandoahGenerationalHeap::coalesce_and_fill_old_regions(bool concurrent) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 9dd837b90d2..d78bdae6a51 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -2834,3 +2834,13 @@ void ShenandoahHeap::log_heap_status(const char* msg) const { global_generation()->log_status(msg); } } + +ShenandoahHeapLocker::ShenandoahHeapLocker(ShenandoahHeapLock* lock, bool allow_block_for_safepoint) : _lock(lock) { +#ifdef ASSERT + ShenandoahFreeSet* free_set = ShenandoahHeap::heap()->free_set(); + // free_set is nullptr only at pre-initialized state + assert(free_set == nullptr || !free_set->rebuild_lock()->owned_by_self(), "Dead lock, can't acquire heap lock while holding free-set rebuild lock"); + assert(_lock != nullptr, "Must not"); +#endif + _lock->lock(allow_block_for_safepoint); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 4a4499667ff..85ad339469d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -117,9 +117,23 @@ public: virtual bool is_thread_safe() { return false; } }; -typedef ShenandoahLock ShenandoahHeapLock; -typedef ShenandoahLocker ShenandoahHeapLocker; -typedef Stack ShenandoahScanObjectStack; +typedef ShenandoahLock ShenandoahHeapLock; +// ShenandoahHeapLocker implements locker to assure mutually exclusive access to the global heap data structures. +// Asserts in the implementation detect potential deadlock usage with regards the rebuild lock that is present +// in ShenandoahFreeSet. Whenever both locks are acquired, this lock should be acquired before the +// ShenandoahFreeSet rebuild lock. +class ShenandoahHeapLocker : public StackObj { +private: + ShenandoahHeapLock* _lock; +public: + ShenandoahHeapLocker(ShenandoahHeapLock* lock, bool allow_block_for_safepoint = false); + + ~ShenandoahHeapLocker() { + _lock->unlock(); + } +}; + +typedef Stack ShenandoahScanObjectStack; // Shenandoah GC is low-pause concurrent GC that uses a load reference barrier // for concurent evacuation and a snapshot-at-the-beginning write barrier for diff --git a/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp b/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp index 7eec0b9af64..7e317f53424 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp @@ -93,7 +93,7 @@ ShenandoahSimpleLock::ShenandoahSimpleLock() { assert(os::mutex_init_done(), "Too early!"); } -void ShenandoahSimpleLock::lock() { +void ShenandoahSimpleLock::lock(bool allow_block_for_safepoint) { _lock.lock(); } @@ -101,28 +101,31 @@ void ShenandoahSimpleLock::unlock() { _lock.unlock(); } -ShenandoahReentrantLock::ShenandoahReentrantLock() : - ShenandoahSimpleLock(), _owner(nullptr), _count(0) { - assert(os::mutex_init_done(), "Too early!"); +template +ShenandoahReentrantLock::ShenandoahReentrantLock() : + Lock(), _owner(nullptr), _count(0) { } -ShenandoahReentrantLock::~ShenandoahReentrantLock() { +template +ShenandoahReentrantLock::~ShenandoahReentrantLock() { assert(_count == 0, "Unbalance"); } -void ShenandoahReentrantLock::lock() { +template +void ShenandoahReentrantLock::lock(bool allow_block_for_safepoint) { Thread* const thread = Thread::current(); Thread* const owner = _owner.load_relaxed(); if (owner != thread) { - ShenandoahSimpleLock::lock(); + Lock::lock(allow_block_for_safepoint); _owner.store_relaxed(thread); } _count++; } -void ShenandoahReentrantLock::unlock() { +template +void ShenandoahReentrantLock::unlock() { assert(owned_by_self(), "Invalid owner"); assert(_count > 0, "Invalid count"); @@ -130,12 +133,17 @@ void ShenandoahReentrantLock::unlock() { if (_count == 0) { _owner.store_relaxed((Thread*)nullptr); - ShenandoahSimpleLock::unlock(); + Lock::unlock(); } } -bool ShenandoahReentrantLock::owned_by_self() const { +template +bool ShenandoahReentrantLock::owned_by_self() const { Thread* const thread = Thread::current(); Thread* const owner = _owner.load_relaxed(); return owner == thread; } + +// Explicit template instantiation +template class ShenandoahReentrantLock; +template class ShenandoahReentrantLock; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp b/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp index 2e44810cd5d..7c91df191e5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp @@ -31,7 +31,7 @@ #include "runtime/javaThread.hpp" #include "runtime/safepoint.hpp" -class ShenandoahLock { +class ShenandoahLock { private: enum LockState { unlocked = 0, locked = 1 }; @@ -48,7 +48,7 @@ private: public: ShenandoahLock() : _state(unlocked), _owner(nullptr) {}; - void lock(bool allow_block_for_safepoint) { + void lock(bool allow_block_for_safepoint = false) { assert(_owner.load_relaxed() != Thread::current(), "reentrant locking attempt, would deadlock"); if ((allow_block_for_safepoint && SafepointSynchronize::is_synchronizing()) || @@ -83,34 +83,19 @@ public: } }; -class ShenandoahLocker : public StackObj { -private: - ShenandoahLock* const _lock; -public: - ShenandoahLocker(ShenandoahLock* lock, bool allow_block_for_safepoint = false) : _lock(lock) { - if (_lock != nullptr) { - _lock->lock(allow_block_for_safepoint); - } - } - - ~ShenandoahLocker() { - if (_lock != nullptr) { - _lock->unlock(); - } - } -}; - +// Simple lock using PlatformMonitor class ShenandoahSimpleLock { private: PlatformMonitor _lock; // native lock public: ShenandoahSimpleLock(); - - virtual void lock(); - virtual void unlock(); + void lock(bool allow_block_for_safepoint = false); + void unlock(); }; -class ShenandoahReentrantLock : public ShenandoahSimpleLock { +// templated reentrant lock +template +class ShenandoahReentrantLock : public Lock { private: Atomic _owner; uint64_t _count; @@ -119,30 +104,25 @@ public: ShenandoahReentrantLock(); ~ShenandoahReentrantLock(); - virtual void lock(); - virtual void unlock(); + void lock(bool allow_block_for_safepoint = false); + void unlock(); // If the lock already owned by this thread bool owned_by_self() const ; }; -class ShenandoahReentrantLocker : public StackObj { -private: - ShenandoahReentrantLock* const _lock; - +// template based ShenandoahLocker +template +class ShenandoahLocker : public StackObj { + Lock* const _lock; public: - ShenandoahReentrantLocker(ShenandoahReentrantLock* lock) : - _lock(lock) { - if (_lock != nullptr) { - _lock->lock(); - } + ShenandoahLocker(Lock* lock, bool allow_block_for_safepoint = false) : _lock(lock) { + assert(_lock != nullptr, "Must not"); + _lock->lock(allow_block_for_safepoint); } - ~ShenandoahReentrantLocker() { - if (_lock != nullptr) { - assert(_lock->owned_by_self(), "Must be owner"); - _lock->unlock(); - } + ~ShenandoahLocker() { + _lock->unlock(); } }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp index facaefd4b62..594ad614d90 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp @@ -241,7 +241,7 @@ void ShenandoahNMethodTable::register_nmethod(nmethod* nm) { assert(nm == data->nm(), "Must be same nmethod"); // Prevent updating a nmethod while concurrent iteration is in progress. wait_until_concurrent_iteration_done(); - ShenandoahReentrantLocker data_locker(data->lock()); + ShenandoahNMethodLocker data_locker(data->lock()); data->update(); } else { // For a new nmethod, we can safely append it to the list, because diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp index 77faf6c0dcb..2686b4f4985 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp @@ -33,6 +33,10 @@ #include "runtime/atomic.hpp" #include "utilities/growableArray.hpp" +// Use ShenandoahReentrantLock as ShenandoahNMethodLock +typedef ShenandoahReentrantLock ShenandoahNMethodLock; +typedef ShenandoahLocker ShenandoahNMethodLocker; + // ShenandoahNMethod tuple records the internal locations of oop slots within reclocation stream in // the nmethod. This allows us to quickly scan the oops without doing the nmethod-internal scans, // that sometimes involves parsing the machine code. Note it does not record the oops themselves, @@ -44,16 +48,16 @@ private: int _oops_count; bool _has_non_immed_oops; bool _unregistered; - ShenandoahReentrantLock _lock; - ShenandoahReentrantLock _ic_lock; + ShenandoahNMethodLock _lock; + ShenandoahNMethodLock _ic_lock; public: ShenandoahNMethod(nmethod *nm, GrowableArray& oops, bool has_non_immed_oops); ~ShenandoahNMethod(); inline nmethod* nm() const; - inline ShenandoahReentrantLock* lock(); - inline ShenandoahReentrantLock* ic_lock(); + inline ShenandoahNMethodLock* lock(); + inline ShenandoahNMethodLock* ic_lock(); inline void oops_do(OopClosure* oops, bool fix_relocations = false); // Update oops when the nmethod is re-registered void update(); @@ -61,8 +65,8 @@ public: inline bool is_unregistered() const; static ShenandoahNMethod* for_nmethod(nmethod* nm); - static inline ShenandoahReentrantLock* lock_for_nmethod(nmethod* nm); - static inline ShenandoahReentrantLock* ic_lock_for_nmethod(nmethod* nm); + static inline ShenandoahNMethodLock* lock_for_nmethod(nmethod* nm); + static inline ShenandoahNMethodLock* ic_lock_for_nmethod(nmethod* nm); static void heal_nmethod(nmethod* nm); static inline void heal_nmethod_metadata(ShenandoahNMethod* nmethod_data); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp index 6758298675b..ef9e347b821 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp @@ -35,11 +35,11 @@ nmethod* ShenandoahNMethod::nm() const { return _nm; } -ShenandoahReentrantLock* ShenandoahNMethod::lock() { +ShenandoahNMethodLock* ShenandoahNMethod::lock() { return &_lock; } -ShenandoahReentrantLock* ShenandoahNMethod::ic_lock() { +ShenandoahNMethodLock* ShenandoahNMethod::ic_lock() { return &_ic_lock; } @@ -85,11 +85,11 @@ void ShenandoahNMethod::attach_gc_data(nmethod* nm, ShenandoahNMethod* gc_data) nm->set_gc_data(gc_data); } -ShenandoahReentrantLock* ShenandoahNMethod::lock_for_nmethod(nmethod* nm) { +ShenandoahNMethodLock* ShenandoahNMethod::lock_for_nmethod(nmethod* nm) { return gc_data(nm)->lock(); } -ShenandoahReentrantLock* ShenandoahNMethod::ic_lock_for_nmethod(nmethod* nm) { +ShenandoahNMethodLock* ShenandoahNMethod::ic_lock_for_nmethod(nmethod* nm) { return gc_data(nm)->ic_lock(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp index 7187431c8f8..37e9729b7ff 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp @@ -504,7 +504,7 @@ void ShenandoahReferenceProcessor::process_references(ShenandoahRefProcThreadLoc if (!CompressedOops::is_null(*list)) { oop head = lrb(CompressedOops::decode_not_null(*list)); shenandoah_assert_not_in_cset_except(&head, head, ShenandoahHeap::heap()->cancelled_gc() || !ShenandoahLoadRefBarrier); - oop prev = AtomicAccess::xchg(&_pending_list, head); + oop prev = _pending_list.exchange(head); set_oop_field(p, prev); if (prev == nullptr) { // First to prepend to list, record tail @@ -519,14 +519,14 @@ void ShenandoahReferenceProcessor::process_references(ShenandoahRefProcThreadLoc void ShenandoahReferenceProcessor::work() { // Process discovered references uint max_workers = ShenandoahHeap::heap()->max_workers(); - uint worker_id = AtomicAccess::add(&_iterate_discovered_list_id, 1U, memory_order_relaxed) - 1; + uint worker_id = _iterate_discovered_list_id.fetch_then_add(1U, memory_order_relaxed); while (worker_id < max_workers) { if (UseCompressedOops) { process_references(_ref_proc_thread_locals[worker_id], worker_id); } else { process_references(_ref_proc_thread_locals[worker_id], worker_id); } - worker_id = AtomicAccess::add(&_iterate_discovered_list_id, 1U, memory_order_relaxed) - 1; + worker_id = _iterate_discovered_list_id.fetch_then_add(1U, memory_order_relaxed); } } @@ -559,7 +559,7 @@ public: void ShenandoahReferenceProcessor::process_references(ShenandoahPhaseTimings::Phase phase, WorkerThreads* workers, bool concurrent) { - AtomicAccess::release_store_fence(&_iterate_discovered_list_id, 0U); + _iterate_discovered_list_id.release_store_fence(0U); // Process discovered lists ShenandoahReferenceProcessorTask task(phase, concurrent, this); @@ -576,7 +576,7 @@ void ShenandoahReferenceProcessor::process_references(ShenandoahPhaseTimings::Ph void ShenandoahReferenceProcessor::enqueue_references_locked() { // Prepend internal pending list to external pending list - shenandoah_assert_not_in_cset_except(&_pending_list, _pending_list, ShenandoahHeap::heap()->cancelled_gc() || !ShenandoahLoadRefBarrier); + shenandoah_assert_not_in_cset_except(&_pending_list, _pending_list.load_relaxed(), ShenandoahHeap::heap()->cancelled_gc() || !ShenandoahLoadRefBarrier); // During reference processing, we maintain a local list of references that are identified by // _pending_list and _pending_list_tail. _pending_list_tail points to the next field of the last Reference object on @@ -589,7 +589,7 @@ void ShenandoahReferenceProcessor::enqueue_references_locked() { // 2. Overwriting the next field of the last Reference on my local list to point at the previous head of the // global Universe::_reference_pending_list - oop former_head_of_global_list = Universe::swap_reference_pending_list(_pending_list); + oop former_head_of_global_list = Universe::swap_reference_pending_list(_pending_list.load_relaxed()); if (UseCompressedOops) { set_oop_field(reinterpret_cast(_pending_list_tail), former_head_of_global_list); } else { @@ -598,7 +598,7 @@ void ShenandoahReferenceProcessor::enqueue_references_locked() { } void ShenandoahReferenceProcessor::enqueue_references(bool concurrent) { - if (_pending_list == nullptr) { + if (_pending_list.load_relaxed() == nullptr) { // Nothing to enqueue return; } @@ -616,7 +616,7 @@ void ShenandoahReferenceProcessor::enqueue_references(bool concurrent) { } // Reset internal pending list - _pending_list = nullptr; + _pending_list.store_relaxed(nullptr); _pending_list_tail = &_pending_list; } @@ -640,9 +640,9 @@ void ShenandoahReferenceProcessor::abandon_partial_discovery() { clean_discovered_list(_ref_proc_thread_locals[index].discovered_list_addr()); } } - if (_pending_list != nullptr) { - oop pending = _pending_list; - _pending_list = nullptr; + if (_pending_list.load_relaxed() != nullptr) { + oop pending = _pending_list.load_relaxed(); + _pending_list.store_relaxed(nullptr); if (UseCompressedOops) { narrowOop* list = reference_discovered_addr(pending); clean_discovered_list(list); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp index 14adb924585..01c79029132 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp @@ -31,6 +31,7 @@ #include "gc/shared/referenceProcessorStats.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" class ShenandoahMarkRefsSuperClosure; class WorkerThreads; @@ -133,10 +134,10 @@ private: ShenandoahRefProcThreadLocal* _ref_proc_thread_locals; - oop _pending_list; + Atomic _pending_list; void* _pending_list_tail; // T* - volatile uint _iterate_discovered_list_id; + Atomic _iterate_discovered_list_id; ReferenceProcessorStats _stats; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp index b248fab7958..ac7fe1f9a3a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp @@ -80,7 +80,7 @@ public: virtual bool has_dead_oop(nmethod* nm) const { assert(ShenandoahHeap::heap()->is_concurrent_weak_root_in_progress(), "Only for this phase"); ShenandoahNMethod* data = ShenandoahNMethod::gc_data(nm); - ShenandoahReentrantLocker locker(data->lock()); + ShenandoahNMethodLocker locker(data->lock()); ShenandoahIsUnloadingOopClosure cl; data->oops_do(&cl); return cl.is_unloading(); @@ -90,14 +90,14 @@ public: class ShenandoahCompiledICProtectionBehaviour : public CompiledICProtectionBehaviour { public: virtual bool lock(nmethod* nm) { - ShenandoahReentrantLock* const lock = ShenandoahNMethod::ic_lock_for_nmethod(nm); + ShenandoahNMethodLock* const lock = ShenandoahNMethod::ic_lock_for_nmethod(nm); assert(lock != nullptr, "Not yet registered?"); lock->lock(); return true; } virtual void unlock(nmethod* nm) { - ShenandoahReentrantLock* const lock = ShenandoahNMethod::ic_lock_for_nmethod(nm); + ShenandoahNMethodLock* const lock = ShenandoahNMethod::ic_lock_for_nmethod(nm); assert(lock != nullptr, "Not yet registered?"); lock->unlock(); } @@ -107,7 +107,7 @@ public: return true; } - ShenandoahReentrantLock* const lock = ShenandoahNMethod::ic_lock_for_nmethod(nm); + ShenandoahNMethodLock* const lock = ShenandoahNMethod::ic_lock_for_nmethod(nm); assert(lock != nullptr, "Not yet registered?"); return lock->owned_by_self(); } diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index ca7174389cf..e3cf5d589c2 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -711,9 +711,7 @@ void InterpreterRuntime::resolve_get_put(Bytecodes::Code bytecode, int field_ind } ResolvedFieldEntry* entry = pool->resolved_field_entry_at(field_index); - entry->set_flags(info.access_flags().is_final(), info.access_flags().is_volatile()); - entry->fill_in(info.field_holder(), info.offset(), - checked_cast(info.index()), checked_cast(state), + entry->fill_in(info, checked_cast(state), static_cast(get_code), static_cast(put_code)); } @@ -1189,10 +1187,9 @@ JRT_LEAF(void, InterpreterRuntime::at_unwind(JavaThread* current)) JRT_END JRT_ENTRY(void, InterpreterRuntime::post_field_access(JavaThread* current, oopDesc* obj, - ResolvedFieldEntry *entry)) + ResolvedFieldEntry* entry)) // check the access_flags for the field in the klass - InstanceKlass* ik = entry->field_holder(); int index = entry->field_index(); if (!ik->field_status(index).is_access_watched()) return; @@ -1212,11 +1209,10 @@ JRT_ENTRY(void, InterpreterRuntime::post_field_access(JavaThread* current, oopDe JRT_END JRT_ENTRY(void, InterpreterRuntime::post_field_modification(JavaThread* current, oopDesc* obj, - ResolvedFieldEntry *entry, jvalue *value)) - - InstanceKlass* ik = entry->field_holder(); + ResolvedFieldEntry* entry, jvalue* value)) // check the access_flags for the field in the klass + InstanceKlass* ik = entry->field_holder(); int index = entry->field_index(); // bail out if field modifications are not watched if (!ik->field_status(index).is_modification_watched()) return; diff --git a/src/hotspot/share/oops/cpCache.hpp b/src/hotspot/share/oops/cpCache.hpp index e9e4f9a40e5..14202f226d1 100644 --- a/src/hotspot/share/oops/cpCache.hpp +++ b/src/hotspot/share/oops/cpCache.hpp @@ -196,7 +196,7 @@ class ConstantPoolCache: public MetaspaceObj { #endif public: - static int size() { return align_metadata_size(sizeof(ConstantPoolCache) / wordSize); } + static int size() { return align_metadata_size(sizeof_auto(ConstantPoolCache) / wordSize); } private: // Helpers diff --git a/src/hotspot/share/oops/resolvedFieldEntry.cpp b/src/hotspot/share/oops/resolvedFieldEntry.cpp index 83f1a6919a6..49e9115ca9a 100644 --- a/src/hotspot/share/oops/resolvedFieldEntry.cpp +++ b/src/hotspot/share/oops/resolvedFieldEntry.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,7 +24,12 @@ #include "cds/archiveBuilder.hpp" #include "cppstdlib/type_traits.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/instanceOop.hpp" #include "oops/resolvedFieldEntry.hpp" +#include "runtime/fieldDescriptor.inline.hpp" +#include "utilities/checkedCast.hpp" +#include "utilities/globalDefinitions.hpp" static_assert(std::is_trivially_copyable_v); @@ -34,6 +39,19 @@ class ResolvedFieldEntryWithExtra : public ResolvedFieldEntry { }; static_assert(sizeof(ResolvedFieldEntryWithExtra) > sizeof(ResolvedFieldEntry)); +void ResolvedFieldEntry::fill_in(const fieldDescriptor& info, u1 tos_state, u1 get_code, u1 put_code) { + set_flags(info.access_flags().is_final(), info.access_flags().is_volatile()); + _field_holder = info.field_holder(); + _field_offset = info.offset(); + _field_index = checked_cast(info.index()); + _tos_state = tos_state; + + // These must be set after the other fields + set_bytecode(&_get_code, get_code); + set_bytecode(&_put_code, put_code); + assert_is_valid(); +} + void ResolvedFieldEntry::print_on(outputStream* st) const { st->print_cr("Field Entry:"); @@ -52,6 +70,20 @@ void ResolvedFieldEntry::print_on(outputStream* st) const { st->print_cr(" - Put Bytecode: %s", Bytecodes::name((Bytecodes::Code)put_code())); } +#ifdef ASSERT +void ResolvedFieldEntry::assert_is_valid() const { + assert(field_holder()->is_instance_klass(), "should be instanceKlass"); + assert(field_offset() >= instanceOopDesc::base_offset_in_bytes(), + "field offset out of range %d >= %d", field_offset(), instanceOopDesc::base_offset_in_bytes()); + assert(as_BasicType((TosState)tos_state()) != T_ILLEGAL, "tos_state is ILLEGAL"); + assert(_flags < (1 << (max_flag_shift + 1)), "flags are too large %d", _flags); + assert((get_code() == 0 || get_code() == Bytecodes::_getstatic || get_code() == Bytecodes::_getfield), + "invalid get bytecode %d", get_code()); + assert((put_code() == 0 || put_code() == Bytecodes::_putstatic || put_code() == Bytecodes::_putfield), + "invalid put bytecode %d", put_code()); +} +#endif + #if INCLUDE_CDS void ResolvedFieldEntry::remove_unshareable_info() { *this = ResolvedFieldEntry(_cpool_index); diff --git a/src/hotspot/share/oops/resolvedFieldEntry.hpp b/src/hotspot/share/oops/resolvedFieldEntry.hpp index 77ad4815730..bdd9999dd63 100644 --- a/src/hotspot/share/oops/resolvedFieldEntry.hpp +++ b/src/hotspot/share/oops/resolvedFieldEntry.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * 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,6 @@ #define SHARE_OOPS_RESOLVEDFIELDENTRY_HPP #include "interpreter/bytecodes.hpp" -#include "oops/instanceKlass.hpp" #include "runtime/atomicAccess.hpp" #include "utilities/checkedCast.hpp" #include "utilities/sizes.hpp" @@ -46,7 +45,8 @@ // The explicit paddings are necessary for generating deterministic CDS archives. They prevent // the C++ compiler from potentially inserting random values in unused gaps. -//class InstanceKlass; +class InstanceKlass; + class ResolvedFieldEntry { friend class VMStructs; @@ -84,6 +84,7 @@ public: enum { is_volatile_shift = 0, is_final_shift = 1, // unused + max_flag_shift = is_final_shift }; // Getters @@ -113,6 +114,7 @@ public: // Printing void print_on(outputStream* st) const; + private: void set_flags(bool is_final_flag, bool is_volatile_flag) { int new_flags = (is_final_flag << is_final_shift) | static_cast(is_volatile_flag); _flags = checked_cast(new_flags); @@ -129,17 +131,12 @@ public: AtomicAccess::release_store(code, new_code); } - // Populate the strucutre with resolution information - void fill_in(InstanceKlass* klass, int offset, u2 index, u1 tos_state, u1 b1, u1 b2) { - _field_holder = klass; - _field_offset = offset; - _field_index = index; - _tos_state = tos_state; + // Debug help + void assert_is_valid() const NOT_DEBUG_RETURN; - // These must be set after the other fields - set_bytecode(&_get_code, b1); - set_bytecode(&_put_code, b2); - } + public: + // Populate the strucutre with resolution information + void fill_in(const fieldDescriptor& info, u1 tos_state, u1 get_code, u1 put_code); // CDS #if INCLUDE_CDS @@ -155,7 +152,6 @@ public: static ByteSize put_code_offset() { return byte_offset_of(ResolvedFieldEntry, _put_code); } static ByteSize type_offset() { return byte_offset_of(ResolvedFieldEntry, _tos_state); } static ByteSize flags_offset() { return byte_offset_of(ResolvedFieldEntry, _flags); } - }; #endif //SHARE_OOPS_RESOLVEDFIELDENTRY_HPP diff --git a/src/hotspot/share/opto/idealKit.cpp b/src/hotspot/share/opto/idealKit.cpp index dd7e9ae52b7..fbb61bfdf72 100644 --- a/src/hotspot/share/opto/idealKit.cpp +++ b/src/hotspot/share/opto/idealKit.cpp @@ -360,6 +360,17 @@ Node* IdealKit::load(Node* ctl, return transform(ld); } +// Load AOT runtime constant +Node* IdealKit::load_aot_const(Node* adr, const Type* t) { + BasicType bt = t->basic_type(); + const TypePtr* adr_type = nullptr; // debug-mode-only argument + DEBUG_ONLY(adr_type = C->get_adr_type(Compile::AliasIdxRaw)); + Node* ctl = (Node*)C->root(); // Raw memory access needs control + Node* ld = LoadNode::make(_gvn, ctl, C->immutable_memory(), adr, adr_type, t, bt, MemNode::unordered, + LoadNode::DependsOnlyOnTest, false, false, false, false, 0); + return transform(ld); +} + Node* IdealKit::store(Node* ctl, Node* adr, Node *val, BasicType bt, int adr_idx, MemNode::MemOrd mo, bool require_atomic_access, diff --git a/src/hotspot/share/opto/idealKit.hpp b/src/hotspot/share/opto/idealKit.hpp index 280f61fd9a9..518c3b92136 100644 --- a/src/hotspot/share/opto/idealKit.hpp +++ b/src/hotspot/share/opto/idealKit.hpp @@ -224,6 +224,9 @@ class IdealKit: public StackObj { MemNode::MemOrd mo = MemNode::unordered, LoadNode::ControlDependency control_dependency = LoadNode::DependsOnlyOnTest); + // Load AOT runtime constant + Node* load_aot_const(Node* adr, const Type* t); + // Return the new StoreXNode Node* store(Node* ctl, Node* adr, diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index c637737eef9..1a0872ee0e6 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -97,7 +97,7 @@ const Type::TypeInfo Type::_type_info[Type::lastype] = { { Bad, T_ILLEGAL, "vectorz:", false, Op_VecZ, relocInfo::none }, // VectorZ #endif { Bad, T_ADDRESS, "anyptr:", false, Op_RegP, relocInfo::none }, // AnyPtr - { Bad, T_ADDRESS, "rawptr:", false, Op_RegP, relocInfo::none }, // RawPtr + { Bad, T_ADDRESS, "rawptr:", false, Op_RegP, relocInfo::external_word_type }, // RawPtr { Bad, T_OBJECT, "oop:", true, Op_RegP, relocInfo::oop_type }, // OopPtr { Bad, T_OBJECT, "inst:", true, Op_RegP, relocInfo::oop_type }, // InstPtr { Bad, T_OBJECT, "ary:", true, Op_RegP, relocInfo::oop_type }, // AryPtr diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 2b3a2966c27..423e1a5a1f4 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -3641,7 +3641,9 @@ JVM_ENTRY(jobjectArray, JVM_GetVmArguments(JNIEnv *env)) int index = 0; for (int j = 0; j < num_flags; j++, index++) { - Handle h = java_lang_String::create_from_platform_dependent_str(vm_flags[j], CHECK_NULL); + stringStream prefixed; + prefixed.print("-XX:%s", vm_flags[j]); + Handle h = java_lang_String::create_from_platform_dependent_str(prefixed.base(), CHECK_NULL); result_h->obj_at_put(index, h()); } for (int i = 0; i < num_args; i++, index++) { diff --git a/src/hotspot/share/prims/jvmtiEnvBase.cpp b/src/hotspot/share/prims/jvmtiEnvBase.cpp index 4894a4dd21a..401bb4dfdb8 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.cpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2491,7 +2491,7 @@ SetOrClearFramePopClosure::do_thread(Thread *target) { _result = JVMTI_ERROR_NO_MORE_FRAMES; return; } - assert(_state->get_thread_or_saved() == java_thread, "Must be"); + assert(_state->get_thread() == java_thread, "Must be"); RegisterMap reg_map(java_thread, RegisterMap::UpdateMap::include, diff --git a/src/hotspot/share/prims/jvmtiEnvThreadState.cpp b/src/hotspot/share/prims/jvmtiEnvThreadState.cpp index 571c4ca5528..303923076b1 100644 --- a/src/hotspot/share/prims/jvmtiEnvThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiEnvThreadState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -151,11 +151,6 @@ bool JvmtiEnvThreadState::is_virtual() { return _state->is_virtual(); } -// Use _thread_saved if cthread is detached from JavaThread (_thread == nullptr). -JavaThread* JvmtiEnvThreadState::get_thread_or_saved() { - return _state->get_thread_or_saved(); -} - JavaThread* JvmtiEnvThreadState::get_thread() { return _state->get_thread(); } @@ -344,7 +339,7 @@ void JvmtiEnvThreadState::reset_current_location(jvmtiEvent event_type, bool ena if (enabled) { // If enabling breakpoint, no need to reset. // Can't do anything if empty stack. - JavaThread* thread = get_thread_or_saved(); + JavaThread* thread = get_thread(); if (event_type == JVMTI_EVENT_SINGLE_STEP && ((thread == nullptr && is_virtual()) || thread->has_last_Java_frame())) { diff --git a/src/hotspot/share/prims/jvmtiEnvThreadState.hpp b/src/hotspot/share/prims/jvmtiEnvThreadState.hpp index a2ab25a59dd..16b369e0857 100644 --- a/src/hotspot/share/prims/jvmtiEnvThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiEnvThreadState.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -170,8 +170,6 @@ public: inline JvmtiThreadState* jvmti_thread_state() { return _state; } - // use _thread_saved if cthread is detached from JavaThread - JavaThread *get_thread_or_saved(); JavaThread *get_thread(); inline JvmtiEnv *get_env() { return _env; } diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp index 9df3bbb4b3e..cb44b833c48 100644 --- a/src/hotspot/share/prims/jvmtiEventController.cpp +++ b/src/hotspot/share/prims/jvmtiEventController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -217,6 +217,10 @@ class EnterInterpOnlyModeClosure : public HandshakeClosure { assert(state != nullptr, "sanity check"); assert(state->get_thread() == jt, "handshake unsafe conditions"); + assert(jt->jvmti_thread_state() == state, "sanity check"); + assert(!jt->is_interp_only_mode(), "sanity check"); + assert(!state->is_interp_only_mode(), "sanity check"); + if (!state->is_pending_interp_only_mode()) { _completed = true; return; // The pending flag has been already cleared, so bail out. @@ -361,7 +365,8 @@ void VM_ChangeSingleStep::doit() { void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state) { EC_TRACE(("[%s] # Entering interpreter only mode", - JvmtiTrace::safe_get_thread_name(state->get_thread_or_saved()))); + JvmtiTrace::safe_get_thread_name(state->get_thread()))); + JavaThread *target = state->get_thread(); Thread *current = Thread::current(); @@ -371,8 +376,13 @@ void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state } // This flag will be cleared in EnterInterpOnlyModeClosure handshake. state->set_pending_interp_only_mode(true); - if (target == nullptr) { // an unmounted virtual thread - return; // EnterInterpOnlyModeClosure will be executed right after mount. + + // There are two cases when entering interp_only_mode is postponed: + // 1. Unmounted virtual thread - EnterInterpOnlyModeClosure::do_thread will be executed at mount; + // 2. Carrier thread with mounted virtual thread - EnterInterpOnlyModeClosure::do_thread will be executed at unmount. + if (target == nullptr || // an unmounted virtual thread + JvmtiEnvBase::is_thread_carrying_vthread(target, state->get_thread_oop())) { // a vthread carrying thread + return; // EnterInterpOnlyModeClosure will be executed right after mount or unmount. } EnterInterpOnlyModeClosure hs(state); if (target->is_handshake_safe_for(current)) { @@ -388,7 +398,8 @@ void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state void JvmtiEventControllerPrivate::leave_interp_only_mode(JvmtiThreadState *state) { EC_TRACE(("[%s] # Leaving interpreter only mode", - JvmtiTrace::safe_get_thread_name(state->get_thread_or_saved()))); + JvmtiTrace::safe_get_thread_name(state->get_thread()))); + if (state->is_pending_interp_only_mode()) { state->set_pending_interp_only_mode(false); // Just clear the pending flag. assert(!state->is_interp_only_mode(), "sanity check"); @@ -409,7 +420,7 @@ JvmtiEventControllerPrivate::trace_changed(JvmtiThreadState *state, jlong now_en if (changed & bit) { // it changed, print it log_trace(jvmti)("[%s] # %s event %s", - JvmtiTrace::safe_get_thread_name(state->get_thread_or_saved()), + JvmtiTrace::safe_get_thread_name(state->get_thread()), (now_enabled & bit)? "Enabling" : "Disabling", JvmtiTrace::event_name((jvmtiEvent)ei)); } } @@ -932,7 +943,7 @@ JvmtiEventControllerPrivate::set_user_enabled(JvmtiEnvBase *env, JavaThread *thr void JvmtiEventControllerPrivate::set_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) { EC_TRACE(("[%s] # set frame pop - frame=%d", - JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved()), + JvmtiTrace::safe_get_thread_name(ets->get_thread()), fpop.frame_number() )); ets->get_frame_pops()->set(fpop); @@ -943,7 +954,7 @@ JvmtiEventControllerPrivate::set_frame_pop(JvmtiEnvThreadState *ets, JvmtiFrameP void JvmtiEventControllerPrivate::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) { EC_TRACE(("[%s] # clear frame pop - frame=%d", - JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved()), + JvmtiTrace::safe_get_thread_name(ets->get_thread()), fpop.frame_number() )); ets->get_frame_pops()->clear(fpop); @@ -953,7 +964,7 @@ JvmtiEventControllerPrivate::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFram void JvmtiEventControllerPrivate::clear_all_frame_pops(JvmtiEnvThreadState *ets) { EC_TRACE(("[%s] # clear all frame pops", - JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved()) + JvmtiTrace::safe_get_thread_name(ets->get_thread()) )); ets->get_frame_pops()->clear_all(); @@ -965,7 +976,7 @@ JvmtiEventControllerPrivate::clear_to_frame_pop(JvmtiEnvThreadState *ets, JvmtiF int cleared_cnt = ets->get_frame_pops()->clear_to(fpop); EC_TRACE(("[%s] # clear to frame pop - frame=%d, count=%d", - JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved()), + JvmtiTrace::safe_get_thread_name(ets->get_thread()), fpop.frame_number(), cleared_cnt )); diff --git a/src/hotspot/share/prims/jvmtiThreadState.cpp b/src/hotspot/share/prims/jvmtiThreadState.cpp index d7accfd9a0a..32bf2c4e98e 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiThreadState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,7 +57,6 @@ JvmtiThreadState::JvmtiThreadState(JavaThread* thread, oop thread_oop) : _thread_event_enable() { assert(JvmtiThreadState_lock->is_locked(), "sanity check"); _thread = thread; - _thread_saved = nullptr; _exception_state = ES_CLEARED; _hide_single_stepping = false; _pending_interp_only_mode = false; @@ -118,11 +117,11 @@ JvmtiThreadState::JvmtiThreadState(JavaThread* thread, oop thread_oop) if (thread != nullptr) { if (thread_oop == nullptr || thread->jvmti_vthread() == nullptr || thread->jvmti_vthread() == thread_oop) { - // The JavaThread for carrier or mounted virtual thread case. + // The JavaThread for an active carrier or a mounted virtual thread case. // Set this only if thread_oop is current thread->jvmti_vthread(). thread->set_jvmti_thread_state(this); + assert(!thread->is_interp_only_mode(), "sanity check"); } - thread->set_interp_only_mode(false); } } @@ -135,7 +134,10 @@ JvmtiThreadState::~JvmtiThreadState() { } // clear this as the state for the thread + assert(get_thread() != nullptr, "sanity check"); + assert(get_thread()->jvmti_thread_state() == this, "sanity check"); get_thread()->set_jvmti_thread_state(nullptr); + get_thread()->set_interp_only_mode(false); // zap our env thread states { @@ -323,6 +325,9 @@ void JvmtiThreadState::enter_interp_only_mode() { assert(_thread != nullptr, "sanity check"); assert(JvmtiThreadState_lock->is_locked(), "sanity check"); assert(!is_interp_only_mode(), "entering interp only when in interp only mode"); + assert(_thread->jvmti_vthread() == nullptr || _thread->jvmti_vthread() == get_thread_oop(), "sanity check"); + assert(_thread->jvmti_thread_state() == this, "sanity check"); + _saved_interp_only_mode = true; _thread->set_interp_only_mode(true); invalidate_cur_stack_depth(); } @@ -330,10 +335,9 @@ void JvmtiThreadState::enter_interp_only_mode() { void JvmtiThreadState::leave_interp_only_mode() { assert(JvmtiThreadState_lock->is_locked(), "sanity check"); assert(is_interp_only_mode(), "leaving interp only when not in interp only mode"); - if (_thread == nullptr) { - // Unmounted virtual thread updates the saved value. - _saved_interp_only_mode = false; - } else { + _saved_interp_only_mode = false; + if (_thread != nullptr && _thread->jvmti_thread_state() == this) { + assert(_thread->jvmti_vthread() == nullptr || _thread->jvmti_vthread() == get_thread_oop(), "sanity check"); _thread->set_interp_only_mode(false); } } @@ -341,7 +345,7 @@ void JvmtiThreadState::leave_interp_only_mode() { // Helper routine used in several places int JvmtiThreadState::count_frames() { - JavaThread* thread = get_thread_or_saved(); + JavaThread* thread = get_thread(); javaVFrame *jvf; ResourceMark rm; if (thread == nullptr) { @@ -578,11 +582,8 @@ void JvmtiThreadState::update_thread_oop_during_vm_start() { } } +// For virtual threads only. void JvmtiThreadState::set_thread(JavaThread* thread) { - _thread_saved = nullptr; // Common case. - if (!_is_virtual && thread == nullptr) { - // Save JavaThread* if carrier thread is being detached. - _thread_saved = _thread; - } + assert(is_virtual(), "sanity check"); _thread = thread; } diff --git a/src/hotspot/share/prims/jvmtiThreadState.hpp b/src/hotspot/share/prims/jvmtiThreadState.hpp index 866d8828c4c..fa77518a8b6 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -123,8 +123,11 @@ class JvmtiVTSuspender : AllStatic { class JvmtiThreadState : public CHeapObj { private: friend class JvmtiEnv; + // The _thread field is a link to the JavaThread associated with JvmtiThreadState. + // A platform (including carrier) thread should always have a stable link to its JavaThread. + // The _thread field of a virtual thread should point to the JavaThread when + // virtual thread is mounted. It should be set to null when it is unmounted. JavaThread *_thread; - JavaThread *_thread_saved; OopHandle _thread_oop_h; // Jvmti Events that cannot be posted in their current context. JvmtiDeferredEventQueue* _jvmti_event_queue; @@ -219,7 +222,7 @@ class JvmtiThreadState : public CHeapObj { // Used by the interpreter for fullspeed debugging support bool is_interp_only_mode() { - return _thread == nullptr ? _saved_interp_only_mode : _thread->is_interp_only_mode(); + return _saved_interp_only_mode; } void enter_interp_only_mode(); void leave_interp_only_mode(); @@ -248,8 +251,10 @@ class JvmtiThreadState : public CHeapObj { int count_frames(); - inline JavaThread *get_thread() { return _thread; } - inline JavaThread *get_thread_or_saved(); // return _thread_saved if _thread is null + inline JavaThread *get_thread() { + assert(is_virtual() || _thread != nullptr, "sanity check"); + return _thread; + } // Needed for virtual threads as they can migrate to different JavaThread's. // Also used for carrier threads to clear/restore _thread. diff --git a/src/hotspot/share/prims/jvmtiThreadState.inline.hpp b/src/hotspot/share/prims/jvmtiThreadState.inline.hpp index 831a2369a7e..aa81463a696 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.inline.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -130,22 +130,21 @@ inline JvmtiThreadState* JvmtiThreadState::state_for(JavaThread *thread, Handle return state; } -inline JavaThread* JvmtiThreadState::get_thread_or_saved() { - // Use _thread_saved if cthread is detached from JavaThread (_thread == null). - return (_thread == nullptr && !is_virtual()) ? _thread_saved : _thread; -} - inline void JvmtiThreadState::set_should_post_on_exceptions(bool val) { - get_thread_or_saved()->set_should_post_on_exceptions_flag(val ? JNI_TRUE : JNI_FALSE); + get_thread()->set_should_post_on_exceptions_flag(val ? JNI_TRUE : JNI_FALSE); } inline void JvmtiThreadState::unbind_from(JvmtiThreadState* state, JavaThread* thread) { if (state == nullptr) { + assert(!thread->is_interp_only_mode(), "sanity check"); return; } - // Save thread's interp_only_mode. - state->_saved_interp_only_mode = thread->is_interp_only_mode(); - state->set_thread(nullptr); // Make sure stale _thread value is never used. + assert(thread->jvmti_thread_state() == state, "sanity check"); + assert(state->get_thread() == thread, "sanity check"); + assert(thread->is_interp_only_mode() == state->_saved_interp_only_mode, "sanity check"); + if (state->is_virtual()) { // clean _thread link for virtual threads only + state->set_thread(nullptr); // make sure stale _thread value is never used + } } inline void JvmtiThreadState::bind_to(JvmtiThreadState* state, JavaThread* thread) { @@ -158,7 +157,7 @@ inline void JvmtiThreadState::bind_to(JvmtiThreadState* state, JavaThread* threa // Bind JavaThread to JvmtiThreadState. thread->set_jvmti_thread_state(state); - if (state != nullptr) { + if (state != nullptr && state->is_virtual()) { // Bind to JavaThread. state->set_thread(thread); } diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index ea4f6f99ae6..9560d863a2c 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -168,6 +168,29 @@ class oopDesc; #define SIZE_FORMAT_X_0 "0x%08" PRIxPTR #endif // _LP64 + +template +constexpr auto sizeof_auto_impl() { + if constexpr (N <= std::numeric_limits::max()) return uint8_t(N); + else if constexpr (N <= std::numeric_limits::max()) return uint16_t(N); + else if constexpr (N <= std::numeric_limits::max()) return uint32_t(N); + else return uint64_t(N); +} + +// Yields the size (in bytes) of the operand, using the smallest +// unsigned type that can represent the size value. The operand may be +// an expression, which is an unevaluated operand, or it may be a +// type. All of the restrictions for sizeof operands apply to the +// operand. The result is a constant expression. +// +// Example of correct usage of sizeof/sizeof_auto: +// // this will wrap using sizeof_auto, use sizeof to ensure computation using size_t +// size_t size = std::numeric_limits::max() * sizeof(uint16_t); +// // implicit narrowing conversion or compiler warning/error using stricter compiler flags when using sizeof +// int count = 42 / sizeof_auto(uint16_t); + +#define sizeof_auto(...) sizeof_auto_impl() + // Convert pointer to intptr_t, for use in printing pointers. inline intptr_t p2i(const volatile void* p) { return (intptr_t) p; diff --git a/src/java.base/share/classes/java/lang/Character.java b/src/java.base/share/classes/java/lang/Character.java index ffda729a45a..33284d86e2d 100644 --- a/src/java.base/share/classes/java/lang/Character.java +++ b/src/java.base/share/classes/java/lang/Character.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 @@ -11233,7 +11233,7 @@ class Character implements java.io.Serializable, Comparable, Constabl * @param codePoint the character (Unicode code point) to be tested. * @return {@code true} if the character is an Emoji; * {@code false} otherwise. - * @spec https://unicode.org/reports/tr51/ Unicode Emoji + * @spec https://www.unicode.org/reports/tr51/ Unicode Emoji * @since 21 */ public static boolean isEmoji(int codePoint) { @@ -11252,7 +11252,7 @@ class Character implements java.io.Serializable, Comparable, Constabl * @param codePoint the character (Unicode code point) to be tested. * @return {@code true} if the character has the Emoji Presentation * property; {@code false} otherwise. - * @spec https://unicode.org/reports/tr51/ Unicode Emoji + * @spec https://www.unicode.org/reports/tr51/ Unicode Emoji * @since 21 */ public static boolean isEmojiPresentation(int codePoint) { @@ -11271,7 +11271,7 @@ class Character implements java.io.Serializable, Comparable, Constabl * @param codePoint the character (Unicode code point) to be tested. * @return {@code true} if the character is an Emoji Modifier; * {@code false} otherwise. - * @spec https://unicode.org/reports/tr51/ Unicode Emoji + * @spec https://www.unicode.org/reports/tr51/ Unicode Emoji * @since 21 */ public static boolean isEmojiModifier(int codePoint) { @@ -11290,7 +11290,7 @@ class Character implements java.io.Serializable, Comparable, Constabl * @param codePoint the character (Unicode code point) to be tested. * @return {@code true} if the character is an Emoji Modifier Base; * {@code false} otherwise. - * @spec https://unicode.org/reports/tr51/ Unicode Emoji + * @spec https://www.unicode.org/reports/tr51/ Unicode Emoji * @since 21 */ public static boolean isEmojiModifierBase(int codePoint) { @@ -11309,7 +11309,7 @@ class Character implements java.io.Serializable, Comparable, Constabl * @param codePoint the character (Unicode code point) to be tested. * @return {@code true} if the character is an Emoji Component; * {@code false} otherwise. - * @spec https://unicode.org/reports/tr51/ Unicode Emoji + * @spec https://www.unicode.org/reports/tr51/ Unicode Emoji * @since 21 */ public static boolean isEmojiComponent(int codePoint) { @@ -11328,7 +11328,7 @@ class Character implements java.io.Serializable, Comparable, Constabl * @param codePoint the character (Unicode code point) to be tested. * @return {@code true} if the character is an Extended Pictographic; * {@code false} otherwise. - * @spec https://unicode.org/reports/tr51/ Unicode Emoji + * @spec https://www.unicode.org/reports/tr51/ Unicode Emoji * @since 21 */ public static boolean isExtendedPictographic(int codePoint) { diff --git a/src/java.base/share/classes/java/lang/Shutdown.java b/src/java.base/share/classes/java/lang/Shutdown.java index 87c4732a5ce..a9d4d6a28a9 100644 --- a/src/java.base/share/classes/java/lang/Shutdown.java +++ b/src/java.base/share/classes/java/lang/Shutdown.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 @@ -55,11 +55,10 @@ class Shutdown { private static int currentRunningHook = -1; /* The preceding static fields are protected by this lock */ - private static class Lock { }; - private static Object lock = new Lock(); + private static final Object lock = new Object(); /* Lock object for the native halt method */ - private static Object haltLock = new Lock(); + private static final Object haltLock = new Object(); /** * Add a new system shutdown hook. Checks the shutdown state and diff --git a/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java b/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java index 4b6d74b1b2e..be3805cf5b1 100644 --- a/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java +++ b/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.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 @@ -746,15 +746,7 @@ abstract class ClassSpecializer.SpeciesDat new Consumer<>() { @Override public void accept(CodeBuilder cob) { - cob.aload(0); // this - final List ctorArgs = AFTER_THIS.fromTypes(superCtorType.parameterList()); - for (Var ca : ctorArgs) { - ca.emitLoadInstruction(cob); - } - - // super(ca...) - cob.invokespecial(superClassDesc, INIT_NAME, methodDesc(superCtorType)); // store down fields Var lastFV = AFTER_THIS.lastOf(ctorArgs); @@ -766,6 +758,12 @@ abstract class ClassSpecializer.SpeciesDat cob.putfield(classDesc, f.name, f.desc); } + // super(ca...) + cob.aload(0); // this + for (Var ca : ctorArgs) { + ca.emitLoadInstruction(cob); + } + cob.invokespecial(superClassDesc, INIT_NAME, methodDesc(superCtorType)); cob.return_(); } }); diff --git a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java index 4dac59771e8..d5cfc6f11a2 100644 --- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java +++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, 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 @@ -391,15 +391,15 @@ import sun.invoke.util.Wrapper; new Consumer<>() { @Override public void accept(CodeBuilder cob) { - cob.aload(0) - .invokespecial(CD_Object, INIT_NAME, MTD_void); int parameterCount = factoryType.parameterCount(); for (int i = 0; i < parameterCount; i++) { cob.aload(0) .loadLocal(TypeKind.from(factoryType.parameterType(i)), cob.parameterSlot(i)) .putfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); } - cob.return_(); + cob.aload(0) + .invokespecial(CD_Object, INIT_NAME, MTD_void) + .return_(); } }); } diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java index 70592351827..16f5c7e59b8 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java @@ -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 @@ -362,10 +362,8 @@ public final class MethodHandleProxies { // (Lookup, MethodHandle target, MethodHandle callerBoundTarget) clb.withMethodBody(INIT_NAME, MTD_void_Lookup_MethodHandle_MethodHandle, 0, cob -> { - cob.aload(0) - .invokespecial(CD_Object, INIT_NAME, MTD_void) - // call ensureOriginalLookup to verify the given Lookup has access - .aload(1) + // call ensureOriginalLookup to verify the given Lookup has access + cob.aload(1) .invokestatic(proxyDesc, ENSURE_ORIGINAL_LOOKUP, MTD_void_Lookup) // this.target = target; .aload(0) @@ -383,7 +381,9 @@ public final class MethodHandleProxies { } // complete - cob.return_(); + cob.aload(0) + .invokespecial(CD_Object, INIT_NAME, MTD_void) + .return_(); }); // private static void ensureOriginalLookup(Lookup) checks if the given Lookup diff --git a/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java b/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java index d3879d4a8fc..ee1892e8878 100644 --- a/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java +++ b/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,8 +60,7 @@ public class ReferenceQueue<@jdk.internal.RequiresIdentity T> { private volatile Reference head; private long queueLength = 0; - private static class Lock { }; - private final Lock lock = new Lock(); + private final Object lock = new Object(); /** * Constructs a new reference-object queue. diff --git a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java index 18aa6f29f1f..e4b2886404f 100644 --- a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java +++ b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.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 @@ -363,9 +363,9 @@ public final class ObjectMethods { * @return the method handle */ private static MethodHandle makeToString(MethodHandles.Lookup lookup, - Class receiverClass, - MethodHandle[] getters, - List names) { + Class receiverClass, + MethodHandle[] getters, + List names) { assert getters.length == names.size(); if (getters.length == 0) { // special case @@ -516,8 +516,8 @@ public final class ObjectMethods { requireNonNull(type); requireNonNull(recordClass); requireNonNull(names); - requireNonNull(getters); - Arrays.stream(getters).forEach(Objects::requireNonNull); + List getterList = List.of(getters); // deep null check + MethodType methodType; if (type instanceof MethodType mt) methodType = mt; @@ -526,7 +526,14 @@ public final class ObjectMethods { if (!MethodHandle.class.equals(type)) throw new IllegalArgumentException(type.toString()); } - List getterList = List.of(getters); + + for (MethodHandle getter : getterList) { + var getterType = getter.type(); + if (getterType.parameterCount() != 1 || getterType.returnType() == void.class || getterType.parameterType(0) != recordClass) { + throw new IllegalArgumentException("Illegal getter type %s for recordClass %s".formatted(getterType, recordClass.getTypeName())); + } + } + MethodHandle handle = switch (methodName) { case "equals" -> { if (methodType != null && !methodType.equals(MethodType.methodType(boolean.class, recordClass, Object.class))) @@ -541,7 +548,7 @@ public final class ObjectMethods { case "toString" -> { if (methodType != null && !methodType.equals(MethodType.methodType(String.class, recordClass))) throw new IllegalArgumentException("Bad method type: " + methodType); - List nameList = "".equals(names) ? List.of() : List.of(names.split(";")); + List nameList = names.isEmpty() ? List.of() : List.of(names.split(";")); if (nameList.size() != getterList.size()) throw new IllegalArgumentException("Name list and accessor list do not match"); yield makeToString(lookup, recordClass, getters, nameList); diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index 43a3b37b7d0..140d76c8c91 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -1719,8 +1719,10 @@ public class ZipFile implements ZipConstants, Closeable { this.cen = null; return; // only END header present } - if (end.cenlen > end.endpos) + // Validate END header + if (end.cenlen > end.endpos) { zerror("invalid END header (bad central directory size)"); + } long cenpos = end.endpos - end.cenlen; // position of CEN table // Get position of first local file (LOC) header, taking into // account that there may be a stub prefixed to the ZIP file. @@ -1728,18 +1730,22 @@ public class ZipFile implements ZipConstants, Closeable { if (locpos < 0) { zerror("invalid END header (bad central directory offset)"); } - // read in the CEN if (end.cenlen > MAX_CEN_SIZE) { zerror("invalid END header (central directory size too large)"); } if (end.centot < 0 || end.centot > end.cenlen / CENHDR) { zerror("invalid END header (total entries count too large)"); } - cen = this.cen = new byte[(int)end.cenlen]; - if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen) { + // Validation ensures these are <= Integer.MAX_VALUE + int cenlen = Math.toIntExact(end.cenlen); + int centot = Math.toIntExact(end.centot); + + // read in the CEN + cen = this.cen = new byte[cenlen]; + if (readFullyAt(cen, 0, cen.length, cenpos) != cenlen) { zerror("read CEN tables failed"); } - this.total = Math.toIntExact(end.centot); + this.total = centot; } else { cen = this.cen; this.total = knownTotal; diff --git a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java index de7a5e44b91..c220455e80b 100644 --- a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java +++ b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.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 @@ -599,8 +599,8 @@ public class ArraysSupport { if (length > 3) { if (a[aFromIndex] != b[bFromIndex]) return 0; - long aOffset = Unsafe.ARRAY_CHAR_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_CHAR_INDEX_SCALE); - long bOffset = Unsafe.ARRAY_CHAR_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_CHAR_INDEX_SCALE); + long aOffset = Unsafe.ARRAY_CHAR_BASE_OFFSET + ((long) aFromIndex << LOG2_ARRAY_CHAR_INDEX_SCALE); + long bOffset = Unsafe.ARRAY_CHAR_BASE_OFFSET + ((long) bFromIndex << LOG2_ARRAY_CHAR_INDEX_SCALE); i = vectorizedMismatch( a, aOffset, b, bOffset, @@ -648,8 +648,8 @@ public class ArraysSupport { if (length > 3) { if (a[aFromIndex] != b[bFromIndex]) return 0; - long aOffset = Unsafe.ARRAY_SHORT_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_SHORT_INDEX_SCALE); - long bOffset = Unsafe.ARRAY_SHORT_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_SHORT_INDEX_SCALE); + long aOffset = Unsafe.ARRAY_SHORT_BASE_OFFSET + ((long) aFromIndex << LOG2_ARRAY_SHORT_INDEX_SCALE); + long bOffset = Unsafe.ARRAY_SHORT_BASE_OFFSET + ((long) bFromIndex << LOG2_ARRAY_SHORT_INDEX_SCALE); i = vectorizedMismatch( a, aOffset, b, bOffset, @@ -697,8 +697,8 @@ public class ArraysSupport { if (length > 1) { if (a[aFromIndex] != b[bFromIndex]) return 0; - long aOffset = Unsafe.ARRAY_INT_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_INT_INDEX_SCALE); - long bOffset = Unsafe.ARRAY_INT_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_INT_INDEX_SCALE); + long aOffset = Unsafe.ARRAY_INT_BASE_OFFSET + ((long) aFromIndex << LOG2_ARRAY_INT_INDEX_SCALE); + long bOffset = Unsafe.ARRAY_INT_BASE_OFFSET + ((long) bFromIndex << LOG2_ARRAY_INT_INDEX_SCALE); i = vectorizedMismatch( a, aOffset, b, bOffset, @@ -729,8 +729,8 @@ public class ArraysSupport { int i = 0; if (length > 1) { if (Float.floatToRawIntBits(a[aFromIndex]) == Float.floatToRawIntBits(b[bFromIndex])) { - long aOffset = Unsafe.ARRAY_FLOAT_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_FLOAT_INDEX_SCALE); - long bOffset = Unsafe.ARRAY_FLOAT_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_FLOAT_INDEX_SCALE); + long aOffset = Unsafe.ARRAY_FLOAT_BASE_OFFSET + ((long) aFromIndex << LOG2_ARRAY_FLOAT_INDEX_SCALE); + long bOffset = Unsafe.ARRAY_FLOAT_BASE_OFFSET + ((long) bFromIndex << LOG2_ARRAY_FLOAT_INDEX_SCALE); i = vectorizedMismatch( a, aOffset, b, bOffset, @@ -787,8 +787,8 @@ public class ArraysSupport { } if (a[aFromIndex] != b[bFromIndex]) return 0; - long aOffset = Unsafe.ARRAY_LONG_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_LONG_INDEX_SCALE); - long bOffset = Unsafe.ARRAY_LONG_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_LONG_INDEX_SCALE); + long aOffset = Unsafe.ARRAY_LONG_BASE_OFFSET + ((long) aFromIndex << LOG2_ARRAY_LONG_INDEX_SCALE); + long bOffset = Unsafe.ARRAY_LONG_BASE_OFFSET + ((long) bFromIndex << LOG2_ARRAY_LONG_INDEX_SCALE); int i = vectorizedMismatch( a, aOffset, b, bOffset, @@ -813,8 +813,8 @@ public class ArraysSupport { } int i = 0; if (Double.doubleToRawLongBits(a[aFromIndex]) == Double.doubleToRawLongBits(b[bFromIndex])) { - long aOffset = Unsafe.ARRAY_DOUBLE_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_DOUBLE_INDEX_SCALE); - long bOffset = Unsafe.ARRAY_DOUBLE_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_DOUBLE_INDEX_SCALE); + long aOffset = Unsafe.ARRAY_DOUBLE_BASE_OFFSET + ((long) aFromIndex << LOG2_ARRAY_DOUBLE_INDEX_SCALE); + long bOffset = Unsafe.ARRAY_DOUBLE_BASE_OFFSET + ((long) bFromIndex << LOG2_ARRAY_DOUBLE_INDEX_SCALE); i = vectorizedMismatch( a, aOffset, b, bOffset, diff --git a/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java b/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java index 03f95222a52..23a787971c0 100644 --- a/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java +++ b/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java @@ -336,7 +336,7 @@ public class VectorSupport { @IntrinsicCandidate public static , E> - V libraryUnaryOp(long addr, Class vClass, Class eClass, int length, String debugName, + V libraryUnaryOp(long addr, Class vClass, int laneType, int length, String debugName, V v, UnaryOperation defaultImpl) { assert isNonCapturingLambda(defaultImpl) : defaultImpl; @@ -374,7 +374,7 @@ public class VectorSupport { @IntrinsicCandidate public static - V libraryBinaryOp(long addr, Class vClass, Class eClass, int length, String debugName, + V libraryBinaryOp(long addr, Class vClass, int laneType, int length, String debugName, V v1, V v2, BinaryOperation defaultImpl) { assert isNonCapturingLambda(defaultImpl) : defaultImpl; diff --git a/src/java.desktop/macosx/classes/sun/font/CStrike.java b/src/java.desktop/macosx/classes/sun/font/CStrike.java index bffb76fb1ad..f7fa00070ff 100644 --- a/src/java.desktop/macosx/classes/sun/font/CStrike.java +++ b/src/java.desktop/macosx/classes/sun/font/CStrike.java @@ -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 @@ -174,7 +174,19 @@ public final class CStrike extends PhysicalStrike { @Override Point2D.Float getGlyphMetrics(final int glyphCode) { - return new Point2D.Float(getGlyphAdvance(glyphCode), 0.0f); + Point2D.Float metrics = new Point2D.Float(); + long glyphPtr = getGlyphImagePtr(glyphCode); + if (glyphPtr != 0L) { + metrics.x = StrikeCache.getGlyphXAdvance(glyphPtr); + metrics.y = StrikeCache.getGlyphYAdvance(glyphPtr); + /* advance is currently in device space, need to convert back + * into user space. + * This must not include the translation component. */ + if (invDevTx != null) { + invDevTx.deltaTransform(metrics, metrics); + } + } + return metrics; } @Override diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CImage.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CImage.m index 051588f95bf..fa534fff275 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CImage.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CImage.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014, 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 @@ -270,7 +270,7 @@ JNI_COCOA_ENTER(env); NSRect fromRect = NSMakeRect(0, 0, sw, sh); NSRect toRect = NSMakeRect(0, 0, dw, dh); CImage_CopyNSImageIntoArray(img, dst, fromRect, toRect); - (*env)->ReleasePrimitiveArrayCritical(env, buffer, dst, JNI_ABORT); + (*env)->ReleasePrimitiveArrayCritical(env, buffer, dst, 0); } JNI_COCOA_EXIT(env); diff --git a/src/java.desktop/share/classes/java/awt/ModalEventFilter.java b/src/java.desktop/share/classes/java/awt/ModalEventFilter.java index 7941be89743..93956c34fc5 100644 --- a/src/java.desktop/share/classes/java/awt/ModalEventFilter.java +++ b/src/java.desktop/share/classes/java/awt/ModalEventFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 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 @@ -26,8 +26,6 @@ package java.awt; import java.awt.event.*; -import sun.awt.AppContext; - abstract class ModalEventFilter implements EventFilter { protected Dialog modalDialog; @@ -129,20 +127,14 @@ abstract class ModalEventFilter implements EventFilter { private static class ToolkitModalEventFilter extends ModalEventFilter { - private AppContext appContext; - ToolkitModalEventFilter(Dialog modalDialog) { super(modalDialog); - appContext = modalDialog.appContext; } protected FilterAction acceptWindow(Window w) { if (w.isModalExcluded(Dialog.ModalExclusionType.TOOLKIT_EXCLUDE)) { return FilterAction.ACCEPT; } - if (w.appContext != appContext) { - return FilterAction.REJECT; - } while (w != null) { if (w == modalDialog) { return FilterAction.ACCEPT_IMMEDIATELY; @@ -155,27 +147,21 @@ abstract class ModalEventFilter implements EventFilter { private static class ApplicationModalEventFilter extends ModalEventFilter { - private AppContext appContext; - ApplicationModalEventFilter(Dialog modalDialog) { super(modalDialog); - appContext = modalDialog.appContext; } protected FilterAction acceptWindow(Window w) { if (w.isModalExcluded(Dialog.ModalExclusionType.APPLICATION_EXCLUDE)) { return FilterAction.ACCEPT; } - if (w.appContext == appContext) { - while (w != null) { - if (w == modalDialog) { - return FilterAction.ACCEPT_IMMEDIATELY; - } - w = w.getOwner(); + while (w != null) { + if (w == modalDialog) { + return FilterAction.ACCEPT_IMMEDIATELY; } - return FilterAction.REJECT; + w = w.getOwner(); } - return FilterAction.ACCEPT; + return FilterAction.REJECT; } } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Chromaticity.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Chromaticity.java index 0481060f73f..25dbb7ddaca 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Chromaticity.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Chromaticity.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 @@ public final class Chromaticity extends EnumSyntax /** * Returns the string table for class {@code Chromaticity}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -121,6 +122,7 @@ public final class Chromaticity extends EnumSyntax /** * Returns the enumeration value table for class {@code Chromaticity}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -135,6 +137,7 @@ public final class Chromaticity extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Chromaticity.class; } @@ -148,6 +151,7 @@ public final class Chromaticity extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "chromaticity"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/ColorSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/ColorSupported.java index 6affb3e28dc..8ce2c5eef7c 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/ColorSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/ColorSupported.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 @@ -104,6 +104,7 @@ public final class ColorSupported extends EnumSyntax /** * Returns the string table for class {@code ColorSupported}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -111,6 +112,7 @@ public final class ColorSupported extends EnumSyntax /** * Returns the enumeration value table for class {@code ColorSupported}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -125,6 +127,7 @@ public final class ColorSupported extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return ColorSupported.class; } @@ -138,6 +141,7 @@ public final class ColorSupported extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "color-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Compression.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Compression.java index b1e7f1e89fc..9eeab5d9688 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Compression.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Compression.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 @@ -106,6 +106,7 @@ public class Compression extends EnumSyntax implements DocAttribute { /** * Returns the string table for class {@code Compression}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -113,6 +114,7 @@ public class Compression extends EnumSyntax implements DocAttribute { /** * Returns the enumeration value table for class {@code Compression}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -127,6 +129,7 @@ public class Compression extends EnumSyntax implements DocAttribute { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Compression.class; } @@ -140,6 +143,7 @@ public class Compression extends EnumSyntax implements DocAttribute { * * @return attribute category name */ + @Override public final String getName() { return "compression"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Copies.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Copies.java index 33a98c035be..6292cc4c8c2 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Copies.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Copies.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 @@ public final class Copies extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this copies * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return super.equals (object) && object instanceof Copies; } @@ -110,6 +111,7 @@ public final class Copies extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Copies.class; } @@ -122,6 +124,7 @@ public final class Copies extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "copies"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/CopiesSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/CopiesSupported.java index e7b411d8e55..3c2cf4bb550 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/CopiesSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/CopiesSupported.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 @@ -105,6 +105,7 @@ public final class CopiesSupported extends SetOfIntegerSyntax * @return {@code true} if {@code object} is equivalent to this copies * supported attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return super.equals (object) && object instanceof CopiesSupported; } @@ -119,6 +120,7 @@ public final class CopiesSupported extends SetOfIntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return CopiesSupported.class; } @@ -132,6 +134,7 @@ public final class CopiesSupported extends SetOfIntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "copies-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCompleted.java b/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCompleted.java index 4e0a3b27259..a7a31dacd8d 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCompleted.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCompleted.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 final class DateTimeAtCompleted extends DateTimeSyntax * @return {@code true} if {@code object} is equivalent to this date-time at * completed attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return(super.equals (object) && object instanceof DateTimeAtCompleted); @@ -105,6 +106,7 @@ public final class DateTimeAtCompleted extends DateTimeSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return DateTimeAtCompleted.class; } @@ -118,6 +120,7 @@ public final class DateTimeAtCompleted extends DateTimeSyntax * * @return attribute category name */ + @Override public final String getName() { return "date-time-at-completed"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCreation.java b/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCreation.java index fc09f0672c2..53b85b7cf5c 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCreation.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCreation.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 final class DateTimeAtCreation extends DateTimeSyntax * @return {@code true} if {@code object} is equivalent to this date-time at * creation attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return(super.equals (object) && object instanceof DateTimeAtCreation); @@ -103,6 +104,7 @@ public final class DateTimeAtCreation extends DateTimeSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return DateTimeAtCreation.class; } @@ -116,6 +118,7 @@ public final class DateTimeAtCreation extends DateTimeSyntax * * @return attribute category name */ + @Override public final String getName() { return "date-time-at-creation"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtProcessing.java b/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtProcessing.java index 8b1f3efdc0f..310f3f756c7 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtProcessing.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtProcessing.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 @@ public final class DateTimeAtProcessing extends DateTimeSyntax * @return {@code true} if {@code object} is equivalent to this date-time at * processing attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return(super.equals (object) && object instanceof DateTimeAtProcessing); @@ -104,6 +105,7 @@ public final class DateTimeAtProcessing extends DateTimeSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return DateTimeAtProcessing.class; } @@ -117,6 +119,7 @@ public final class DateTimeAtProcessing extends DateTimeSyntax * * @return attribute category name */ + @Override public final String getName() { return "date-time-at-processing"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Destination.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Destination.java index bc1d240c9c1..655a314b136 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Destination.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Destination.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 @@ public final class Destination extends URISyntax * @return {@code true} if {@code object} is equivalent to this destination * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof Destination); @@ -104,6 +105,7 @@ public final class Destination extends URISyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Destination.class; } @@ -117,6 +119,7 @@ public final class Destination extends URISyntax * * @return attribute category name */ + @Override public final String getName() { return "spool-data-destination"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/DialogOwner.java b/src/java.desktop/share/classes/javax/print/attribute/standard/DialogOwner.java index 593e656cf6b..01a410d075b 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/DialogOwner.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/DialogOwner.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 @@ -50,6 +50,7 @@ public final class DialogOwner implements PrintRequestAttribute { private static class Accessor extends DialogOwnerAccessor { + @Override public long getOwnerID(DialogOwner owner) { return owner.getID(); } @@ -133,6 +134,7 @@ public final class DialogOwner implements PrintRequestAttribute { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return DialogOwner.class; } @@ -145,6 +147,7 @@ public final class DialogOwner implements PrintRequestAttribute { * {@code "dialog-owner"}. * */ + @Override public final String getName() { return "dialog-owner"; diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/DialogTypeSelection.java b/src/java.desktop/share/classes/javax/print/attribute/standard/DialogTypeSelection.java index ae3195d0280..2cf003060b1 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/DialogTypeSelection.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/DialogTypeSelection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -98,6 +98,7 @@ public final class DialogTypeSelection extends EnumSyntax /** * Returns the string table for class {@code DialogTypeSelection}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -106,6 +107,7 @@ public final class DialogTypeSelection extends EnumSyntax * Returns the enumeration value table for class * {@code DialogTypeSelection}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -120,6 +122,7 @@ public final class DialogTypeSelection extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return DialogTypeSelection.class; } @@ -133,6 +136,7 @@ public final class DialogTypeSelection extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "dialog-type-selection"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/DocumentName.java b/src/java.desktop/share/classes/javax/print/attribute/standard/DocumentName.java index eb2bd7a870d..bd1f574e4ba 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/DocumentName.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/DocumentName.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 @@ -86,6 +86,7 @@ public final class DocumentName extends TextSyntax implements DocAttribute { * @return {@code true} if {@code object} is equivalent to this document * name attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof DocumentName); } @@ -100,6 +101,7 @@ public final class DocumentName extends TextSyntax implements DocAttribute { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return DocumentName.class; } @@ -113,6 +115,7 @@ public final class DocumentName extends TextSyntax implements DocAttribute { * * @return attribute category name */ + @Override public final String getName() { return "document-name"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Fidelity.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Fidelity.java index b0e4b70c87c..8c8941c76c4 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Fidelity.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Fidelity.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 @@ -101,6 +101,7 @@ public final class Fidelity extends EnumSyntax /** * Returns the string table for class {@code Fidelity}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -108,6 +109,7 @@ public final class Fidelity extends EnumSyntax /** * Returns the enumeration value table for class {@code Fidelity}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -122,6 +124,7 @@ public final class Fidelity extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Fidelity.class; } @@ -135,6 +138,7 @@ public final class Fidelity extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "ipp-attribute-fidelity"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Finishings.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Finishings.java index 46d520ecb48..7d9d042b3e7 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Finishings.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Finishings.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 @@ -344,6 +344,7 @@ public class Finishings extends EnumSyntax /** * Returns the string table for class {@code Finishings}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -351,6 +352,7 @@ public class Finishings extends EnumSyntax /** * Returns the enumeration value table for class {@code Finishings}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -358,6 +360,7 @@ public class Finishings extends EnumSyntax /** * Returns the lowest integer value used by class {@code Finishings}. */ + @Override protected int getOffset() { return 3; } @@ -372,6 +375,7 @@ public class Finishings extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Finishings.class; } @@ -385,6 +389,7 @@ public class Finishings extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "finishings"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobHoldUntil.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobHoldUntil.java index d4e2c2625df..2b9ede6940f 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobHoldUntil.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobHoldUntil.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 @@ -123,6 +123,7 @@ public final class JobHoldUntil extends DateTimeSyntax * @return {@code true} if {@code object} is equivalent to this job hold * until attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof JobHoldUntil); } @@ -137,6 +138,7 @@ public final class JobHoldUntil extends DateTimeSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobHoldUntil.class; } @@ -150,6 +152,7 @@ public final class JobHoldUntil extends DateTimeSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-hold-until"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressions.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressions.java index ed9600838be..70d7c7e5e46 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressions.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressions.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 @@ -110,6 +110,7 @@ public final class JobImpressions extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job * impressions attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return super.equals (object) && object instanceof JobImpressions; } @@ -124,6 +125,7 @@ public final class JobImpressions extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobImpressions.class; } @@ -137,6 +139,7 @@ public final class JobImpressions extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-impressions"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsCompleted.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsCompleted.java index 4974f9e13c9..e6fdeb93d05 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsCompleted.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsCompleted.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 @@ -94,6 +94,7 @@ public final class JobImpressionsCompleted extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job * impressions completed attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return(super.equals (object) && object instanceof JobImpressionsCompleted); @@ -109,6 +110,7 @@ public final class JobImpressionsCompleted extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobImpressionsCompleted.class; } @@ -122,6 +124,7 @@ public final class JobImpressionsCompleted extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-impressions-completed"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsSupported.java index 131b19f5ad2..82b3049b8bd 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsSupported.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 @@ -94,6 +94,7 @@ public final class JobImpressionsSupported extends SetOfIntegerSyntax * @return {@code true} if {@code object} is equivalent to this job * impressions supported attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof JobImpressionsSupported); @@ -109,6 +110,7 @@ public final class JobImpressionsSupported extends SetOfIntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobImpressionsSupported.class; } @@ -122,6 +124,7 @@ public final class JobImpressionsSupported extends SetOfIntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-impressions-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctets.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctets.java index 454a30a3a28..acd04fab21d 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctets.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctets.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 @@ -159,6 +159,7 @@ public final class JobKOctets extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job K octets * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return super.equals(object) && object instanceof JobKOctets; } @@ -173,6 +174,7 @@ public final class JobKOctets extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobKOctets.class; } @@ -186,6 +188,7 @@ public final class JobKOctets extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-k-octets"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsProcessed.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsProcessed.java index 9acc44f5777..efd9cac2441 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsProcessed.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsProcessed.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 @@ -104,6 +104,7 @@ public final class JobKOctetsProcessed extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job K octets * processed attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return(super.equals (object) && object instanceof JobKOctetsProcessed); @@ -119,6 +120,7 @@ public final class JobKOctetsProcessed extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobKOctetsProcessed.class; } @@ -132,6 +134,7 @@ public final class JobKOctetsProcessed extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-k-octets-processed"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsSupported.java index 032d172d6e2..221328caeb8 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsSupported.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 @@ -93,6 +93,7 @@ public final class JobKOctetsSupported extends SetOfIntegerSyntax * @return {@code true} if {@code object} is equivalent to this job K octets * supported attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof JobKOctetsSupported); @@ -108,6 +109,7 @@ public final class JobKOctetsSupported extends SetOfIntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobKOctetsSupported.class; } @@ -121,6 +123,7 @@ public final class JobKOctetsSupported extends SetOfIntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-k-octets-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheets.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheets.java index a3720748f68..181bda79826 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheets.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheets.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 @@ -101,6 +101,7 @@ public class JobMediaSheets extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job media * sheets attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return super.equals(object) && object instanceof JobMediaSheets; } @@ -115,6 +116,7 @@ public class JobMediaSheets extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobMediaSheets.class; } @@ -128,6 +130,7 @@ public class JobMediaSheets extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-media-sheets"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsCompleted.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsCompleted.java index e6f1eb9b4d8..fea87dc8ebd 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsCompleted.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsCompleted.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 @@ -94,6 +94,7 @@ public final class JobMediaSheetsCompleted extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job media * sheets completed attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof JobMediaSheetsCompleted); @@ -109,6 +110,7 @@ public final class JobMediaSheetsCompleted extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobMediaSheetsCompleted.class; } @@ -122,6 +124,7 @@ public final class JobMediaSheetsCompleted extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-media-sheets-completed"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsSupported.java index a942f277175..8feeafef18a 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsSupported.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 @@ -94,6 +94,7 @@ public final class JobMediaSheetsSupported extends SetOfIntegerSyntax * @return {@code true} if {@code object} is equivalent to this job media * sheets supported attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof JobMediaSheetsSupported); @@ -109,6 +110,7 @@ public final class JobMediaSheetsSupported extends SetOfIntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobMediaSheetsSupported.class; } @@ -122,6 +124,7 @@ public final class JobMediaSheetsSupported extends SetOfIntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-media-sheets-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMessageFromOperator.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMessageFromOperator.java index cd944103d23..796fe37774c 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMessageFromOperator.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMessageFromOperator.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 @@ -94,6 +94,7 @@ public final class JobMessageFromOperator extends TextSyntax * @return {@code true} if {@code object} is equivalent to this job message * from operator attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof JobMessageFromOperator); @@ -109,6 +110,7 @@ public final class JobMessageFromOperator extends TextSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobMessageFromOperator.class; } @@ -122,6 +124,7 @@ public final class JobMessageFromOperator extends TextSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-message-from-operator"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobName.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobName.java index cceabd8b8f0..4fa9eaee6fd 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobName.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobName.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 final class JobName extends TextSyntax * @return {@code true} if {@code object} is equivalent to this job name * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof JobName); } @@ -105,6 +106,7 @@ public final class JobName extends TextSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobName.class; } @@ -117,6 +119,7 @@ public final class JobName extends TextSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-name"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobOriginatingUserName.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobOriginatingUserName.java index d8dfd56a920..eda27d142f9 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobOriginatingUserName.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobOriginatingUserName.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 final class JobOriginatingUserName extends TextSyntax * @return {@code true} if {@code object} is equivalent to this job * originating user name attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof JobOriginatingUserName); @@ -106,6 +107,7 @@ public final class JobOriginatingUserName extends TextSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobOriginatingUserName.class; } @@ -119,6 +121,7 @@ public final class JobOriginatingUserName extends TextSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-originating-user-name"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobPriority.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobPriority.java index 7c77a504673..5ca9990ec13 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobPriority.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobPriority.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 @@ -94,6 +94,7 @@ public final class JobPriority extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job priority * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof JobPriority); } @@ -108,6 +109,7 @@ public final class JobPriority extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobPriority.class; } @@ -121,6 +123,7 @@ public final class JobPriority extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-priority"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobPrioritySupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobPrioritySupported.java index 6df729a9813..422d823bff7 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobPrioritySupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobPrioritySupported.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 @@ -86,6 +86,7 @@ public final class JobPrioritySupported extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job priority * supported attribute, {@code false} otherwise */ + @Override public boolean equals (Object object) { return (super.equals(object) && @@ -102,6 +103,7 @@ public final class JobPrioritySupported extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobPrioritySupported.class; } @@ -115,6 +117,7 @@ public final class JobPrioritySupported extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-priority-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobSheets.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobSheets.java index 130b8eb27a3..59a31096d2a 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobSheets.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobSheets.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 @@ -101,6 +101,7 @@ public class JobSheets extends EnumSyntax /** * Returns the string table for class {@code JobSheets}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -108,6 +109,7 @@ public class JobSheets extends EnumSyntax /** * Returns the enumeration value table for class {@code JobSheets}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -122,6 +124,7 @@ public class JobSheets extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobSheets.class; } @@ -135,6 +138,7 @@ public class JobSheets extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-sheets"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobState.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobState.java index 13499a9b2a7..0b0ba9d3276 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobState.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobState.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 @@ -206,6 +206,7 @@ public class JobState extends EnumSyntax implements PrintJobAttribute { /** * Returns the string table for class {@code JobState}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -213,6 +214,7 @@ public class JobState extends EnumSyntax implements PrintJobAttribute { /** * Returns the enumeration value table for class {@code JobState}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -227,6 +229,7 @@ public class JobState extends EnumSyntax implements PrintJobAttribute { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobState.class; } @@ -240,6 +243,7 @@ public class JobState extends EnumSyntax implements PrintJobAttribute { * * @return attribute category name */ + @Override public final String getName() { return "job-state"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReason.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReason.java index 2f8526cb7b1..aba72ec9c1a 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReason.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReason.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, 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 @@ -436,6 +436,7 @@ public class JobStateReason extends EnumSyntax implements Attribute { /** * Returns the string table for class {@code JobStateReason}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -443,6 +444,7 @@ public class JobStateReason extends EnumSyntax implements Attribute { /** * Returns the enumeration value table for class {@code JobStateReason}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -457,6 +459,7 @@ public class JobStateReason extends EnumSyntax implements Attribute { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobStateReason.class; } @@ -470,6 +473,7 @@ public class JobStateReason extends EnumSyntax implements Attribute { * * @return attribute category name */ + @Override public final String getName() { return "job-state-reason"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReasons.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReasons.java index 22b438c02eb..4e3e66d0ea8 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReasons.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReasons.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 @@ -140,6 +140,7 @@ public final class JobStateReasons * class {@link JobStateReason JobStateReason} * @since 1.5 */ + @Override public boolean add(JobStateReason o) { if (o == null) { throw new NullPointerException(); @@ -157,6 +158,7 @@ public final class JobStateReasons * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobStateReasons.class; } @@ -170,6 +172,7 @@ public final class JobStateReasons * * @return attribute category name */ + @Override public final String getName() { return "job-state-reasons"; } diff --git a/src/java.desktop/share/classes/javax/swing/DebugGraphics.java b/src/java.desktop/share/classes/javax/swing/DebugGraphics.java index 83b44716c04..512b56ee28a 100644 --- a/src/java.desktop/share/classes/javax/swing/DebugGraphics.java +++ b/src/java.desktop/share/classes/javax/swing/DebugGraphics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1492,14 +1492,12 @@ public class DebugGraphics extends Graphics { /** Returns DebugGraphicsInfo, or creates one if none exists. */ static DebugGraphicsInfo info() { - DebugGraphicsInfo debugGraphicsInfo = (DebugGraphicsInfo) - SwingUtilities.appContextGet(debugGraphicsInfoKey); - if (debugGraphicsInfo == null) { - debugGraphicsInfo = new DebugGraphicsInfo(); - SwingUtilities.appContextPut(debugGraphicsInfoKey, - debugGraphicsInfo); + synchronized (DebugGraphicsInfo.class) { + if (debugGraphicsInfo == null) { + debugGraphicsInfo = new DebugGraphicsInfo(); + } + return debugGraphicsInfo; } - return debugGraphicsInfo; } - private static final Class debugGraphicsInfoKey = DebugGraphicsInfo.class; + private static DebugGraphicsInfo debugGraphicsInfo; } diff --git a/src/java.desktop/share/classes/javax/swing/JComponent.java b/src/java.desktop/share/classes/javax/swing/JComponent.java index f515115bade..6351522ab49 100644 --- a/src/java.desktop/share/classes/javax/swing/JComponent.java +++ b/src/java.desktop/share/classes/javax/swing/JComponent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -4885,8 +4885,7 @@ public abstract class JComponent extends Container implements Serializable, * @see RepaintManager#addDirtyRegion */ public void repaint(long tm, int x, int y, int width, int height) { - RepaintManager.currentManager(SunToolkit.targetToAppContext(this)) - .addDirtyRegion(this, x, y, width, height); + RepaintManager.currentManager(this).addDirtyRegion(this, x, y, width, height); } diff --git a/src/java.desktop/share/classes/javax/swing/JDialog.java b/src/java.desktop/share/classes/javax/swing/JDialog.java index 99d6385cd7c..a7d8791c72c 100644 --- a/src/java.desktop/share/classes/javax/swing/JDialog.java +++ b/src/java.desktop/share/classes/javax/swing/JDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -101,13 +101,6 @@ public class JDialog extends Dialog implements WindowConstants, RootPaneContainer, TransferHandler.HasGetTransferHandler { - /** - * Key into the AppContext, used to check if should provide decorations - * by default. - */ - private static final Object defaultLookAndFeelDecoratedKey = - new StringBuffer("JDialog.defaultLookAndFeelDecorated"); - private int defaultCloseOperation = HIDE_ON_CLOSE; /** @@ -1119,6 +1112,8 @@ public class JDialog extends Dialog implements WindowConstants, } } + private static boolean defaultLAFDecorated; + /** * Provides a hint as to whether or not newly created {@code JDialog}s * should have their Window decorations (such as borders, widgets to @@ -1144,11 +1139,7 @@ public class JDialog extends Dialog implements WindowConstants, * @since 1.4 */ public static void setDefaultLookAndFeelDecorated(boolean defaultLookAndFeelDecorated) { - if (defaultLookAndFeelDecorated) { - SwingUtilities.appContextPut(defaultLookAndFeelDecoratedKey, Boolean.TRUE); - } else { - SwingUtilities.appContextPut(defaultLookAndFeelDecoratedKey, Boolean.FALSE); - } + defaultLAFDecorated = defaultLookAndFeelDecorated; } /** @@ -1160,12 +1151,7 @@ public class JDialog extends Dialog implements WindowConstants, * @since 1.4 */ public static boolean isDefaultLookAndFeelDecorated() { - Boolean defaultLookAndFeelDecorated = - (Boolean) SwingUtilities.appContextGet(defaultLookAndFeelDecoratedKey); - if (defaultLookAndFeelDecorated == null) { - defaultLookAndFeelDecorated = Boolean.FALSE; - } - return defaultLookAndFeelDecorated.booleanValue(); + return defaultLAFDecorated; } /** diff --git a/src/java.desktop/share/classes/javax/swing/JEditorPane.java b/src/java.desktop/share/classes/javax/swing/JEditorPane.java index 2f8f0f30722..3134b8bace2 100644 --- a/src/java.desktop/share/classes/javax/swing/JEditorPane.java +++ b/src/java.desktop/share/classes/javax/swing/JEditorPane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1227,12 +1227,11 @@ public class JEditorPane extends JTextComponent { */ @SuppressWarnings("deprecation") public static EditorKit createEditorKitForContentType(String type) { - Hashtable kitRegistry = getKitRegistry(); EditorKit k = kitRegistry.get(type); if (k == null) { // try to dynamically load the support - String classname = getKitTypeRegistry().get(type); - ClassLoader loader = getKitLoaderRegistry().get(type); + String classname = kitTypeRegistry.get(type); + ClassLoader loader = kitLoaderRegistry.get(type); try { Class c; if (loader != null) { @@ -1287,13 +1286,13 @@ public class JEditorPane extends JTextComponent { * @param loader the ClassLoader to use to load the name */ public static void registerEditorKitForContentType(String type, String classname, ClassLoader loader) { - getKitTypeRegistry().put(type, classname); + kitTypeRegistry.put(type, classname); if (loader != null) { - getKitLoaderRegistry().put(type, loader); + kitLoaderRegistry.put(type, loader); } else { - getKitLoaderRegistry().remove(type); + kitLoaderRegistry.remove(type); } - getKitRegistry().remove(type); + kitRegistry.remove(type); } /** @@ -1306,63 +1305,27 @@ public class JEditorPane extends JTextComponent { * @since 1.3 */ public static String getEditorKitClassNameForContentType(String type) { - return getKitTypeRegistry().get(type); + return kitTypeRegistry.get(type); } - private static Hashtable getKitTypeRegistry() { - loadDefaultKitsIfNecessary(); - @SuppressWarnings("unchecked") - Hashtable tmp = - (Hashtable)SwingUtilities.appContextGet(kitTypeRegistryKey); - return tmp; - } + private static final Hashtable kitTypeRegistry = new Hashtable<>(); + private static final Hashtable kitLoaderRegistry = new Hashtable<>(); - private static Hashtable getKitLoaderRegistry() { - loadDefaultKitsIfNecessary(); - @SuppressWarnings("unchecked") - Hashtable tmp = - (Hashtable)SwingUtilities.appContextGet(kitLoaderRegistryKey); - return tmp; - } + private static final Hashtable kitRegistry = new Hashtable<>(3); - private static Hashtable getKitRegistry() { - @SuppressWarnings("unchecked") - Hashtable ht = - (Hashtable)SwingUtilities.appContextGet(kitRegistryKey); - if (ht == null) { - ht = new Hashtable<>(3); - SwingUtilities.appContextPut(kitRegistryKey, ht); - } - return ht; - } - - /** - * This is invoked every time the registries are accessed. Loading - * is done this way instead of via a static as the static is only - * called once when running in an AppContext. - */ - private static void loadDefaultKitsIfNecessary() { - if (SwingUtilities.appContextGet(kitTypeRegistryKey) == null) { - synchronized(defaultEditorKitMap) { - if (defaultEditorKitMap.size() == 0) { - defaultEditorKitMap.put("text/plain", - "javax.swing.JEditorPane$PlainEditorKit"); - defaultEditorKitMap.put("text/html", - "javax.swing.text.html.HTMLEditorKit"); - defaultEditorKitMap.put("text/rtf", - "javax.swing.text.rtf.RTFEditorKit"); - defaultEditorKitMap.put("application/rtf", - "javax.swing.text.rtf.RTFEditorKit"); - } - } - Hashtable ht = new Hashtable<>(); - SwingUtilities.appContextPut(kitTypeRegistryKey, ht); - ht = new Hashtable<>(); - SwingUtilities.appContextPut(kitLoaderRegistryKey, ht); - for (String key : defaultEditorKitMap.keySet()) { - registerEditorKitForContentType(key,defaultEditorKitMap.get(key)); - } + static final Map defaultEditorKitMap = new HashMap(0); + static { + defaultEditorKitMap.put("text/plain", + "javax.swing.JEditorPane$PlainEditorKit"); + defaultEditorKitMap.put("text/html", + "javax.swing.text.html.HTMLEditorKit"); + defaultEditorKitMap.put("text/rtf", + "javax.swing.text.rtf.RTFEditorKit"); + defaultEditorKitMap.put("application/rtf", + "javax.swing.text.rtf.RTFEditorKit"); + for (String key : defaultEditorKitMap.keySet()) { + registerEditorKitForContentType(key,defaultEditorKitMap.get(key)); } } @@ -1587,16 +1550,6 @@ public class JEditorPane extends JTextComponent { */ private Hashtable typeHandlers; - /* - * Private AppContext keys for this class's static variables. - */ - private static final Object kitRegistryKey = - new StringBuffer("JEditorPane.kitRegistry"); - private static final Object kitTypeRegistryKey = - new StringBuffer("JEditorPane.kitTypeRegistry"); - private static final Object kitLoaderRegistryKey = - new StringBuffer("JEditorPane.kitLoaderRegistry"); - /** * @see #getUIClassID * @see #readObject @@ -1633,8 +1586,6 @@ public class JEditorPane extends JTextComponent { */ public static final String HONOR_DISPLAY_PROPERTIES = "JEditorPane.honorDisplayProperties"; - static final Map defaultEditorKitMap = new HashMap(0); - /** * Returns a string representation of this JEditorPane. * This method diff --git a/src/java.desktop/share/classes/javax/swing/JFrame.java b/src/java.desktop/share/classes/javax/swing/JFrame.java index 8bde7e18f03..bab215625b9 100644 --- a/src/java.desktop/share/classes/javax/swing/JFrame.java +++ b/src/java.desktop/share/classes/javax/swing/JFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -126,13 +126,6 @@ public class JFrame extends Frame implements WindowConstants, RootPaneContainer, TransferHandler.HasGetTransferHandler { - /** - * Key into the AppContext, used to check if should provide decorations - * by default. - */ - private static final Object defaultLookAndFeelDecoratedKey = - new StringBuffer("JFrame.defaultLookAndFeelDecorated"); - private int defaultCloseOperation = HIDE_ON_CLOSE; /** @@ -755,6 +748,8 @@ public class JFrame extends Frame implements WindowConstants, } } + private static boolean defaultLAFDecorated; + /** * Provides a hint as to whether or not newly created JFrames * should have their Window decorations (such as borders, widgets to @@ -780,11 +775,7 @@ public class JFrame extends Frame implements WindowConstants, * @since 1.4 */ public static void setDefaultLookAndFeelDecorated(boolean defaultLookAndFeelDecorated) { - if (defaultLookAndFeelDecorated) { - SwingUtilities.appContextPut(defaultLookAndFeelDecoratedKey, Boolean.TRUE); - } else { - SwingUtilities.appContextPut(defaultLookAndFeelDecoratedKey, Boolean.FALSE); - } + defaultLAFDecorated = defaultLookAndFeelDecorated; } @@ -797,12 +788,7 @@ public class JFrame extends Frame implements WindowConstants, * @since 1.4 */ public static boolean isDefaultLookAndFeelDecorated() { - Boolean defaultLookAndFeelDecorated = - (Boolean) SwingUtilities.appContextGet(defaultLookAndFeelDecoratedKey); - if (defaultLookAndFeelDecorated == null) { - defaultLookAndFeelDecorated = Boolean.FALSE; - } - return defaultLookAndFeelDecorated.booleanValue(); + return defaultLAFDecorated; } /** diff --git a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java index 1b04e8c6169..2ecc7cf3b1e 100644 --- a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java +++ b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java @@ -112,12 +112,6 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { */ private static final String uiClassID = "PopupMenuUI"; - /** - * Key used in AppContext to determine if light way popups are the default. - */ - private static final Object defaultLWPopupEnabledKey = - new StringBuffer("JPopupMenu.defaultLWPopupEnabledKey"); - /** Bug#4425878-Property javax.swing.adjustPopupLocationToFit introduced */ static boolean popupPositionFixDisabled = System.getProperty("javax.swing.adjustPopupLocationToFit","").equals("false"); @@ -153,6 +147,8 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { private static final boolean VERBOSE = false; // show reuse hits/misses private static final boolean DEBUG = false; // show bad params, misc. + private static boolean defaultLWPopupEnabled = true; + /** * Sets the default value of the lightWeightPopupEnabled * property. @@ -163,8 +159,7 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { * @see #setLightWeightPopupEnabled */ public static void setDefaultLightWeightPopupEnabled(boolean aFlag) { - SwingUtilities.appContextPut(defaultLWPopupEnabledKey, - Boolean.valueOf(aFlag)); + defaultLWPopupEnabled = aFlag; } /** @@ -177,14 +172,7 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { * @see #setDefaultLightWeightPopupEnabled */ public static boolean getDefaultLightWeightPopupEnabled() { - Boolean b = (Boolean) - SwingUtilities.appContextGet(defaultLWPopupEnabledKey); - if (b == null) { - SwingUtilities.appContextPut(defaultLWPopupEnabledKey, - Boolean.TRUE); - return true; - } - return b.booleanValue(); + return defaultLWPopupEnabled; } /** diff --git a/src/java.desktop/share/classes/javax/swing/PopupFactory.java b/src/java.desktop/share/classes/javax/swing/PopupFactory.java index 2b274c816b1..208177fc55e 100644 --- a/src/java.desktop/share/classes/javax/swing/PopupFactory.java +++ b/src/java.desktop/share/classes/javax/swing/PopupFactory.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 @@ -80,13 +80,6 @@ public class PopupFactory { } }); } - /** - * The shared instanceof PopupFactory is per - * AppContext. This is the key used in the - * AppContext to locate the PopupFactory. - */ - private static final Object SharedInstanceKey = - new StringBuffer("PopupFactory.SharedInstanceKey"); /** * Max number of items to store in any one particular cache. @@ -118,6 +111,7 @@ public class PopupFactory { */ public PopupFactory() {} + static PopupFactory sharedFactory; /** * Sets the PopupFactory that will be used to obtain * Popups. @@ -132,7 +126,9 @@ public class PopupFactory { if (factory == null) { throw new IllegalArgumentException("PopupFactory can not be null"); } - SwingUtilities.appContextPut(SharedInstanceKey, factory); + synchronized (PopupFactory.class) { + sharedFactory = factory; + } } /** @@ -142,14 +138,12 @@ public class PopupFactory { * @return Shared PopupFactory */ public static PopupFactory getSharedInstance() { - PopupFactory factory = (PopupFactory)SwingUtilities.appContextGet( - SharedInstanceKey); - - if (factory == null) { - factory = new PopupFactory(); - setSharedInstance(factory); + synchronized (PopupFactory.class) { + if (sharedFactory == null) { + sharedFactory = new PopupFactory(); + } + return sharedFactory; } - return factory; } @@ -351,8 +345,6 @@ public class PopupFactory { * Popup implementation that uses a Window as the popup. */ private static class HeavyWeightPopup extends Popup { - private static final Object heavyWeightPopupCacheKey = - new StringBuffer("PopupFactory.heavyWeightPopupCache"); private volatile boolean isCacheEnabled = true; @@ -434,21 +426,16 @@ public class PopupFactory { } } + private static Map> cache; /** * Returns the cache to use for heavy weight popups. Maps from * Window to a List of * HeavyWeightPopups. */ - @SuppressWarnings("unchecked") private static Map> getHeavyWeightPopupCache() { synchronized (HeavyWeightPopup.class) { - Map> cache = (Map>)SwingUtilities.appContextGet( - heavyWeightPopupCacheKey); - if (cache == null) { cache = new HashMap<>(2); - SwingUtilities.appContextPut(heavyWeightPopupCacheKey, - cache); } return cache; } @@ -728,18 +715,17 @@ public class PopupFactory { return popup; } + private static List cache; /** * Returns the cache to use for heavy weight popups. */ - @SuppressWarnings("unchecked") private static List getLightWeightPopupCache() { - List cache = (List)SwingUtilities.appContextGet( - lightWeightPopupCacheKey); - if (cache == null) { - cache = new ArrayList<>(); - SwingUtilities.appContextPut(lightWeightPopupCacheKey, cache); + synchronized (LightWeightPopup.class) { + if (cache == null) { + cache = new ArrayList<>(); + } + return cache; } - return cache; } /** @@ -849,8 +835,6 @@ public class PopupFactory { * Popup implementation that uses a Panel as the popup. */ private static class MediumWeightPopup extends ContainerPopup { - private static final Object mediumWeightPopupCacheKey = - new StringBuffer("PopupFactory.mediumPopupCache"); /** Child of the panel. The contents are added to this. */ private JRootPane rootPane; @@ -877,19 +861,18 @@ public class PopupFactory { return popup; } + private static List cache; /** * Returns the cache to use for medium weight popups. */ @SuppressWarnings("unchecked") private static List getMediumWeightPopupCache() { - List cache = (List)SwingUtilities.appContextGet( - mediumWeightPopupCacheKey); - - if (cache == null) { - cache = new ArrayList<>(); - SwingUtilities.appContextPut(mediumWeightPopupCacheKey, cache); + synchronized (MediumWeightPopup.class) { + if (cache == null) { + cache = new ArrayList<>(); + } + return cache; } - return cache; } /** diff --git a/src/java.desktop/share/classes/javax/swing/RepaintManager.java b/src/java.desktop/share/classes/javax/swing/RepaintManager.java index dcc755cb4bc..db8b13afa22 100644 --- a/src/java.desktop/share/classes/javax/swing/RepaintManager.java +++ b/src/java.desktop/share/classes/javax/swing/RepaintManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -119,8 +119,6 @@ public class RepaintManager */ private PaintManager paintManager; - private static final Object repaintManagerKey = RepaintManager.class; - // Whether or not a VolatileImage should be used for double-buffered painting static boolean volatileImageBufferEnabled = true; /** @@ -236,8 +234,10 @@ public class RepaintManager } } + private static RepaintManager repaintManager; + /** - * Return the RepaintManager for the calling thread given a Component. + * Return the RepaintManager given a Component. * * @param c a Component -- unused in the default implementation, but could * be used by an overridden version to return a different RepaintManager @@ -249,21 +249,15 @@ public class RepaintManager // component is ever used to determine the current // RepaintManager, DisplayChangedRunnable will need to be modified // accordingly. - return currentManager(AppContext.getAppContext()); - } - /** - * Returns the RepaintManager for the specified AppContext. If - * a RepaintManager has not been created for the specified - * AppContext this will return null. - */ - static RepaintManager currentManager(AppContext appContext) { - RepaintManager rm = (RepaintManager)appContext.get(repaintManagerKey); - if (rm == null) { - rm = new RepaintManager(BUFFER_STRATEGY_TYPE); - appContext.put(repaintManagerKey, rm); + synchronized (RepaintManager.class) { + RepaintManager rm = repaintManager; + if (rm == null) { + rm = new RepaintManager(BUFFER_STRATEGY_TYPE); + repaintManager = rm; + } + return rm; } - return rm; } /** @@ -282,16 +276,12 @@ public class RepaintManager /** - * Set the RepaintManager that should be used for the calling - * thread. aRepaintManager will become the current RepaintManager - * for the calling thread's thread group. + * Set the RepaintManager. * @param aRepaintManager the RepaintManager object to use */ public static void setCurrentManager(RepaintManager aRepaintManager) { - if (aRepaintManager != null) { - SwingUtilities.appContextPut(repaintManagerKey, aRepaintManager); - } else { - SwingUtilities.appContextRemove(repaintManagerKey); + synchronized (RepaintManager.class) { + repaintManager = aRepaintManager; } } @@ -373,7 +363,7 @@ public class RepaintManager // Queue a Runnable to invoke paintDirtyRegions and // validateInvalidComponents. - scheduleProcessingRunnable(SunToolkit.targetToAppContext(invalidComponent)); + scheduleProcessingRunnable(); } @@ -460,7 +450,7 @@ public class RepaintManager // Queue a Runnable to invoke paintDirtyRegions and // validateInvalidComponents. - scheduleProcessingRunnable(SunToolkit.targetToAppContext(c)); + scheduleProcessingRunnable(); } /** @@ -530,8 +520,7 @@ public class RepaintManager // This is called from the toolkit thread when a native expose is // received. // - void nativeAddDirtyRegion(AppContext appContext, Container c, - int x, int y, int w, int h) { + void nativeAddDirtyRegion(Container c, int x, int y, int w, int h) { if (w > 0 && h > 0) { synchronized(this) { Rectangle dirty = hwDirtyComponents.get(c); @@ -543,7 +532,7 @@ public class RepaintManager x, y, w, h, dirty)); } } - scheduleProcessingRunnable(appContext); + scheduleProcessingRunnable(); } } @@ -551,8 +540,7 @@ public class RepaintManager // This is called from the toolkit thread when awt needs to run a // Runnable before we paint. // - void nativeQueueSurfaceDataRunnable(AppContext appContext, - final Component c, final Runnable r) + void nativeQueueSurfaceDataRunnable(final Component c, final Runnable r) { synchronized(this) { if (runnableList == null) { @@ -560,7 +548,7 @@ public class RepaintManager } runnableList.add(r); } - scheduleProcessingRunnable(appContext); + scheduleProcessingRunnable(); } /** @@ -1411,11 +1399,11 @@ public class RepaintManager return paintManager; } - private void scheduleProcessingRunnable(AppContext context) { + private void scheduleProcessingRunnable() { if (processingRunnable.markPending()) { Toolkit tk = Toolkit.getDefaultToolkit(); if (tk instanceof SunToolkit) { - SunToolkit.getSystemEventQueueImplPP(context). + SunToolkit.getSystemEventQueueImplPP(). postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(), processingRunnable)); } else { @@ -1733,8 +1721,7 @@ public class RepaintManager /** * Listener installed to detect display changes. When display changes, * schedules a callback to notify all RepaintManagers of the display - * changes. Only one DisplayChangedHandler is ever installed. The - * singleton instance will schedule notification for all AppContexts. + * changes. Only one DisplayChangedHandler is ever installed. */ private static final class DisplayChangedHandler implements DisplayChangedListener { diff --git a/src/java.desktop/share/classes/javax/swing/SwingPaintEventDispatcher.java b/src/java.desktop/share/classes/javax/swing/SwingPaintEventDispatcher.java index 4a7b0821a3d..8c904f7d00c 100644 --- a/src/java.desktop/share/classes/javax/swing/SwingPaintEventDispatcher.java +++ b/src/java.desktop/share/classes/javax/swing/SwingPaintEventDispatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ import java.awt.Component; import java.awt.Container; import java.awt.Rectangle; import java.awt.event.PaintEvent; -import sun.awt.AppContext; import sun.awt.SunToolkit; import sun.awt.event.IgnorePaintEvent; @@ -52,12 +51,10 @@ class SwingPaintEventDispatcher extends sun.awt.PaintEventDispatcher { public PaintEvent createPaintEvent(Component component, int x, int y, int w, int h) { if (component instanceof RootPaneContainer) { - AppContext appContext = SunToolkit.targetToAppContext(component); - RepaintManager rm = RepaintManager.currentManager(appContext); + RepaintManager rm = RepaintManager.currentManager(component); if (!SHOW_FROM_DOUBLE_BUFFER || !rm.show((Container)component, x, y, w, h)) { - rm.nativeAddDirtyRegion(appContext, (Container)component, - x, y, w, h); + rm.nativeAddDirtyRegion((Container)component, x, y, w, h); } // For backward compatibility generate an empty paint // event. Not doing this broke parts of Netbeans. @@ -65,10 +62,8 @@ class SwingPaintEventDispatcher extends sun.awt.PaintEventDispatcher { new Rectangle(x, y, w, h)); } else if (component instanceof SwingHeavyWeight) { - AppContext appContext = SunToolkit.targetToAppContext(component); - RepaintManager rm = RepaintManager.currentManager(appContext); - rm.nativeAddDirtyRegion(appContext, (Container)component, - x, y, w, h); + RepaintManager rm = RepaintManager.currentManager(component); + rm.nativeAddDirtyRegion((Container)component, x, y, w, h); return new IgnorePaintEvent(component, PaintEvent.PAINT, new Rectangle(x, y, w, h)); } @@ -81,9 +76,7 @@ class SwingPaintEventDispatcher extends sun.awt.PaintEventDispatcher { public boolean queueSurfaceDataReplacing(Component c, Runnable r) { if (c instanceof RootPaneContainer) { - AppContext appContext = SunToolkit.targetToAppContext(c); - RepaintManager.currentManager(appContext). - nativeQueueSurfaceDataRunnable(appContext, c, r); + RepaintManager.currentManager(c).nativeQueueSurfaceDataRunnable(c, r); return true; } return super.queueSurfaceDataReplacing(c, r); diff --git a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java index bc6441212de..afe1c444c31 100644 --- a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java +++ b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1899,11 +1899,6 @@ public class SwingUtilities implements SwingConstants return null; } - - // Don't use String, as it's not guaranteed to be unique in a Hashtable. - private static final Object sharedOwnerFrameKey = - new StringBuffer("SwingUtilities.sharedOwnerFrame"); - @SuppressWarnings("serial") // JDK-implementation class static class SharedOwnerFrame extends Frame implements WindowListener { public void addNotify() { @@ -1961,6 +1956,7 @@ public class SwingUtilities implements SwingConstants } } + private static Frame sharedOwnerFrame; /** * Returns a toolkit-private, shared, invisible Frame * to be the owner for JDialogs and JWindows created with @@ -1970,14 +1966,12 @@ public class SwingUtilities implements SwingConstants * @see java.awt.GraphicsEnvironment#isHeadless */ static Frame getSharedOwnerFrame() throws HeadlessException { - Frame sharedOwnerFrame = - (Frame)SwingUtilities.appContextGet(sharedOwnerFrameKey); - if (sharedOwnerFrame == null) { - sharedOwnerFrame = new SharedOwnerFrame(); - SwingUtilities.appContextPut(sharedOwnerFrameKey, - sharedOwnerFrame); + synchronized (SharedOwnerFrame.class) { + if (sharedOwnerFrame == null) { + sharedOwnerFrame = new SharedOwnerFrame(); + } + return sharedOwnerFrame; } - return sharedOwnerFrame; } /** diff --git a/src/java.desktop/share/classes/javax/swing/ToolTipManager.java b/src/java.desktop/share/classes/javax/swing/ToolTipManager.java index a9e2c6e69bb..4c3d0209823 100644 --- a/src/java.desktop/share/classes/javax/swing/ToolTipManager.java +++ b/src/java.desktop/share/classes/javax/swing/ToolTipManager.java @@ -61,7 +61,6 @@ public final class ToolTipManager extends MouseAdapter implements MouseMotionLis JComponent insideComponent; MouseEvent mouseEvent; boolean showImmediately; - private static final Object TOOL_TIP_MANAGER_KEY = new Object(); transient Popup tipWindow; /** The Window tip is being displayed in. This will be non-null if * the Window tip is in differs from that of insideComponent's Window. @@ -394,19 +393,19 @@ public final class ToolTipManager extends MouseAdapter implements MouseMotionLis } } + private static ToolTipManager manager; /** * Returns a shared ToolTipManager instance. * * @return a shared ToolTipManager object */ public static ToolTipManager sharedInstance() { - Object value = SwingUtilities.appContextGet(TOOL_TIP_MANAGER_KEY); - if (value instanceof ToolTipManager) { - return (ToolTipManager) value; + synchronized(ToolTipManager.class) { + if (manager == null) { + manager = new ToolTipManager(); + } + return manager; } - ToolTipManager manager = new ToolTipManager(); - SwingUtilities.appContextPut(TOOL_TIP_MANAGER_KEY, manager); - return manager; } // add keylistener here to trigger tip for access diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalTitlePane.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalTitlePane.java index ae8b379a579..4ed6734df1f 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalTitlePane.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalTitlePane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2014, 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 @@ -437,6 +437,7 @@ class MetalTitlePane extends JComponent { */ private JMenu createMenu() { JMenu menu = new JMenu(""); + menu.setPreferredSize(new Dimension(IMAGE_WIDTH, IMAGE_HEIGHT)); if (getWindowDecorationStyle() == JRootPane.FRAME) { addMenuItems(menu); } diff --git a/src/java.desktop/share/classes/sun/awt/SunToolkit.java b/src/java.desktop/share/classes/sun/awt/SunToolkit.java index 10fb62e2fdc..e97b654486f 100644 --- a/src/java.desktop/share/classes/sun/awt/SunToolkit.java +++ b/src/java.desktop/share/classes/sun/awt/SunToolkit.java @@ -1034,8 +1034,7 @@ public abstract class SunToolkit extends Toolkit return getSystemEventQueueImplPP(); } - // Package private implementation - static EventQueue getSystemEventQueueImplPP() { + public static EventQueue getSystemEventQueueImplPP() { return getSystemEventQueueImplPP(AppContext.getAppContext()); } diff --git a/src/java.desktop/share/classes/sun/font/GlyphLayout.java b/src/java.desktop/share/classes/sun/font/GlyphLayout.java index df42cce344e..5bff127f143 100644 --- a/src/java.desktop/share/classes/sun/font/GlyphLayout.java +++ b/src/java.desktop/share/classes/sun/font/GlyphLayout.java @@ -76,7 +76,7 @@ import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; import java.util.ArrayList; -import java.util.concurrent.ConcurrentHashMap; +import java.util.WeakHashMap; import static java.lang.Character.*; @@ -125,18 +125,12 @@ public final class GlyphLayout { } private static final class SDCache { - public Font key_font; - public FontRenderContext key_frc; - public AffineTransform dtx; public AffineTransform gtx; public Point2D.Float delta; public FontStrikeDesc sd; private SDCache(Font font, FontRenderContext frc) { - key_font = font; - key_frc = frc; - // !!! add getVectorTransform and hasVectorTransform to frc? then // we could just skip this work... @@ -177,7 +171,7 @@ public final class GlyphLayout { private static final Point2D.Float ZERO_DELTA = new Point2D.Float(); private static - SoftReference> cacheRef; + SoftReference> cacheRef; private static final class SDKey { private final Font font; @@ -232,7 +226,7 @@ public final class GlyphLayout { } SDKey key = new SDKey(font, frc); // garbage, yuck... - ConcurrentHashMap cache = null; + WeakHashMap cache = null; SDCache res = null; if (cacheRef != null) { cache = cacheRef.get(); @@ -243,13 +237,17 @@ public final class GlyphLayout { if (res == null) { res = new SDCache(font, frc); if (cache == null) { - cache = new ConcurrentHashMap(10); + cache = new WeakHashMap(10); cacheRef = new - SoftReference>(cache); - } else if (cache.size() >= 512) { - cache.clear(); + SoftReference>(cache); + } else if (cache.size() >= 128) { + synchronized (SDCache.class) { + cache.clear(); + } + } + synchronized (SDCache.class) { + cache.put(key, res); } - cache.put(key, res); } return res; } diff --git a/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java b/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java index 754af87b94e..32728efde6c 100644 --- a/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java +++ b/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1610,6 +1610,11 @@ public abstract class RasterPrinterJob extends PrinterJob { cancelDoc(); } + } catch (PrinterException pe) { + throw pe; + } catch (Throwable printError) { + throw (PrinterException) + new PrinterException().initCause(printError.getCause()); } finally { // reset previousPaper in case this job is invoked again. previousPaper = null; diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java index 1b3fb9a85e9..00ad60c8bb3 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.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 @@ -902,7 +902,7 @@ public abstract class WComponentPeer extends WObjectPeer */ void postEvent(AWTEvent event) { preprocessPostEvent(event); - WToolkit.postEvent(WToolkit.targetToAppContext(target), event); + WToolkit.postEvent(event); } void preprocessPostEvent(AWTEvent event) {} diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFrame.java b/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFrame.java index 9a74a1c25f7..0bd44d8ca85 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFrame.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFrame.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 @@ -238,8 +238,7 @@ public final class WEmbeddedFrame extends EmbeddedFrame { peer.emulateActivation(true); } }; - WToolkit.postEvent(WToolkit.targetToAppContext(this), - new InvocationEvent(this, r)); + WToolkit.postEvent(new InvocationEvent(this, r)); } } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java b/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java index a9237e21ff6..50ad23fabcd 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -602,7 +602,7 @@ final class WInputMethod extends InputMethodAdapter commitedTextLength, TextHitInfo.leading(caretPos), TextHitInfo.leading(visiblePos)); - WToolkit.postEvent(WToolkit.targetToAppContext(source), event); + WToolkit.postEvent(event); } public void inquireCandidatePosition() @@ -641,8 +641,7 @@ final class WInputMethod extends InputMethodAdapter openCandidateWindow(awtFocussedComponentPeer, x, y); } }; - WToolkit.postEvent(WToolkit.targetToAppContext(source), - new InvocationEvent(source, r)); + WToolkit.postEvent(new InvocationEvent(source, r)); } // java.awt.Toolkit#getNativeContainer() is not available diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WMenuItemPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WMenuItemPeer.java index 4abb9fa84ae..2594bd38a7d 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WMenuItemPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WMenuItemPeer.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 @@ -123,7 +123,7 @@ class WMenuItemPeer extends WObjectPeer implements MenuItemPeer { * Post an event. Queue it for execution by the callback thread. */ void postEvent(AWTEvent event) { - WToolkit.postEvent(WToolkit.targetToAppContext(target), event); + WToolkit.postEvent(event); } native void create(WMenuPeer parent); diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java b/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java index 0fdc4c6005b..4ed3e6b7e68 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.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 @@ -123,7 +123,6 @@ import javax.swing.text.JTextComponent; import sun.awt.AWTAccessor; import sun.awt.AWTAutoShutdown; -import sun.awt.AppContext; import sun.awt.DisplayChangedListener; import sun.awt.LightweightFrame; import sun.awt.SunToolkit; @@ -777,20 +776,7 @@ public final class WToolkit extends SunToolkit implements Runnable { ((DisplayChangedListener) lge).displayChanged(); } }; - if (AppContext.getAppContext() != null) { - // Common case, standalone application - EventQueue.invokeLater(runnable); - } else { - if (displayChangeExecutor == null) { - // No synchronization, called on the Toolkit thread only - displayChangeExecutor = Executors.newFixedThreadPool(1, r -> { - Thread t = Executors.defaultThreadFactory().newThread(r); - t.setDaemon(true); - return t; - }); - } - displayChangeExecutor.submit(runnable); - } + EventQueue.invokeLater(runnable); } /** @@ -910,17 +896,7 @@ public final class WToolkit extends SunToolkit implements Runnable { } updateXPStyleEnabled(props.get(XPSTYLE_THEME_ACTIVE)); - - if (AppContext.getAppContext() == null) { - // We cannot post the update to any EventQueue. Listeners will - // be called on EDTs by DesktopPropertyChangeSupport - updateProperties(props); - } else { - // Cannot update on Toolkit thread. - // DesktopPropertyChangeSupport will call listeners on Toolkit - // thread if it has AppContext (standalone mode) - EventQueue.invokeLater(() -> updateProperties(props)); - } + EventQueue.invokeLater(() -> updateProperties(props)); } private synchronized void updateProperties(final Map props) { diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WTrayIconPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WTrayIconPeer.java index 7cf92292a32..5c04803669d 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WTrayIconPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WTrayIconPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -181,7 +181,7 @@ final class WTrayIconPeer extends WObjectPeer implements TrayIconPeer { } void postEvent(AWTEvent event) { - WToolkit.postEvent(WToolkit.targetToAppContext(target), event); + WToolkit.postEvent(event); } native void create(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index 16f26c836f8..df5da5cb954 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -4421,12 +4421,13 @@ public class JavacParser implements Parser { JCMethodDecl methDef = (JCMethodDecl) def; if (methDef.name == names.init && methDef.params.isEmpty() && (methDef.mods.flags & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0) { ListBuffer tmpParams = new ListBuffer<>(); + TreeCopier copier = new TreeCopier<>(F); for (JCVariableDecl param : headerFields) { tmpParams.add(F.at(param) // we will get flags plus annotations from the record component .VarDef(F.Modifiers(Flags.PARAMETER | Flags.GENERATED_MEMBER | Flags.MANDATED | param.mods.flags & Flags.VARARGS, - param.mods.annotations), - param.name, param.vartype, null)); + copier.copy(param.mods.annotations)), + param.name, copier.copy(param.vartype), null)); } methDef.params = tmpParams.toList(); } @@ -5406,6 +5407,12 @@ public class JavacParser implements Parser { if (recordComponent) { mods = modifiersOpt(); + /* it could be that the user added a javadoc with the @deprecated tag, when analyzing this + * javadoc, javac will set the DEPRECATED flag. This is correct in most cases but not for + * record components and thus should be removed in that case. Any javadoc applied to + * record components is ignored + */ + mods.flags &= ~Flags.DEPRECATED; } else { mods = optFinal(Flags.PARAMETER | (lambdaParameter ? Flags.LAMBDA_PARAMETER : 0)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java index 519122b4d28..5e7c97dc56d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java @@ -724,7 +724,7 @@ public abstract class DoubleVector extends AbstractVector { @ForceInline final DoubleVector unaryMathOp(VectorOperators.Unary op) { - return VectorMathLibrary.unaryMathOp(op, opCode(op), species(), DoubleVector::unaryOperations, + return VectorMathLibrary.unaryMathOp(op, opCode(op), vspecies(), DoubleVector::unaryOperations, this); } @@ -851,7 +851,7 @@ public abstract class DoubleVector extends AbstractVector { @ForceInline final DoubleVector binaryMathOp(VectorOperators.Binary op, DoubleVector that) { - return VectorMathLibrary.binaryMathOp(op, opCode(op), species(), DoubleVector::binaryOperations, + return VectorMathLibrary.binaryMathOp(op, opCode(op), vspecies(), DoubleVector::binaryOperations, this, that); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java index 9950abf696d..5862a295fa3 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java @@ -724,7 +724,7 @@ public abstract class FloatVector extends AbstractVector { @ForceInline final FloatVector unaryMathOp(VectorOperators.Unary op) { - return VectorMathLibrary.unaryMathOp(op, opCode(op), species(), FloatVector::unaryOperations, + return VectorMathLibrary.unaryMathOp(op, opCode(op), vspecies(), FloatVector::unaryOperations, this); } @@ -851,7 +851,7 @@ public abstract class FloatVector extends AbstractVector { @ForceInline final FloatVector binaryMathOp(VectorOperators.Binary op, FloatVector that) { - return VectorMathLibrary.binaryMathOp(op, opCode(op), species(), FloatVector::binaryOperations, + return VectorMathLibrary.binaryMathOp(op, opCode(op), vspecies(), FloatVector::binaryOperations, this, that); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java index 4898cb70884..1c1cfcc78c7 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.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 @@ -283,7 +283,7 @@ import static jdk.internal.vm.vector.Utils.debug; @ForceInline /*package-private*/ static > - V unaryMathOp(Unary op, int opc, VectorSpecies vspecies, + V unaryMathOp(Unary op, int opc, AbstractSpecies vspecies, IntFunction> implSupplier, V v) { var entry = lookup(op, opc, vspecies, implSupplier); @@ -293,7 +293,7 @@ import static jdk.internal.vm.vector.Utils.debug; @SuppressWarnings({"unchecked"}) Class vt = (Class)vspecies.vectorType(); return VectorSupport.libraryUnaryOp( - entry.entry.address(), vt, vspecies.elementType(), vspecies.length(), entry.name, + entry.entry.address(), vt, vspecies.laneTypeOrdinal(), vspecies.length(), entry.name, v, entry.impl); } else { @@ -304,7 +304,7 @@ import static jdk.internal.vm.vector.Utils.debug; @ForceInline /*package-private*/ static > - V binaryMathOp(Binary op, int opc, VectorSpecies vspecies, + V binaryMathOp(Binary op, int opc, AbstractSpecies vspecies, IntFunction> implSupplier, V v1, V v2) { var entry = lookup(op, opc, vspecies, implSupplier); @@ -314,7 +314,7 @@ import static jdk.internal.vm.vector.Utils.debug; @SuppressWarnings({"unchecked"}) Class vt = (Class)vspecies.vectorType(); return VectorSupport.libraryBinaryOp( - entry.entry.address(), vt, vspecies.elementType(), vspecies.length(), entry.name, + entry.entry.address(), vt, vspecies.laneTypeOrdinal(), vspecies.length(), entry.name, v1, v2, entry.impl); } else { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ExternalSpecsWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ExternalSpecsWriter.java index 0115de5558f..b8767dd9913 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ExternalSpecsWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ExternalSpecsWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,7 +57,11 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.IndexItem; import jdk.javadoc.internal.html.Content; import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlAttr; +import jdk.javadoc.internal.html.HtmlId; +import jdk.javadoc.internal.html.HtmlTag; import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Script; import jdk.javadoc.internal.html.Text; import static java.util.stream.Collectors.groupingBy; @@ -108,6 +112,24 @@ public class ExternalSpecsWriter extends HtmlDocletWriter { HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING, contents.getContent("doclet.External_Specifications")))) .addMainContent(mainContent) + .addMainContent(new Script(""" + let select = document.getElementById('specs-by-domain'); + select.addEventListener("change", selectHost); + addEventListener("pageshow", selectHost); + function selectHost() { + const selectedClass = select.value ? "external-specs-tab" + select.value : "external-specs"; + let tabPanel = document.getElementById("external-specs.tabpanel"); + let count = 0; + tabPanel.querySelectorAll("div.external-specs").forEach(function(elem) { + elem.style.display = elem.classList.contains(selectedClass) ? "" : "none"; + if (elem.style.display === "") { + let isEvenRow = count++ % 4 < 2; + toggleStyle(elem.classList, isEvenRow, evenRowColor, oddRowColor); + } + }); + } + selectHost(); + """).asContent()) .setFooter(getFooter())); printHtmlDocument(null, "external specifications", body); @@ -180,7 +202,7 @@ public class ExternalSpecsWriter extends HtmlDocletWriter { boolean noHost = false; for (var searchIndexItems : searchIndexMap.values()) { try { - URI uri = getSpecURI(searchIndexItems.get(0)); + URI uri = getSpecURI(searchIndexItems.getFirst()); String host = uri.getHost(); if (host != null) { hostNamesSet.add(host); @@ -191,14 +213,19 @@ public class ExternalSpecsWriter extends HtmlDocletWriter { // ignore } } - var hostNamesList = new ArrayList<>(hostNamesSet); var table = new Table(HtmlStyles.summaryTable) .setCaption(contents.externalSpecifications) .setHeader(new TableHeader(contents.specificationLabel, contents.referencedIn)) .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast) - .setId(HtmlIds.EXTERNAL_SPECS); + .setId(HtmlIds.EXTERNAL_SPECS) + .setDefaultTab(contents.externalSpecifications) + .setRenderTabs(false); + + var hostNamesList = new ArrayList<>(hostNamesSet); + Content selector = Text.EMPTY; if ((hostNamesList.size() + (noHost ? 1 : 0)) > 1) { + selector = createHostSelect(hostNamesList, noHost); for (var host : hostNamesList) { table.addTab(Text.of(host), u -> host.equals(u.getHost())); } @@ -207,10 +234,9 @@ public class ExternalSpecsWriter extends HtmlDocletWriter { u -> u.getHost() == null); } } - table.setDefaultTab(Text.of(resources.getText("doclet.External_Specifications.All_Specifications"))); for (List searchIndexItems : searchIndexMap.values()) { - IndexItem ii = searchIndexItems.get(0); + IndexItem ii = searchIndexItems.getFirst(); Content specName = createSpecLink(ii); Content referencesList = HtmlTree.UL(HtmlStyles.refList, searchIndexItems, item -> HtmlTree.LI(createLink(item))); @@ -227,6 +253,7 @@ public class ExternalSpecsWriter extends HtmlDocletWriter { table.addRow(specName, references); } } + content.add(selector); content.add(table); } @@ -235,6 +262,29 @@ public class ExternalSpecsWriter extends HtmlDocletWriter { .collect(groupingBy(IndexItem::getLabel, () -> new TreeMap<>(getTitleComparator()), toList())); } + private Content createHostSelect(List hosts, boolean hasLocal) { + var index = 1; + var id = HtmlId.of("specs-by-domain"); + var specsByHost = resources.getText("doclet.External_Specifications.by-host"); + var select = HtmlTree.of(HtmlTag.SELECT) + .setId(id) + .add(HtmlTree.of(HtmlTag.OPTION) + .put(HtmlAttr.VALUE, "") + .add(Text.of(resources.getText("doclet.External_Specifications.all-hosts")))); + + for (var host : hosts) { + select.add(HtmlTree.of(HtmlTag.OPTION) + .put(HtmlAttr.VALUE, Integer.toString(index++)) + .add(Text.of(host))); + } + if (hasLocal) { + select.add(HtmlTree.of(HtmlTag.OPTION) + .put(HtmlAttr.VALUE, Integer.toString(index)) + .add(Text.of("Local"))); + } + return new ContentBuilder(HtmlTree.LABEL(id.name(), Text.of(specsByHost)), Text.of(" "), select); + } + Comparator getTitleComparator() { Collator collator = Collator.getInstance(); return (s1, s2) -> { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties index 4366295477b..1aba5c9862b 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties @@ -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 @@ -180,7 +180,8 @@ doclet.Inheritance_Tree=Inheritance Tree doclet.DefinedIn=Defined In doclet.ReferencedIn=Referenced In doclet.External_Specifications=External Specifications -doclet.External_Specifications.All_Specifications=All Specifications +doclet.External_Specifications.by-host=Show specifications by host name: +doclet.External_Specifications.all-hosts=All host names doclet.External_Specifications.no-host=Local doclet.Specification=Specification doclet.Summary_Page=Summary Page diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css index 6198df5c2f3..5bd14f7cf33 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css @@ -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. * * Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ @@ -1228,7 +1228,7 @@ input::placeholder { input:focus::placeholder { color: transparent; } -select#search-modules { +select { margin: 0 10px 10px 2px; font-size: var(--nav-font-size); padding: 3px 5px; 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 0598e6bc2a9..4cd4f386ad8 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java @@ -29,6 +29,8 @@ import static jdk.jpackage.internal.FromOptions.createPackageBuilder; import static jdk.jpackage.internal.MacPackagingPipeline.APPLICATION_LAYOUT; import static jdk.jpackage.internal.MacRuntimeValidator.validateRuntimeHasJliLib; 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; @@ -120,23 +122,39 @@ final class MacFromOptions { final Optional pkgSigningIdentityBuilder; - if (sign && (MAC_INSTALLER_SIGN_IDENTITY.containsIn(options) || MAC_SIGNING_KEY_NAME.containsIn(options))) { + if (!sign) { + pkgSigningIdentityBuilder = Optional.empty(); + } else if (hasAppImageSignIdentity(options) && !hasPkgInstallerSignIdentity(options)) { + // They explicitly request to sign the app image, + // but don't specify signing identity for signing the PKG package. + // They want signed app image inside of unsigned PKG. + pkgSigningIdentityBuilder = Optional.empty(); + } else { final var signingIdentityBuilder = createSigningIdentityBuilder(options); - MAC_INSTALLER_SIGN_IDENTITY.ifPresentIn(options, signingIdentityBuilder::signingIdentity); - MAC_SIGNING_KEY_NAME.findIn(options).ifPresent(userName -> { - final StandardCertificateSelector domain; - if (appStore) { - domain = StandardCertificateSelector.APP_STORE_PKG_INSTALLER; - } else { - domain = StandardCertificateSelector.PKG_INSTALLER; - } - signingIdentityBuilder.certificateSelector(StandardCertificateSelector.create(userName, domain)); - }); + MAC_INSTALLER_SIGN_IDENTITY.findIn(options).ifPresentOrElse( + signingIdentityBuilder::signingIdentity, + () -> { + MAC_SIGNING_KEY_NAME.findIn(options).or(() -> { + if (MAC_APP_IMAGE_SIGN_IDENTITY.findIn(options).isPresent()) { + return Optional.empty(); + } else { + return Optional.of(""); + } + }).ifPresent(userName -> { + final StandardCertificateSelector domain; + if (appStore) { + domain = StandardCertificateSelector.APP_STORE_PKG_INSTALLER; + } else { + domain = StandardCertificateSelector.PKG_INSTALLER; + } + + signingIdentityBuilder.certificateSelector(StandardCertificateSelector.create(userName, domain)); + }); + } + ); pkgSigningIdentityBuilder = Optional.of(signingIdentityBuilder); - } else { - pkgSigningIdentityBuilder = Optional.empty(); } ApplicationWithDetails app = null; @@ -230,33 +248,47 @@ final class MacFromOptions { MAC_BUNDLE_IDENTIFIER.ifPresentIn(options, appBuilder::bundleIdentifier); MAC_APP_CATEGORY.ifPresentIn(options, appBuilder::category); - final boolean sign; + final boolean sign = MAC_SIGN.getFrom(options); final boolean appStore; - if (PREDEFINED_APP_IMAGE.containsIn(options) && OptionUtils.bundlingOperation(options) != SIGN_MAC_APP_IMAGE) { + if (PREDEFINED_APP_IMAGE.containsIn(options)) { final var appImageFileOptions = superAppBuilder.externalApplication().orElseThrow().extra(); - sign = MAC_SIGN.getFrom(appImageFileOptions); appStore = MAC_APP_STORE.getFrom(appImageFileOptions); } else { - sign = MAC_SIGN.getFrom(options); appStore = MAC_APP_STORE.getFrom(options); } appBuilder.appStore(appStore); - if (sign && (MAC_APP_IMAGE_SIGN_IDENTITY.containsIn(options) || MAC_SIGNING_KEY_NAME.containsIn(options))) { - final var signingIdentityBuilder = createSigningIdentityBuilder(options); - MAC_APP_IMAGE_SIGN_IDENTITY.ifPresentIn(options, signingIdentityBuilder::signingIdentity); - MAC_SIGNING_KEY_NAME.findIn(options).ifPresent(userName -> { - final StandardCertificateSelector domain; - if (appStore) { - domain = StandardCertificateSelector.APP_STORE_APP_IMAGE; - } else { - domain = StandardCertificateSelector.APP_IMAGE; - } + final var signOnlyPkgInstaller = sign && ( + isBundlingOperation(options, CREATE_MAC_PKG) + && !hasAppImageSignIdentity(options) + && hasPkgInstallerSignIdentity(options)); - signingIdentityBuilder.certificateSelector(StandardCertificateSelector.create(userName, domain)); - }); + if (sign && !signOnlyPkgInstaller) { + final var signingIdentityBuilder = createSigningIdentityBuilder(options); + + MAC_APP_IMAGE_SIGN_IDENTITY.findIn(options).ifPresentOrElse( + signingIdentityBuilder::signingIdentity, + () -> { + MAC_SIGNING_KEY_NAME.findIn(options).or(() -> { + if (MAC_INSTALLER_SIGN_IDENTITY.containsIn(options)) { + return Optional.empty(); + } else { + return Optional.of(""); + } + }).ifPresent(userName -> { + final StandardCertificateSelector domain; + if (appStore) { + domain = StandardCertificateSelector.APP_STORE_APP_IMAGE; + } else { + domain = StandardCertificateSelector.APP_IMAGE; + } + + signingIdentityBuilder.certificateSelector(StandardCertificateSelector.create(userName, domain)); + }); + } + ); final var signingBuilder = new AppImageSigningConfigBuilder(signingIdentityBuilder); if (appStore) { @@ -331,4 +363,12 @@ final class MacFromOptions { return builder.create(fa); } + + private static boolean hasAppImageSignIdentity(Options options) { + return options.contains(MAC_SIGNING_KEY_NAME) || options.contains(MAC_APP_IMAGE_SIGN_IDENTITY); + } + + private static boolean hasPkgInstallerSignIdentity(Options options) { + return options.contains(MAC_SIGNING_KEY_NAME) || options.contains(MAC_INSTALLER_SIGN_IDENTITY); + } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionUtils.java index 97e1274f078..b39e67a9eb7 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,7 @@ import static jdk.jpackage.internal.cli.StandardOption.PREDEFINED_APP_IMAGE; import static jdk.jpackage.internal.cli.StandardOption.PREDEFINED_RUNTIME_IMAGE; import java.nio.file.Path; +import java.util.Objects; import jdk.jpackage.internal.cli.Options; import jdk.jpackage.internal.cli.StandardBundlingOperation; @@ -51,4 +52,8 @@ final class OptionUtils { static StandardBundlingOperation bundlingOperation(Options options) { return StandardBundlingOperation.valueOf(BUNDLING_OPERATION_DESCRIPTOR.getFrom(options)).orElseThrow(); } + + static boolean isBundlingOperation(Options options, StandardBundlingOperation op) { + return bundlingOperation(options).equals(Objects.requireNonNull(op)); + } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java index 4450a6168e2..eca82da99aa 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java @@ -109,7 +109,7 @@ public final class StandardOption { public static final OptionValue VERBOSE = auxilaryOption("verbose").create(); - public static final OptionValue TYPE = option("type", BundleType.class).addAliases("t") + static final OptionValue TYPE = option("type", BundleType.class).addAliases("t") .scope(StandardBundlingOperation.values()).inScope(NOT_BUILDING_APP_IMAGE) .converterExceptionFactory(ERROR_WITH_VALUE).converterExceptionFormatString("ERR_InvalidInstallerType") .converter(str -> { diff --git a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java index b3db11eb1fe..3223ff9dccd 100644 --- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java +++ b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java @@ -105,6 +105,9 @@ class ZipFileSystem extends FileSystem { private static final String COMPRESSION_METHOD_DEFLATED = "DEFLATED"; // Value specified for compressionMethod property to not compress Zip entries private static final String COMPRESSION_METHOD_STORED = "STORED"; + // CEN size is limited to the maximum array size in the JDK + // See ArraysSupport.SOFT_MAX_ARRAY_LENGTH; + private static final int MAX_CEN_SIZE = Integer.MAX_VALUE - 8; private final ZipFileSystemProvider provider; private final Path zfpath; @@ -1353,7 +1356,7 @@ class ZipFileSystem extends FileSystem { // to use the end64 values end.cenlen = cenlen64; end.cenoff = cenoff64; - end.centot = (int)centot64; // assume total < 2g + end.centot = centot64; end.endpos = end64pos; return end; } @@ -1575,23 +1578,34 @@ class ZipFileSystem extends FileSystem { buildNodeTree(); return null; // only END header present } - if (end.cenlen > end.endpos) - throw new ZipException("invalid END header (bad central directory size)"); + // Validate END header + if (end.cenlen > end.endpos) { + zerror("invalid END header (bad central directory size)"); + } long cenpos = end.endpos - end.cenlen; // position of CEN table - // Get position of first local file (LOC) header, taking into - // account that there may be a stub prefixed to the zip file. + // account that there may be a stub prefixed to the ZIP file. locpos = cenpos - end.cenoff; - if (locpos < 0) - throw new ZipException("invalid END header (bad central directory offset)"); + if (locpos < 0) { + zerror("invalid END header (bad central directory offset)"); + } + if (end.cenlen > MAX_CEN_SIZE) { + zerror("invalid END header (central directory size too large)"); + } + if (end.centot < 0 || end.centot > end.cenlen / CENHDR) { + zerror("invalid END header (total entries count too large)"); + } + // Validation ensures these are <= Integer.MAX_VALUE + int cenlen = Math.toIntExact(end.cenlen); + int centot = Math.toIntExact(end.centot); // read in the CEN - byte[] cen = new byte[(int)(end.cenlen)]; - if (readNBytesAt(cen, 0, cen.length, cenpos) != end.cenlen) { - throw new ZipException("read CEN tables failed"); + byte[] cen = new byte[cenlen]; + if (readNBytesAt(cen, 0, cen.length, cenpos) != cenlen) { + zerror("read CEN tables failed"); } // Iterate through the entries in the central directory - inodes = LinkedHashMap.newLinkedHashMap(end.centot + 1); + inodes = LinkedHashMap.newLinkedHashMap(centot + 1); int pos = 0; int limit = cen.length; while (pos < limit) { @@ -2666,7 +2680,7 @@ class ZipFileSystem extends FileSystem { // int disknum; // int sdisknum; // int endsub; - int centot; // 4 bytes + long centot; // 4 bytes long cenlen; // 4 bytes long cenoff; // 4 bytes // int comlen; // comment length @@ -2689,7 +2703,7 @@ class ZipFileSystem extends FileSystem { xoff = ZIP64_MINVAL; hasZip64 = true; } - int count = centot; + int count = Math.toIntExact(centot); if (count >= ZIP64_MINVAL32) { count = ZIP64_MINVAL32; hasZip64 = true; diff --git a/test/hotspot/gtest/utilities/test_globalDefinitions.cpp b/test/hotspot/gtest/utilities/test_globalDefinitions.cpp index f24d74ea529..6636efbba8e 100644 --- a/test/hotspot/gtest/utilities/test_globalDefinitions.cpp +++ b/test/hotspot/gtest/utilities/test_globalDefinitions.cpp @@ -321,3 +321,38 @@ TEST(globalDefinitions, jlong_from) { val = jlong_from(0xABCD, 0xEFEF); EXPECT_EQ(val, CONST64(0x0000ABCD0000EFEF)); } + +struct NoCopy { + int x; + NONCOPYABLE(NoCopy); +}; + +TEST(globalDefinitions, sizeof_auto) { + char x = 5; + char& y = x; + char* z = &x; + EXPECT_EQ(sizeof_auto(x), sizeof(x)); + EXPECT_EQ(sizeof_auto(y), sizeof(y)); + EXPECT_EQ(sizeof_auto(z), sizeof(z)); + + NoCopy nc{0}; + sizeof_auto(nc); + + static_assert(sizeof_auto(char[1LL]) == 1); + static_assert(sizeof_auto(char[std::numeric_limits::max() + 1LL]) == std::numeric_limits::max() + 1LL); + static_assert(sizeof_auto(char[std::numeric_limits::max() + 1LL]) == std::numeric_limits::max() + 1LL); +#if defined(_LP64) && !defined(_WINDOWS) + // char array sometimes limited to 2 gig length on 32 bit platforms (signed), disabled for Windows because of compiler error C2148. + static_assert(sizeof_auto(char[std::numeric_limits::max() + 1LL]) == std::numeric_limits::max() + 1LL); +#endif + + static_assert(sizeof(sizeof_auto(char[std::numeric_limits::max()])) == sizeof(uint8_t)); + static_assert(sizeof(sizeof_auto(char[std::numeric_limits::max() + 1LL])) == sizeof(uint16_t)); + static_assert(sizeof(sizeof_auto(char[std::numeric_limits::max()])) == sizeof(uint16_t)); + static_assert(sizeof(sizeof_auto(char[std::numeric_limits::max() + 1LL])) == sizeof(uint32_t)); +#if defined(_LP64) && !defined(_WINDOWS) + // char array sometimes limited to 2 gig length on 32 bit platforms (signed), disabled for Windows because of compiler error C2148. + static_assert(sizeof(sizeof_auto(char[std::numeric_limits::max()])) == sizeof(uint32_t)); + static_assert(sizeof(sizeof_auto(char[std::numeric_limits::max() + 1LL])) == sizeof(uint64_t)); +#endif +} diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java b/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java index d9a98582aec..3612f5ab3d3 100644 --- a/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,8 @@ import java.lang.reflect.Method; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import jtreg.SkippedException; /** * This is the entry-point for the Compile Framework. Its purpose it to allow @@ -132,10 +134,31 @@ public class CompileFramework { } catch (IllegalAccessException e) { throw new CompileFrameworkException("Illegal access:", e); } catch (InvocationTargetException e) { + // Rethrow jtreg.SkippedException so the tests are properly skipped. + // If we wrapped the SkippedException instead, it would get buried + // in the exception causes and cause a failed test instead. + findJtregSkippedExceptionInCauses(e).ifPresent(ex -> { throw ex; }); throw new CompileFrameworkException("Invocation target:", e); } } + private static Optional findJtregSkippedExceptionInCauses(Throwable ex) { + while (ex != null) { + // jtreg.SkippedException can be from a different classloader, comparing by name + if (ex.getClass().getName().equals(SkippedException.class.getName())) { + return Optional.of((RuntimeException) ex); + } + + if (ex.getCause() == ex) { + break; + } + + ex = ex.getCause(); + } + + return Optional.empty(); + } + private Method findMethod(String className, String methodName) { Class c = getClass(className); Method[] methods = c.getDeclaredMethods(); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index c31ce198644..3508c06ad0a 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -1488,6 +1488,12 @@ public class IRNode { beforeMatchingNameRegex(VECTOR_MASK_FIRST_TRUE, "VectorMaskFirstTrue"); } + // Can only be used if libjsvml or libsleef is available + public static final String CALL_LEAF_VECTOR = PREFIX + "CALL_LEAF_VECTOR" + POSTFIX; + static { + beforeMatchingNameRegex(CALL_LEAF_VECTOR, "CallLeafVector"); + } + // Can only be used if avx512_vnni is available. public static final String MUL_ADD_VS2VI_VNNI = PREFIX + "MUL_ADD_VS2VI_VNNI" + POSTFIX; static { diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java index 7399a1ec411..873533aaba8 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java @@ -514,7 +514,7 @@ public class TestCompatibleUseDefTypeSize { // Narrowing @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", ">0" }) public Object[] testIntToShort(int[] ints, short[] res) { @@ -527,7 +527,7 @@ public class TestCompatibleUseDefTypeSize { @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE + "min(max_int, max_char)", ">0" }) public Object[] testIntToChar(int[] ints, char[] res) { @@ -539,7 +539,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_I2B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", ">0" }) public Object[] testIntToByte(int[] ints, byte[] res) { @@ -551,7 +551,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_S2B, IRNode.VECTOR_SIZE + "min(max_short, max_byte)", ">0" }) public Object[] testShortToByte(short[] shorts, byte[] res) { @@ -575,7 +575,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_L2S, IRNode.VECTOR_SIZE + "min(max_long, max_short)", ">0" }) public Object[] testLongToShort(long[] longs, short[] res) { @@ -587,7 +587,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_L2S, IRNode.VECTOR_SIZE + "min(max_long, max_char)", ">0" }) public Object[] testLongToChar(long[] longs, char[] res) { @@ -599,7 +599,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_L2I, IRNode.VECTOR_SIZE + "min(max_long, max_int)", ">0" }) public Object[] testLongToInt(long[] longs, int[] res) { @@ -611,7 +611,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.STORE_VECTOR, ">0" }) public Object[] testShortToChar(short[] shorts, char[] res) { @@ -625,7 +625,7 @@ public class TestCompatibleUseDefTypeSize { // Widening @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_S2I, IRNode.VECTOR_SIZE + "min(max_short, max_int)", ">0" }) public Object[] testShortToInt(short[] shorts, int[] res) { @@ -637,7 +637,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_B2I, IRNode.VECTOR_SIZE + "min(max_byte, max_int)", ">0" }) public Object[] testByteToInt(byte[] bytes, int[] res) { @@ -649,7 +649,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_B2S, IRNode.VECTOR_SIZE + "min(max_byte, max_short)", ">0" }) public Object[] testByteToShort(byte[] bytes, short[] res) { @@ -661,7 +661,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_B2S, IRNode.VECTOR_SIZE + "min(max_byte, max_char)", ">0" }) public Object[] testByteToChar(byte[] bytes, char[] res) { @@ -685,7 +685,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_S2L, IRNode.VECTOR_SIZE + "min(max_short, max_long)", ">0" }) public Object[] testShortToLong(short[] shorts, long[] res) { @@ -697,7 +697,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_I2L, IRNode.VECTOR_SIZE + "min(max_int, max_long)", ">0" }) public Object[] testIntToLong(int[] ints, long[] res) { diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java index e0f44bcdb3b..9d674950499 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java @@ -458,7 +458,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -475,7 +475,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -492,7 +492,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -531,7 +531,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -548,7 +548,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -566,7 +566,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -583,7 +583,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -600,7 +600,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -639,7 +639,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -656,7 +656,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -674,7 +674,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -691,7 +691,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -708,7 +708,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -747,7 +747,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -764,7 +764,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1016,7 +1016,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1033,7 +1033,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1050,7 +1050,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1089,7 +1089,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1106,7 +1106,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1124,7 +1124,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1141,7 +1141,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1158,7 +1158,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1197,7 +1197,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1214,7 +1214,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1232,7 +1232,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1249,7 +1249,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1266,7 +1266,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1305,7 +1305,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1322,7 +1322,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorLibraryUnaryOpAndBinaryOp.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorLibraryUnaryOpAndBinaryOp.java new file mode 100644 index 00000000000..f7837e1abfa --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorLibraryUnaryOpAndBinaryOp.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA + * 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 compiler.lib.ir_framework.*; +import jdk.incubator.vector.*; + +/** + * @test + * @bug 8378312 + * @library /test/lib / + * @summary VectorAPI: libraryUnaryOp and libraryBinaryOp should be intrinsified. + * @modules jdk.incubator.vector + * + * @run driver compiler.vectorapi.TestVectorLibraryUnaryOpAndBinaryOp + */ + +public class TestVectorLibraryUnaryOpAndBinaryOp { + + @Test + @IR(counts = { IRNode.CALL_LEAF_VECTOR, "= 1" }, applyIfCPUFeatureOr = { "asimd", "true", "avx", "true" }) + public static void testUnary() { + var vec = FloatVector.broadcast(FloatVector.SPECIES_128, 3.14f); + vec.lanewise(VectorOperators.COS); + } + + @Test + @IR(counts = { IRNode.CALL_LEAF_VECTOR, "= 1" }, applyIfCPUFeatureOr = { "asimd", "true", "avx", "true" }) + public static void testBinary() { + var vec = FloatVector.broadcast(FloatVector.SPECIES_128, 2.0f); + vec.lanewise(VectorOperators.HYPOT, 1.0f); + } + + public static void main(String[] args) { + TestFramework testFramework = new TestFramework(); + testFramework.addFlags("--add-modules=jdk.incubator.vector") + .start(); + } + +} diff --git a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java index 29331cc1845..2f6296e41d2 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java @@ -74,7 +74,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupShortArray") public Object[] testShortLeadingZeros(short[] in) { short[] res = new short[SIZE]; @@ -100,7 +100,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupShortArray") public Object[] testShortTrailingZeros(short[] in) { short[] res = new short[SIZE]; @@ -126,7 +126,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupShortArray") public Object[] testShortReverse(short[] in) { short[] res = new short[SIZE]; @@ -152,7 +152,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupShortArray") public Object[] testShortBitCount(short[] in) { short[] res = new short[SIZE]; @@ -282,7 +282,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupByteArray") public Object[] testByteLeadingZeros(byte[] in) { byte[] res = new byte[SIZE]; @@ -308,7 +308,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupByteArray") public Object[] testByteTrailingZeros(byte[] in) { byte[] res = new byte[SIZE]; @@ -334,7 +334,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupByteArray") public Object[] testByteReverse(byte[] in) { byte[] res = new byte[SIZE]; @@ -411,7 +411,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupByteArray") public Object[] testByteBitCount(byte[] in) { byte[] res = new byte[SIZE]; diff --git a/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java b/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java index 96440ba15ae..2877e8a4a85 100644 --- a/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java +++ b/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java @@ -30,7 +30,7 @@ * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "ppc64" | os.arch == "ppc64le" * @requires vm.gc.Shenandoah * - * @run main/othervm --enable-native-access=ALL-UNNAMED -XX:+UnlockDiagnosticVMOptions + * @run main/native/othervm --enable-native-access=ALL-UNNAMED -XX:+UnlockDiagnosticVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive TestLinkToNativeRBP * */ diff --git a/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithCDS.java b/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithCDS.java index 7c0b5896a98..eb42bb9f93d 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithCDS.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithCDS.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 @@ -22,7 +22,7 @@ */ /* - * @test + * @test id=COH * @bug 8232069 * @requires vm.cds * @requires vm.bits == 64 @@ -31,7 +31,32 @@ * @requires vm.gc == null * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds * @compile test-classes/Hello.java - * @run driver TestZGCWithCDS + * @comment Only run if COH is unset or enabled. + * @requires vm.opt.UseCompactObjectHeaders != "false" + * @comment Driver sets compressed oops/class pointers, jtreg overrides will cause problems. + Only run the test if the flags are not set via the command line. + * @requires vm.opt.UseCompressedOops == null + * @requires vm.opt.UseCompressedClassPointers == null + * @run driver TestZGCWithCDS true + */ + +/* + * @test id=NO-COH + * @bug 8232069 + * @requires vm.cds + * @requires vm.bits == 64 + * @requires vm.gc.Z + * @requires vm.gc.Serial + * @requires vm.gc == null + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * @compile test-classes/Hello.java + * @comment Only run if COH is unset or disabled. + * @requires vm.opt.UseCompactObjectHeaders != "true" + * @comment Driver sets compressed oops/class pointers, jtreg overrides will cause problems. + Only run the test if the flags are not set via the command line. + * @requires vm.opt.UseCompressedOops == null + * @requires vm.opt.UseCompressedClassPointers == null + * @run driver TestZGCWithCDS false */ import jdk.test.lib.Platform; @@ -42,7 +67,8 @@ public class TestZGCWithCDS { public final static String UNABLE_TO_USE_ARCHIVE = "Unable to use shared archive."; public final static String ERR_MSG = "The saved state of UseCompressedOops and UseCompressedClassPointers is different from runtime, CDS will be disabled."; public static void main(String... args) throws Exception { - String compactHeaders = "-XX:+UseCompactObjectHeaders"; + boolean compactHeadersOn = Boolean.valueOf(args[0]); + String compactHeaders = "-XX:" + (compactHeadersOn ? "+" : "-") + "UseCompactObjectHeaders"; String helloJar = JarBuilder.build("hello", "Hello"); System.out.println("0. Dump with ZGC"); OutputAnalyzer out = TestCommon diff --git a/test/jdk/com/sun/jdi/EATests.java b/test/jdk/com/sun/jdi/EATests.java index cb51e91021b..3a936f288cc 100644 --- a/test/jdk/com/sun/jdi/EATests.java +++ b/test/jdk/com/sun/jdi/EATests.java @@ -84,6 +84,15 @@ * @comment Regression test for using the wrong thread when logging during re-locking from deoptimization. * * @comment DiagnoseSyncOnValueBasedClasses=2 will cause logging when locking on \@ValueBased objects. + * @run driver EATests + * -XX:+UnlockDiagnosticVMOptions + * -Xms256m -Xmx256m + * -Xbootclasspath/a:. + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+WhiteBoxAPI + * -Xbatch + * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks + * -XX:DiagnoseSyncOnValueBasedClasses=2 * * @comment Re-lock may inflate monitors when re-locking, which cause monitorinflation trace logging. * @run driver EATests diff --git a/test/jdk/java/awt/Dialog/CloseDialog/CloseDialogTest.java b/test/jdk/java/awt/Dialog/CloseDialog/CloseDialogTest.java deleted file mode 100644 index 1201e5c835c..00000000000 --- a/test/jdk/java/awt/Dialog/CloseDialog/CloseDialogTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2014, 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. - */ - -import java.awt.Dialog; -import java.awt.Frame; -import java.io.*; -import javax.swing.*; -import sun.awt.SunToolkit; -import java.util.concurrent.atomic.AtomicReference; - -/** - * @test - * @key headful - * @bug 8043705 - * @summary Can't exit color chooser dialog when running in non-default AppContext - * @modules java.desktop/sun.awt - * @run main CloseDialogTest - */ - -public class CloseDialogTest { - - private static volatile Frame frame; - private static volatile Dialog dialog; - private static volatile InputStream testErrorStream; - private static final PrintStream systemErrStream = System.err; - private static final AtomicReference caughtException - = new AtomicReference<>(); - - public static void main(String[] args) throws Exception { - - // redirect System err - PipedOutputStream errorOutputStream = new PipedOutputStream(); - testErrorStream = new PipedInputStream(errorOutputStream); - System.setErr(new PrintStream(errorOutputStream)); - - ThreadGroup swingTG = new ThreadGroup(getRootThreadGroup(), "SwingTG"); - try { - new Thread(swingTG, () -> { - SunToolkit.createNewAppContext(); - SwingUtilities.invokeLater(() -> { - frame = new Frame(); - frame.setSize(300, 300); - frame.setVisible(true); - - dialog = new Dialog(frame); - dialog.setSize(200, 200); - dialog.setModal(true); - dialog.setVisible(true); - }); - }).start(); - - Thread.sleep(400); - - Thread disposeThread = new Thread(swingTG, () -> - SwingUtilities.invokeLater(() -> { - try { - while (dialog == null || !dialog.isVisible()) { - Thread.sleep(100); - } - dialog.setVisible(false); - dialog.dispose(); - frame.dispose(); - } catch (Exception e) { - caughtException.set(e); - } - })); - disposeThread.start(); - disposeThread.join(); - Thread.sleep(500); - - // read System err - final char[] buffer = new char[2048]; - System.err.print("END"); - System.setErr(systemErrStream); - try (Reader in = new InputStreamReader(testErrorStream, "UTF-8")) { - int size = in.read(buffer, 0, buffer.length); - String errorString = new String(buffer, 0, size); - if (!errorString.startsWith("END")) { - System.err.println(errorString. - substring(0, errorString.length() - 4)); - throw new RuntimeException("Error output is not empty!"); - } - } - } finally { - if (caughtException.get() != null) { - throw new RuntimeException("Failed. Caught exception!", - caughtException.get()); - } - } - } - - private static ThreadGroup getRootThreadGroup() { - ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); - while (threadGroup.getParent() != null) { - threadGroup = threadGroup.getParent(); - } - return threadGroup; - } -} diff --git a/test/jdk/java/awt/Toolkit/DisplayChangesException/DisplayChangesException.java b/test/jdk/java/awt/Toolkit/DisplayChangesException/DisplayChangesException.java deleted file mode 100644 index 0f13265ac58..00000000000 --- a/test/jdk/java/awt/Toolkit/DisplayChangesException/DisplayChangesException.java +++ /dev/null @@ -1,133 +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.EventQueue; -import java.awt.GraphicsEnvironment; -import java.awt.Toolkit; -import java.lang.reflect.Method; -import java.util.concurrent.CountDownLatch; - -import javax.swing.JButton; -import javax.swing.JFrame; - -import sun.awt.DisplayChangedListener; -import sun.awt.SunToolkit; - -/** - * @test - * @key headful - * @bug 8207070 - * @modules java.desktop/sun.java2d - * java.desktop/sun.awt - * java.desktop/sun.awt.windows:open - */ -public final class DisplayChangesException { - - private static boolean fail; - private static CountDownLatch go = new CountDownLatch(1); - - static final class TestThread extends Thread { - - private JFrame frame; - - private TestThread(ThreadGroup tg, String threadName) { - super(tg, threadName); - } - - public void run() { - try { - test(); - } catch (final Exception e) { - throw new RuntimeException(e); - } - } - - private void test() throws Exception { - SunToolkit.createNewAppContext(); - EventQueue.invokeAndWait(() -> { - frame = new JFrame(); - final JButton b = new JButton(); - b.addPropertyChangeListener(evt -> { - if (!SunToolkit.isDispatchThreadForAppContext(b)) { - System.err.println("Wrong thread:" + currentThread()); - fail = true; - } - }); - frame.add(b); - frame.setSize(100, 100); - frame.setLocationRelativeTo(null); - frame.pack(); - }); - go.await(); - EventQueue.invokeAndWait(() -> { - frame.dispose(); - }); - } - } - - public static void main(final String[] args) throws Exception { - ThreadGroup tg0 = new ThreadGroup("ThreadGroup0"); - ThreadGroup tg1 = new ThreadGroup("ThreadGroup1"); - - TestThread t0 = new TestThread(tg0, "TestThread 0"); - TestThread t1 = new TestThread(tg1, "TestThread 1"); - - t0.start(); - t1.start(); - Thread.sleep(1500); // Cannot use Robot.waitForIdle - testToolkit(); - Thread.sleep(1500); - testGE(); - Thread.sleep(1500); - go.countDown(); - - if (fail) { - throw new RuntimeException(); - } - } - - private static void testGE() { - Object ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - if (!(ge instanceof DisplayChangedListener)) { - return; - } - ((DisplayChangedListener) ge).displayChanged(); - } - - private static void testToolkit() { - final Class toolkit; - try { - toolkit = Class.forName("sun.awt.windows.WToolkit"); - } catch (final ClassNotFoundException ignored) { - return; - } - try { - final Method displayChanged = toolkit.getMethod("displayChanged"); - displayChanged.invoke(Toolkit.getDefaultToolkit()); - } catch (final Exception e) { - e.printStackTrace(); - fail = true; - } - } -} - diff --git a/test/jdk/java/awt/font/GlyphVector/GlyphMetricsRotatedFontTest.java b/test/jdk/java/awt/font/GlyphVector/GlyphMetricsRotatedFontTest.java new file mode 100644 index 00000000000..eb00fd4f34d --- /dev/null +++ b/test/jdk/java/awt/font/GlyphVector/GlyphMetricsRotatedFontTest.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. + */ + +/* + * @test + * @bug 8148334 8377937 + * @summary Checks behavior of GlyphVector.getGlyphMetrics(int) with rotated fonts. + */ + +import java.awt.Font; +import java.awt.GraphicsEnvironment; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphMetrics; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; + +public class GlyphMetricsRotatedFontTest { + + public static void main(String[] args) { + + String text = "The quick brown \r\n fox JUMPS over \t the lazy dog."; + FontRenderContext frc = new FontRenderContext(null, true, true); + + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + String[] names = ge.getAvailableFontFamilyNames(); + + for (String name : names) { + + Font normal = new Font(name, Font.PLAIN, 60); + if (normal.canDisplayUpTo("AZaz09") != -1) { + continue; + } + + double theta = 0.5; // about 30 degrees + AffineTransform tx = AffineTransform.getRotateInstance(theta); + Font rotated = normal.deriveFont(tx); + + GlyphVector gv1 = normal.createGlyphVector(frc, text); + GlyphVector gv2 = rotated.createGlyphVector(frc, text); + + for (int i = 0; i < gv1.getNumGlyphs(); i++) { + GlyphMetrics gm1 = gv1.getGlyphMetrics(i); + GlyphMetrics gm2 = gv2.getGlyphMetrics(i); + assertEqual(0, gm1.getAdvanceY(), 0, name + " normal y", i); + double expectedX = Math.cos(theta) * gm1.getAdvanceX(); + double expectedY = Math.sin(theta) * gm1.getAdvanceX(); + assertEqual(expectedX, gm2.getAdvanceX(), 1, name + " rotated x", i); + assertEqual(expectedY, gm2.getAdvanceY(), 1, name + " rotated y", i); + } + } + } + + private static void assertEqual(double d1, double d2, double variance, + String scenario, int index) { + if (Math.abs(d1 - d2) > variance) { + String msg = String.format("%s for index %d: %f != %f", scenario, index, d1, d2); + throw new RuntimeException(msg); + } + } +} diff --git a/test/jdk/java/awt/print/PrinterJob/ExceptionFromPrintableIsIgnoredTest.java b/test/jdk/java/awt/print/PrinterJob/ExceptionFromPrintableIsIgnoredTest.java index a62ae536e1b..4c2ff370cb8 100644 --- a/test/jdk/java/awt/print/PrinterJob/ExceptionFromPrintableIsIgnoredTest.java +++ b/test/jdk/java/awt/print/PrinterJob/ExceptionFromPrintableIsIgnoredTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,15 +21,16 @@ * questions. */ -/* @test - @bug 8262731 - @key headful printer - @summary Verify that "PrinterJob.print" throws the expected exception, - if "Printable.print" throws an exception. - @run main ExceptionFromPrintableIsIgnoredTest MAIN PE - @run main ExceptionFromPrintableIsIgnoredTest MAIN RE - @run main ExceptionFromPrintableIsIgnoredTest EDT PE - @run main ExceptionFromPrintableIsIgnoredTest EDT RE +/* + * @test + * @bug 8262731 8268675 + * @key printer + * @summary Verify that "PrinterJob.print" throws the expected exception, + * if "Printable.print" throws an exception. + * @run main ExceptionFromPrintableIsIgnoredTest MAIN PE + * @run main ExceptionFromPrintableIsIgnoredTest MAIN RE + * @run main ExceptionFromPrintableIsIgnoredTest EDT PE + * @run main ExceptionFromPrintableIsIgnoredTest EDT RE */ import java.awt.Graphics; @@ -64,14 +65,6 @@ public class ExceptionFromPrintableIsIgnoredTest { "Test started. threadType='%s', exceptionType='%s'", threadType, exceptionType)); - String osName = System.getProperty("os.name"); - boolean isOSX = osName.toLowerCase().startsWith("mac"); - if ((exceptionType == TestExceptionType.RE) && !isOSX) { - System.out.println( - "Currently this test scenario can be verified only on macOS."); - return; - } - printError = null; if (threadType == TestThreadType.MAIN) { diff --git a/test/jdk/java/lang/management/RuntimeMXBean/InputArgument.java b/test/jdk/java/lang/management/RuntimeMXBean/InputArgument.java index 66c3d3f41f5..0e7609b86ed 100644 --- a/test/jdk/java/lang/management/RuntimeMXBean/InputArgument.java +++ b/test/jdk/java/lang/management/RuntimeMXBean/InputArgument.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, 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 @@ -62,41 +62,60 @@ * @run main/othervm -Dprops=something InputArgument -Dprops=something */ +/* + * @test + * @bug 8378110 + * @summary RuntimeMXBean.getInputArguments() handling of flags from settings file. + * + * @run driver InputArgument generateFlagsFile + * @run main/othervm -XX:+UseFastJNIAccessors -XX:Flags=InputArgument.flags InputArgument + * -XX:+UseFastJNIAccessors -XX:-UseG1GC -XX:+UseParallelGC -XX:MaxHeapSize=100M + */ + import java.lang.management.*; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.List; import java.util.ListIterator; public class InputArgument { private static RuntimeMXBean rm = ManagementFactory.getRuntimeMXBean(); - private static String vmOption = null; public static void main(String args[]) throws Exception { - // set the expected input argument - if (args.length > 0) { - vmOption = args[0]; - } - - List options = rm.getInputArguments(); - if (vmOption == null) { + if (args.length == 1 && "generateFlagsFile".equals(args[0])) { + generateFlagsFile(); return; } - boolean testFailed = true; + String[] vmOptions = args; + List options = rm.getInputArguments(); System.out.println("Number of arguments = " + options.size()); int i = 0; for (String arg : options) { System.out.println("Input arg " + i + " = " + arg); i++; - if (arg.equals(vmOption)) { - testFailed = false; - break; - } } - if (testFailed) { - throw new RuntimeException("TEST FAILED: " + - "VM option " + vmOption + " not found"); + + for (String expected : vmOptions) { + boolean found = false; + for (String arg : options) { + if (arg.equals(expected)) { + found = true; + break; + } + } + if (!found) { + throw new RuntimeException("TEST FAILED: " + + "VM option " + expected + " not found"); + } } System.out.println("Test passed."); } + + private static void generateFlagsFile() throws Exception { + // 3 types of flag; both boolean cases and 1 numeric + Files.writeString(Paths.get("", "InputArgument.flags"), + String.format("-UseG1GC%n+UseParallelGC%nMaxHeapSize=100M%n")); + } } diff --git a/test/jdk/java/lang/runtime/ObjectMethodsTest.java b/test/jdk/java/lang/runtime/ObjectMethodsTest.java index 951d3b68383..d7ca5912273 100644 --- a/test/jdk/java/lang/runtime/ObjectMethodsTest.java +++ b/test/jdk/java/lang/runtime/ObjectMethodsTest.java @@ -36,11 +36,11 @@ import java.lang.invoke.MethodType; import java.lang.runtime.ObjectMethods; import static java.lang.invoke.MethodType.methodType; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.*; public class ObjectMethodsTest { @@ -144,8 +144,8 @@ public class ObjectMethodsTest { assertEquals("Empty[]", (String)handle.invokeExact(new Empty())); } - Class NPE = NullPointerException.class; - Class IAE = IllegalArgumentException.class; + private static final Class NPE = NullPointerException.class; + private static final Class IAE = IllegalArgumentException.class; @Test public void exceptions() { @@ -157,25 +157,60 @@ public class ObjectMethodsTest { assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", C.EQUALS_DESC, C.class, "x;y", C.ACCESSORS)); assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "hashCode", C.TO_STRING_DESC, C.class, "x;y", C.ACCESSORS)); assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "equals", C.HASHCODE_DESC, C.class, "x;y", C.ACCESSORS)); + } - record NamePlusType(String mn, MethodType mt) {} - List namePlusTypeList = List.of( + record NamePlusType(String name, MethodType type) {} + + static List namePlusTypeList() { + return List.of( new NamePlusType("toString", C.TO_STRING_DESC), new NamePlusType("equals", C.EQUALS_DESC), new NamePlusType("hashCode", C.HASHCODE_DESC) ); - - for (NamePlusType npt : namePlusTypeList) { - assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), C.class, "x;y", null)); - assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), C.class, "x;y", new MethodHandle[]{null})); - assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), C.class, null, C.ACCESSORS)); - assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), null, "x;y", C.ACCESSORS)); - assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), null, C.class, "x;y", C.ACCESSORS)); - assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, null, npt.mt(), C.class, "x;y", C.ACCESSORS)); - assertThrows(NPE, () -> ObjectMethods.bootstrap(null, npt.mn(), npt.mt(), C.class, "x;y", C.ACCESSORS)); - } } + @MethodSource("namePlusTypeList") + @ParameterizedTest + void commonExceptions(NamePlusType npt) { + String name = npt.name(); + MethodType type = npt.type(); + + // Null checks + assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, "x;y", null)); + assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, "x;y", new MethodHandle[]{null})); + assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, null, C.ACCESSORS)); + assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, null, "x;y", C.ACCESSORS)); + assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, name, null, C.class, "x;y", C.ACCESSORS)); + assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, null, type, C.class, "x;y", C.ACCESSORS)); + assertThrows(NPE, () -> ObjectMethods.bootstrap(null, name, type, C.class, "x;y", C.ACCESSORS)); + + // Bad indy call receiver type - change C to this test class + assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type.changeParameterType(0, this.getClass()), C.class, "x;y", C.ACCESSORS)); + + // Bad getter types + var wrongReceiverGetter = assertDoesNotThrow(() -> MethodHandles.lookup().findGetter(this.getClass(), "y", int.class)); + assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, "x;y", + new MethodHandle[]{ + C.ACCESSORS[0], + wrongReceiverGetter, + })); + var extraArgGetter = MethodHandles.dropArguments(C.ACCESSORS[1], 1, int.class); + assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, "x;y", + new MethodHandle[]{ + C.ACCESSORS[0], + extraArgGetter, + })); + var voidReturnGetter = MethodHandles.empty(MethodType.methodType(void.class, C.class)); + assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, "x;y", + new MethodHandle[]{ + C.ACCESSORS[0], + voidReturnGetter, + })); + } + + // same field name and type as C::y, for wrongReceiverGetter + private int y; + // Based on the ObjectMethods internal implementation private static int hashCombiner(int x, int y) { return x*31 + y; diff --git a/test/jdk/java/net/URLClassLoader/HttpTest.java b/test/jdk/java/net/URLClassLoader/HttpTest.java index 620eb34dbaa..09ee6dcfa85 100644 --- a/test/jdk/java/net/URLClassLoader/HttpTest.java +++ b/test/jdk/java/net/URLClassLoader/HttpTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2019, 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 @@ -24,233 +24,298 @@ /** * @test * @bug 4636331 + * @modules jdk.httpserver * @library /test/lib - * @summary Check that URLClassLoader doesn't create excessive http - * connections + * @summary Check that URLClassLoader with HTTP paths lookups produce the expected http requests + * @run junit HttpTest */ import java.net.*; import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; import jdk.test.lib.net.URIBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class HttpTest { - /* - * Simple http server to service http requests. Auto shutdown - * if "idle" (no requests) for 10 seconds. Forks worker thread - * to service persistent connections. Work threads shutdown if - * "idle" for 5 seconds. - */ - static class HttpServer implements Runnable { + // HTTP server used to track requests + static HttpServer server; - private static HttpServer svr = null; - private static Counters cnts = null; - private static ServerSocket ss; + // RequestLog for capturing requests + static class RequestLog { + List log = new ArrayList<>(); - private static Object counterLock = new Object(); - private static int getCount = 0; - private static int headCount = 0; - - class Worker extends Thread { - Socket s; - Worker(Socket s) { - this.s = s; - } - - public void run() { - InputStream in = null; - try { - in = s.getInputStream(); - for (;;) { - - // read entire request from client - byte b[] = new byte[1024]; - int n, total=0; - - // max 5 seconds to wait for new request - s.setSoTimeout(5000); - try { - do { - n = in.read(b, total, b.length-total); - // max 0.5 seconds between each segment - // of request. - s.setSoTimeout(500); - if (n > 0) total += n; - } while (n > 0); - } catch (SocketTimeoutException e) { } - - if (total == 0) { - s.close(); - return; - } - - boolean getRequest = false; - if (b[0] == 'G' && b[1] == 'E' && b[2] == 'T') - getRequest = true; - - synchronized (counterLock) { - if (getRequest) - getCount++; - else - headCount++; - } - - // response to client - PrintStream out = new PrintStream( - new BufferedOutputStream( - s.getOutputStream() )); - out.print("HTTP/1.1 200 OK\r\n"); - - out.print("Content-Length: 75000\r\n"); - out.print("\r\n"); - if (getRequest) { - for (int i=0; i<75*1000; i++) { - out.write( (byte)'.' ); - } - } - out.flush(); - - } // for - - } catch (Exception e) { - unexpected(e); - } finally { - if (in != null) { try {in.close(); } catch(IOException e) {unexpected(e);} } - } - } + // Add a request to the log + public synchronized void capture(String method, URI uri) { + log.add(new Request(method, uri)); } - HttpServer() throws Exception { - ss = new ServerSocket(); - ss.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); + // Clear requests + public synchronized void clear() { + log.clear(); } - public void run() { - try { - // shutdown if no request in 10 seconds. - ss.setSoTimeout(10000); - for (;;) { - Socket s = ss.accept(); - (new Worker(s)).start(); - } - } catch (Exception e) { - } + public synchronized List requests() { + return List.copyOf(log); } - - void unexpected(Exception e) { - System.out.println(e); - e.printStackTrace(); - } - - public static HttpServer create() throws Exception { - if (svr != null) - return svr; - cnts = new Counters(); - svr = new HttpServer(); - (new Thread(svr)).start(); - return svr; - } - - public static void shutdown() throws Exception { - if (svr != null) { - ss.close(); - svr = null; - } - } - - public int port() { - return ss.getLocalPort(); - } - - public static class Counters { - public void reset() { - synchronized (counterLock) { - getCount = 0; - headCount = 0; - } - } - - public int getCount() { - synchronized (counterLock) { - return getCount; - } - } - - public int headCount() { - synchronized (counterLock) { - return headCount; - } - } - - public String toString() { - synchronized (counterLock) { - return "GET count: " + getCount + "; " + - "HEAD count: " + headCount; - } - } - } - - public Counters counters() { - return cnts; - } - } - public static void main(String args[]) throws Exception { - boolean failed = false; + // Represents a single request + record Request(String method, URI path) {} - // create http server - HttpServer svr = HttpServer.create(); + // Request log for this test + static RequestLog log = new RequestLog(); - // create class loader - URL urls[] = { - URIBuilder.newBuilder().scheme("http").loopback().port(svr.port()) - .path("/dir1/").toURL(), - URIBuilder.newBuilder().scheme("http").loopback().port(svr.port()) - .path("/dir2/").toURL(), + // Handlers specific to tests + static Map handlers = new ConcurrentHashMap<>(); + + // URLClassLoader with HTTP URL class path + private static URLClassLoader loader; + + @BeforeAll + static void setup() throws Exception { + server = HttpServer.create(); + server.bind(new InetSocketAddress( + InetAddress.getLoopbackAddress(), 0), 0); + server.createContext("/", e -> { + // Capture request in the log + log.capture(e.getRequestMethod(), e.getRequestURI()); + // Check for custom handler + HttpHandler custom = handlers.get(e.getRequestURI()); + if (custom != null) { + custom.handle(e); + } else { + // Successful responses echo the request path in the body + byte[] response = e.getRequestURI().getPath() + .getBytes(StandardCharsets.UTF_8); + e.sendResponseHeaders(200, response.length); + try (var out = e.getResponseBody()) { + out.write(response); + } + } + e.close(); + }); + server.start(); + int port = server.getAddress().getPort(); + + // Create class loader with two HTTP URLs + URL[] searchPath = new URL[] { + getHttpUri("/dir1/", port), + getHttpUri("/dir2/", port) }; - URLClassLoader cl = new URLClassLoader(urls); + loader = new URLClassLoader(searchPath); + } - // Test 1 - check that getResource does single HEAD request - svr.counters().reset(); - URL url = cl.getResource("foo.gif"); - System.out.println(svr.counters()); + // Create an HTTP URL for the given path and port using the loopback address + private static URL getHttpUri(String path, int port) throws Exception { + return URIBuilder.newBuilder() + .scheme("http") + .loopback() + .port(port) + .path(path).toURL(); + } - if (svr.counters().getCount() > 0 || - svr.counters().headCount() > 1) { - failed = true; + // Add redirect handler for a given path + private static void redirect(String path, String target) { + handlers.put(URI.create(path), e -> { + e.getResponseHeaders().set("Location", target); + e.sendResponseHeaders(301, 0); + }); + } + + // Return 404 not found for a given path + private static void notFound(String path) { + handlers.put(URI.create(path), e -> + e.sendResponseHeaders(404, 0)); + } + + @AfterAll + static void shutdown() { + server.stop(2000); + } + + @BeforeEach + void reset() { + synchronized (log) { + log.clear(); + } + handlers.clear(); + } + + // Check that getResource does single HEAD request + @Test + void getResourceSingleHead() { + URL url = loader.getResource("foo.gif"); + // Expect one HEAD + assertRequests(e -> e + .request("HEAD", "/dir1/foo.gif") + ); + } + + // Check that getResource follows redirects + @Test + void getResourceShouldFollowRedirect() { + redirect("/dir1/foo.gif", "/dir1/target.gif"); + URL url = loader.getResource("foo.gif"); + // Expect extra HEAD for redirect target + assertRequests(e -> e + .request("HEAD", "/dir1/foo.gif") + .request("HEAD", "/dir1/target.gif") + ); + + /* + * Note: Long-standing behavior is that URLClassLoader:getResource + * returns a URL for the requested resource, not the location redirected to + */ + assertEquals("/dir1/foo.gif", url.getPath()); + + } + + // Check that getResource treats a redirect to a not-found resource as a not-found resource + @Test + void getResourceRedirectTargetNotFound() { + redirect("/dir1/foo.gif", "/dir1/target.gif"); + notFound("/dir1/target.gif"); + URL url = loader.getResource("foo.gif"); + // Expect extra HEAD for redirect target and next URL in search path + assertRequests(e -> e + .request("HEAD", "/dir1/foo.gif") + .request("HEAD", "/dir1/target.gif") + .request("HEAD", "/dir2/foo.gif") + + ); + // Should find URL for /dir2 + assertEquals("/dir2/foo.gif", url.getPath()); + } + + // Check that getResourceAsStream does one HEAD and one GET request + @Test + void getResourceAsStreamSingleGet() throws IOException { + // Expect content from the first path + try (var in = loader.getResourceAsStream("foo2.gif")) { + assertEquals("/dir1/foo2.gif", + new String(in.readAllBytes(), StandardCharsets.UTF_8)); + } + // Expect one HEAD, one GET + assertRequests( e -> e + .request("HEAD", "/dir1/foo2.gif") + .request("GET", "/dir1/foo2.gif") + ); + } + + // Check that getResourceAsStream follows redirects + @Test + void getResourceAsStreamFollowRedirect() throws IOException { + redirect("/dir1/foo.gif", "/dir1/target.gif"); + // Expect content from the redirected location + try (var in = loader.getResourceAsStream("foo.gif")) { + assertEquals("/dir1/target.gif", + new String(in.readAllBytes(), StandardCharsets.UTF_8)); } - // Test 2 - check that getResourceAsStream does at most - // one GET request - svr.counters().reset(); - InputStream in = cl.getResourceAsStream("foo2.gif"); - in.close(); - System.out.println(svr.counters()); - if (svr.counters().getCount() > 1) { - failed = true; + /* + * Note: Long standing behavior of URLClassLoader::getResourceAsStream + * is to use HEAD during the findResource resource discovery and to not + * "remember" the HEAD redirect location when performing the GET. This + * explains why we observe two redirects here, one for HEAD, one for GET. + */ + assertRequests( e -> e + .request("HEAD", "/dir1/foo.gif") + .request("HEAD", "/dir1/target.gif") + .request("GET", "/dir1/foo.gif") + .request("GET", "/dir1/target.gif") + ); + } + + // getResourceAsStream on a 404 should try next path + @Test + void getResourceTryNextPath() throws IOException { + // Make the first path return 404 + notFound("/dir1/foo.gif"); + // Expect content from the second path + try (var in = loader.getResourceAsStream("foo.gif")) { + assertEquals("/dir2/foo.gif", + new String(in.readAllBytes(), StandardCharsets.UTF_8)); } + // Expect two HEADs, one GET + assertRequests(e -> e + .request("HEAD", "/dir1/foo.gif") + .request("HEAD", "/dir2/foo.gif") + .request("GET", "/dir2/foo.gif") + ); + } - // Test 3 - check that getResources only does HEAD requests - svr.counters().reset(); - Enumeration e = cl.getResources("foos.gif"); - try { - for (;;) { - e.nextElement(); - } - } catch (NoSuchElementException exc) { } - System.out.println(svr.counters()); - if (svr.counters().getCount() > 1) { - failed = true; - } + // Check that getResources only does HEAD requests + @Test + void getResourcesOnlyHead() throws IOException { + Collections.list(loader.getResources("foos.gif")); + // Expect one HEAD for each path + assertRequests(e -> e + .request("HEAD", "/dir1/foos.gif") + .request("HEAD", "/dir2/foos.gif") + ); + } - // shutdown http server - svr.shutdown(); + // Check that getResources skips 404 URL + @Test + void getResourcesShouldSkipFailedHead() throws IOException { + // Make first path fail with 404 + notFound("/dir1/foos.gif"); + List resources = Collections.list(loader.getResources("foos.gif")); + // Expect one HEAD for each path + assertRequests(e -> e + .request("HEAD", "/dir1/foos.gif") + .request("HEAD", "/dir2/foos.gif") + ); - if (failed) { - throw new Exception("Excessive http connections established - Test failed"); + // Expect a single URL to be returned + assertEquals(1, resources.size()); + } + + // Utils for asserting requests + static class Expect { + List requests = new ArrayList<>(); + + Expect request(String method, String path) { + requests.add(new Request(method, URI.create(path))); + return this; } } + static void assertRequests(Consumer e) { + // Collect expected requests + Expect exp = new Expect(); + e.accept(exp); + List expected = exp.requests; + + // Actual requests + List requests = log.requests(); + + // Verify expected number of requests + assertEquals(expected.size(), requests.size(), "Unexpected request count"); + + // Verify expected requests in order + for (int i = 0; i < expected.size(); i++) { + Request ex = expected.get(i); + Request req = requests.get(i); + // Verify method + assertEquals(ex.method, req.method, + String.format("Request %s has unexpected method %s", i, ex.method) + ); + // Verify path + assertEquals(ex.path, req.path, + String.format("Request %s has unexpected request URI %s", i, ex.path) + ); + } + } } diff --git a/test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java b/test/jdk/java/nio/channels/SocketChannel/ConnectionRefusedMessage.java similarity index 92% rename from test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java rename to test/jdk/java/nio/channels/SocketChannel/ConnectionRefusedMessage.java index 04490f63efe..d71bc6569cb 100644 --- a/test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java +++ b/test/jdk/java/nio/channels/SocketChannel/ConnectionRefusedMessage.java @@ -42,7 +42,8 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue; * @summary Verify that when a SocketChannel is registered with a Selector * with an interest in CONNECT operation, then SocketChannel.finishConnect() * throws the correct exception message, if the connect() fails - * @run junit ${test.main.class} + * @run junit/othervm -Djdk.includeInExceptions=hostInfoExclSocket ${test.main.class} + * @run junit/othervm -Djdk.includeInExceptions=hostInfo -Dcheck.relaxed=true ${test.main.class} */ class ConnectionRefusedMessage { @@ -108,10 +109,14 @@ class ConnectionRefusedMessage { } private static void assertExceptionMessage(final ConnectException ce) { - if (!"Connection refused".equals(ce.getMessage())) { - // propagate the original exception - fail("unexpected exception message: " + ce.getMessage(), ce); + if ("Connection refused".equals(ce.getMessage())) { + return; } + if (Boolean.getBoolean("check.relaxed") && ce.getMessage() != null && ce.getMessage().startsWith("Connection refused")) { + return; + } + // propagate the original exception + fail("unexpected exception message: " + ce.getMessage(), ce); } // Try to find a suitable port to provoke a "Connection Refused" error. diff --git a/test/jdk/java/util/zip/ZipFile/EndOfCenValidation.java b/test/jdk/java/util/zip/ZipFile/EndOfCenValidation.java index 7adcfb9c128..7ca71c9890a 100644 --- a/test/jdk/java/util/zip/ZipFile/EndOfCenValidation.java +++ b/test/jdk/java/util/zip/ZipFile/EndOfCenValidation.java @@ -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 @@ -25,33 +25,26 @@ * @bug 8272746 * @modules java.base/jdk.internal.util * @summary Verify that ZipFile rejects files with CEN sizes exceeding the implementation limit + * @library /test/lib + * @build jdk.test.lib.util.ZipUtils * @run junit/othervm EndOfCenValidation */ import jdk.internal.util.ArraysSupport; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import java.io.*; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.channels.FileChannel; -import java.nio.charset.StandardCharsets; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.Arrays; -import java.util.EnumSet; -import java.util.HexFormat; -import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; -import java.util.zip.ZipOutputStream; -import static org.junit.jupiter.api.Assertions.*; +import static jdk.test.lib.util.ZipUtils.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * This test augments {@link TestTooManyEntries}. It creates sparse ZIPs where @@ -65,36 +58,13 @@ import static org.junit.jupiter.api.Assertions.*; public class EndOfCenValidation { // Zip files produced by this test - public static final Path CEN_TOO_LARGE_ZIP = Path.of("cen-size-too-large.zip"); - public static final Path INVALID_CEN_SIZE = Path.of("invalid-zen-size.zip"); - public static final Path BAD_CEN_OFFSET_ZIP = Path.of("bad-cen-offset.zip"); - // Some ZipFile constants for manipulating the 'End of central directory record' (END header) - private static final int ENDHDR = ZipFile.ENDHDR; // End of central directory record size - private static final int ENDSIZ = ZipFile.ENDSIZ; // Offset of CEN size field within ENDHDR - private static final int ENDOFF = ZipFile.ENDOFF; // Offset of CEN offset field within ENDHDR + static final Path CEN_TOO_LARGE_ZIP = Path.of("cen-size-too-large.zip"); + static final Path INVALID_CEN_SIZE = Path.of("invalid-zen-size.zip"); + static final Path BAD_CEN_OFFSET_ZIP = Path.of("bad-cen-offset.zip"); + static final Path BAD_ENTRY_COUNT_ZIP = Path.of("bad-entry-count.zip"); + // Maximum allowed CEN size allowed by ZipFile - private static final int MAX_CEN_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; - - // Expected message when CEN size does not match file size - private static final String INVALID_CEN_BAD_SIZE = "invalid END header (bad central directory size)"; - // Expected message when CEN offset is too large - private static final String INVALID_CEN_BAD_OFFSET = "invalid END header (bad central directory offset)"; - // Expected message when CEN size is too large - private static final String INVALID_CEN_SIZE_TOO_LARGE = "invalid END header (central directory size too large)"; - // Expected message when total entry count is too large - private static final String INVALID_BAD_ENTRY_COUNT = "invalid END header (total entries count too large)"; - - // A valid ZIP file, used as a template - private byte[] zipBytes; - - /** - * Create a valid ZIP file, used as a template - * @throws IOException if an error occurs - */ - @BeforeEach - public void setup() throws IOException { - zipBytes = templateZip(); - } + static final int MAX_CEN_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; /** * Delete big files after test, in case the file system did not support sparse files. @@ -105,6 +75,7 @@ public class EndOfCenValidation { Files.deleteIfExists(CEN_TOO_LARGE_ZIP); Files.deleteIfExists(INVALID_CEN_SIZE); Files.deleteIfExists(BAD_CEN_OFFSET_ZIP); + Files.deleteIfExists(BAD_ENTRY_COUNT_ZIP); } /** @@ -115,14 +86,8 @@ public class EndOfCenValidation { @Test public void shouldRejectTooLargeCenSize() throws IOException { int size = MAX_CEN_SIZE + 1; - Path zip = zipWithModifiedEndRecord(size, true, 0, CEN_TOO_LARGE_ZIP); - - ZipException ex = assertThrows(ZipException.class, () -> { - new ZipFile(zip.toFile()); - }); - - assertEquals(INVALID_CEN_SIZE_TOO_LARGE, ex.getMessage()); + verifyRejection(zip, INVALID_CEN_SIZE_TOO_LARGE); } /** @@ -133,16 +98,9 @@ public class EndOfCenValidation { */ @Test public void shouldRejectInvalidCenSize() throws IOException { - int size = MAX_CEN_SIZE; - Path zip = zipWithModifiedEndRecord(size, false, 0, INVALID_CEN_SIZE); - - ZipException ex = assertThrows(ZipException.class, () -> { - new ZipFile(zip.toFile()); - }); - - assertEquals(INVALID_CEN_BAD_SIZE, ex.getMessage()); + verifyRejection(zip, INVALID_CEN_BAD_SIZE); } /** @@ -153,16 +111,9 @@ public class EndOfCenValidation { */ @Test public void shouldRejectInvalidCenOffset() throws IOException { - int size = MAX_CEN_SIZE; - Path zip = zipWithModifiedEndRecord(size, true, 100, BAD_CEN_OFFSET_ZIP); - - ZipException ex = assertThrows(ZipException.class, () -> { - new ZipFile(zip.toFile()); - }); - - assertEquals(INVALID_CEN_BAD_OFFSET, ex.getMessage()); + verifyRejection(zip, INVALID_CEN_BAD_OFFSET); } /** @@ -181,192 +132,20 @@ public class EndOfCenValidation { Long.MAX_VALUE // Unreasonably large }) public void shouldRejectBadTotalEntries(long totalEntries) throws IOException { - /** - * A small ZIP using the ZIP64 format. - * - * ZIP created using: "echo -n hello | zip zip64.zip -" - * Hex encoded using: "cat zip64.zip | xxd -ps" - * - * The file has the following structure: - * - * 0000 LOCAL HEADER #1 04034B50 - * 0004 Extract Zip Spec 2D '4.5' - * 0005 Extract OS 00 'MS-DOS' - * 0006 General Purpose Flag 0000 - * 0008 Compression Method 0000 'Stored' - * 000A Last Mod Time 5947AB78 'Mon Oct 7 21:27:48 2024' - * 000E CRC 363A3020 - * 0012 Compressed Length FFFFFFFF - * 0016 Uncompressed Length FFFFFFFF - * 001A Filename Length 0001 - * 001C Extra Length 0014 - * 001E Filename '-' - * 001F Extra ID #0001 0001 'ZIP64' - * 0021 Length 0010 - * 0023 Uncompressed Size 0000000000000006 - * 002B Compressed Size 0000000000000006 - * 0033 PAYLOAD hello. - * - * 0039 CENTRAL HEADER #1 02014B50 - * 003D Created Zip Spec 1E '3.0' - * 003E Created OS 03 'Unix' - * 003F Extract Zip Spec 2D '4.5' - * 0040 Extract OS 00 'MS-DOS' - * 0041 General Purpose Flag 0000 - * 0043 Compression Method 0000 'Stored' - * 0045 Last Mod Time 5947AB78 'Mon Oct 7 21:27:48 2024' - * 0049 CRC 363A3020 - * 004D Compressed Length 00000006 - * 0051 Uncompressed Length FFFFFFFF - * 0055 Filename Length 0001 - * 0057 Extra Length 000C - * 0059 Comment Length 0000 - * 005B Disk Start 0000 - * 005D Int File Attributes 0001 - * [Bit 0] 1 Text Data - * 005F Ext File Attributes 11B00000 - * 0063 Local Header Offset 00000000 - * 0067 Filename '-' - * 0068 Extra ID #0001 0001 'ZIP64' - * 006A Length 0008 - * 006C Uncompressed Size 0000000000000006 - * - * 0074 ZIP64 END CENTRAL DIR 06064B50 - * RECORD - * 0078 Size of record 000000000000002C - * 0080 Created Zip Spec 1E '3.0' - * 0081 Created OS 03 'Unix' - * 0082 Extract Zip Spec 2D '4.5' - * 0083 Extract OS 00 'MS-DOS' - * 0084 Number of this disk 00000000 - * 0088 Central Dir Disk no 00000000 - * 008C Entries in this disk 0000000000000001 - * 0094 Total Entries 0000000000000001 - * 009C Size of Central Dir 000000000000003B - * 00A4 Offset to Central dir 0000000000000039 - * - * 00AC ZIP64 END CENTRAL DIR 07064B50 - * LOCATOR - * 00B0 Central Dir Disk no 00000000 - * 00B4 Offset to Central dir 0000000000000074 - * 00BC Total no of Disks 00000001 - * - * 00C0 END CENTRAL HEADER 06054B50 - * 00C4 Number of this disk 0000 - * 00C6 Central Dir Disk no 0000 - * 00C8 Entries in this disk 0001 - * 00CA Total Entries 0001 - * 00CC Size of Central Dir 0000003B - * 00D0 Offset to Central Dir FFFFFFFF - * 00D4 Comment Length 0000 - */ + Path zip = zip64WithModifiedTotalEntries(BAD_ENTRY_COUNT_ZIP, totalEntries); + verifyRejection(zip, INVALID_BAD_ENTRY_COUNT); + } - byte[] zipBytes = HexFormat.of().parseHex(""" - 504b03042d000000000078ab475920303a36ffffffffffffffff01001400 - 2d010010000600000000000000060000000000000068656c6c6f0a504b01 - 021e032d000000000078ab475920303a3606000000ffffffff01000c0000 - 00000001000000b011000000002d010008000600000000000000504b0606 - 2c000000000000001e032d00000000000000000001000000000000000100 - 0000000000003b000000000000003900000000000000504b060700000000 - 740000000000000001000000504b050600000000010001003b000000ffff - ffff0000 - """.replaceAll("\n","")); - - // Buffer to manipulate the above ZIP - ByteBuffer buf = ByteBuffer.wrap(zipBytes).order(ByteOrder.LITTLE_ENDIAN); - // Offset of the 'total entries' in the 'ZIP64 END CENTRAL DIR' record - // Update ZIP64 entry count to a value which cannot possibly fit in the small CEN - buf.putLong(0x94, totalEntries); - // The corresponding END field needs the ZIP64 magic value - buf.putShort(0xCA, (short) 0xFFFF); - // Write the ZIP to disk - Path zipFile = Path.of("bad-entry-count.zip"); - Files.write(zipFile, zipBytes); - - // Verify that the END header is rejected + /** + * Verify that ZipFile rejects the ZIP file with a ZipException + * with the given message + * @param zip ZIP file to open + * @param msg exception message to expect + */ + private static void verifyRejection(Path zip, String msg) { ZipException ex = assertThrows(ZipException.class, () -> { - try (var zf = new ZipFile(zipFile.toFile())) { - } + new ZipFile(zip.toFile()); }); - - assertEquals(INVALID_BAD_ENTRY_COUNT, ex.getMessage()); - } - - /** - * Create an ZIP file with a single entry, then modify the CEN size - * in the 'End of central directory record' (END header) to the given size. - * - * The CEN is optionally "inflated" with trailing zero bytes such that - * its actual size matches the one stated in the END header. - * - * The CEN offset is optiontially adjusted by the given amount - * - * The resulting ZIP is technically not valid, but it does allow us - * to test that large or invalid CEN sizes are rejected - * @param cenSize the CEN size to put in the END record - * @param inflateCen if true, zero-pad the CEN to the desired size - * @param cenOffAdjust Adjust the CEN offset field of the END record with this amount - * @throws IOException if an error occurs - */ - private Path zipWithModifiedEndRecord(int cenSize, - boolean inflateCen, - int cenOffAdjust, - Path zip) throws IOException { - - // A byte buffer for reading the END - ByteBuffer buffer = ByteBuffer.wrap(zipBytes.clone()).order(ByteOrder.LITTLE_ENDIAN); - - // Offset of the END header - int endOffset = buffer.limit() - ENDHDR; - - // Modify the CEN size - int sizeOffset = endOffset + ENDSIZ; - int currentCenSize = buffer.getInt(sizeOffset); - buffer.putInt(sizeOffset, cenSize); - - // Optionally modify the CEN offset - if (cenOffAdjust != 0) { - int offOffset = endOffset + ENDOFF; - int currentCenOff = buffer.getInt(offOffset); - buffer.putInt(offOffset, currentCenOff + cenOffAdjust); - } - - // When creating a sparse file, the file must not already exit - Files.deleteIfExists(zip); - - // Open a FileChannel for writing a sparse file - EnumSet options = EnumSet.of(StandardOpenOption.CREATE_NEW, - StandardOpenOption.WRITE, - StandardOpenOption.SPARSE); - - try (FileChannel channel = FileChannel.open(zip, options)) { - - // Write everything up to END - channel.write(buffer.slice(0, buffer.limit() - ENDHDR)); - - if (inflateCen) { - // Inject "empty bytes" to make the actual CEN size match the END - int injectBytes = cenSize - currentCenSize; - channel.position(channel.position() + injectBytes); - } - // Write the modified END - channel.write(buffer.slice(buffer.limit() - ENDHDR, ENDHDR)); - } - return zip; - } - - /** - * Produce a byte array of a ZIP with a single entry - * - * @throws IOException if an error occurs - */ - private byte[] templateZip() throws IOException { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - try (ZipOutputStream zo = new ZipOutputStream(bout)) { - ZipEntry entry = new ZipEntry("duke.txt"); - zo.putNextEntry(entry); - zo.write("duke".getBytes(StandardCharsets.UTF_8)); - } - return bout.toByteArray(); + assertEquals(msg, ex.getMessage()); } } diff --git a/test/jdk/javax/swing/JFileChooser/FileSystemView/SystemIconPixelDataTest.java b/test/jdk/javax/swing/JFileChooser/FileSystemView/SystemIconPixelDataTest.java new file mode 100644 index 00000000000..c71c7a3e6a9 --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/FileSystemView/SystemIconPixelDataTest.java @@ -0,0 +1,93 @@ +/* + * 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. + */ + +import java.awt.EventQueue; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.swing.Icon; +import javax.swing.UIManager; +import javax.swing.UIManager.LookAndFeelInfo; +import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.filechooser.FileSystemView; + +/** + * @test + * @bug 8376253 + * @summary FileSystemView may not report system icons when -Xcheck:jni enabled + * @key headful + * @run main SystemIconPixelDataTest + * @run main/othervm -Xcheck:jni SystemIconPixelDataTest + */ +public final class SystemIconPixelDataTest { + + public static void main(String[] args) throws Exception { + for (LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) { + AtomicBoolean ok = new AtomicBoolean(); + EventQueue.invokeAndWait(() -> ok.set(setLookAndFeel(laf))); + if (ok.get()) { + EventQueue.invokeAndWait(SystemIconPixelDataTest::test); + } + } + } + + private static boolean setLookAndFeel(LookAndFeelInfo laf) { + try { + UIManager.setLookAndFeel(laf.getClassName()); + System.out.println("LookAndFeel: " + laf.getClassName()); + return true; + } catch (UnsupportedLookAndFeelException ignored) { + return false; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static void test() { + FileSystemView fsv = FileSystemView.getFileSystemView(); + File home = fsv.getHomeDirectory(); + Icon icon = fsv.getSystemIcon(home); + if (icon == null) { + return; + } + int w = icon.getIconWidth(); + int h = icon.getIconHeight(); + if (w <= 0 || h <= 0) { + throw new RuntimeException("Invalid icon size: " + w + "x" + h); + } + var img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = img.createGraphics(); + icon.paintIcon(null, g, 0, 0); + g.dispose(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + if (img.getRGB(x, y) != 0) { + return; + } + } + } + throw new RuntimeException("All pixels are zero"); + } +} diff --git a/test/jdk/javax/swing/ToolTipManager/Test6657026.java b/test/jdk/javax/swing/ToolTipManager/Test6657026.java deleted file mode 100644 index 0678d57f768..00000000000 --- a/test/jdk/javax/swing/ToolTipManager/Test6657026.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. - * 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 6657026 - * @summary Tests shared ToolTipManager in different application contexts - * @author Sergey Malenkov - * @modules java.desktop/sun.awt - */ - -import sun.awt.SunToolkit; -import javax.swing.ToolTipManager; - -public class Test6657026 implements Runnable { - - private static final int DISMISS = 4000; - private static final int INITIAL = 750; - private static final int RESHOW = 500; - - public static void main(String[] args) throws InterruptedException { - ToolTipManager manager = ToolTipManager.sharedInstance(); - if (DISMISS != manager.getDismissDelay()) { - throw new Error("unexpected dismiss delay"); - } - if (INITIAL != manager.getInitialDelay()) { - throw new Error("unexpected initial delay"); - } - if (RESHOW != manager.getReshowDelay()) { - throw new Error("unexpected reshow delay"); - } - manager.setDismissDelay(DISMISS + 1); - manager.setInitialDelay(INITIAL + 1); - manager.setReshowDelay(RESHOW + 1); - - ThreadGroup group = new ThreadGroup("$$$"); - Thread thread = new Thread(group, new Test6657026()); - thread.start(); - thread.join(); - } - - public void run() { - SunToolkit.createNewAppContext(); - ToolTipManager manager = ToolTipManager.sharedInstance(); - if (DISMISS != manager.getDismissDelay()) { - throw new Error("shared dismiss delay"); - } - if (INITIAL != manager.getInitialDelay()) { - throw new Error("shared initial delay"); - } - if (RESHOW != manager.getReshowDelay()) { - throw new Error("shared reshow delay"); - } - } -} diff --git a/test/jdk/javax/swing/plaf/metal/MetalTitlePaneBug.java b/test/jdk/javax/swing/plaf/metal/MetalTitlePaneBug.java new file mode 100644 index 00000000000..dde6249752d --- /dev/null +++ b/test/jdk/javax/swing/plaf/metal/MetalTitlePaneBug.java @@ -0,0 +1,65 @@ +/* + * 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 8078744 + * @summary Verifies right half of system menu icon on title bar + * activates when clicked + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MetalTitlePaneBug + */ + +import javax.swing.JFrame; +import javax.swing.UIManager; + +public class MetalTitlePaneBug { + + static final String INSTRUCTIONS = """ + A Frame is shown with a titlepane. + Click on the left edge of the system menu icon on the title pane. + It should show "Restore", "Minimize", "Maximize", "Close" menu. + Click on the right edge of the system menu icon. + It should also show "Restore", "Minimize", "Maximize", "Close" menu. + If it shows, press Pass else press Fail. + """; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(MetalTitlePaneBug::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame.setDefaultLookAndFeelDecorated(true); + JFrame frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(200, 100); + return frame; + } +} diff --git a/test/jdk/jdk/nio/zipfs/EndOfCenValidation.java b/test/jdk/jdk/nio/zipfs/EndOfCenValidation.java new file mode 100644 index 00000000000..ed0bdbb52ea --- /dev/null +++ b/test/jdk/jdk/nio/zipfs/EndOfCenValidation.java @@ -0,0 +1,150 @@ +/* + * 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 + * 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 + * @modules java.base/jdk.internal.util + * @summary Verify that ZipFileSystem rejects files with CEN sizes exceeding the implementation limit + * @library /test/lib + * @build jdk.test.lib.util.ZipUtils + * @run junit/othervm EndOfCenValidation + */ + +import jdk.internal.util.ArraysSupport; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.zip.ZipException; + +import static jdk.test.lib.util.ZipUtils.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * This test augments {@link TestTooManyEntries}. It creates sparse ZIPs where + * the CEN size is inflated to the desired value. This helps this test run + * fast with much less resources. + * + * While the CEN in these files are zero-filled and the produced ZIPs are technically + * invalid, the CEN is never actually read by ZipFileSystem since it does + * 'End of central directory record' (END header) validation before reading the CEN. + */ +public class EndOfCenValidation { + + // Zip files produced by this test + static final Path CEN_TOO_LARGE_ZIP = Path.of("cen-size-too-large.zip"); + static final Path INVALID_CEN_SIZE = Path.of("invalid-zen-size.zip"); + static final Path BAD_CEN_OFFSET_ZIP = Path.of("bad-cen-offset.zip"); + static final Path BAD_ENTRY_COUNT_ZIP = Path.of("bad-entry-count.zip"); + + // Maximum allowed CEN size allowed by ZipFileSystem + static final int MAX_CEN_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; + + /** + * Delete big files after test, in case the file system did not support sparse files. + * @throws IOException if an error occurs + */ + @AfterEach + public void cleanup() throws IOException { + Files.deleteIfExists(CEN_TOO_LARGE_ZIP); + Files.deleteIfExists(INVALID_CEN_SIZE); + Files.deleteIfExists(BAD_CEN_OFFSET_ZIP); + Files.deleteIfExists(BAD_ENTRY_COUNT_ZIP); + } + + /** + * Validates that an 'End of central directory record' (END header) with a CEN + * length exceeding {@link #MAX_CEN_SIZE} limit is rejected + * @throws IOException if an error occurs + */ + @Test + public void shouldRejectTooLargeCenSize() throws IOException { + int size = MAX_CEN_SIZE + 1; + Path zip = zipWithModifiedEndRecord(size, true, 0, CEN_TOO_LARGE_ZIP); + verifyRejection(zip, INVALID_CEN_SIZE_TOO_LARGE); + } + + /** + * Validate that an 'End of central directory record' (END header) + * where the value of the CEN size field exceeds the position of + * the END header is rejected. + * @throws IOException if an error occurs + */ + @Test + public void shouldRejectInvalidCenSize() throws IOException { + int size = MAX_CEN_SIZE; + Path zip = zipWithModifiedEndRecord(size, false, 0, INVALID_CEN_SIZE); + verifyRejection(zip, INVALID_CEN_BAD_SIZE); + } + + /** + * Validate that an 'End of central directory record' (the END header) + * where the value of the CEN offset field is larger than the position + * of the END header minus the CEN size is rejected + * @throws IOException if an error occurs + */ + @Test + public void shouldRejectInvalidCenOffset() throws IOException { + int size = MAX_CEN_SIZE; + Path zip = zipWithModifiedEndRecord(size, true, 100, BAD_CEN_OFFSET_ZIP); + verifyRejection(zip, INVALID_CEN_BAD_OFFSET); + } + + /** + * Validate that a 'Zip64 End of Central Directory' record (the END header) + * where the value of the 'total entries' field is larger than what fits + * in the CEN size is rejected. + * + * @throws IOException if an error occurs + */ + @ParameterizedTest + @ValueSource(longs = { + -1, // Negative + Long.MIN_VALUE, // Very negative + 0x3B / 3L - 1, // Cannot fit in test ZIP's CEN + MAX_CEN_SIZE / 3 + 1, // Too large to allocate int[] entries array + Long.MAX_VALUE // Unreasonably large + }) + public void shouldRejectBadTotalEntries(long totalEntries) throws IOException { + Path zip = zip64WithModifiedTotalEntries(BAD_ENTRY_COUNT_ZIP, totalEntries); + verifyRejection(zip, INVALID_BAD_ENTRY_COUNT); + } + + /** + * Verify that ZipFileSystem.newFileSystem rejects the ZIP file with a ZipException + * with the given message + * @param zip ZIP file to open + * @param msg exception message to expect + */ + private static void verifyRejection(Path zip, String msg) { + ZipException ex = assertThrows(ZipException.class, () -> { + FileSystems.newFileSystem(zip); + }); + assertEquals(msg, ex.getMessage()); + } +} diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketReset.java b/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketReset.java index 6b0f365edc7..64779facd26 100644 --- a/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketReset.java +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketReset.java @@ -21,16 +21,14 @@ * questions. */ -// -// Please run in othervm mode. SunJSSE does not support dynamic system -// properties, no way to re-use system properties in samevm/agentvm mode. -// - /* * @test * @bug 8268965 * @summary Socket reset issue for TLS socket close - * @run main/othervm -Djdk.net.usePlainSocketImpl=true SSLSocketReset + * @comment The test uses SSLContext.getDefault(), so we use othervm to prevent + * usage of unexpected default SSLContext that might be set by some + * other test + * @run main/othervm SSLSocketReset */ import javax.net.ssl.*; diff --git a/test/jdk/tools/jpackage/TEST.properties b/test/jdk/tools/jpackage/TEST.properties index 19eda305364..5cc4aa7a1b9 100644 --- a/test/jdk/tools/jpackage/TEST.properties +++ b/test/jdk/tools/jpackage/TEST.properties @@ -13,7 +13,7 @@ maxOutputSize = 2000000 # Run jpackage tests on windows platform sequentially. # Having "share" directory in the list affects tests on other platforms. # The better option would be: -# if (platfrom == windowws) { +# if (platfrom == windows) { # exclusiveAccess.dirs=share windows # } # but conditionals are not supported by jtreg configuration files. @@ -29,4 +29,6 @@ modules = \ jdk.jpackage/jdk.jpackage.internal.util.function \ jdk.jpackage/jdk.jpackage.internal.resources:+open \ java.base/jdk.internal.util \ + java.base/sun.security.util \ + java.base/sun.security.x509 \ jdk.jlink/jdk.tools.jlink.internal diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/MacHelperTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/MacHelperTest.java index 5d14f021eaf..b8f7bfd456e 100644 --- a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/MacHelperTest.java +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/MacHelperTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,14 +31,20 @@ import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; import jdk.jpackage.internal.util.PListReader; import jdk.jpackage.internal.util.XmlUtils; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.Node; import org.xml.sax.InputSource; import org.xml.sax.SAXException; -public class MacHelperTest { +public class MacHelperTest extends JUnitAdapter { @Test public void test_flatMapPList() { @@ -105,6 +111,18 @@ public class MacHelperTest { ), props); } + @ParameterizedTest + @MethodSource + public void test_appImageSigned(SignedTestSpec spec) { + spec.test(MacHelper::appImageSigned); + } + + @ParameterizedTest + @MethodSource + public void test_nativePackageSigned(SignedTestSpec spec) { + spec.test(MacHelper::nativePackageSigned); + } + private static String createPListXml(String ...xml) { final List content = new ArrayList<>(); content.add(""); @@ -125,4 +143,126 @@ public class MacHelperTest { throw new RuntimeException(ex); } } + + private static Stream test_appImageSigned() { + + List data = new ArrayList<>(); + + for (var signingIdentityOption : List.of( + List.of(), + List.of("--mac-signing-key-user-name", "foo"), + List.of("--mac-app-image-sign-identity", "foo"), + List.of("--mac-installer-sign-identity", "foo"), + List.of("--mac-installer-sign-identity", "foo", "--mac-app-image-sign-identity", "bar") + )) { + for (var type : List.of(PackageType.IMAGE, PackageType.MAC_DMG, PackageType.MAC_PKG)) { + for (var withMacSign : List.of(true, false)) { + if (signingIdentityOption.contains("--mac-installer-sign-identity") && type != PackageType.MAC_PKG) { + continue; + } + + var builder = SignedTestSpec.build().type(type).cmdline(signingIdentityOption); + if (withMacSign) { + builder.cmdline("--mac-sign"); + if (Stream.of( + "--mac-signing-key-user-name", + "--mac-app-image-sign-identity" + ).anyMatch(signingIdentityOption::contains) || signingIdentityOption.isEmpty()) { + builder.signed(); + } + } + + data.add(builder); + } + } + } + + return data.stream().map(SignedTestSpec.Builder::create); + } + + private static Stream test_nativePackageSigned() { + + List data = new ArrayList<>(); + + for (var signingIdentityOption : List.of( + List.of(), + List.of("--mac-signing-key-user-name", "foo"), + List.of("--mac-app-image-sign-identity", "foo"), + List.of("--mac-installer-sign-identity", "foo"), + List.of("--mac-installer-sign-identity", "foo", "--mac-app-image-sign-identity", "bar") + )) { + for (var type : List.of(PackageType.MAC_DMG, PackageType.MAC_PKG)) { + for (var withMacSign : List.of(true, false)) { + if (signingIdentityOption.contains("--mac-installer-sign-identity") && type != PackageType.MAC_PKG) { + continue; + } + + var builder = SignedTestSpec.build().type(type).cmdline(signingIdentityOption); + if (withMacSign) { + builder.cmdline("--mac-sign"); + if (type == PackageType.MAC_PKG && (Stream.of( + "--mac-signing-key-user-name", + "--mac-installer-sign-identity" + ).anyMatch(signingIdentityOption::contains) || signingIdentityOption.isEmpty())) { + builder.signed(); + } + } + + data.add(builder); + } + } + } + + return data.stream().map(SignedTestSpec.Builder::create); + } + + private record SignedTestSpec(boolean expectedSigned, PackageType type, List cmdline) { + + SignedTestSpec { + Objects.requireNonNull(type); + cmdline.forEach(Objects::requireNonNull); + } + + void test(Function func) { + var actualSigned = func.apply((new JPackageCommand().addArguments(cmdline).setPackageType(type))); + assertEquals(expectedSigned, actualSigned); + } + + static Builder build() { + return new Builder(); + } + + static final class Builder { + + SignedTestSpec create() { + return new SignedTestSpec( + expectedSigned, + Optional.ofNullable(type).orElse(PackageType.IMAGE), + cmdline); + } + + Builder signed() { + expectedSigned = true; + return this; + } + + Builder type(PackageType v) { + type = v; + return this; + } + + Builder cmdline(String... args) { + return cmdline(List.of(args)); + } + + Builder cmdline(List v) { + cmdline.addAll(v); + return this; + } + + private boolean expectedSigned; + private PackageType type; + private List cmdline = new ArrayList<>(); + } + } } 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 da6af4bed33..251d95a1089 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -1292,7 +1292,7 @@ public class JPackageCommand extends CommandArguments { } }), MAC_BUNDLE_UNSIGNED_SIGNATURE(cmd -> { - if (TKit.isOSX() && !MacHelper.appImageSigned(cmd)) { + if (TKit.isOSX()) { MacHelper.verifyUnsignedBundleSignature(cmd); } }), @@ -1316,7 +1316,14 @@ public class JPackageCommand extends CommandArguments { }), PREDEFINED_APP_IMAGE_COPY(cmd -> { Optional.ofNullable(cmd.getArgumentValue("--app-image")).filter(_ -> { - return !TKit.isOSX() || !MacHelper.signPredefinedAppImage(cmd); + if (!TKit.isOSX() || !cmd.hasArgument("--mac-sign")) { + return true; + } else { + var signAppImage = MacHelper.signPredefinedAppImage(cmd) + || MacHelper.hasAppImageSignIdentity(cmd) + || MacHelper.isSignWithoutSignIdentity(cmd); + return !signAppImage; + } }).filter(_ -> { // Don't examine the contents of the output app image if this is Linux package installing in the "/usr" subtree. return Optional.ofNullable(cmd.onLinuxPackageInstallDir(null, _ -> false)).orElse(true); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java index 1191cd02221..1372058d440 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java @@ -76,6 +76,7 @@ import jdk.jpackage.internal.util.RetryExecutor; import jdk.jpackage.internal.util.XmlUtils; import jdk.jpackage.internal.util.function.ThrowingConsumer; import jdk.jpackage.internal.util.function.ThrowingSupplier; +import jdk.jpackage.test.MacSign.CertificateHash; import jdk.jpackage.test.MacSign.CertificateRequest; import jdk.jpackage.test.MacSign.CertificateType; import jdk.jpackage.test.MacSign.ResolvedKeychain; @@ -259,10 +260,7 @@ public final class MacHelper { * predefined app image in place and {@code false} otherwise. */ public static boolean signPredefinedAppImage(JPackageCommand cmd) { - Objects.requireNonNull(cmd); - if (!TKit.isOSX()) { - throw new UnsupportedOperationException(); - } + cmd.verifyIsOfType(PackageType.MAC_DMG, PackageType.MAC_PKG, PackageType.IMAGE); return cmd.hasArgument("--mac-sign") && cmd.hasArgument("--app-image") && cmd.isImagePackageType(); } @@ -279,10 +277,7 @@ public final class MacHelper { * otherwise. */ public static boolean appImageSigned(JPackageCommand cmd) { - Objects.requireNonNull(cmd); - if (!TKit.isOSX()) { - throw new UnsupportedOperationException(); - } + cmd.verifyIsOfType(PackageType.MAC_DMG, PackageType.MAC_PKG, PackageType.IMAGE); var runtimeImageBundle = Optional.ofNullable(cmd.getArgumentValue("--runtime-image")).map(Path::of).flatMap(MacBundle::fromPath); var appImage = Optional.ofNullable(cmd.getArgumentValue("--app-image")).map(Path::of); @@ -291,23 +286,102 @@ public final class MacHelper { // If the predefined runtime is a signed bundle, bundled image should be signed too. return true; } else if (appImage.map(MacHelper::isBundleSigned).orElse(false)) { - // The external app image is signed, so the app image is signed too. + // The predefined app image is signed, so the app image is signed too. return true; } - if (!cmd.isImagePackageType() && appImage.isPresent()) { - // Building a ".pkg" or a ".dmg" bundle from the predefined app image. - // The predefined app image is unsigned, so the app image bundled - // in the output native package will be unsigned too - // (even if the ".pkg" file may be signed itself, and we never sign ".dmg" files). - return false; - } - if (!cmd.hasArgument("--mac-sign")) { return false; + } else { + return isSignWithoutSignIdentity(cmd) || hasAppImageSignIdentity(cmd); } + } - return (cmd.hasArgument("--mac-signing-key-user-name") || cmd.hasArgument("--mac-app-image-sign-identity")); + /** + * Returns {@code true} if the given jpackage command line is configured such + * that the native package it will produce will be signed. + * + * @param cmd the jpackage command to examine + * @return {@code true} if the given jpackage command line is configured such + * the native package it will produce will be signed and {@code false} + * otherwise. + */ + public static boolean nativePackageSigned(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.MAC); + + switch (cmd.packageType()) { + case MAC_DMG -> { + return false; + } + case MAC_PKG -> { + if (!cmd.hasArgument("--mac-sign")) { + return false; + } else { + return isSignWithoutSignIdentity(cmd) || hasPkgInstallerSignIdentity(cmd); + } + } + default -> { + throw new IllegalStateException(); + } + } + } + + /** + * Returns {@code true} if the given jpackage command line has app image signing + * identity option. The command line must have "--mac-sign" option. + * + * @param cmd the jpackage command to examine + * @return {@code true} if the given jpackage command line has app image signing + * identity option and {@code false} otherwise. + */ + public static boolean hasAppImageSignIdentity(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.MAC_DMG, PackageType.MAC_PKG, PackageType.IMAGE); + if (!cmd.hasArgument("--mac-sign")) { + throw new IllegalArgumentException(); + } + return Stream.of( + "--mac-signing-key-user-name", + "--mac-app-image-sign-identity" + ).anyMatch(cmd::hasArgument); + } + + /** + * Returns {@code true} if the given jpackage command line has PKG installer signing + * identity option. The command line must have "--mac-sign" option. + * + * @param cmd the jpackage command to examine + * @return {@code true} if the given jpackage command line has PKG installer signing + * identity option and {@code false} otherwise. + */ + public static boolean hasPkgInstallerSignIdentity(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.MAC_PKG); + if (!cmd.hasArgument("--mac-sign")) { + throw new IllegalArgumentException(); + } + return Stream.of( + "--mac-signing-key-user-name", + "--mac-installer-sign-identity" + ).anyMatch(cmd::hasArgument); + } + + /** + * Returns {@code true} if the given jpackage command line doesn't have signing + * identity options. The command line must have "--mac-sign" option. + * + * @param cmd the jpackage command to examine + * @return {@code true} if the given jpackage command line doesn't have signing + * identity options and {@code false} otherwise. + */ + public static boolean isSignWithoutSignIdentity(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.MAC_DMG, PackageType.MAC_PKG, PackageType.IMAGE); + if (!cmd.hasArgument("--mac-sign")) { + throw new IllegalArgumentException(); + } + return Stream.of( + "--mac-signing-key-user-name", + "--mac-app-image-sign-identity", + "--mac-installer-sign-identity" + ).noneMatch(cmd::hasArgument); } public static void writeFaPListFragment(JPackageCommand cmd, XMLStreamWriter xml) { @@ -702,6 +776,14 @@ public final class MacHelper { MacSign.CertificateType.CODE_SIGN, Name.KEY_IDENTITY_APP_IMAGE, MacSign.CertificateType.INSTALLER, Name.KEY_IDENTITY_INSTALLER)), + /** + * "--mac-installer-sign-identity" or "--mac-app-image-sign-identity" option + * with the SHA1 of a signing certificate + */ + SIGN_KEY_IDENTITY_SHA1(Map.of( + MacSign.CertificateType.CODE_SIGN, Name.KEY_IDENTITY_APP_IMAGE, + MacSign.CertificateType.INSTALLER, Name.KEY_IDENTITY_INSTALLER)), + /** * "--mac-app-image-sign-identity" regardless of the type of signing identity * (for signing app image or .pkg installer). @@ -714,6 +796,12 @@ public final class MacHelper { */ SIGN_KEY_IDENTITY_INSTALLER(Name.KEY_IDENTITY_INSTALLER), + /** + * No explicit option specifying signing identity. jpackage will pick one from + * the specified keychain. + */ + SIGN_KEY_IMPLICIT, + ; Type(Map optionNameMap) { @@ -736,11 +824,24 @@ public final class MacHelper { return optionNameMapper.apply(Objects.requireNonNull(certType)); } + public boolean passThrough() { + return Stream.of(MacSign.CertificateType.values()) + .map(this::mapOptionName) + .flatMap(Optional::stream) + .map(Name::passThrough) + .distinct() + .reduce((_, _) -> { + throw new IllegalStateException(); + }).orElse(false); + } + public static Type[] defaultValues() { return new Type[] { SIGN_KEY_USER_SHORT_NAME, SIGN_KEY_USER_FULL_NAME, - SIGN_KEY_IDENTITY + SIGN_KEY_IDENTITY, + SIGN_KEY_IDENTITY_SHA1, + SIGN_KEY_IMPLICIT }; } @@ -751,7 +852,7 @@ public final class MacHelper { public String toString() { var sb = new StringBuilder(); sb.append('{'); - applyTo((optionName, _) -> { + type.mapOptionName(certRequest.type()).ifPresent(optionName -> { sb.append(optionName); switch (type) { case SIGN_KEY_USER_FULL_NAME -> { @@ -762,6 +863,9 @@ public final class MacHelper { sb.append("=").append(ENQUOTER.applyTo(optionValue)); }); } + case SIGN_KEY_IDENTITY_SHA1 -> { + sb.append("/sha1"); + } default -> { // NOP } @@ -787,12 +891,16 @@ public final class MacHelper { } public List asCmdlineArgs() { - String[] args = new String[2]; - applyTo((optionName, optionValue) -> { - args[0] = optionName; - args[1] = optionValue; - }); - return List.of(args); + if (type == Type.SIGN_KEY_IMPLICIT) { + return List.of(); + } else { + String[] args = new String[2]; + applyTo((optionName, optionValue) -> { + args[0] = optionName; + args[1] = optionValue; + }); + return List.of(args); + } } public Optional passThrough() { @@ -817,6 +925,9 @@ public final class MacHelper { case SIGN_KEY_USER_SHORT_NAME -> { return certRequest.shortName(); } + case SIGN_KEY_IDENTITY_SHA1 -> { + return CertificateHash.of(certRequest.cert()).toString(); + } default -> { throw new IllegalStateException(); } @@ -960,18 +1071,21 @@ public final class MacHelper { } static void verifyUnsignedBundleSignature(JPackageCommand cmd) { - if (!cmd.isImagePackageType()) { + + if (!cmd.isImagePackageType() && !nativePackageSigned(cmd)) { MacSignVerify.assertUnsigned(cmd.outputBundle()); } - final Path bundleRoot; - if (cmd.isImagePackageType()) { - bundleRoot = cmd.outputBundle(); - } else { - bundleRoot = cmd.pathToUnpackedPackageFile(cmd.appInstallationDirectory()); - } + if (!appImageSigned(cmd)) { + final Path bundleRoot; + if (cmd.isImagePackageType()) { + bundleRoot = cmd.outputBundle(); + } else { + bundleRoot = cmd.pathToUnpackedPackageFile(cmd.appInstallationDirectory()); + } - MacSignVerify.assertAdhocSigned(bundleRoot); + MacSignVerify.assertAdhocSigned(bundleRoot); + } } static PackageHandlers createDmgPackageHandlers() { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java index 4dbb2bc1e18..20f7ac41eef 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java @@ -63,9 +63,11 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.stream.Stream; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; +import jdk.jpackage.internal.util.MemoizingSupplier; import jdk.jpackage.internal.util.function.ExceptionBox; import jdk.jpackage.internal.util.function.ThrowingConsumer; import jdk.jpackage.internal.util.function.ThrowingSupplier; @@ -280,8 +282,15 @@ public final class MacSign { return sb.toString(); } + public static Builder build() { + return new Builder(); + } + public static final class Builder { + private Builder() { + } + public Builder name(String v) { keychainBuilder.name(v); return this; @@ -306,8 +315,8 @@ public final class MacSign { return new KeychainWithCertsSpec(keychain, List.copyOf(certs)); } - private Keychain.Builder keychainBuilder = new Keychain.Builder(); - private List certs = new ArrayList<>(); + private final Keychain.Builder keychainBuilder = Keychain.build(); + private final List certs = new ArrayList<>(); } } @@ -326,8 +335,15 @@ public final class MacSign { } } + public static Builder build() { + return new Builder(); + } + public static final class Builder { + private Builder() { + } + public Builder name(String v) { name = v; return this; @@ -599,12 +615,7 @@ public final class MacSign { @Override public String toString() { - final var sb = new StringBuilder(); - sb.append(frame("BEGIN " + label)); - sb.append(ENCODER.encodeToString(data)); - sb.append("\n"); - sb.append(frame("END " + label)); - return sb.toString(); + return PemDataFormatter.format(label, data); } static PemData of(X509Certificate cert) { @@ -619,6 +630,21 @@ public final class MacSign { throw new UncheckedIOException(ex); } } + } + + private final class PemDataFormatter { + + static String format(String label, byte[] data) { + Objects.requireNonNull(label); + Objects.requireNonNull(data); + + final var sb = new StringBuilder(); + sb.append(frame("BEGIN " + label)); + sb.append(ENCODER.encodeToString(data)); + sb.append("\n"); + sb.append(frame("END " + label)); + return sb.toString(); + } private static String frame(String str) { return String.format("-----%s-----\n", Objects.requireNonNull(str)); @@ -627,6 +653,11 @@ public final class MacSign { private static final Base64.Encoder ENCODER = Base64.getMimeEncoder(64, "\n".getBytes()); } + public static String formatX509Certificate(X509Certificate cert) { + Objects.requireNonNull(cert); + return PemDataFormatter.format("CERTIFICATE", toSupplier(cert::getEncoded).get()); + } + public enum DigestAlgorithm { SHA1(20, () -> MessageDigest.getInstance("SHA-1")), SHA256(32, () -> MessageDigest.getInstance("SHA-256")); @@ -773,8 +804,15 @@ public final class MacSign { return COMPARATOR.compare(this, o); } + public static Builder build() { + return new Builder(); + } + public static final class Builder { + private Builder() { + } + public Builder userName(String v) { userName = v; return this; @@ -1068,6 +1106,15 @@ public final class MacSign { return !missingKeychain && !missingCertificates && !invalidCertificates; } + /** + * Creates an empty keychain with unique name in the work directory of the current test. + */ + public static Keychain createEmptyKeychain() { + return Keychain.build() + .name(TKit.createUniquePath(TKit.workDir().resolve("empty.keychain")).toAbsolutePath().toString()) + .create().create(); + } + public static Keychain.UsageBuilder withKeychains(KeychainWithCertsSpec... keychains) { return withKeychains(Stream.of(keychains).map(KeychainWithCertsSpec::keychain).toArray(Keychain[]::new)); } @@ -1100,9 +1147,14 @@ public final class MacSign { public static void withKeychain(Consumer consumer, Consumer mutator, ResolvedKeychain keychain) { Objects.requireNonNull(consumer); - withKeychains(() -> { + Objects.requireNonNull(mutator); + if (keychain.isMock()) { consumer.accept(keychain); - }, mutator, keychain.spec().keychain()); + } else { + withKeychains(() -> { + consumer.accept(keychain); + }, mutator, keychain.spec().keychain()); + } } public static void withKeychain(Consumer consumer, ResolvedKeychain keychain) { @@ -1111,7 +1163,15 @@ public final class MacSign { public static final class ResolvedKeychain { public ResolvedKeychain(KeychainWithCertsSpec spec) { + isMock = false; this.spec = Objects.requireNonNull(spec); + certMapSupplier = MemoizingSupplier.runOnce(() -> { + return MacSign.mapCertificateRequests(spec); + }); + } + + public static ResolvedKeychain createMock(String name, Map certs) { + return new ResolvedKeychain(name, certs); } public KeychainWithCertsSpec spec() { @@ -1122,15 +1182,30 @@ public final class MacSign { return spec.keychain().name(); } - public Map mapCertificateRequests() { - if (certMap == null) { - synchronized (this) { - if (certMap == null) { - certMap = MacSign.mapCertificateRequests(spec); - } - } + public boolean isMock() { + return isMock; + } + + public ResolvedKeychain toMock(Map signEnv) { + if (isMock) { + throw new UnsupportedOperationException("Already a mock"); } - return certMap; + + var comm = Comm.compare(Set.copyOf(spec.certificateRequests()), signEnv.keySet()); + if (!comm.unique1().isEmpty()) { + throw new IllegalArgumentException(String.format( + "Signing environment missing %s certificate request mappings in [%s] keychain", + comm.unique1(), name())); + } + + var certs = new HashMap<>(signEnv); + certs.keySet().retainAll(comm.common()); + + return createMock(name(), certs); + } + + public Map mapCertificateRequests() { + return certMapSupplier.get(); } public Function asCertificateResolver() { @@ -1145,8 +1220,23 @@ public final class MacSign { }; } + private ResolvedKeychain(String name, Map certs) { + + var keychainBuilder = KeychainWithCertsSpec.build().name(Objects.requireNonNull(name)); + certs.keySet().forEach(keychainBuilder::addCert); + + var certsCopy = Map.copyOf(Objects.requireNonNull(certs)); + + isMock = true; + spec = keychainBuilder.create(); + certMapSupplier = MemoizingSupplier.runOnce(() -> { + return certsCopy; + }); + } + + private final boolean isMock; private final KeychainWithCertsSpec spec; - private volatile Map certMap; + private final Supplier> certMapSupplier; } private static Map mapCertificateRequests(KeychainWithCertsSpec spec) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandAction.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandAction.java index d9ab38e006a..c4899a5376f 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandAction.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandAction.java @@ -60,6 +60,36 @@ public interface CommandAction { public MockIllegalStateException unexpectedArguments() { return new MockIllegalStateException(String.format("Unexpected arguments: %s", args)); } + + public Context shift(int count) { + if (count < 0) { + throw new IllegalArgumentException(); + } else if (count == 0) { + return this; + } else { + return new Context(out, err, args.subList(Integer.min(count, args.size()), args.size())); + } + } + + public Context shift() { + return shift(1); + } + + public void printlnOut(Object obj) { + out.println(obj); + } + + public void printlnOut(String str) { + out.println(str); + } + + public void printlnErr(Object obj) { + err.println(obj); + } + + public void printlnErr(String str) { + err.println(str); + } } /** diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIllegalStateException.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIllegalStateException.java index 1817587364a..4656e1d4f7b 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIllegalStateException.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIllegalStateException.java @@ -22,13 +22,15 @@ */ package jdk.jpackage.test.mock; +import java.util.Objects; + /** * Indicates command mock internal error. */ public final class MockIllegalStateException extends IllegalStateException { public MockIllegalStateException(String msg) { - super(msg); + super(Objects.requireNonNull(msg)); } private static final long serialVersionUID = 1L; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/MacSecurityMock.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/MacSecurityMock.java new file mode 100644 index 00000000000..fe9a67f1889 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/MacSecurityMock.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.test.stdmock; + +import java.nio.file.Path; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.jpackage.test.MacSign; +import jdk.jpackage.test.MacSign.CertificateRequest; +import jdk.jpackage.test.MacSign.ResolvedKeychain; +import jdk.jpackage.test.mock.CommandAction; +import jdk.jpackage.test.mock.MockIllegalStateException; + +/** + * Mocks /usr/bin/security command. + */ +final class MacSecurityMock implements CommandAction { + + MacSecurityMock(MacSignMockUtils.SignEnv signEnv) { + Objects.requireNonNull(signEnv); + + var keychains = signEnv.keychains(); + + var stdUserKeychains = Stream.of(StandardKeychain.values()).map(StandardKeychain::keychainName).filter(name -> { + // Add standard keychain unless it is defined in the signing environment. + return keychains.stream().noneMatch(keychain -> { + return keychain.name().equals(name); + }); + }).map(name -> { + // Assume the standard keychain is empty. + return ResolvedKeychain.createMock(name, Map.of()); + }); + + allKnownKeychains = Stream.of( + stdUserKeychains, + keychains.stream() + ).flatMap(x -> x).collect(Collectors.toUnmodifiableMap(ResolvedKeychain::name, x -> x)); + + currentKeychains.addAll(Stream.of(StandardKeychain.values()) + .map(StandardKeychain::keychainName) + .map(allKnownKeychains::get) + .map(Objects::requireNonNull).toList()); + } + + @Override + public Optional run(Context context) { + switch (context.args().getFirst()) { + case "list-keychains" -> { + listKeychains(context.shift()); + return Optional.of(0); + } + case "find-certificate" -> { + findCertificate(context.shift()); + return Optional.of(0); + } + default -> { + throw context.unexpectedArguments(); + } + } + } + + private void listKeychains(Context context) { + if (context.args().getFirst().equals("-s")) { + currentKeychains.clear(); + currentKeychains.addAll(context.shift().args().stream().map(name -> { + return Optional.ofNullable(allKnownKeychains.get(name)).orElseThrow(() -> { + throw new MockIllegalStateException(String.format("Unknown keychain name: %s", name)); + }); + }).toList()); + } else if (context.args().isEmpty()) { + currentKeychains.stream().map(keychain -> { + return String.format(" \"%s\"", keychain.name()); + }).forEach(context::printlnOut); + } else { + throw context.unexpectedArguments(); + } + } + + private void findCertificate(Context context) { + + var args = new ArrayList<>(context.args()); + for (var mandatoryArg : List.of("-p", "-a")) { + if (!args.remove(mandatoryArg)) { + throw context.unexpectedArguments(); + } + } + + var certNameFilter = context.findOptionValue("-c").map(certNameSubstr -> { + + // Remove option name and its value. + var idx = args.indexOf("-c"); + args.remove(idx); + args.remove(idx); + + Predicate> pred = e -> { + return e.getKey().name().contains(certNameSubstr); + }; + return pred; + }); + + Stream keychains; + if (args.isEmpty()) { + keychains = currentKeychains.stream(); + } else { + // Remaining arguments must be keychain names. + keychains = args.stream().map(keychainName -> { + return Optional.ofNullable(allKnownKeychains.get(keychainName)).orElseThrow(() -> { + throw new MockIllegalStateException(String.format("Unknown keychain name: %s", keychainName)); + }); + }); + } + + var certStream = keychains.flatMap(keychain -> { + return keychain.mapCertificateRequests().entrySet().stream(); + }); + + if (certNameFilter.isPresent()) { + certStream = certStream.filter(certNameFilter.get()); + } + + certStream.map(Map.Entry::getValue).map(MacSign::formatX509Certificate).forEach(formattedCert -> { + context.out().print(formattedCert); + }); + } + + // Keep the order of the items as the corresponding keychains appear + // in the output of the "/usr/bin/security list-keychains" command. + private enum StandardKeychain { + USER_KEYCHAIN { + @Override + String keychainName() { + return Path.of(System.getProperty("user.home")).resolve("Library/Keychains/login.keychain-db").toString(); + } + }, + SYSTEM_KEYCHAIN { + @Override + String keychainName() { + return "/Library/Keychains/System.keychain"; + } + }, + ; + + abstract String keychainName(); + } + + private final List currentKeychains = new ArrayList(); + private final Map allKnownKeychains; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/MacSignMockUtils.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/MacSignMockUtils.java new file mode 100644 index 00000000000..164261a6000 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/MacSignMockUtils.java @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.test.stdmock; + +import static jdk.jpackage.internal.util.function.ExceptionBox.toUnchecked; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; +import static jdk.jpackage.internal.util.function.ThrowingRunnable.toRunnable; +import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.test.MacSign.CertificateRequest; +import jdk.jpackage.test.MacSign.KeychainWithCertsSpec; +import jdk.jpackage.test.MacSign.ResolvedKeychain; +import jdk.jpackage.test.mock.CommandActionSpec; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMockSpec; + + +/** + * Utilities to create macOS signing tool mocks. + */ +public final class MacSignMockUtils { + + private MacSignMockUtils() { + } + + public static Map resolveCertificateRequests( + Collection certificateRequests) { + Objects.requireNonNull(certificateRequests); + + var caKeys = createKeyPair(); + + Function resolver = toFunction(certRequest -> { + var builder = new CertificateBuilder() + .setSubjectName("CN=" + certRequest.name()) + .setPublicKey(caKeys.getPublic()) + .setSerialNumber(BigInteger.ONE) + .addSubjectKeyIdExt(caKeys.getPublic()) + .addAuthorityKeyIdExt(caKeys.getPublic()); + + Instant from; + Instant to; + if (certRequest.expired()) { + from = LocalDate.now().minusDays(10).atStartOfDay(ZoneId.systemDefault()).toInstant(); + to = from.plus(Duration.ofDays(1)); + } else { + from = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant(); + to = from.plus(Duration.ofDays(certRequest.days())); + } + builder.setValidity(Date.from(from), Date.from(to)); + + return builder.build(null, caKeys.getPrivate()); + }); + + return certificateRequests.stream() + .distinct() + .collect(Collectors.toUnmodifiableMap(x -> x, resolver)); + } + + public static Map resolveCertificateRequests( + CertificateRequest... certificateRequests) { + return resolveCertificateRequests(List.of(certificateRequests)); + } + + public static final class SignEnv { + + public SignEnv(List spec) { + Objects.requireNonNull(spec); + + spec.stream().map(keychain -> { + return keychain.keychain().name(); + }).collect(Collectors.toMap(x -> x, x -> x, (a, b) -> { + throw new IllegalArgumentException(String.format("Multiple keychains with the same name: %s", a)); + })); + + this.spec = List.copyOf(spec); + this.env = resolveCertificateRequests( + spec.stream().map(KeychainWithCertsSpec::certificateRequests).flatMap(Collection::stream).toList()); + } + + public SignEnv(KeychainWithCertsSpec... spec) { + this(List.of(spec)); + } + + public List keychains() { + return spec.stream().map(ResolvedKeychain::new).map(keychain -> { + return keychain.toMock(env); + }).toList(); + } + + public Map env() { + return env; + } + + private final Map env; + private final List spec; + } + + public static CommandMockSpec securityMock(SignEnv signEnv) { + var action = CommandActionSpec.create("/usr/bin/security", new MacSecurityMock(signEnv)); + return new CommandMockSpec(action.description(), CommandActionSpecs.build().action(action).create()); + } + + private static KeyPair createKeyPair() { + try { + var kpg = KeyPairGenerator.getInstance("RSA"); + return kpg.generateKeyPair(); + } catch (NoSuchAlgorithmException ex) { + throw ExceptionBox.toUnchecked(ex); + } + } + + // + // Reflection proxy for jdk.test.lib.security.CertificateBuilder class. + // + // Can't use it directly because it is impossible to cherry-pick this class from the JDK test lib in JUnit tests due to limitations of jtreg. + // + // Shared jpackage JUnit tests don't require "jdk.jpackage.test.stdmock", but they depend on "jdk.jpackage.test" package. + // Source code for these two packages resides in the same directory tree, so jtreg will pull in classes from both packages for the jpackage JUnit tests. + // Static dependency on jdk.test.lib.security.CertificateBuilder class will force pulling in the entire JDK test lib, because of jtreg limitations. + // + // Use dynamic dependency as a workaround. Tests that require jdk.test.lib.security.CertificateBuilder class, should have + // + // /* + // * ... + // * @library /test/lib + // * @build jdk.test.lib.security.CertificateBuilder + // */ + // + // in their declarations. They also should have + // + // --add-exports java.base/sun.security.x509=ALL-UNNAMED + // --add-exports java.base/sun.security.util=ALL-UNNAMED + // + // on javac and java command lines. + // + private static final class CertificateBuilder { + + CertificateBuilder() { + instance = toSupplier(ctor::newInstance).get(); + } + + CertificateBuilder setSubjectName(String v) { + toRunnable(() -> { + setSubjectName.invoke(instance, v); + }).run(); + return this; + } + + CertificateBuilder setPublicKey(PublicKey v) { + toRunnable(() -> { + setPublicKey.invoke(instance, v); + }).run(); + return this; + } + + CertificateBuilder setSerialNumber(BigInteger v) { + toRunnable(() -> { + setSerialNumber.invoke(instance, v); + }).run(); + return this; + } + + CertificateBuilder addSubjectKeyIdExt(PublicKey v) { + toRunnable(() -> { + addSubjectKeyIdExt.invoke(instance, v); + }).run(); + return this; + } + + CertificateBuilder addAuthorityKeyIdExt(PublicKey v) { + toRunnable(() -> { + addAuthorityKeyIdExt.invoke(instance, v); + }).run(); + return this; + } + + CertificateBuilder setValidity(Date from, Date to) { + toRunnable(() -> { + setValidity.invoke(instance, from, to); + }).run(); + return this; + } + + X509Certificate build(X509Certificate issuerCert, PrivateKey issuerKey) throws IOException, CertificateException { + try { + return (X509Certificate)toSupplier(() -> { + return build.invoke(instance, issuerCert, issuerKey); + }).get(); + } catch (ExceptionBox box) { + switch (ExceptionBox.unbox(box)) { + case IOException ex -> { + throw ex; + } + case CertificateException ex -> { + throw ex; + } + default -> { + throw box; + } + } + } + } + + private final Object instance; + + private static final Constructor ctor; + private static final Method setSubjectName; + private static final Method setPublicKey; + private static final Method setSerialNumber; + private static final Method addSubjectKeyIdExt; + private static final Method addAuthorityKeyIdExt; + private static final Method setValidity; + private static final Method build; + + static { + try { + var certificateBuilderClass = Class.forName("jdk.test.lib.security.CertificateBuilder"); + + ctor = certificateBuilderClass.getConstructor(); + + setSubjectName = certificateBuilderClass.getMethod("setSubjectName", String.class); + + setPublicKey = certificateBuilderClass.getMethod("setPublicKey", PublicKey.class); + + setSerialNumber = certificateBuilderClass.getMethod("setSerialNumber", BigInteger.class); + + addSubjectKeyIdExt = certificateBuilderClass.getMethod("addSubjectKeyIdExt", PublicKey.class); + + addAuthorityKeyIdExt = certificateBuilderClass.getMethod("addAuthorityKeyIdExt", PublicKey.class); + + setValidity = certificateBuilderClass.getMethod("setValidity", Date.class, Date.class); + + build = certificateBuilderClass.getMethod("build", X509Certificate.class, PrivateKey.class); + + } catch (ClassNotFoundException | NoSuchMethodException | SecurityException ex) { + throw toUnchecked(ex); + } + } + } +} diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes index 40f73e624b6..0b98c051238 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes @@ -39,6 +39,16 @@ ErrorTest.test(WIN_MSI; app-desc=Hello; args-add=[--app-version, 1234]; errors=[ ErrorTest.test(WIN_MSI; app-desc=Hello; args-add=[--app-version, 256.1]; errors=[message.error-header+[error.msi-product-version-major-out-of-range, 256.1], message.advice-header+[error.version-string-wrong-format.advice]]) ErrorTest.test(WIN_MSI; app-desc=Hello; args-add=[--launcher-as-service]; errors=[message.error-header+[error.missing-service-installer], message.advice-header+[error.missing-service-installer.advice]]) ErrorTest.test(args-add=[@foo]; errors=[message.error-header+[ERR_CannotParseOptions, foo]]) +ErrorTest.testMacSignWithoutIdentity(IMAGE; app-desc=Hello; args-add=[--mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN]]) +ErrorTest.testMacSignWithoutIdentity(IMAGE; args-add=[--app-image, @@APP_IMAGE_WITH_SHORT_NAME@@, --mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN]]) +ErrorTest.testMacSignWithoutIdentity(MAC_DMG; app-desc=Hello; args-add=[--mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN]]) +ErrorTest.testMacSignWithoutIdentity(MAC_DMG; args-add=[--app-image, @@APP_IMAGE_WITH_SHORT_NAME@@, --mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN]]) +ErrorTest.testMacSignWithoutIdentity(MAC_PKG; app-desc=Hello; args-add=[--mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN], message.error-header+[error.cert.not.found, INSTALLER, EMPTY_KEYCHAIN]]) +ErrorTest.testMacSignWithoutIdentity(MAC_PKG; app-desc=Hello; args-add=[--mac-sign, --mac-signing-keychain, @@KEYCHAIN_WITH_APP_IMAGE_CERT@@]; args-del=[--name]; errors=[message.error-header+[error.cert.not.found, INSTALLER, KEYCHAIN_WITH_APP_IMAGE_CERT]]) +ErrorTest.testMacSignWithoutIdentity(MAC_PKG; app-desc=Hello; args-add=[--mac-sign, --mac-signing-keychain, @@KEYCHAIN_WITH_PKG_CERT@@]; args-del=[--name]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, KEYCHAIN_WITH_PKG_CERT]]) +ErrorTest.testMacSignWithoutIdentity(MAC_PKG; args-add=[--app-image, @@APP_IMAGE_WITH_SHORT_NAME@@, --mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN], message.error-header+[error.cert.not.found, INSTALLER, EMPTY_KEYCHAIN]]) +ErrorTest.testMacSignWithoutIdentity(MAC_PKG; args-add=[--mac-sign, --mac-signing-keychain, @@KEYCHAIN_WITH_APP_IMAGE_CERT@@, --app-image, @@APP_IMAGE_WITH_SHORT_NAME@@]; errors=[message.error-header+[error.cert.not.found, INSTALLER, KEYCHAIN_WITH_APP_IMAGE_CERT]]) +ErrorTest.testMacSignWithoutIdentity(MAC_PKG; args-add=[--mac-sign, --mac-signing-keychain, @@KEYCHAIN_WITH_PKG_CERT@@, --app-image, @@APP_IMAGE_WITH_SHORT_NAME@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, KEYCHAIN_WITH_PKG_CERT]]) ErrorTest.testMacSigningIdentityValidation(IMAGE, --mac-app-image-sign-identity, true) ErrorTest.testMacSigningIdentityValidation(IMAGE, --mac-signing-key-user-name, false) ErrorTest.testMacSigningIdentityValidation(MAC_DMG, --mac-app-image-sign-identity, true) diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java index d71cf7c4d41..b179f32447f 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java @@ -458,7 +458,7 @@ public class CommandOutputControlTest { processDestroyer.get().join(); } - @DisabledOnOs(value = OS.MAC, disabledReason = "Closing a stream doesn't consistently cause a trouble as it should") + @DisabledOnOs(value = {OS.MAC, OS.LINUX}, disabledReason = "Closing a stream doesn't consistently cause a trouble as expected") @ParameterizedTest @EnumSource(OutputStreams.class) public void test_close_streams(OutputStreams action) throws InterruptedException, IOException { diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java index 0f299cb5a24..3a257a425b6 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java @@ -82,10 +82,17 @@ public class SigningAppImageTest { SigningBase.StandardCertificateRequest.CODESIGN_UNICODE )) { for (var signIdentityType : SignKeyOption.Type.defaultValues()) { - data.add(new SignKeyOptionWithKeychain( - signIdentityType, - certRequest, - SigningBase.StandardKeychain.MAIN.keychain())); + SigningBase.StandardKeychain keychain; + if (signIdentityType == SignKeyOption.Type.SIGN_KEY_IMPLICIT) { + keychain = SigningBase.StandardKeychain.SINGLE; + if (!keychain.contains(certRequest)) { + continue; + } + } else { + keychain = SigningBase.StandardKeychain.MAIN; + } + + data.add(new SignKeyOptionWithKeychain(signIdentityType, certRequest, keychain.keychain())); } } diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java index 2c7ae205e69..a6d94b59bd9 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java @@ -93,6 +93,11 @@ public class SigningAppImageTwoStepsTest { return new TestSpec(Optional.ofNullable(signAppImage), sign); } + Builder keychain(SigningBase.StandardKeychain v) { + keychain = Objects.requireNonNull(v); + return this; + } + Builder certRequest(SigningBase.StandardCertificateRequest v) { certRequest = Objects.requireNonNull(v); return this; @@ -117,9 +122,10 @@ public class SigningAppImageTwoStepsTest { return new SignKeyOptionWithKeychain( signIdentityType, certRequest, - SigningBase.StandardKeychain.MAIN.keychain()); + keychain.keychain()); } + private SigningBase.StandardKeychain keychain = SigningBase.StandardKeychain.MAIN; private SigningBase.StandardCertificateRequest certRequest = SigningBase.StandardCertificateRequest.CODESIGN; private SignKeyOption.Type signIdentityType = SignKeyOption.Type.SIGN_KEY_IDENTITY; @@ -144,18 +150,26 @@ public class SigningAppImageTwoStepsTest { }, signOption.keychain()); }, appImageCmd::execute); - var cmd = new JPackageCommand() - .setPackageType(PackageType.IMAGE) - .addArguments("--app-image", appImageCmd.outputBundle()) - .mutate(sign::addTo); + MacSign.withKeychain(keychain -> { + var cmd = new JPackageCommand() + .setPackageType(PackageType.IMAGE) + .addArguments("--app-image", appImageCmd.outputBundle()) + .mutate(sign::addTo); - cmd.executeAndAssertHelloAppImageCreated(); - MacSignVerify.verifyAppImageSigned(cmd, sign.certRequest()); + cmd.executeAndAssertHelloAppImageCreated(); + MacSignVerify.verifyAppImageSigned(cmd, sign.certRequest()); + }, sign.keychain()); } } public static Collection test() { + var signIdentityTypes = List.of( + SignKeyOption.Type.SIGN_KEY_USER_SHORT_NAME, + SignKeyOption.Type.SIGN_KEY_IDENTITY_APP_IMAGE, + SignKeyOption.Type.SIGN_KEY_IMPLICIT + ); + List data = new ArrayList<>(); for (var appImageSign : withAndWithout(SignKeyOption.Type.SIGN_KEY_IDENTITY)) { @@ -167,9 +181,12 @@ public class SigningAppImageTwoStepsTest { .certRequest(SigningBase.StandardCertificateRequest.CODESIGN_ACME_TECH_LTD) .signAppImage(); }); - for (var signIdentityType : SignKeyOption.Type.defaultValues()) { + for (var signIdentityType : signIdentityTypes) { builder.signIdentityType(signIdentityType) .certRequest(SigningBase.StandardCertificateRequest.CODESIGN); + if (signIdentityType == SignKeyOption.Type.SIGN_KEY_IMPLICIT) { + builder.keychain(SigningBase.StandardKeychain.SINGLE); + } data.add(builder.sign().create()); } } diff --git a/test/jdk/tools/jpackage/macosx/SigningBase.java b/test/jdk/tools/jpackage/macosx/SigningBase.java index 5f4367e6096..e7e2d7e44cf 100644 --- a/test/jdk/tools/jpackage/macosx/SigningBase.java +++ b/test/jdk/tools/jpackage/macosx/SigningBase.java @@ -38,7 +38,7 @@ import jdk.jpackage.test.TKit; * @test * @summary Setup the environment for jpackage macos signing tests. * Creates required keychains and signing identities. - * Does NOT run any jpackag tests. + * Does NOT run any jpackage tests. * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @compile -Xlint:all -Werror SigningBase.java @@ -51,7 +51,7 @@ import jdk.jpackage.test.TKit; * @test * @summary Tear down the environment for jpackage macos signing tests. * Deletes required keychains and signing identities. - * Does NOT run any jpackag tests. + * Does NOT run any jpackage tests. * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @compile -Xlint:all -Werror SigningBase.java @@ -83,7 +83,7 @@ public class SigningBase { } private static CertificateRequest.Builder cert() { - return new CertificateRequest.Builder(); + return CertificateRequest.build(); } private final CertificateRequest spec; @@ -118,6 +118,12 @@ public class SigningBase { StandardCertificateRequest.PKG, StandardCertificateRequest.CODESIGN_COPY, StandardCertificateRequest.PKG_COPY), + /** + * A keychain with a single certificate for each role. + */ + SINGLE("jpackagerTest-single.keychain", + StandardCertificateRequest.CODESIGN, + StandardCertificateRequest.PKG), ; StandardKeychain(String keychainName, StandardCertificateRequest... certs) { @@ -145,7 +151,7 @@ public class SigningBase { } private static KeychainWithCertsSpec.Builder keychain(String name) { - return new KeychainWithCertsSpec.Builder().name(name); + return KeychainWithCertsSpec.build().name(name); } private static List signingEnv() { @@ -164,13 +170,13 @@ public class SigningBase { } public static void verifySignTestEnvReady() { - if (!Inner.SIGN_ENV_READY) { + if (!SignEnvReady.VALUE) { TKit.throwSkippedException(new IllegalStateException("Misconfigured signing test environment")); } } - private final class Inner { - private static final boolean SIGN_ENV_READY = MacSign.isDeployed(StandardKeychain.signingEnv()); + private final class SignEnvReady { + static final boolean VALUE = MacSign.isDeployed(StandardKeychain.signingEnv()); } private static final String NAME_ASCII = "jpackage.openjdk.java.net"; diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java index 8a93ce5f749..d1c17fb61cd 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java @@ -94,7 +94,7 @@ public class SigningPackageTest { } public static Collection test() { - return TestSpec.testCases(true).stream().map(v -> { + return TestSpec.testCases().stream().map(v -> { return new Object[] {v}; }).toList(); } @@ -126,7 +126,10 @@ public class SigningPackageTest { } if (appImageSignOption.isEmpty()) { - if (packageSignOption.get().type() != SignKeyOption.Type.SIGN_KEY_IDENTITY) { + if (!List.of( + SignKeyOption.Type.SIGN_KEY_IDENTITY, + SignKeyOption.Type.SIGN_KEY_IDENTITY_SHA1 + ).contains(packageSignOption.get().type())) { // They request to sign the .pkg installer without // the "--mac-installer-sign-identity" option, // but didn't specify a signing option for the packaged app image. @@ -204,15 +207,61 @@ public class SigningPackageTest { } MacSign.ResolvedKeychain keychain() { - return SigningBase.StandardKeychain.MAIN.keychain(); + return chooseKeychain(Stream.of( + appImageSignOption.stream(), + packageSignOption.stream() + ).flatMap(x -> x).map(SignKeyOption::type).findFirst().orElseThrow()).keychain(); } - static List testCases(boolean withUnicode) { + /** + * Types of test cases to skip. + */ + enum SkipTestCases { + /** + * Skip test cases with signing identities/key names with symbols outside of the + * ASCII codepage. + */ + SKIP_UNICODE, + /** + * Skip test cases in which the value of the "--mac-signing-key-user-name" + * option is the full signing identity name. + */ + SKIP_SIGN_KEY_USER_FULL_NAME, + /** + * Skip test cases in which the value of the "--mac-installer-sign-identity" or + * "--mac-app-image-sign-identity" option is the SHA1 digest of the signing + * certificate. + */ + SKIP_SIGN_KEY_IDENTITY_SHA1, + ; + } + + static List minimalTestCases() { + return testCases(SkipTestCases.values()); + } + + static List testCases(SkipTestCases... skipTestCases) { + + final var skipTestCasesAsSet = Set.of(skipTestCases); + + final var signIdentityTypes = Stream.of(SignKeyOption.Type.defaultValues()).filter(v -> { + switch (v) { + case SIGN_KEY_USER_FULL_NAME -> { + return !skipTestCasesAsSet.contains(SkipTestCases.SKIP_SIGN_KEY_USER_FULL_NAME); + } + case SIGN_KEY_IDENTITY_SHA1 -> { + return !skipTestCasesAsSet.contains(SkipTestCases.SKIP_SIGN_KEY_IDENTITY_SHA1); + } + default -> { + return true; + } + } + }).toList(); List data = new ArrayList<>(); List> certRequestGroups; - if (withUnicode) { + if (!skipTestCasesAsSet.contains(SkipTestCases.SKIP_UNICODE)) { certRequestGroups = List.of( List.of(SigningBase.StandardCertificateRequest.CODESIGN, SigningBase.StandardCertificateRequest.PKG), List.of(SigningBase.StandardCertificateRequest.CODESIGN_UNICODE, SigningBase.StandardCertificateRequest.PKG_UNICODE) @@ -224,19 +273,33 @@ public class SigningPackageTest { } for (var certRequests : certRequestGroups) { - for (var signIdentityType : SignKeyOption.Type.defaultValues()) { - var keychain = SigningBase.StandardKeychain.MAIN.keychain(); + for (var signIdentityType : signIdentityTypes) { + if (signIdentityType == SignKeyOption.Type.SIGN_KEY_IMPLICIT + && !SigningBase.StandardKeychain.SINGLE.contains(certRequests.getFirst())) { + // Skip invalid test case: the keychain for testing signing without + // an explicitly specified signing key option doesn't have this signing key. + break; + } + + if (signIdentityType.passThrough() && !certRequests.contains(SigningBase.StandardCertificateRequest.CODESIGN)) { + // Using a pass-through signing option. + // Doesn't make sense to waste time on testing it with multiple certificates. + // Skip the test cases using non "default" certificate. + break; + } + + var keychain = chooseKeychain(signIdentityType).keychain(); var appImageSignKeyOption = new SignKeyOption(signIdentityType, certRequests.getFirst(), keychain); var pkgSignKeyOption = new SignKeyOption(signIdentityType, certRequests.getLast(), keychain); switch (signIdentityType) { - case SIGN_KEY_IDENTITY -> { + case SIGN_KEY_IDENTITY, SIGN_KEY_IDENTITY_SHA1 -> { // Use "--mac-installer-sign-identity" and "--mac-app-image-sign-identity" signing options. // They allows to sign the packaged app image and the installer (.pkg) separately. data.add(new TestSpec(Optional.of(appImageSignKeyOption), Optional.empty(), PackageType.MAC)); data.add(new TestSpec(Optional.empty(), Optional.of(pkgSignKeyOption), PackageType.MAC_PKG)); } - case SIGN_KEY_USER_SHORT_NAME -> { + case SIGN_KEY_USER_SHORT_NAME, SIGN_KEY_IMPLICIT -> { // Use "--mac-signing-key-user-name" signing option with short user name or implicit signing option. // It signs both the packaged app image and the installer (.pkg). // Thus, if the installer is not signed, it can be used only with .dmg packaging. @@ -263,5 +326,13 @@ public class SigningPackageTest { return data; } + + private static SigningBase.StandardKeychain chooseKeychain(SignKeyOption.Type signIdentityType) { + if (signIdentityType == SignKeyOption.Type.SIGN_KEY_IMPLICIT) { + return SigningBase.StandardKeychain.SINGLE; + } else { + return SigningBase.StandardKeychain.MAIN; + } + } } } diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java index 23195c9a856..d0de9c8c704 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java @@ -108,7 +108,7 @@ public class SigningPackageTwoStepTest { appImageSignOption = Optional.empty(); } - for (var signPackage : SigningPackageTest.TestSpec.testCases(false)) { + for (var signPackage : SigningPackageTest.TestSpec.minimalTestCases()) { data.add(new TwoStepsTestSpec(appImageSignOption, signPackage)); } } diff --git a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java index 604399ebd7a..54021aa2a0b 100644 --- a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java @@ -125,7 +125,7 @@ public class SigningRuntimeImagePackageTest { runtimeSignOption = Optional.empty(); } - for (var signPackage : SigningPackageTest.TestSpec.testCases(false)) { + for (var signPackage : SigningPackageTest.TestSpec.minimalTestCases()) { data.add(new RuntimeTestSpec(runtimeSignOption, runtimeType, signPackage)); } } diff --git a/test/jdk/tools/jpackage/share/ErrorTest.java b/test/jdk/tools/jpackage/share/ErrorTest.java index 86390a85068..2133823381c 100644 --- a/test/jdk/tools/jpackage/share/ErrorTest.java +++ b/test/jdk/tools/jpackage/share/ErrorTest.java @@ -26,7 +26,7 @@ import static java.util.stream.Collectors.toMap; import static jdk.internal.util.OperatingSystem.LINUX; import static jdk.internal.util.OperatingSystem.MACOS; import static jdk.internal.util.OperatingSystem.WINDOWS; -import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; +import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import static jdk.jpackage.test.JPackageCommand.makeAdvice; import static jdk.jpackage.test.JPackageCommand.makeError; @@ -45,6 +45,7 @@ import java.util.function.UnaryOperator; import java.util.regex.Pattern; import java.util.stream.IntStream; import java.util.stream.Stream; +import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.util.TokenReplace; import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.ParameterSupplier; @@ -53,14 +54,27 @@ import jdk.jpackage.test.CannedArgument; import jdk.jpackage.test.CannedFormattedString; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JPackageOutputValidator; +import jdk.jpackage.test.MacSign; +import jdk.jpackage.test.MacSign.CertificateRequest; +import jdk.jpackage.test.MacSign.CertificateType; +import jdk.jpackage.test.MacSign.KeychainWithCertsSpec; +import jdk.jpackage.test.MacSign.ResolvedKeychain; +import jdk.jpackage.test.MacSign.StandardCertificateNamePrefix; import jdk.jpackage.test.PackageType; import jdk.jpackage.test.TKit; +import jdk.jpackage.test.mock.Script; +import jdk.jpackage.test.mock.VerbatimCommandMock; +import jdk.jpackage.test.stdmock.JPackageMockUtils; +import jdk.jpackage.test.stdmock.MacSignMockUtils; /* * @test * @summary Test jpackage output for erroneous input * @library /test/jdk/tools/jpackage/helpers + * @library /test/lib * @build jdk.jpackage.test.* + * @build jdk.jpackage.test.stdmock.* + * @build jdk.test.lib.security.CertificateBuilder * @compile -Xlint:all -Werror ErrorTest.java * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=ErrorTest @@ -71,7 +85,10 @@ import jdk.jpackage.test.TKit; * @test * @summary Test jpackage output for erroneous input * @library /test/jdk/tools/jpackage/helpers + * @library /test/lib * @build jdk.jpackage.test.* + * @build jdk.jpackage.test.stdmock.* + * @build jdk.test.lib.security.CertificateBuilder * @compile -Xlint:all -Werror ErrorTest.java * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=ErrorTest @@ -81,10 +98,10 @@ import jdk.jpackage.test.TKit; public final class ErrorTest { enum Token { - JAVA_HOME(cmd -> { + JAVA_HOME(() -> { return System.getProperty("java.home"); }), - APP_IMAGE(cmd -> { + APP_IMAGE(() -> { final var appImageRoot = TKit.createTempDirectory("appimage"); final var appImageCmd = JPackageCommand.helloAppImage() @@ -92,28 +109,44 @@ public final class ErrorTest { appImageCmd.execute(); - return appImageCmd.outputBundle().toString(); + return appImageCmd.outputBundle(); }), - INVALID_MAC_RUNTIME_BUNDLE(toFunction(cmd -> { + APP_IMAGE_WITH_SHORT_NAME(() -> { + final var appImageRoot = TKit.createTempDirectory("appimage"); + + final var appImageCmd = JPackageCommand.helloAppImage() + .setFakeRuntime().setArgumentValue("--dest", appImageRoot); + + // Let jpackage pick the name from the main class (Hello). It qualifies as the "short" name. + appImageCmd.removeArgumentWithValue("--name"); + + appImageCmd.execute(); + + return appImageCmd.outputBundle(); + }), + INVALID_MAC_RUNTIME_BUNDLE(toSupplier(() -> { // Has "Contents/MacOS/libjli.dylib", but missing "Contents/Home/lib/libjli.dylib". final Path root = TKit.createTempDirectory("mac-invalid-runtime-bundle"); Files.createDirectories(root.resolve("Contents/Home")); Files.createFile(root.resolve("Contents/Info.plist")); Files.createDirectories(root.resolve("Contents/MacOS")); Files.createFile(root.resolve("Contents/MacOS/libjli.dylib")); - return root.toString(); + return root; })), - INVALID_MAC_RUNTIME_IMAGE(toFunction(cmd -> { + INVALID_MAC_RUNTIME_IMAGE(toSupplier(() -> { // Has some files in the "lib" subdirectory, but doesn't have the "lib/libjli.dylib" file. final Path root = TKit.createTempDirectory("mac-invalid-runtime-image"); Files.createDirectories(root.resolve("lib")); Files.createFile(root.resolve("lib/foo")); - return root.toString(); + return root; })), - EMPTY_DIR(toFunction(cmd -> { + EMPTY_DIR(() -> { return TKit.createTempDirectory("empty-dir"); - })), + }), ADD_LAUNCHER_PROPERTY_FILE, + EMPTY_KEYCHAIN, + KEYCHAIN_WITH_APP_IMAGE_CERT, + KEYCHAIN_WITH_PKG_CERT, ; private Token() { @@ -124,6 +157,12 @@ public final class ErrorTest { this.valueSupplier = Optional.of(valueSupplier); } + private Token(Supplier valueSupplier) { + this(_ -> { + return valueSupplier.get(); + }); + } + String token() { return makeToken(name()); } @@ -558,7 +597,7 @@ public final class ErrorTest { } public static Collection invalidAppVersion() { - return fromTestSpecBuilders(Stream.of( + return toTestArgs(Stream.of( // Invalid app version. Just cover all different error messages. // Extensive testing of invalid version strings is done in DottedVersionTest unit test. testSpec().addArgs("--app-version", "").error("error.version-string-empty"), @@ -601,7 +640,7 @@ public final class ErrorTest { argsStream = Stream.concat(argsStream, Stream.of(List.of("--win-console"))); } - return fromTestSpecBuilders(argsStream.map(args -> { + return toTestArgs(argsStream.map(args -> { var builder = testSpec().noAppDesc().nativeType() .addArgs("--runtime-image", Token.JAVA_HOME.token()) .addArgs(args); @@ -629,7 +668,7 @@ public final class ErrorTest { } public static Collection testAdditionLaunchers() { - return fromTestSpecBuilders(Stream.of( + return toTestArgs(Stream.of( testSpec().addArgs("--add-launcher", Token.ADD_LAUNCHER_PROPERTY_FILE.token()) .error("error.parameter-add-launcher-malformed", Token.ADD_LAUNCHER_PROPERTY_FILE, "--add-launcher"), testSpec().removeArgs("--name").addArgs("--name", "foo", "--add-launcher", "foo=" + Token.ADD_LAUNCHER_PROPERTY_FILE.token()) @@ -637,6 +676,184 @@ public final class ErrorTest { )); } + @Test(ifOS = MACOS) + @ParameterSupplier + @ParameterSupplier("testMacPkgSignWithoutIdentity") + public static void testMacSignWithoutIdentity(TestSpec spec) { + // The test called JPackage Command.useToolProviderBy Default(), + // which alters global variables in the test library, + // so run the test case with a new global state to isolate the alteration of the globals. + TKit.withNewState(() -> { + testMacSignWithoutIdentityWithNewTKitState(spec); + }); + } + + private static void testMacSignWithoutIdentityWithNewTKitState(TestSpec spec) { + final Token keychainToken = spec.expectedMessages().stream().flatMap(cannedStr -> { + return Stream.of(cannedStr.args()).filter(Token.class::isInstance).map(Token.class::cast).filter(token -> { + switch (token) { + case EMPTY_KEYCHAIN, KEYCHAIN_WITH_APP_IMAGE_CERT, KEYCHAIN_WITH_PKG_CERT -> { + return true; + } + default -> { + return false; + } + } + }); + }).distinct().reduce((a, b) -> { + throw new IllegalStateException(String.format( + "Error messages %s reference multiple keychains: %s and %s", spec.expectedMessages(), a, b)); + }).orElseThrow(); + + final ResolvedKeychain keychain; + + switch (keychainToken) { + case EMPTY_KEYCHAIN -> { + keychain = new ResolvedKeychain(new KeychainWithCertsSpec(MacSign.createEmptyKeychain(), List.of())); + } + case KEYCHAIN_WITH_APP_IMAGE_CERT, KEYCHAIN_WITH_PKG_CERT -> { + CertificateType existingCertType; + switch (keychainToken) { + case KEYCHAIN_WITH_APP_IMAGE_CERT -> { + existingCertType = CertificateType.CODE_SIGN; + } + case KEYCHAIN_WITH_PKG_CERT -> { + existingCertType = CertificateType.INSTALLER; + } + default -> { + throw new AssertionError(); + } + } + + keychain = Stream.of(SignEnvMock.SingleCertificateKeychain.values()).filter(k -> { + return k.certificateType() == existingCertType; + }).findFirst().orElseThrow().keychain(); + + var script = Script.build() + // Disable the mutation making mocks "run once". + .commandMockBuilderMutator(null) + // Replace "/usr/bin/security" with the mock bound to the keychain mock. + .map(MacSignMockUtils.securityMock(SignEnvMock.VALUE)) + // Don't mock other external commands. + .use(VerbatimCommandMock.INSTANCE) + .createLoop(); + + // Create jpackage tool provider using the /usr/bin/security mock. + var jpackage = JPackageMockUtils.createJPackageToolProvider(OperatingSystem.MACOS, script); + + // Override the default jpackage tool provider with the one using the /usr/bin/security mock. + JPackageCommand.useToolProviderByDefault(jpackage); + } + default -> { + throw new AssertionError(); + } + } + + MacSign.withKeychain(_ -> { + spec.mapExpectedMessages(cannedStr -> { + return cannedStr.mapArgs(arg -> { + switch (arg) { + case StandardCertificateNamePrefix certPrefix -> { + return certPrefix.value(); + } + case Token _ -> { + return keychain.name(); + } + default -> { + return arg; + } + } + }); + }).test(Map.of(keychainToken, _ -> keychain.name())); + }, keychain); + } + + public static Collection testMacSignWithoutIdentity() { + final List testCases = new ArrayList<>(); + + final var signArgs = List.of("--mac-sign", "--mac-signing-keychain", Token.EMPTY_KEYCHAIN.token()); + final var appImageArgs = List.of("--app-image", Token.APP_IMAGE_WITH_SHORT_NAME.token()); + + for (var withAppImage : List.of(true, false)) { + var builder = testSpec(); + if (withAppImage) { + builder.noAppDesc().addArgs(appImageArgs); + } + builder.addArgs(signArgs); + + for (var type: List.of(PackageType.IMAGE, PackageType.MAC_PKG, PackageType.MAC_DMG)) { + builder.setMessages().error("error.cert.not.found", + MacSign.StandardCertificateNamePrefix.CODE_SIGN, Token.EMPTY_KEYCHAIN); + switch (type) { + case MAC_PKG -> { + // jpackage must report two errors: + // 1. It can't find signing identity to sign the app image + // 2. It can't find signing identity to sign the PKG installer + builder.error("error.cert.not.found", + MacSign.StandardCertificateNamePrefix.INSTALLER, Token.EMPTY_KEYCHAIN); + } + default -> { + // NOP + } + } + var testSpec = builder.type(type).create(); + testCases.add(testSpec); + } + } + + return toTestArgs(testCases); + } + + public static Collection testMacPkgSignWithoutIdentity() { + final List testCases = new ArrayList<>(); + + final var appImageArgs = List.of("--app-image", Token.APP_IMAGE_WITH_SHORT_NAME.token()); + + for (var withAppImage : List.of(true, false)) { + for (var existingCertType : CertificateType.values()) { + Token keychain; + StandardCertificateNamePrefix missingCertificateNamePrefix; + switch (existingCertType) { + case INSTALLER -> { + keychain = Token.KEYCHAIN_WITH_PKG_CERT; + missingCertificateNamePrefix = StandardCertificateNamePrefix.CODE_SIGN; + } + case CODE_SIGN -> { + keychain = Token.KEYCHAIN_WITH_APP_IMAGE_CERT; + missingCertificateNamePrefix = StandardCertificateNamePrefix.INSTALLER; + } + default -> { + throw new AssertionError(); + } + } + + var builder = testSpec() + .type(PackageType.MAC_PKG) + .addArgs("--mac-sign", "--mac-signing-keychain", keychain.token()) + .error("error.cert.not.found", missingCertificateNamePrefix, keychain); + + if (withAppImage) { + builder.noAppDesc().addArgs(appImageArgs); + } else { + /* + * Use shorter name to avoid + * + * [03:08:55.623] --mac-package-name is set to 'MacSignWithoutIdentityErrorTest', which is longer than 16 characters. For a better Mac experience consider shortening it. + * + * in the output. + * The same idea is behind using the "APP_IMAGE_WITH_SHORT_NAME" token + * instead of the "APP_IMAGE" for the predefined app image. + */ + builder.removeArgs("--name"); + } + + testCases.add(builder); + } + } + + return toTestArgs(testCases); + } + @Test @ParameterSupplier("invalidNames") public static void testInvalidAppName(InvalidName name) { @@ -1007,8 +1224,14 @@ public final class ErrorTest { ); } - private static Collection toTestArgs(Stream stream) { - return stream.filter(v -> { + private static Collection toTestArgs(Stream stream) { + return stream.map(v -> { + if (v instanceof TestSpec.Builder builder) { + return builder.create(); + } else { + return v; + } + }).filter(v -> { if (v instanceof TestSpec ts) { return ts.isSupported(); } else { @@ -1019,8 +1242,8 @@ public final class ErrorTest { }).toList(); } - private static Collection fromTestSpecBuilders(Stream stream) { - return toTestArgs(stream.map(TestSpec.Builder::create)); + private static Collection toTestArgs(Collection col) { + return toTestArgs(col.stream()); } private static String adjustTextStreamVerifierArg(String str) { @@ -1028,4 +1251,40 @@ public final class ErrorTest { } private static final Pattern LINE_SEP_REGEXP = Pattern.compile("\\R"); + + private final class SignEnvMock { + + enum SingleCertificateKeychain { + FOO(CertificateType.CODE_SIGN), + BAR(CertificateType.INSTALLER), + ; + + SingleCertificateKeychain(CertificateType certificateType) { + this.keychain = KeychainWithCertsSpec.build() + .name(name().toLowerCase() + ".keychain") + .addCert(CertificateRequest.build() + .userName(name().toLowerCase()) + .type(Objects.requireNonNull(certificateType))) + .create(); + } + + static List signingEnv() { + return Stream.of(values()).map(v -> { + return v.keychain; + }).toList(); + } + + CertificateType certificateType() { + return keychain.certificateRequests().getFirst().type(); + } + + ResolvedKeychain keychain() { + return new ResolvedKeychain(keychain).toMock(VALUE.env()); + } + + private final KeychainWithCertsSpec keychain; + } + + static final MacSignMockUtils.SignEnv VALUE = new MacSignMockUtils.SignEnv(SingleCertificateKeychain.signingEnv()); + } } diff --git a/test/langtools/jdk/javadoc/doclet/testSpecTag/TestSpecTag.java b/test/langtools/jdk/javadoc/doclet/testSpecTag/TestSpecTag.java index e914f8022fd..1c3e8306d7b 100644 --- a/test/langtools/jdk/javadoc/doclet/testSpecTag/TestSpecTag.java +++ b/test/langtools/jdk/javadoc/doclet/testSpecTag/TestSpecTag.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 @@ -23,7 +23,7 @@ /* * @test - * @bug 6251738 8226279 8297802 8305407 + * @bug 6251738 8226279 8297802 8305407 8309748 * @summary JDK-8226279 javadoc should support a new at-spec tag * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool @@ -343,16 +343,15 @@ public class TestSpecTag extends JavadocTester { checkOutput("external-specs.html", true, """ -
\ - \ - \ -
+ +
+
+
External Specifications
+
Specification
@@ -369,7 +368,25 @@ public class TestSpecTag extends JavadocTester { -
"""); +
""", + """ + """); } @Test diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsPositionsOnRecords.java b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsPositionsOnRecords.java index dfa266ef035..b8ee32fcacd 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsPositionsOnRecords.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsPositionsOnRecords.java @@ -78,6 +78,17 @@ public class TypeAnnotationsPositionsOnRecords { record Record6(String t1, @Nullable String t2) { public Record6 {} } + + class Test2 { + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + public @interface Anno {} + + class Foo {} + record Record7(Test2.@Anno Foo foo) { + public Record7 {} // compact constructor + } + } """; public static void main(String... args) throws Exception { @@ -100,6 +111,8 @@ public class TypeAnnotationsPositionsOnRecords { "Record5.class").toUri()), 1); checkClassFile(new File(Paths.get(System.getProperty("user.dir"), "Record6.class").toUri()), 1); + checkClassFile(new File(Paths.get(System.getProperty("user.dir"), + "Test2$Record7.class").toUri()), 0); } void compileTestClass() throws Exception { @@ -110,6 +123,7 @@ public class TypeAnnotationsPositionsOnRecords { void checkClassFile(final File cfile, int... taPositions) throws Exception { ClassModel classFile = ClassFile.of().parse(cfile.toPath()); + System.err.println("-----------loading " + cfile.getPath()); int accessorPos = 0; int checkedAccessors = 0; for (MethodModel method : classFile.methods()) { diff --git a/test/langtools/tools/javac/records/RecordCompilationTests.java b/test/langtools/tools/javac/records/RecordCompilationTests.java index a8384ba4692..48b5cdd8588 100644 --- a/test/langtools/tools/javac/records/RecordCompilationTests.java +++ b/test/langtools/tools/javac/records/RecordCompilationTests.java @@ -2169,4 +2169,56 @@ class RecordCompilationTests extends CompilationTestCase { """ ); } + + @Test + void testDeprecatedJavadoc() { + String[] previousOptions = getCompileOptions(); + try { + setCompileOptions(new String[] {"-Xlint:deprecation"}); + assertOKWithWarning("compiler.warn.has.been.deprecated", + """ + record R( + /** + * @deprecated + */ + @Deprecated + int i + ) {} + class Client { + R r; + int j = r.i(); + } + """ + ); + assertOKWithWarning("compiler.warn.has.been.deprecated", + """ + record R( + @Deprecated + int i + ) {} + class Client { + R r; + int j = r.i(); + } + """ + ); + // javadoc tag only has no effect + assertOK( + """ + record R( + /** + * @deprecated + */ + int i + ) {} + class Client { + R r; + int j = r.i(); + } + """ + ); + } finally { + setCompileOptions(previousOptions); + } + } } diff --git a/test/lib/jdk/test/lib/util/ZipUtils.java b/test/lib/jdk/test/lib/util/ZipUtils.java new file mode 100644 index 00000000000..a1568e51cc1 --- /dev/null +++ b/test/lib/jdk/test/lib/util/ZipUtils.java @@ -0,0 +1,247 @@ +/* + * 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 + * 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.test.lib.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.EnumSet; +import java.util.HexFormat; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +/** + * This class consists exclusively of static utility methods that are useful + * for creating and manipulating ZIP files. + */ +public final class ZipUtils { + // Some ZipFile constants for manipulating the 'End of central directory record' (END header) + private static final int ENDHDR = ZipFile.ENDHDR; // End of central directory record size + private static final int ENDSIZ = ZipFile.ENDSIZ; // Offset of CEN size field within ENDHDR + private static final int ENDOFF = ZipFile.ENDOFF; // Offset of CEN offset field within ENDHDR + // Expected message when CEN size does not match file size + public static final String INVALID_CEN_BAD_SIZE = "invalid END header (bad central directory size)"; + // Expected message when CEN offset is too large + public static final String INVALID_CEN_BAD_OFFSET = "invalid END header (bad central directory offset)"; + // Expected message when CEN size is too large + public static final String INVALID_CEN_SIZE_TOO_LARGE = "invalid END header (central directory size too large)"; + // Expected message when total entry count is too large + public static final String INVALID_BAD_ENTRY_COUNT = "invalid END header (total entries count too large)"; + + private ZipUtils() { } + + /** + * Create an ZIP file with a single entry, then modify the CEN size + * in the 'End of central directory record' (END header) to the given size. + * + * The CEN is optionally "inflated" with trailing zero bytes such that + * its actual size matches the one stated in the END header. + * + * The CEN offset is optiontially adjusted by the given amount + * + * The resulting ZIP is technically not valid, but it does allow us + * to test that large or invalid CEN sizes are rejected + * @param cenSize the CEN size to put in the END record + * @param inflateCen if true, zero-pad the CEN to the desired size + * @param cenOffAdjust Adjust the CEN offset field of the END record with this amount + * @throws IOException if an error occurs + */ + public static Path zipWithModifiedEndRecord(int cenSize, + boolean inflateCen, + int cenOffAdjust, + Path zip) throws IOException { + // A byte buffer for reading the END + ByteBuffer buffer = ByteBuffer.wrap(templateZip()).order(ByteOrder.LITTLE_ENDIAN); + + // Offset of the END header + int endOffset = buffer.limit() - ENDHDR; + + // Modify the CEN size + int sizeOffset = endOffset + ENDSIZ; + int currentCenSize = buffer.getInt(sizeOffset); + buffer.putInt(sizeOffset, cenSize); + + // Optionally modify the CEN offset + if (cenOffAdjust != 0) { + int offOffset = endOffset + ENDOFF; + int currentCenOff = buffer.getInt(offOffset); + buffer.putInt(offOffset, currentCenOff + cenOffAdjust); + } + // When creating a sparse file, the file must not already exit + Files.deleteIfExists(zip); + + // Open a FileChannel for writing a sparse file + EnumSet options = EnumSet.of(StandardOpenOption.CREATE_NEW, + StandardOpenOption.WRITE, + StandardOpenOption.SPARSE); + + try (FileChannel channel = FileChannel.open(zip, options)) { + // Write everything up to END + channel.write(buffer.slice(0, buffer.limit() - ENDHDR)); + if (inflateCen) { + // Inject "empty bytes" to make the actual CEN size match the END + int injectBytes = cenSize - currentCenSize; + channel.position(channel.position() + injectBytes); + } + // Write the modified END + channel.write(buffer.slice(buffer.limit() - ENDHDR, ENDHDR)); + } + return zip; + } + + /** + * Create a small Zip64 ZIP file, then modify the Zip64 END header + * with a possibly very large total entry count + * + * @param zip file to write to + * @param totalEntries the number of entries wanted in the Zip64 END header + * @return the modified ZIP file + * @throws IOException if an unexpeced IO error occurs + */ + public static Path zip64WithModifiedTotalEntries(Path zip, long totalEntries) throws IOException { + /** + * A small ZIP using the ZIP64 format. + * + * ZIP created using: "echo -n hello | zip zip64.zip -" + * Hex encoded using: "cat zip64.zip | xxd -ps" + * + * The file has the following structure: + * + * 0000 LOCAL HEADER #1 04034B50 + * 0004 Extract Zip Spec 2D '4.5' + * 0005 Extract OS 00 'MS-DOS' + * 0006 General Purpose Flag 0000 + * 0008 Compression Method 0000 'Stored' + * 000A Last Mod Time 5947AB78 'Mon Oct 7 21:27:48 2024' + * 000E CRC 363A3020 + * 0012 Compressed Length FFFFFFFF + * 0016 Uncompressed Length FFFFFFFF + * 001A Filename Length 0001 + * 001C Extra Length 0014 + * 001E Filename '-' + * 001F Extra ID #0001 0001 'ZIP64' + * 0021 Length 0010 + * 0023 Uncompressed Size 0000000000000006 + * 002B Compressed Size 0000000000000006 + * 0033 PAYLOAD hello. + * + * 0039 CENTRAL HEADER #1 02014B50 + * 003D Created Zip Spec 1E '3.0' + * 003E Created OS 03 'Unix' + * 003F Extract Zip Spec 2D '4.5' + * 0040 Extract OS 00 'MS-DOS' + * 0041 General Purpose Flag 0000 + * 0043 Compression Method 0000 'Stored' + * 0045 Last Mod Time 5947AB78 'Mon Oct 7 21:27:48 2024' + * 0049 CRC 363A3020 + * 004D Compressed Length 00000006 + * 0051 Uncompressed Length FFFFFFFF + * 0055 Filename Length 0001 + * 0057 Extra Length 000C + * 0059 Comment Length 0000 + * 005B Disk Start 0000 + * 005D Int File Attributes 0001 + * [Bit 0] 1 Text Data + * 005F Ext File Attributes 11B00000 + * 0063 Local Header Offset 00000000 + * 0067 Filename '-' + * 0068 Extra ID #0001 0001 'ZIP64' + * 006A Length 0008 + * 006C Uncompressed Size 0000000000000006 + * + * 0074 ZIP64 END CENTRAL DIR 06064B50 + * RECORD + * 0078 Size of record 000000000000002C + * 0080 Created Zip Spec 1E '3.0' + * 0081 Created OS 03 'Unix' + * 0082 Extract Zip Spec 2D '4.5' + * 0083 Extract OS 00 'MS-DOS' + * 0084 Number of this disk 00000000 + * 0088 Central Dir Disk no 00000000 + * 008C Entries in this disk 0000000000000001 + * 0094 Total Entries 0000000000000001 + * 009C Size of Central Dir 000000000000003B + * 00A4 Offset to Central dir 0000000000000039 + * + * 00AC ZIP64 END CENTRAL DIR 07064B50 + * LOCATOR + * 00B0 Central Dir Disk no 00000000 + * 00B4 Offset to Central dir 0000000000000074 + * 00BC Total no of Disks 00000001 + * + * 00C0 END CENTRAL HEADER 06054B50 + * 00C4 Number of this disk 0000 + * 00C6 Central Dir Disk no 0000 + * 00C8 Entries in this disk 0001 + * 00CA Total Entries 0001 + * 00CC Size of Central Dir 0000003B + * 00D0 Offset to Central Dir FFFFFFFF + * 00D4 Comment Length 0000 + */ + + byte[] zipBytes = HexFormat.of().parseHex(""" + 504b03042d000000000078ab475920303a36ffffffffffffffff01001400 + 2d010010000600000000000000060000000000000068656c6c6f0a504b01 + 021e032d000000000078ab475920303a3606000000ffffffff01000c0000 + 00000001000000b011000000002d010008000600000000000000504b0606 + 2c000000000000001e032d00000000000000000001000000000000000100 + 0000000000003b000000000000003900000000000000504b060700000000 + 740000000000000001000000504b050600000000010001003b000000ffff + ffff0000 + """.replaceAll("\n","")); + + // Buffer to manipulate the above ZIP + ByteBuffer buf = ByteBuffer.wrap(zipBytes).order(ByteOrder.LITTLE_ENDIAN); + // Offset of the 'total entries' in the 'ZIP64 END CENTRAL DIR' record + // Update ZIP64 entry count to a value which cannot possibly fit in the small CEN + buf.putLong(0x94, totalEntries); + // The corresponding END field needs the ZIP64 magic value + buf.putShort(0xCA, (short) 0xFFFF); + // Write the ZIP to disk + Files.write(zip, zipBytes); + return zip; + } + + /** + * Produce a byte array of a ZIP with a single entry + * + * @throws IOException if an error occurs + */ + private static byte[] templateZip() throws IOException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + try (ZipOutputStream zo = new ZipOutputStream(bout)) { + ZipEntry entry = new ZipEntry("duke.txt"); + zo.putNextEntry(entry); + zo.write("duke".getBytes(StandardCharsets.UTF_8)); + } + return bout.toByteArray(); + } +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java index ebbfbb01cc6..cbfe9958924 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package org.openjdk.bench.java.lang; +package org.openjdk.bench.jdk.incubator.vector; import java.util.stream.IntStream; import java.util.concurrent.TimeUnit;