diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 0cdf3c1b8b599..5734519301e28 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1266,20 +1266,20 @@ source %{ // adlc register classes to make AArch64 rheapbase (r27) and rfp (r29) // registers conditionally reserved. - _ANY_REG32_mask = _ALL_REG32_mask; + _ANY_REG32_mask.assignFrom(_ALL_REG32_mask); _ANY_REG32_mask.remove(OptoReg::as_OptoReg(r31_sp->as_VMReg())); - _ANY_REG_mask = _ALL_REG_mask; + _ANY_REG_mask.assignFrom(_ALL_REG_mask); - _PTR_REG_mask = _ALL_REG_mask; + _PTR_REG_mask.assignFrom(_ALL_REG_mask); - _NO_SPECIAL_REG32_mask = _ALL_REG32_mask; + _NO_SPECIAL_REG32_mask.assignFrom(_ALL_REG32_mask); _NO_SPECIAL_REG32_mask.subtract(_NON_ALLOCATABLE_REG32_mask); - _NO_SPECIAL_REG_mask = _ALL_REG_mask; + _NO_SPECIAL_REG_mask.assignFrom(_ALL_REG_mask); _NO_SPECIAL_REG_mask.subtract(_NON_ALLOCATABLE_REG_mask); - _NO_SPECIAL_PTR_REG_mask = _ALL_REG_mask; + _NO_SPECIAL_PTR_REG_mask.assignFrom(_ALL_REG_mask); _NO_SPECIAL_PTR_REG_mask.subtract(_NON_ALLOCATABLE_REG_mask); // r27 is not allocatable when compressed oops is on and heapbase is not @@ -1297,7 +1297,7 @@ source %{ _NO_SPECIAL_PTR_REG_mask.remove(OptoReg::as_OptoReg(r29->as_VMReg())); } - _NO_SPECIAL_NO_RFP_PTR_REG_mask = _NO_SPECIAL_PTR_REG_mask; + _NO_SPECIAL_NO_RFP_PTR_REG_mask.assignFrom(_NO_SPECIAL_PTR_REG_mask); _NO_SPECIAL_NO_RFP_PTR_REG_mask.remove(OptoReg::as_OptoReg(r29->as_VMReg())); } @@ -2545,27 +2545,27 @@ bool Matcher::use_asm_for_ldiv_by_con(jlong divisor) { return false; } -RegMask Matcher::divI_proj_mask() { +const RegMask& Matcher::divI_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } // Register for MODI projection of divmodI. -RegMask Matcher::modI_proj_mask() { +const RegMask& Matcher::modI_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } // Register for DIVL projection of divmodL. -RegMask Matcher::divL_proj_mask() { +const RegMask& Matcher::divL_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } // Register for MODL projection of divmodL. -RegMask Matcher::modL_proj_mask() { +const RegMask& Matcher::modL_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } bool size_fits_all_mem_uses(AddPNode* addp, int shift) { diff --git a/src/hotspot/cpu/aarch64/aarch64_vector.ad b/src/hotspot/cpu/aarch64/aarch64_vector.ad index ef35b66003d1b..3379041b2ccac 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector.ad +++ b/src/hotspot/cpu/aarch64/aarch64_vector.ad @@ -7081,29 +7081,31 @@ instruct vcompress(vReg dst, vReg src, pRegGov pg) %{ %} instruct vcompressB(vReg dst, vReg src, pReg pg, vReg tmp1, vReg tmp2, - vReg tmp3, vReg tmp4, pReg ptmp, pRegGov pgtmp) %{ + vReg tmp3, pReg ptmp, pRegGov pgtmp) %{ predicate(UseSVE > 0 && Matcher::vector_element_basic_type(n) == T_BYTE); - effect(TEMP_DEF dst, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP ptmp, TEMP pgtmp); + effect(TEMP_DEF dst, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP ptmp, TEMP pgtmp); match(Set dst (CompressV src pg)); - format %{ "vcompressB $dst, $src, $pg\t# KILL $tmp1, $tmp2, $tmp3, tmp4, $ptmp, $pgtmp" %} + format %{ "vcompressB $dst, $src, $pg\t# KILL $tmp1, $tmp2, $tmp3, $ptmp, $pgtmp" %} ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); __ sve_compress_byte($dst$$FloatRegister, $src$$FloatRegister, $pg$$PRegister, - $tmp1$$FloatRegister,$tmp2$$FloatRegister, - $tmp3$$FloatRegister,$tmp4$$FloatRegister, - $ptmp$$PRegister, $pgtmp$$PRegister); + $tmp1$$FloatRegister, $tmp2$$FloatRegister, $tmp3$$FloatRegister, + $ptmp$$PRegister, $pgtmp$$PRegister, length_in_bytes); %} ins_pipe(pipe_slow); %} -instruct vcompressS(vReg dst, vReg src, pReg pg, - vReg tmp1, vReg tmp2, pRegGov pgtmp) %{ +instruct vcompressS(vReg dst, vReg src, pReg pg, vReg tmp1, vReg tmp2, pRegGov pgtmp) %{ predicate(UseSVE > 0 && Matcher::vector_element_basic_type(n) == T_SHORT); effect(TEMP_DEF dst, TEMP tmp1, TEMP tmp2, TEMP pgtmp); match(Set dst (CompressV src pg)); format %{ "vcompressS $dst, $src, $pg\t# KILL $tmp1, $tmp2, $pgtmp" %} ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + __ sve_dup($tmp1$$FloatRegister, __ H, 0); __ sve_compress_short($dst$$FloatRegister, $src$$FloatRegister, $pg$$PRegister, - $tmp1$$FloatRegister,$tmp2$$FloatRegister, $pgtmp$$PRegister); + $tmp1$$FloatRegister, $tmp2$$FloatRegister, $pgtmp$$PRegister, + length_in_bytes); %} ins_pipe(pipe_slow); %} diff --git a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 index 012de7e46d809..6d296cbdb3ac3 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 @@ -5069,29 +5069,31 @@ instruct vcompress(vReg dst, vReg src, pRegGov pg) %{ %} instruct vcompressB(vReg dst, vReg src, pReg pg, vReg tmp1, vReg tmp2, - vReg tmp3, vReg tmp4, pReg ptmp, pRegGov pgtmp) %{ + vReg tmp3, pReg ptmp, pRegGov pgtmp) %{ predicate(UseSVE > 0 && Matcher::vector_element_basic_type(n) == T_BYTE); - effect(TEMP_DEF dst, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP ptmp, TEMP pgtmp); + effect(TEMP_DEF dst, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP ptmp, TEMP pgtmp); match(Set dst (CompressV src pg)); - format %{ "vcompressB $dst, $src, $pg\t# KILL $tmp1, $tmp2, $tmp3, tmp4, $ptmp, $pgtmp" %} + format %{ "vcompressB $dst, $src, $pg\t# KILL $tmp1, $tmp2, $tmp3, $ptmp, $pgtmp" %} ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); __ sve_compress_byte($dst$$FloatRegister, $src$$FloatRegister, $pg$$PRegister, - $tmp1$$FloatRegister,$tmp2$$FloatRegister, - $tmp3$$FloatRegister,$tmp4$$FloatRegister, - $ptmp$$PRegister, $pgtmp$$PRegister); + $tmp1$$FloatRegister, $tmp2$$FloatRegister, $tmp3$$FloatRegister, + $ptmp$$PRegister, $pgtmp$$PRegister, length_in_bytes); %} ins_pipe(pipe_slow); %} -instruct vcompressS(vReg dst, vReg src, pReg pg, - vReg tmp1, vReg tmp2, pRegGov pgtmp) %{ +instruct vcompressS(vReg dst, vReg src, pReg pg, vReg tmp1, vReg tmp2, pRegGov pgtmp) %{ predicate(UseSVE > 0 && Matcher::vector_element_basic_type(n) == T_SHORT); effect(TEMP_DEF dst, TEMP tmp1, TEMP tmp2, TEMP pgtmp); match(Set dst (CompressV src pg)); format %{ "vcompressS $dst, $src, $pg\t# KILL $tmp1, $tmp2, $pgtmp" %} ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + __ sve_dup($tmp1$$FloatRegister, __ H, 0); __ sve_compress_short($dst$$FloatRegister, $src$$FloatRegister, $pg$$PRegister, - $tmp1$$FloatRegister,$tmp2$$FloatRegister, $pgtmp$$PRegister); + $tmp1$$FloatRegister, $tmp2$$FloatRegister, $pgtmp$$PRegister, + length_in_bytes); %} ins_pipe(pipe_slow); %} diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index 4c4251fbe9f25..a8f378e524fc3 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -3486,6 +3486,7 @@ template INSN(sve_smaxv, 0b00000100, 0b001000001); // signed maximum reduction to scalar INSN(sve_smin, 0b00000100, 0b001010000); // signed minimum vectors INSN(sve_sminv, 0b00000100, 0b001010001); // signed minimum reduction to scalar + INSN(sve_splice,0b00000101, 0b101100100); // splice two vectors under predicate control, destructive INSN(sve_sub, 0b00000100, 0b000001000); // vector sub INSN(sve_uaddv, 0b00000100, 0b000001001); // unsigned add reduction to scalar INSN(sve_umax, 0b00000100, 0b001001000); // unsigned maximum vectors diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index b61a0e4e3789d..328ef0c53e6bd 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -2203,114 +2203,117 @@ void C2_MacroAssembler::sve_gen_mask_imm(PRegister dst, BasicType bt, uint32_t l // Pack active elements of src, under the control of mask, into the lowest-numbered elements of dst. // Any remaining elements of dst will be filled with zero. // Clobbers: rscratch1 -// Preserves: src, mask +// Preserves: mask, vzr void C2_MacroAssembler::sve_compress_short(FloatRegister dst, FloatRegister src, PRegister mask, - FloatRegister vtmp1, FloatRegister vtmp2, - PRegister pgtmp) { + FloatRegister vzr, FloatRegister vtmp, + PRegister pgtmp, unsigned vector_length_in_bytes) { assert(pgtmp->is_governing(), "This register has to be a governing predicate register"); - assert_different_registers(dst, src, vtmp1, vtmp2); + // When called by sve_compress_byte, src and vtmp may be the same register. + assert_different_registers(dst, src, vzr); + assert_different_registers(dst, vtmp, vzr); assert_different_registers(mask, pgtmp); - - // Example input: src = 8888 7777 6666 5555 4444 3333 2222 1111 - // mask = 0001 0000 0000 0001 0001 0000 0001 0001 - // Expected result: dst = 0000 0000 0000 8888 5555 4444 2222 1111 - sve_dup(vtmp2, H, 0); + // high <-- low + // Example input: src = hh gg ff ee dd cc bb aa, one character is 8 bits. + // mask = 01 00 00 01 01 00 01 01, one character is 1 bit. + // Expected result: dst = 00 00 00 hh ee dd bb aa // Extend lowest half to type INT. - // dst = 00004444 00003333 00002222 00001111 + // dst = 00dd 00cc 00bb 00aa sve_uunpklo(dst, S, src); - // pgtmp = 00000001 00000000 00000001 00000001 + // pgtmp = 0001 0000 0001 0001 sve_punpklo(pgtmp, mask); // Pack the active elements in size of type INT to the right, // and fill the remainings with zero. - // dst = 00000000 00004444 00002222 00001111 + // dst = 0000 00dd 00bb 00aa sve_compact(dst, S, dst, pgtmp); // Narrow the result back to type SHORT. - // dst = 0000 0000 0000 0000 0000 4444 2222 1111 - sve_uzp1(dst, H, dst, vtmp2); + // dst = 00 00 00 00 00 dd bb aa + sve_uzp1(dst, H, dst, vzr); + + // Return if the vector length is no more than MaxVectorSize/2, since the + // highest half is invalid. + if (vector_length_in_bytes <= (MaxVectorSize >> 1)) { + return; + } + // Count the active elements of lowest half. // rscratch1 = 3 sve_cntp(rscratch1, S, ptrue, pgtmp); // Repeat to the highest half. - // pgtmp = 00000001 00000000 00000000 00000001 + // pgtmp = 0001 0000 0000 0001 sve_punpkhi(pgtmp, mask); - // vtmp1 = 00008888 00007777 00006666 00005555 - sve_uunpkhi(vtmp1, S, src); - // vtmp1 = 00000000 00000000 00008888 00005555 - sve_compact(vtmp1, S, vtmp1, pgtmp); - // vtmp1 = 0000 0000 0000 0000 0000 0000 8888 5555 - sve_uzp1(vtmp1, H, vtmp1, vtmp2); - - // Compressed low: dst = 0000 0000 0000 0000 0000 4444 2222 1111 - // Compressed high: vtmp1 = 0000 0000 0000 0000 0000 0000 8888 5555 - // Left shift(cross lane) compressed high with TRUE_CNT lanes, - // TRUE_CNT is the number of active elements in the compressed low. - neg(rscratch1, rscratch1); - // vtmp2 = {4 3 2 1 0 -1 -2 -3} - sve_index(vtmp2, H, rscratch1, 1); - // vtmp1 = 0000 0000 0000 8888 5555 0000 0000 0000 - sve_tbl(vtmp1, H, vtmp1, vtmp2); - - // Combine the compressed high(after shifted) with the compressed low. - // dst = 0000 0000 0000 8888 5555 4444 2222 1111 - sve_orr(dst, dst, vtmp1); + // vtmp = 00hh 00gg 00ff 00ee + sve_uunpkhi(vtmp, S, src); + // vtmp = 0000 0000 00hh 00ee + sve_compact(vtmp, S, vtmp, pgtmp); + // vtmp = 00 00 00 00 00 00 hh ee + sve_uzp1(vtmp, H, vtmp, vzr); + + // pgtmp = 00 00 00 00 00 01 01 01 + sve_whilelt(pgtmp, H, zr, rscratch1); + // Compressed low: dst = 00 00 00 00 00 dd bb aa + // Compressed high: vtmp = 00 00 00 00 00 00 hh ee + // Combine the compressed low with the compressed high: + // dst = 00 00 00 hh ee dd bb aa + sve_splice(dst, H, pgtmp, vtmp); } // Clobbers: rscratch1, rscratch2 // Preserves: src, mask void C2_MacroAssembler::sve_compress_byte(FloatRegister dst, FloatRegister src, PRegister mask, - FloatRegister vtmp1, FloatRegister vtmp2, - FloatRegister vtmp3, FloatRegister vtmp4, - PRegister ptmp, PRegister pgtmp) { + FloatRegister vtmp1, FloatRegister vtmp2, FloatRegister vtmp3, + PRegister ptmp, PRegister pgtmp, unsigned vector_length_in_bytes) { assert(pgtmp->is_governing(), "This register has to be a governing predicate register"); - assert_different_registers(dst, src, vtmp1, vtmp2, vtmp3, vtmp4); + assert_different_registers(dst, src, vtmp1, vtmp2, vtmp3); assert_different_registers(mask, ptmp, pgtmp); - // Example input: src = 88 77 66 55 44 33 22 11 - // mask = 01 00 00 01 01 00 01 01 - // Expected result: dst = 00 00 00 88 55 44 22 11 + // high <-- low + // Example input: src = q p n m l k j i h g f e d c b a, one character is 8 bits. + // mask = 0 1 0 0 0 0 0 1 0 1 0 0 0 1 0 1, one character is 1 bit. + // Expected result: dst = 0 0 0 0 0 0 0 0 0 0 0 p i g c a + FloatRegister vzr = vtmp3; + sve_dup(vzr, B, 0); - sve_dup(vtmp4, B, 0); // Extend lowest half to type SHORT. - // vtmp1 = 0044 0033 0022 0011 + // vtmp1 = 0h 0g 0f 0e 0d 0c 0b 0a sve_uunpklo(vtmp1, H, src); - // ptmp = 0001 0000 0001 0001 + // ptmp = 00 01 00 00 00 01 00 01 sve_punpklo(ptmp, mask); - // Count the active elements of lowest half. - // rscratch2 = 3 - sve_cntp(rscratch2, H, ptrue, ptmp); // Pack the active elements in size of type SHORT to the right, // and fill the remainings with zero. - // dst = 0000 0044 0022 0011 - sve_compress_short(dst, vtmp1, ptmp, vtmp2, vtmp3, pgtmp); + // dst = 00 00 00 00 00 0g 0c 0a + unsigned extended_size = vector_length_in_bytes << 1; + sve_compress_short(dst, vtmp1, ptmp, vzr, vtmp2, pgtmp, extended_size > MaxVectorSize ? MaxVectorSize : extended_size); // Narrow the result back to type BYTE. - // dst = 00 00 00 00 00 44 22 11 - sve_uzp1(dst, B, dst, vtmp4); + // dst = 0 0 0 0 0 0 0 0 0 0 0 0 0 g c a + sve_uzp1(dst, B, dst, vzr); + + // Return if the vector length is no more than MaxVectorSize/2, since the + // highest half is invalid. + if (vector_length_in_bytes <= (MaxVectorSize >> 1)) { + return; + } + // Count the active elements of lowest half. + // rscratch2 = 3 + sve_cntp(rscratch2, H, ptrue, ptmp); // Repeat to the highest half. - // ptmp = 0001 0000 0000 0001 + // ptmp = 00 01 00 00 00 00 00 01 sve_punpkhi(ptmp, mask); - // vtmp1 = 0088 0077 0066 0055 + // vtmp2 = 0q 0p 0n 0m 0l 0k 0j 0i sve_uunpkhi(vtmp2, H, src); - // vtmp1 = 0000 0000 0088 0055 - sve_compress_short(vtmp1, vtmp2, ptmp, vtmp3, vtmp4, pgtmp); - - sve_dup(vtmp4, B, 0); - // vtmp1 = 00 00 00 00 00 00 88 55 - sve_uzp1(vtmp1, B, vtmp1, vtmp4); - - // Compressed low: dst = 00 00 00 00 00 44 22 11 - // Compressed high: vtmp1 = 00 00 00 00 00 00 88 55 - // Left shift(cross lane) compressed high with TRUE_CNT lanes, - // TRUE_CNT is the number of active elements in the compressed low. - neg(rscratch2, rscratch2); - // vtmp2 = {4 3 2 1 0 -1 -2 -3} - sve_index(vtmp2, B, rscratch2, 1); - // vtmp1 = 00 00 00 88 55 00 00 00 - sve_tbl(vtmp1, B, vtmp1, vtmp2); - // Combine the compressed high(after shifted) with the compressed low. - // dst = 00 00 00 88 55 44 22 11 - sve_orr(dst, dst, vtmp1); + // vtmp1 = 00 00 00 00 00 00 0p 0i + sve_compress_short(vtmp1, vtmp2, ptmp, vzr, vtmp2, pgtmp, extended_size - MaxVectorSize); + // vtmp1 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 p i + sve_uzp1(vtmp1, B, vtmp1, vzr); + + // ptmp = 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 + sve_whilelt(ptmp, B, zr, rscratch2); + // Compressed low: dst = 0 0 0 0 0 0 0 0 0 0 0 0 0 g c a + // Compressed high: vtmp1 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 p i + // Combine the compressed low with the compressed high: + // dst = 0 0 0 0 0 0 0 0 0 0 0 p i g c a + sve_splice(dst, B, ptmp, vtmp1); } void C2_MacroAssembler::neon_reverse_bits(FloatRegister dst, FloatRegister src, BasicType bt, bool isQ) { diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp index cb8ded142f48a..09850a60c64d3 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp @@ -173,13 +173,12 @@ // lowest-numbered elements of dst. Any remaining elements of dst will // be filled with zero. void sve_compress_byte(FloatRegister dst, FloatRegister src, PRegister mask, - FloatRegister vtmp1, FloatRegister vtmp2, - FloatRegister vtmp3, FloatRegister vtmp4, - PRegister ptmp, PRegister pgtmp); + FloatRegister vtmp1, FloatRegister vtmp2, FloatRegister vtmp3, + PRegister ptmp, PRegister pgtmp, unsigned vector_length_in_bytes); void sve_compress_short(FloatRegister dst, FloatRegister src, PRegister mask, - FloatRegister vtmp1, FloatRegister vtmp2, - PRegister pgtmp); + FloatRegister vzr, FloatRegister vtmp, + PRegister pgtmp, unsigned vector_length_in_bytes); void neon_reverse_bits(FloatRegister dst, FloatRegister src, BasicType bt, bool isQ); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index a37edab8578db..1400978931986 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -148,56 +148,34 @@ extern "C" void disnm(intptr_t p); // strictly should be 64 bit movz #imm16<<0 // 110___10100 (i.e. requires insn[31:21] == 11010010100) // -class RelocActions { -protected: - typedef int (*reloc_insn)(address insn_addr, address &target); - virtual reloc_insn adrpMem() = 0; - virtual reloc_insn adrpAdd() = 0; - virtual reloc_insn adrpMovk() = 0; - - const address _insn_addr; - const uint32_t _insn; +static uint32_t insn_at(address insn_addr, int n) { + return ((uint32_t*)insn_addr)[n]; +} - static uint32_t insn_at(address insn_addr, int n) { - return ((uint32_t*)insn_addr)[n]; - } - uint32_t insn_at(int n) const { - return insn_at(_insn_addr, n); - } +template +class RelocActions : public AllStatic { public: - RelocActions(address insn_addr) : _insn_addr(insn_addr), _insn(insn_at(insn_addr, 0)) {} - RelocActions(address insn_addr, uint32_t insn) - : _insn_addr(insn_addr), _insn(insn) {} - - virtual int unconditionalBranch(address insn_addr, address &target) = 0; - virtual int conditionalBranch(address insn_addr, address &target) = 0; - virtual int testAndBranch(address insn_addr, address &target) = 0; - virtual int loadStore(address insn_addr, address &target) = 0; - virtual int adr(address insn_addr, address &target) = 0; - virtual int adrp(address insn_addr, address &target, reloc_insn inner) = 0; - virtual int immediate(address insn_addr, address &target) = 0; - virtual void verify(address insn_addr, address &target) = 0; - - int ALWAYSINLINE run(address insn_addr, address &target) { + static int ALWAYSINLINE run(address insn_addr, address &target) { int instructions = 1; + uint32_t insn = insn_at(insn_addr, 0); - uint32_t dispatch = Instruction_aarch64::extract(_insn, 30, 25); + uint32_t dispatch = Instruction_aarch64::extract(insn, 30, 25); switch(dispatch) { case 0b001010: case 0b001011: { - instructions = unconditionalBranch(insn_addr, target); + instructions = T::unconditionalBranch(insn_addr, target); break; } case 0b101010: // Conditional branch (immediate) case 0b011010: { // Compare & branch (immediate) - instructions = conditionalBranch(insn_addr, target); - break; + instructions = T::conditionalBranch(insn_addr, target); + break; } case 0b011011: { - instructions = testAndBranch(insn_addr, target); + instructions = T::testAndBranch(insn_addr, target); break; } case 0b001100: @@ -209,9 +187,9 @@ class RelocActions { case 0b111100: case 0b111110: { // load/store - if ((Instruction_aarch64::extract(_insn, 29, 24) & 0b111011) == 0b011000) { + if ((Instruction_aarch64::extract(insn, 29, 24) & 0b111011) == 0b011000) { // Load register (literal) - instructions = loadStore(insn_addr, target); + instructions = T::loadStore(insn_addr, target); break; } else { // nothing to do @@ -224,27 +202,27 @@ class RelocActions { case 0b101000: case 0b111000: { // adr/adrp - assert(Instruction_aarch64::extract(_insn, 28, 24) == 0b10000, "must be"); - int shift = Instruction_aarch64::extract(_insn, 31, 31); + assert(Instruction_aarch64::extract(insn, 28, 24) == 0b10000, "must be"); + int shift = Instruction_aarch64::extract(insn, 31, 31); if (shift) { - uint32_t insn2 = insn_at(1); + uint32_t insn2 = insn_at(insn_addr, 1); if (Instruction_aarch64::extract(insn2, 29, 24) == 0b111001 && - Instruction_aarch64::extract(_insn, 4, 0) == + Instruction_aarch64::extract(insn, 4, 0) == Instruction_aarch64::extract(insn2, 9, 5)) { - instructions = adrp(insn_addr, target, adrpMem()); + instructions = T::adrp(insn_addr, target, T::adrpMem); } else if (Instruction_aarch64::extract(insn2, 31, 22) == 0b1001000100 && - Instruction_aarch64::extract(_insn, 4, 0) == + Instruction_aarch64::extract(insn, 4, 0) == Instruction_aarch64::extract(insn2, 4, 0)) { - instructions = adrp(insn_addr, target, adrpAdd()); + instructions = T::adrp(insn_addr, target, T::adrpAdd); } else if (Instruction_aarch64::extract(insn2, 31, 21) == 0b11110010110 && - Instruction_aarch64::extract(_insn, 4, 0) == + Instruction_aarch64::extract(insn, 4, 0) == Instruction_aarch64::extract(insn2, 4, 0)) { - instructions = adrp(insn_addr, target, adrpMovk()); + instructions = T::adrp(insn_addr, target, T::adrpMovk); } else { ShouldNotReachHere(); } } else { - instructions = adr(insn_addr, target); + instructions = T::adr(insn_addr, target); } break; } @@ -252,7 +230,7 @@ class RelocActions { case 0b011001: case 0b101001: case 0b111001: { - instructions = immediate(insn_addr, target); + instructions = T::immediate(insn_addr, target); break; } default: { @@ -260,42 +238,36 @@ class RelocActions { } } - verify(insn_addr, target); + T::verify(insn_addr, target); return instructions * NativeInstruction::instruction_size; } }; -class Patcher : public RelocActions { - virtual reloc_insn adrpMem() { return &Patcher::adrpMem_impl; } - virtual reloc_insn adrpAdd() { return &Patcher::adrpAdd_impl; } - virtual reloc_insn adrpMovk() { return &Patcher::adrpMovk_impl; } - +class Patcher : public AllStatic { public: - Patcher(address insn_addr) : RelocActions(insn_addr) {} - - virtual int unconditionalBranch(address insn_addr, address &target) { + static int unconditionalBranch(address insn_addr, address &target) { intptr_t offset = (target - insn_addr) >> 2; Instruction_aarch64::spatch(insn_addr, 25, 0, offset); return 1; } - virtual int conditionalBranch(address insn_addr, address &target) { + static int conditionalBranch(address insn_addr, address &target) { intptr_t offset = (target - insn_addr) >> 2; Instruction_aarch64::spatch(insn_addr, 23, 5, offset); return 1; } - virtual int testAndBranch(address insn_addr, address &target) { + static int testAndBranch(address insn_addr, address &target) { intptr_t offset = (target - insn_addr) >> 2; Instruction_aarch64::spatch(insn_addr, 18, 5, offset); return 1; } - virtual int loadStore(address insn_addr, address &target) { + static int loadStore(address insn_addr, address &target) { intptr_t offset = (target - insn_addr) >> 2; Instruction_aarch64::spatch(insn_addr, 23, 5, offset); return 1; } - virtual int adr(address insn_addr, address &target) { + static int adr(address insn_addr, address &target) { #ifdef ASSERT - assert(Instruction_aarch64::extract(_insn, 28, 24) == 0b10000, "must be"); + assert(Instruction_aarch64::extract(insn_at(insn_addr, 0), 28, 24) == 0b10000, "must be"); #endif // PC-rel. addressing ptrdiff_t offset = target - insn_addr; @@ -305,17 +277,18 @@ class Patcher : public RelocActions { Instruction_aarch64::patch(insn_addr, 30, 29, offset_lo); return 1; } - virtual int adrp(address insn_addr, address &target, reloc_insn inner) { + template + static int adrp(address insn_addr, address &target, U inner) { int instructions = 1; #ifdef ASSERT - assert(Instruction_aarch64::extract(_insn, 28, 24) == 0b10000, "must be"); + assert(Instruction_aarch64::extract(insn_at(insn_addr, 0), 28, 24) == 0b10000, "must be"); #endif ptrdiff_t offset = target - insn_addr; instructions = 2; precond(inner != nullptr); // Give the inner reloc a chance to modify the target. address adjusted_target = target; - instructions = (*inner)(insn_addr, adjusted_target); + instructions = inner(insn_addr, adjusted_target); uintptr_t pc_page = (uintptr_t)insn_addr >> 12; uintptr_t adr_page = (uintptr_t)adjusted_target >> 12; offset = adr_page - pc_page; @@ -325,7 +298,7 @@ class Patcher : public RelocActions { Instruction_aarch64::patch(insn_addr, 30, 29, offset_lo); return instructions; } - static int adrpMem_impl(address insn_addr, address &target) { + static int adrpMem(address insn_addr, address &target) { uintptr_t dest = (uintptr_t)target; int offset_lo = dest & 0xfff; uint32_t insn2 = insn_at(insn_addr, 1); @@ -334,21 +307,21 @@ class Patcher : public RelocActions { guarantee(((dest >> size) << size) == dest, "misaligned target"); return 2; } - static int adrpAdd_impl(address insn_addr, address &target) { + static int adrpAdd(address insn_addr, address &target) { uintptr_t dest = (uintptr_t)target; int offset_lo = dest & 0xfff; Instruction_aarch64::patch(insn_addr + sizeof (uint32_t), 21, 10, offset_lo); return 2; } - static int adrpMovk_impl(address insn_addr, address &target) { + static int adrpMovk(address insn_addr, address &target) { uintptr_t dest = uintptr_t(target); Instruction_aarch64::patch(insn_addr + sizeof (uint32_t), 20, 5, (uintptr_t)target >> 32); dest = (dest & 0xffffffffULL) | (uintptr_t(insn_addr) & 0xffff00000000ULL); target = address(dest); return 2; } - virtual int immediate(address insn_addr, address &target) { - assert(Instruction_aarch64::extract(_insn, 31, 21) == 0b11010010100, "must be"); + static int immediate(address insn_addr, address &target) { + assert(Instruction_aarch64::extract(insn_at(insn_addr, 0), 31, 21) == 0b11010010100, "must be"); uint64_t dest = (uint64_t)target; // Move wide constant assert(nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch"); @@ -358,7 +331,7 @@ class Patcher : public RelocActions { Instruction_aarch64::patch(insn_addr+8, 20, 5, (dest >>= 16) & 0xffff); return 3; } - virtual void verify(address insn_addr, address &target) { + static void verify(address insn_addr, address &target) { #ifdef ASSERT address address_is = MacroAssembler::target_addr_for_insn(insn_addr); if (!(address_is == target)) { @@ -392,56 +365,54 @@ static bool offset_for(uint32_t insn1, uint32_t insn2, ptrdiff_t &byte_offset) { return false; } -class AArch64Decoder : public RelocActions { - virtual reloc_insn adrpMem() { return &AArch64Decoder::adrpMem_impl; } - virtual reloc_insn adrpAdd() { return &AArch64Decoder::adrpAdd_impl; } - virtual reloc_insn adrpMovk() { return &AArch64Decoder::adrpMovk_impl; } - +class AArch64Decoder : public AllStatic { public: - AArch64Decoder(address insn_addr, uint32_t insn) : RelocActions(insn_addr, insn) {} - virtual int loadStore(address insn_addr, address &target) { - intptr_t offset = Instruction_aarch64::sextract(_insn, 23, 5); + static int loadStore(address insn_addr, address &target) { + intptr_t offset = Instruction_aarch64::sextract(insn_at(insn_addr, 0), 23, 5); target = insn_addr + (offset << 2); return 1; } - virtual int unconditionalBranch(address insn_addr, address &target) { - intptr_t offset = Instruction_aarch64::sextract(_insn, 25, 0); + static int unconditionalBranch(address insn_addr, address &target) { + intptr_t offset = Instruction_aarch64::sextract(insn_at(insn_addr, 0), 25, 0); target = insn_addr + (offset << 2); return 1; } - virtual int conditionalBranch(address insn_addr, address &target) { - intptr_t offset = Instruction_aarch64::sextract(_insn, 23, 5); + static int conditionalBranch(address insn_addr, address &target) { + intptr_t offset = Instruction_aarch64::sextract(insn_at(insn_addr, 0), 23, 5); target = address(((uint64_t)insn_addr + (offset << 2))); return 1; } - virtual int testAndBranch(address insn_addr, address &target) { - intptr_t offset = Instruction_aarch64::sextract(_insn, 18, 5); + static int testAndBranch(address insn_addr, address &target) { + intptr_t offset = Instruction_aarch64::sextract(insn_at(insn_addr, 0), 18, 5); target = address(((uint64_t)insn_addr + (offset << 2))); return 1; } - virtual int adr(address insn_addr, address &target) { + static int adr(address insn_addr, address &target) { // PC-rel. addressing - intptr_t offset = Instruction_aarch64::extract(_insn, 30, 29); - offset |= Instruction_aarch64::sextract(_insn, 23, 5) << 2; + uint32_t insn = insn_at(insn_addr, 0); + intptr_t offset = Instruction_aarch64::extract(insn, 30, 29); + offset |= Instruction_aarch64::sextract(insn, 23, 5) << 2; target = address((uint64_t)insn_addr + offset); return 1; } - virtual int adrp(address insn_addr, address &target, reloc_insn inner) { - assert(Instruction_aarch64::extract(_insn, 28, 24) == 0b10000, "must be"); - intptr_t offset = Instruction_aarch64::extract(_insn, 30, 29); - offset |= Instruction_aarch64::sextract(_insn, 23, 5) << 2; + template + static int adrp(address insn_addr, address &target, U inner) { + uint32_t insn = insn_at(insn_addr, 0); + assert(Instruction_aarch64::extract(insn, 28, 24) == 0b10000, "must be"); + intptr_t offset = Instruction_aarch64::extract(insn, 30, 29); + offset |= Instruction_aarch64::sextract(insn, 23, 5) << 2; int shift = 12; offset <<= shift; uint64_t target_page = ((uint64_t)insn_addr) + offset; target_page &= ((uint64_t)-1) << shift; - uint32_t insn2 = insn_at(1); + uint32_t insn2 = insn_at(insn_addr, 1); target = address(target_page); precond(inner != nullptr); - (*inner)(insn_addr, target); + inner(insn_addr, target); return 2; } - static int adrpMem_impl(address insn_addr, address &target) { + static int adrpMem(address insn_addr, address &target) { uint32_t insn2 = insn_at(insn_addr, 1); // Load/store register (unsigned immediate) ptrdiff_t byte_offset = Instruction_aarch64::extract(insn2, 21, 10); @@ -450,14 +421,14 @@ class AArch64Decoder : public RelocActions { target += byte_offset; return 2; } - static int adrpAdd_impl(address insn_addr, address &target) { + static int adrpAdd(address insn_addr, address &target) { uint32_t insn2 = insn_at(insn_addr, 1); // add (immediate) ptrdiff_t byte_offset = Instruction_aarch64::extract(insn2, 21, 10); target += byte_offset; return 2; } - static int adrpMovk_impl(address insn_addr, address &target) { + static int adrpMovk(address insn_addr, address &target) { uint32_t insn2 = insn_at(insn_addr, 1); uint64_t dest = uint64_t(target); dest = (dest & 0xffff0000ffffffff) | @@ -476,35 +447,33 @@ class AArch64Decoder : public RelocActions { return 2; } } - virtual int immediate(address insn_addr, address &target) { + static int immediate(address insn_addr, address &target) { uint32_t *insns = (uint32_t *)insn_addr; - assert(Instruction_aarch64::extract(_insn, 31, 21) == 0b11010010100, "must be"); + assert(Instruction_aarch64::extract(insns[0], 31, 21) == 0b11010010100, "must be"); // Move wide constant: movz, movk, movk. See movptr(). assert(nativeInstruction_at(insns+1)->is_movk(), "wrong insns in patch"); assert(nativeInstruction_at(insns+2)->is_movk(), "wrong insns in patch"); - target = address(uint64_t(Instruction_aarch64::extract(_insn, 20, 5)) - + (uint64_t(Instruction_aarch64::extract(insns[1], 20, 5)) << 16) - + (uint64_t(Instruction_aarch64::extract(insns[2], 20, 5)) << 32)); + target = address(uint64_t(Instruction_aarch64::extract(insns[0], 20, 5)) + + (uint64_t(Instruction_aarch64::extract(insns[1], 20, 5)) << 16) + + (uint64_t(Instruction_aarch64::extract(insns[2], 20, 5)) << 32)); assert(nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch"); assert(nativeInstruction_at(insn_addr+8)->is_movk(), "wrong insns in patch"); return 3; } - virtual void verify(address insn_addr, address &target) { + static void verify(address insn_addr, address &target) { } }; -address MacroAssembler::target_addr_for_insn(address insn_addr, uint32_t insn) { - AArch64Decoder decoder(insn_addr, insn); +address MacroAssembler::target_addr_for_insn(address insn_addr) { address target; - decoder.run(insn_addr, target); + RelocActions::run(insn_addr, target); return target; } // Patch any kind of instruction; there may be several instructions. // Return the total length (in bytes) of the instructions. int MacroAssembler::pd_patch_instruction_size(address insn_addr, address target) { - Patcher patcher(insn_addr); - return patcher.run(insn_addr, target); + return RelocActions::run(insn_addr, target); } int MacroAssembler::patch_oop(address insn_addr, address o) { @@ -546,11 +515,11 @@ int MacroAssembler::patch_narrow_klass(address insn_addr, narrowKlass n) { return 2 * NativeInstruction::instruction_size; } -address MacroAssembler::target_addr_for_insn_or_null(address insn_addr, unsigned insn) { - if (NativeInstruction::is_ldrw_to_zr(address(&insn))) { +address MacroAssembler::target_addr_for_insn_or_null(address insn_addr) { + if (NativeInstruction::is_ldrw_to_zr(insn_addr)) { return nullptr; } - return MacroAssembler::target_addr_for_insn(insn_addr, insn); + return MacroAssembler::target_addr_for_insn(insn_addr); } void MacroAssembler::safepoint_poll(Label& slow_path, bool at_return, bool in_nmethod, Register tmp) { diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 705bd19093c1d..d5a16e424e428 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -676,16 +676,8 @@ class MacroAssembler: public Assembler { static bool needs_explicit_null_check(intptr_t offset); static bool uses_implicit_null_check(void* address); - static address target_addr_for_insn(address insn_addr, unsigned insn); - static address target_addr_for_insn_or_null(address insn_addr, unsigned insn); - static address target_addr_for_insn(address insn_addr) { - unsigned insn = *(unsigned*)insn_addr; - return target_addr_for_insn(insn_addr, insn); - } - static address target_addr_for_insn_or_null(address insn_addr) { - unsigned insn = *(unsigned*)insn_addr; - return target_addr_for_insn_or_null(insn_addr, insn); - } + static address target_addr_for_insn(address insn_addr); + static address target_addr_for_insn_or_null(address insn_addr); // Required platform-specific helpers for Label::patch_instructions. // They _shadow_ the declarations in AbstractAssembler, which are undefined. diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 68fece5263d34..31a442be62456 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -1131,27 +1131,27 @@ bool Matcher::use_asm_for_ldiv_by_con( jlong divisor ) { } // Register for DIVI projection of divmodI -RegMask Matcher::divI_proj_mask() { +const RegMask& Matcher::divI_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } // Register for MODI projection of divmodI -RegMask Matcher::modI_proj_mask() { +const RegMask& Matcher::modI_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } // Register for DIVL projection of divmodL -RegMask Matcher::divL_proj_mask() { +const RegMask& Matcher::divL_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } // Register for MODL projection of divmodL -RegMask Matcher::modL_proj_mask() { +const RegMask& Matcher::modL_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } bool maybe_far_call(const CallNode *n) { diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp index d3969427db300..9140dd7ca4edf 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp @@ -133,8 +133,13 @@ class InterpreterMacroAssembler: public MacroAssembler { void get_cache_index_at_bcp(Register Rdst, int bcp_offset, size_t index_size); void load_resolved_indy_entry(Register cache, Register index); - void load_field_entry(Register cache, Register index, int bcp_offset = 1); - void load_method_entry(Register cache, Register index, int bcp_offset = 1); + void load_field_or_method_entry(bool is_method, Register cache, Register index, int bcp_offset, bool for_fast_bytecode); + void load_field_entry(Register cache, Register index, int bcp_offset = 1, bool for_fast_bytecode = false) { + load_field_or_method_entry(false, cache, index, bcp_offset, for_fast_bytecode); + } + void load_method_entry(Register cache, Register index, int bcp_offset = 1, bool for_fast_bytecode = false) { + load_field_or_method_entry(true, cache, index, bcp_offset, for_fast_bytecode); + } void get_u4(Register Rdst, Register Rsrc, int offset, signedOrNot is_signed); diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp index 8df2cc5d273ff..503cc25943253 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp @@ -468,33 +468,33 @@ void InterpreterMacroAssembler::load_resolved_indy_entry(Register cache, Registe add(cache, cache, index); } -void InterpreterMacroAssembler::load_field_entry(Register cache, Register index, int bcp_offset) { +void InterpreterMacroAssembler::load_field_or_method_entry(bool is_method, Register cache, Register index, int bcp_offset, bool for_fast_bytecode) { + const int entry_size = is_method ? sizeof(ResolvedMethodEntry) : sizeof(ResolvedFieldEntry), + base_offset = is_method ? Array::base_offset_in_bytes() : Array::base_offset_in_bytes(), + entries_offset = is_method ? in_bytes(ConstantPoolCache::method_entries_offset()) : in_bytes(ConstantPoolCache::field_entries_offset()); + // Get index out of bytecode pointer get_cache_index_at_bcp(index, bcp_offset, sizeof(u2)); // Take shortcut if the size is a power of 2 - if (is_power_of_2(sizeof(ResolvedFieldEntry))) { + if (is_power_of_2(entry_size)) { // Scale index by power of 2 - sldi(index, index, log2i_exact(sizeof(ResolvedFieldEntry))); + sldi(index, index, log2i_exact(entry_size)); } else { // Scale the index to be the entry index * sizeof(ResolvedFieldEntry) - mulli(index, index, sizeof(ResolvedFieldEntry)); + mulli(index, index, entry_size); } // Get address of field entries array - ld_ptr(cache, in_bytes(ConstantPoolCache::field_entries_offset()), R27_constPoolCache); - addi(cache, cache, Array::base_offset_in_bytes()); + ld_ptr(cache, entries_offset, R27_constPoolCache); + addi(cache, cache, base_offset); add(cache, cache, index); -} -void InterpreterMacroAssembler::load_method_entry(Register cache, Register index, int bcp_offset) { - // Get index out of bytecode pointer - get_cache_index_at_bcp(index, bcp_offset, sizeof(u2)); - // Scale the index to be the entry index * sizeof(ResolvedMethodEntry) - mulli(index, index, sizeof(ResolvedMethodEntry)); - - // Get address of field entries array - ld_ptr(cache, ConstantPoolCache::method_entries_offset(), R27_constPoolCache); - addi(cache, cache, Array::base_offset_in_bytes()); - add(cache, cache, index); // method_entries + base_offset + scaled index + if (for_fast_bytecode) { + // Prevent speculative loading from ResolvedFieldEntry/ResolvedMethodEntry as it can miss the info written by another thread. + // TemplateTable::patch_bytecode uses release-store. + // We reached here via control dependency (Bytecode dispatch has used the rewritten Bytecode). + // So, we can use control-isync based ordering. + isync(); + } } // Load object from cpool->resolved_references(index). diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 2c83b2d576589..03dbd0e780ba2 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -2450,27 +2450,27 @@ bool Matcher::use_asm_for_ldiv_by_con(jlong divisor) { } // Register for DIVI projection of divmodI. -RegMask Matcher::divI_proj_mask() { +const RegMask& Matcher::divI_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } // Register for MODI projection of divmodI. -RegMask Matcher::modI_proj_mask() { +const RegMask& Matcher::modI_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } // Register for DIVL projection of divmodL. -RegMask Matcher::divL_proj_mask() { +const RegMask& Matcher::divL_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } // Register for MODL projection of divmodL. -RegMask Matcher::modL_proj_mask() { +const RegMask& Matcher::modL_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } %} diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index 41fbe66647ecc..09acd1c067da9 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -148,7 +148,9 @@ void TemplateTable::patch_bytecode(Bytecodes::Code new_bc, Register Rnew_bc, Reg __ bind(L_fast_patch); } - // Patch bytecode. + // Patch bytecode with release store to coordinate with ResolvedFieldEntry + // and ResolvedMethodEntry loads in fast bytecode codelets. + __ release(); __ stb(Rnew_bc, 0, R14_bcp); __ bind(L_patch_done); @@ -312,6 +314,7 @@ void TemplateTable::fast_aldc(LdcType type) { // We are resolved if the resolved reference cache entry contains a // non-null object (CallSite, etc.) __ get_cache_index_at_bcp(R31, 1, index_size); // Load index. + // Only rewritten during link time. So, no need for memory barriers for accessing resolved info. __ load_resolved_reference_at_index(R17_tos, R31, R11_scratch1, R12_scratch2, &is_null); // Convert null sentinel to null @@ -3114,7 +3117,7 @@ void TemplateTable::fast_storefield(TosState state) { const ConditionRegister CR_is_vol = CR2; // Non-volatile condition register (survives runtime call in do_oop_store). // Constant pool already resolved => Load flags and offset of field. - __ load_field_entry(Rcache, Rscratch); + __ load_field_entry(Rcache, Rscratch, 1, /* for_fast_bytecode */ true); jvmti_post_field_mod(Rcache, Rscratch, false /* not static */); load_resolved_field_entry(noreg, Rcache, noreg, Roffset, Rflags, false); // Uses R11, R12 @@ -3195,7 +3198,7 @@ void TemplateTable::fast_accessfield(TosState state) { // R12_scratch2 used by load_field_cp_cache_entry // Constant pool already resolved. Get the field offset. - __ load_field_entry(Rcache, Rscratch); + __ load_field_entry(Rcache, Rscratch, 1, /* for_fast_bytecode */ true); load_resolved_field_entry(noreg, Rcache, noreg, Roffset, Rflags, false); // Uses R11, R12 // JVMTI support @@ -3334,7 +3337,7 @@ void TemplateTable::fast_xaccess(TosState state) { __ ld(Rclass_or_obj, 0, R18_locals); // Constant pool already resolved. Get the field offset. - __ load_field_entry(Rcache, Rscratch, 2); + __ load_field_entry(Rcache, Rscratch, 2, /* for_fast_bytecode */ true); load_resolved_field_entry(noreg, Rcache, noreg, Roffset, Rflags, false); // Uses R11, R12 // JVMTI support not needed, since we switch back to single bytecode as soon as debugger attaches. @@ -3495,7 +3498,7 @@ void TemplateTable::fast_invokevfinal(int byte_no) { assert(byte_no == f2_byte, "use this argument"); Register Rcache = R31; - __ load_method_entry(Rcache, R11_scratch1); + __ load_method_entry(Rcache, R11_scratch1, 1, /* for_fast_bytecode */ true); invokevfinal_helper(Rcache, R11_scratch1, R12_scratch2, R22_tmp2, R23_tmp3); } diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp index eec4f0846a577..295f1b221916c 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp @@ -301,12 +301,9 @@ class InterpreterMacroAssembler: public MacroAssembler { void load_method_entry(Register cache, Register index, int bcp_offset = 1); void verify_field_offset(Register reg) NOT_DEBUG_RETURN; - -#ifdef ASSERT void verify_access_flags(Register access_flags, uint32_t flag, - const char* msg, bool stop_by_hit = true); - void verify_frame_setup(); -#endif + const char* msg, bool stop_by_hit = true) NOT_DEBUG_RETURN; + void verify_frame_setup() NOT_DEBUG_RETURN; }; #endif // CPU_RISCV_INTERP_MASM_RISCV_HPP diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 00364d7dab72a..83c59af9113e9 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1092,22 +1092,22 @@ RegMask _NO_SPECIAL_NO_FP_PTR_REG_mask; void reg_mask_init() { - _ANY_REG32_mask = _ALL_REG32_mask; + _ANY_REG32_mask.assignFrom(_ALL_REG32_mask); _ANY_REG32_mask.remove(OptoReg::as_OptoReg(x0->as_VMReg())); - _ANY_REG_mask = _ALL_REG_mask; + _ANY_REG_mask.assignFrom(_ALL_REG_mask); _ANY_REG_mask.subtract(_ZR_REG_mask); - _PTR_REG_mask = _ALL_REG_mask; + _PTR_REG_mask.assignFrom(_ALL_REG_mask); _PTR_REG_mask.subtract(_ZR_REG_mask); - _NO_SPECIAL_REG32_mask = _ALL_REG32_mask; + _NO_SPECIAL_REG32_mask.assignFrom(_ALL_REG32_mask); _NO_SPECIAL_REG32_mask.subtract(_NON_ALLOCATABLE_REG32_mask); - _NO_SPECIAL_REG_mask = _ALL_REG_mask; + _NO_SPECIAL_REG_mask.assignFrom(_ALL_REG_mask); _NO_SPECIAL_REG_mask.subtract(_NON_ALLOCATABLE_REG_mask); - _NO_SPECIAL_PTR_REG_mask = _ALL_REG_mask; + _NO_SPECIAL_PTR_REG_mask.assignFrom(_ALL_REG_mask); _NO_SPECIAL_PTR_REG_mask.subtract(_NON_ALLOCATABLE_REG_mask); // x27 is not allocatable when compressed oops is on @@ -1124,7 +1124,7 @@ void reg_mask_init() { _NO_SPECIAL_PTR_REG_mask.remove(OptoReg::as_OptoReg(x8->as_VMReg())); } - _NO_SPECIAL_NO_FP_PTR_REG_mask = _NO_SPECIAL_PTR_REG_mask; + _NO_SPECIAL_NO_FP_PTR_REG_mask.assignFrom(_NO_SPECIAL_PTR_REG_mask); _NO_SPECIAL_NO_FP_PTR_REG_mask.remove(OptoReg::as_OptoReg(x8->as_VMReg())); } @@ -2129,27 +2129,27 @@ bool Matcher::use_asm_for_ldiv_by_con(jlong divisor) { return false; } -RegMask Matcher::divI_proj_mask() { +const RegMask& Matcher::divI_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } // Register for MODI projection of divmodI. -RegMask Matcher::modI_proj_mask() { +const RegMask& Matcher::modI_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } // Register for DIVL projection of divmodL. -RegMask Matcher::divL_proj_mask() { +const RegMask& Matcher::divL_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } // Register for MODL projection of divmodL. -RegMask Matcher::modL_proj_mask() { +const RegMask& Matcher::modL_proj_mask() { ShouldNotReachHere(); - return RegMask(); + return RegMask::EMPTY; } bool size_fits_all_mem_uses(AddPNode* addp, int shift) { diff --git a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp index 61f4aa3e722a2..692335d8c084a 100644 --- a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp @@ -1073,9 +1073,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { } // start execution -#ifdef ASSERT __ verify_frame_setup(); -#endif // jvmti support __ notify_method_entry(); @@ -1541,9 +1539,7 @@ address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) { } // start execution -#ifdef ASSERT __ verify_frame_setup(); -#endif // jvmti support __ notify_method_entry(); diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index cfc8b19534b59..ab991896b53d8 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -1961,22 +1961,22 @@ bool Matcher::use_asm_for_ldiv_by_con(jlong divisor) { } // Register for DIVI projection of divmodI -RegMask Matcher::divI_proj_mask() { +const RegMask& Matcher::divI_proj_mask() { return _Z_RARG4_INT_REG_mask; } // Register for MODI projection of divmodI -RegMask Matcher::modI_proj_mask() { +const RegMask& Matcher::modI_proj_mask() { return _Z_RARG3_INT_REG_mask; } // Register for DIVL projection of divmodL -RegMask Matcher::divL_proj_mask() { +const RegMask& Matcher::divL_proj_mask() { return _Z_RARG4_LONG_REG_mask; } // Register for MODL projection of divmodL -RegMask Matcher::modL_proj_mask() { +const RegMask& Matcher::modL_proj_mask() { return _Z_RARG3_LONG_REG_mask; } diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index 27daa51b39e20..62306b562d6d4 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -497,7 +497,7 @@ void reg_mask_init() { // _ALL_REG_mask is generated by adlc from the all_reg register class below. // We derive a number of subsets from it. - _ANY_REG_mask = _ALL_REG_mask; + _ANY_REG_mask.assignFrom(_ALL_REG_mask); if (PreserveFramePointer) { _ANY_REG_mask.remove(OptoReg::as_OptoReg(rbp->as_VMReg())); @@ -508,7 +508,7 @@ void reg_mask_init() { _ANY_REG_mask.remove(OptoReg::as_OptoReg(r12->as_VMReg()->next())); } - _PTR_REG_mask = _ANY_REG_mask; + _PTR_REG_mask.assignFrom(_ANY_REG_mask); _PTR_REG_mask.remove(OptoReg::as_OptoReg(rsp->as_VMReg())); _PTR_REG_mask.remove(OptoReg::as_OptoReg(rsp->as_VMReg()->next())); _PTR_REG_mask.remove(OptoReg::as_OptoReg(r15->as_VMReg())); @@ -520,43 +520,43 @@ void reg_mask_init() { } } - _STACK_OR_PTR_REG_mask = _PTR_REG_mask; + _STACK_OR_PTR_REG_mask.assignFrom(_PTR_REG_mask); _STACK_OR_PTR_REG_mask.or_with(STACK_OR_STACK_SLOTS_mask()); - _PTR_REG_NO_RBP_mask = _PTR_REG_mask; + _PTR_REG_NO_RBP_mask.assignFrom(_PTR_REG_mask); _PTR_REG_NO_RBP_mask.remove(OptoReg::as_OptoReg(rbp->as_VMReg())); _PTR_REG_NO_RBP_mask.remove(OptoReg::as_OptoReg(rbp->as_VMReg()->next())); - _PTR_NO_RAX_REG_mask = _PTR_REG_mask; + _PTR_NO_RAX_REG_mask.assignFrom(_PTR_REG_mask); _PTR_NO_RAX_REG_mask.remove(OptoReg::as_OptoReg(rax->as_VMReg())); _PTR_NO_RAX_REG_mask.remove(OptoReg::as_OptoReg(rax->as_VMReg()->next())); - _PTR_NO_RAX_RBX_REG_mask = _PTR_NO_RAX_REG_mask; + _PTR_NO_RAX_RBX_REG_mask.assignFrom(_PTR_NO_RAX_REG_mask); _PTR_NO_RAX_RBX_REG_mask.remove(OptoReg::as_OptoReg(rbx->as_VMReg())); _PTR_NO_RAX_RBX_REG_mask.remove(OptoReg::as_OptoReg(rbx->as_VMReg()->next())); - _LONG_REG_mask = _PTR_REG_mask; - _STACK_OR_LONG_REG_mask = _LONG_REG_mask; + _LONG_REG_mask.assignFrom(_PTR_REG_mask); + _STACK_OR_LONG_REG_mask.assignFrom(_LONG_REG_mask); _STACK_OR_LONG_REG_mask.or_with(STACK_OR_STACK_SLOTS_mask()); - _LONG_NO_RAX_RDX_REG_mask = _LONG_REG_mask; + _LONG_NO_RAX_RDX_REG_mask.assignFrom(_LONG_REG_mask); _LONG_NO_RAX_RDX_REG_mask.remove(OptoReg::as_OptoReg(rax->as_VMReg())); _LONG_NO_RAX_RDX_REG_mask.remove(OptoReg::as_OptoReg(rax->as_VMReg()->next())); _LONG_NO_RAX_RDX_REG_mask.remove(OptoReg::as_OptoReg(rdx->as_VMReg())); _LONG_NO_RAX_RDX_REG_mask.remove(OptoReg::as_OptoReg(rdx->as_VMReg()->next())); - _LONG_NO_RCX_REG_mask = _LONG_REG_mask; + _LONG_NO_RCX_REG_mask.assignFrom(_LONG_REG_mask); _LONG_NO_RCX_REG_mask.remove(OptoReg::as_OptoReg(rcx->as_VMReg())); _LONG_NO_RCX_REG_mask.remove(OptoReg::as_OptoReg(rcx->as_VMReg()->next())); - _LONG_NO_RBP_R13_REG_mask = _LONG_REG_mask; + _LONG_NO_RBP_R13_REG_mask.assignFrom(_LONG_REG_mask); _LONG_NO_RBP_R13_REG_mask.remove(OptoReg::as_OptoReg(rbp->as_VMReg())); _LONG_NO_RBP_R13_REG_mask.remove(OptoReg::as_OptoReg(rbp->as_VMReg()->next())); _LONG_NO_RBP_R13_REG_mask.remove(OptoReg::as_OptoReg(r13->as_VMReg())); _LONG_NO_RBP_R13_REG_mask.remove(OptoReg::as_OptoReg(r13->as_VMReg()->next())); - _INT_REG_mask = _ALL_INT_REG_mask; + _INT_REG_mask.assignFrom(_ALL_INT_REG_mask); if (!UseAPX) { for (uint i = 0; i < sizeof(egprs)/sizeof(Register); i++) { _INT_REG_mask.remove(OptoReg::as_OptoReg(egprs[i]->as_VMReg())); @@ -570,23 +570,23 @@ void reg_mask_init() { _INT_REG_mask.remove(OptoReg::as_OptoReg(r12->as_VMReg())); } - _STACK_OR_INT_REG_mask = _INT_REG_mask; + _STACK_OR_INT_REG_mask.assignFrom(_INT_REG_mask); _STACK_OR_INT_REG_mask.or_with(STACK_OR_STACK_SLOTS_mask()); - _INT_NO_RAX_RDX_REG_mask = _INT_REG_mask; + _INT_NO_RAX_RDX_REG_mask.assignFrom(_INT_REG_mask); _INT_NO_RAX_RDX_REG_mask.remove(OptoReg::as_OptoReg(rax->as_VMReg())); _INT_NO_RAX_RDX_REG_mask.remove(OptoReg::as_OptoReg(rdx->as_VMReg())); - _INT_NO_RCX_REG_mask = _INT_REG_mask; + _INT_NO_RCX_REG_mask.assignFrom(_INT_REG_mask); _INT_NO_RCX_REG_mask.remove(OptoReg::as_OptoReg(rcx->as_VMReg())); - _INT_NO_RBP_R13_REG_mask = _INT_REG_mask; + _INT_NO_RBP_R13_REG_mask.assignFrom(_INT_REG_mask); _INT_NO_RBP_R13_REG_mask.remove(OptoReg::as_OptoReg(rbp->as_VMReg())); _INT_NO_RBP_R13_REG_mask.remove(OptoReg::as_OptoReg(r13->as_VMReg())); // _FLOAT_REG_LEGACY_mask/_FLOAT_REG_EVEX_mask is generated by adlc // from the float_reg_legacy/float_reg_evex register class. - _FLOAT_REG_mask = VM_Version::supports_evex() ? _FLOAT_REG_EVEX_mask : _FLOAT_REG_LEGACY_mask; + _FLOAT_REG_mask.assignFrom(VM_Version::supports_evex() ? _FLOAT_REG_EVEX_mask : _FLOAT_REG_LEGACY_mask); } static bool generate_vzeroupper(Compile* C) { @@ -1678,22 +1678,22 @@ bool Matcher::use_asm_for_ldiv_by_con( jlong divisor ) { } // Register for DIVI projection of divmodI -RegMask Matcher::divI_proj_mask() { +const RegMask& Matcher::divI_proj_mask() { return INT_RAX_REG_mask(); } // Register for MODI projection of divmodI -RegMask Matcher::modI_proj_mask() { +const RegMask& Matcher::modI_proj_mask() { return INT_RDX_REG_mask(); } // Register for DIVL projection of divmodL -RegMask Matcher::divL_proj_mask() { +const RegMask& Matcher::divL_proj_mask() { return LONG_RAX_REG_mask(); } // Register for MODL projection of divmodL -RegMask Matcher::modL_proj_mask() { +const RegMask& Matcher::modL_proj_mask() { return LONG_RDX_REG_mask(); } diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp index ed83487265cb0..2cc0263d2913b 100644 --- a/src/hotspot/os/posix/perfMemory_posix.cpp +++ b/src/hotspot/os/posix/perfMemory_posix.cpp @@ -26,6 +26,7 @@ #include "classfile/vmSymbols.hpp" #include "jvm_io.h" #include "logging/log.hpp" +#include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "nmt/memTracker.hpp" @@ -71,9 +72,7 @@ static char* create_standard_memory(size_t size) { // commit memory if (!os::commit_memory(mapAddress, size, !ExecMem)) { - if (PrintMiscellaneous && Verbose) { - warning("Could not commit PerfData memory\n"); - } + log_debug(perf)("could not commit PerfData memory"); os::release_memory(mapAddress, size); return nullptr; } @@ -297,11 +296,12 @@ static DIR *open_directory_secure(const char* dirname) { RESTARTABLE(::open(dirname, O_RDONLY|O_NOFOLLOW), result); if (result == OS_ERR) { // Directory doesn't exist or is a symlink, so there is nothing to cleanup. - if (PrintMiscellaneous && Verbose) { + if (log_is_enabled(Debug, perf)) { + LogStreamHandle(Debug, perf) log; if (errno == ELOOP) { - warning("directory %s is a symlink and is not secure\n", dirname); + log.print_cr("directory %s is a symlink and is not secure", dirname); } else { - warning("could not open directory %s: %s\n", dirname, os::strerror(errno)); + log.print_cr("could not open directory %s: %s", dirname, os::strerror(errno)); } } return dirp; @@ -371,9 +371,7 @@ static DIR *open_directory_secure_cwd(const char* dirname, int *saved_cwd_fd) { // handle errors, otherwise shared memory files will be created in cwd. result = fchdir(fd); if (result == OS_ERR) { - if (PrintMiscellaneous && Verbose) { - warning("could not change to directory %s", dirname); - } + log_debug(perf)("could not change to directory %s", dirname); if (*saved_cwd_fd != -1) { ::close(*saved_cwd_fd); *saved_cwd_fd = -1; @@ -411,16 +409,12 @@ static bool is_file_secure(int fd, const char *filename) { // Determine if the file is secure. RESTARTABLE(::fstat(fd, &statbuf), result); if (result == OS_ERR) { - if (PrintMiscellaneous && Verbose) { - warning("fstat failed on %s: %s\n", filename, os::strerror(errno)); - } + log_debug(perf)("fstat failed on %s: %s", filename, os::strerror(errno)); return false; } if (statbuf.st_nlink > 1) { // A file with multiple links is not expected. - if (PrintMiscellaneous && Verbose) { - warning("file %s has multiple links\n", filename); - } + log_debug(perf)("file %s has multiple links", filename); return false; } return true; @@ -447,10 +441,10 @@ static char* get_user_name(uid_t uid) { int result = getpwuid_r(uid, &pwent, pwbuf, (size_t)bufsize, &p); if (result != 0 || p == nullptr || p->pw_name == nullptr || *(p->pw_name) == '\0') { - if (PrintMiscellaneous && Verbose) { + if (log_is_enabled(Debug, perf)) { + LogStreamHandle(Debug, perf) log; if (result != 0) { - warning("Could not retrieve passwd entry: %s\n", - os::strerror(result)); + log.print_cr("Could not retrieve passwd entry: %s", os::strerror(result)); } else if (p == nullptr) { // this check is added to protect against an observed problem @@ -463,13 +457,11 @@ static char* get_user_name(uid_t uid) { // message may result in an erroneous message. // Bug Id 89052 was opened with RedHat. // - warning("Could not retrieve passwd entry: %s\n", - os::strerror(errno)); + log.print_cr("Could not retrieve passwd entry: %s", os::strerror(errno)); } else { - warning("Could not determine user name: %s\n", - p->pw_name == nullptr ? "pw_name = null" : - "pw_name zero length"); + log.print_cr("Could not determine user name: %s", + p->pw_name == nullptr ? "pw_name = null" : "pw_name zero length"); } } FREE_C_HEAP_ARRAY(char, pwbuf); @@ -680,10 +672,10 @@ static void remove_file(const char* path) { // maliciously planted, the directory's presence won't hurt anything. // RESTARTABLE(::unlink(path), result); - if (PrintMiscellaneous && Verbose && result == OS_ERR) { + if (log_is_enabled(Debug, perf) && result == OS_ERR) { if (errno != ENOENT) { - warning("Could not unlink shared memory backing" - " store file %s : %s\n", path, os::strerror(errno)); + log_debug(perf)("could not unlink shared memory backing store file %s : %s", + path, os::strerror(errno)); } } } @@ -819,23 +811,16 @@ static bool make_user_tmp_dir(const char* dirname) { // The directory already exists and was probably created by another // JVM instance. However, this could also be the result of a // deliberate symlink. Verify that the existing directory is safe. - // if (!is_directory_secure(dirname)) { // directory is not secure - if (PrintMiscellaneous && Verbose) { - warning("%s directory is insecure\n", dirname); - } + log_debug(perf)("%s directory is insecure", dirname); return false; } } else { // we encountered some other failure while attempting // to create the directory - // - if (PrintMiscellaneous && Verbose) { - warning("could not create directory %s: %s\n", - dirname, os::strerror(errno)); - } + log_debug(perf)("could not create directory %s: %s", dirname, os::strerror(errno)); return false; } } @@ -872,11 +857,12 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size int fd; RESTARTABLE(os::open(filename, O_RDWR|O_CREAT|O_NOFOLLOW, S_IRUSR|S_IWUSR), fd); if (fd == OS_ERR) { - if (PrintMiscellaneous && Verbose) { + if (log_is_enabled(Debug, perf)) { + LogStreamHandle(Debug, perf) log; if (errno == ELOOP) { - warning("file %s is a symlink and is not secure\n", filename); + log.print_cr("file %s is a symlink and is not secure", filename); } else { - warning("could not create file %s: %s\n", filename, os::strerror(errno)); + log.print_cr("could not create file %s: %s", filename, os::strerror(errno)); } } // close the directory and reset the current working directory @@ -924,18 +910,14 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size // truncate the file to get rid of any existing data RESTARTABLE(::ftruncate(fd, (off_t)0), result); if (result == OS_ERR) { - if (PrintMiscellaneous && Verbose) { - warning("could not truncate shared memory file: %s\n", os::strerror(errno)); - } + log_debug(perf)("could not truncate shared memory file: %s", os::strerror(errno)); ::close(fd); return -1; } // set the file size RESTARTABLE(::ftruncate(fd, (off_t)size), result); if (result == OS_ERR) { - if (PrintMiscellaneous && Verbose) { - warning("could not set shared memory file size: %s\n", os::strerror(errno)); - } + log_debug(perf)("could not set shared memory file size: %s", os::strerror(errno)); ::close(fd); return -1; } @@ -1057,9 +1039,7 @@ static char* mmap_create_shared(size_t size) { assert(result != OS_ERR, "could not close file"); if (mapAddress == MAP_FAILED) { - if (PrintMiscellaneous && Verbose) { - warning("mmap failed - %s\n", os::strerror(errno)); - } + log_debug(perf)("mmap failed - %s", os::strerror(errno)); remove_file(filename); FREE_C_HEAP_ARRAY(char, filename); return nullptr; @@ -1135,9 +1115,7 @@ static size_t sharedmem_filesize(int fd, TRAPS) { RESTARTABLE(::fstat(fd, &statbuf), result); if (result == OS_ERR) { - if (PrintMiscellaneous && Verbose) { - warning("fstat failed: %s\n", os::strerror(errno)); - } + log_debug(perf)("fstat failed: %s", os::strerror(errno)); THROW_MSG_0(vmSymbols::java_io_IOException(), "Could not determine PerfMemory size"); } @@ -1212,9 +1190,7 @@ static void mmap_attach_shared(int vmid, char** addr, size_t* sizep, TRAPS) { assert(result != OS_ERR, "could not close file"); if (mapAddress == MAP_FAILED) { - if (PrintMiscellaneous && Verbose) { - warning("mmap failed: %s\n", os::strerror(errno)); - } + log_debug(perf)("mmap failed: %s", os::strerror(errno)); THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), "Could not map PerfMemory"); } @@ -1244,13 +1220,9 @@ void PerfMemory::create_memory_region(size_t size) { else { _start = create_shared_memory(size); if (_start == nullptr) { - // creation of the shared memory region failed, attempt // to create a contiguous, non-shared memory region instead. - // - if (PrintMiscellaneous && Verbose) { - warning("Reverting to non-shared PerfMemory region.\n"); - } + log_debug(perf)("Reverting to non-shared PerfMemory region."); FLAG_SET_ERGO(PerfDisableSharedMem, true); _start = create_standard_memory(size); } diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index 10b340214a1f6..5833e324070b0 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.cpp @@ -357,12 +357,12 @@ static void jdk_misc_signal_init() { } void os::signal_notify(int sig) { - // Signal thread is not created with ReduceSignalUsage and jdk_misc_signal_init - // initialization isn't called. This code is also never called. - assert(!ReduceSignalUsage, "Should not reach here if ReduceSignalUsage is set"); - + // Signal thread is not created with ReduceSignalUsage and jdk_misc_signal_init + // initialization isn't called. + if (!ReduceSignalUsage) { AtomicAccess::inc(&pending_signals[sig]); sig_semaphore->signal(); + } } static int check_pending_signals() { diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index ba05d390c9fe1..7934c9d6ffbd4 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -6259,3 +6259,106 @@ const void* os::get_saved_assert_context(const void** sigInfo) { *sigInfo = nullptr; return nullptr; } + +/* + * Windows/x64 does not use stack frames the way expected by Java: + * [1] in most cases, there is no frame pointer. All locals are addressed via RSP + * [2] in rare cases, when alloca() is used, a frame pointer is used, but this may + * not be RBP. + * See http://msdn.microsoft.com/en-us/library/ew5tede7.aspx + * + * So it's not possible to print the native stack using the + * while (...) {... fr = os::get_sender_for_C_frame(&fr); } + * loop in vmError.cpp. We need to roll our own loop. + * This approach works for Windows AArch64 as well. + */ +bool os::win32::platform_print_native_stack(outputStream* st, const void* context, + char* buf, int buf_size, address& lastpc) +{ + CONTEXT ctx; + if (context != nullptr) { + memcpy(&ctx, context, sizeof(ctx)); + } else { + RtlCaptureContext(&ctx); + } + + st->print_cr("Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)"); + + DWORD machine_type; + STACKFRAME stk; + memset(&stk, 0, sizeof(stk)); + stk.AddrStack.Mode = AddrModeFlat; + stk.AddrFrame.Mode = AddrModeFlat; + stk.AddrPC.Mode = AddrModeFlat; + +#if defined(_M_AMD64) + stk.AddrStack.Offset = ctx.Rsp; + stk.AddrFrame.Offset = ctx.Rbp; + stk.AddrPC.Offset = ctx.Rip; + machine_type = IMAGE_FILE_MACHINE_AMD64; +#elif defined(_M_ARM64) + stk.AddrStack.Offset = ctx.Sp; + stk.AddrFrame.Offset = ctx.Fp; + stk.AddrPC.Offset = ctx.Pc; + machine_type = IMAGE_FILE_MACHINE_ARM64; +#else + #error unknown architecture +#endif + + // Ensure we consider dynamically loaded DLLs + SymbolEngine::refreshModuleList(); + + int count = 0; + address lastpc_internal = 0; + while (count++ < StackPrintLimit) { + intptr_t* sp = (intptr_t*)stk.AddrStack.Offset; + intptr_t* fp = (intptr_t*)stk.AddrFrame.Offset; // NOT necessarily the same as ctx.Rbp! + address pc = (address)stk.AddrPC.Offset; + + if (pc != nullptr) { + if (count == 2 && lastpc_internal == pc) { + // Skip it -- StackWalk64() may return the same PC + // (but different SP) on the first try. + } else { + // Don't try to create a frame(sp, fp, pc) -- on WinX64, stk.AddrFrame + // may not contain what Java expects, and may cause the frame() constructor + // to crash. Let's just print out the symbolic address. + frame::print_C_frame(st, buf, buf_size, pc); + // print source file and line, if available + char buf[128]; + int line_no; + if (SymbolEngine::get_source_info(pc, buf, sizeof(buf), &line_no)) { + st->print(" (%s:%d)", buf, line_no); + } else { + st->print(" (no source info available)"); + } + st->cr(); + } + lastpc_internal = pc; + } + + PVOID p = WindowsDbgHelp::symFunctionTableAccess64(GetCurrentProcess(), stk.AddrPC.Offset); + if (p == nullptr) { + // StackWalk64() can't handle this PC. Calling StackWalk64 again may cause crash. + lastpc = lastpc_internal; + break; + } + + BOOL result = WindowsDbgHelp::stackWalk64( + machine_type, // __in DWORD MachineType, + GetCurrentProcess(), // __in HANDLE hProcess, + GetCurrentThread(), // __in HANDLE hThread, + &stk, // __inout LP STACKFRAME64 StackFrame, + &ctx); // __inout PVOID ContextRecord, + + if (!result) { + break; + } + } + if (count > StackPrintLimit) { + st->print_cr("......"); + } + st->cr(); + + return true; +} diff --git a/src/hotspot/os/windows/perfMemory_windows.cpp b/src/hotspot/os/windows/perfMemory_windows.cpp index a9b2eebb7be0c..f815f3bd1041c 100644 --- a/src/hotspot/os/windows/perfMemory_windows.cpp +++ b/src/hotspot/os/windows/perfMemory_windows.cpp @@ -24,6 +24,7 @@ #include "classfile/vmSymbols.hpp" #include "logging/log.hpp" +#include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "nmt/memTracker.hpp" @@ -62,9 +63,7 @@ static char* create_standard_memory(size_t size) { // commit memory if (!os::commit_memory(mapAddress, size, !ExecMem)) { - if (PrintMiscellaneous && Verbose) { - warning("Could not commit PerfData memory\n"); - } + log_debug(perf)("could not commit PerfData memory"); os::release_memory(mapAddress, size); return nullptr; } @@ -90,25 +89,21 @@ static void delete_standard_memory(char* addr, size_t size) { static void save_memory_to_file(char* addr, size_t size) { const char* destfile = PerfMemory::get_perfdata_file_path(); - assert(destfile[0] != '\0', "invalid Perfdata file path"); + assert(destfile[0] != '\0', "invalid PerfData file path"); int fd = ::_open(destfile, _O_BINARY|_O_CREAT|_O_WRONLY|_O_TRUNC, _S_IREAD|_S_IWRITE); if (fd == OS_ERR) { - if (PrintMiscellaneous && Verbose) { - warning("Could not create Perfdata save file: %s: %s\n", - destfile, os::strerror(errno)); - } + log_debug(perf)("could not create PerfData save file: %s: %s", + destfile, os::strerror(errno)); } else { for (size_t remaining = size; remaining > 0;) { int nbytes = ::_write(fd, addr, (unsigned int)remaining); if (nbytes == OS_ERR) { - if (PrintMiscellaneous && Verbose) { - warning("Could not write Perfdata save file: %s: %s\n", - destfile, os::strerror(errno)); - } + log_debug(perf)("could not write PerfData save file: %s: %s", + destfile, os::strerror(errno)); break; } @@ -117,10 +112,8 @@ static void save_memory_to_file(char* addr, size_t size) { } int result = ::_close(fd); - if (PrintMiscellaneous && Verbose) { - if (result == OS_ERR) { - warning("Could not close %s: %s\n", destfile, os::strerror(errno)); - } + if (result == OS_ERR) { + log_debug(perf)("could not close %s: %s", destfile, os::strerror(errno)); } } @@ -220,10 +213,8 @@ static bool is_directory_secure(const char* path) { } else { // unexpected error, declare the path insecure - if (PrintMiscellaneous && Verbose) { - warning("could not get attributes for file %s: " - " lasterror = %d\n", path, lasterror); - } + log_debug(perf)("could not get attributes for file %s: lasterror = %d", + path, lasterror); return false; } } @@ -234,9 +225,7 @@ static bool is_directory_secure(const char* path) { // as some types of reparse points might be acceptable, but it // is probably more secure to avoid these conditions. // - if (PrintMiscellaneous && Verbose) { - warning("%s is a reparse point\n", path); - } + log_debug(perf)("%s is a reparse point", path); return false; } @@ -253,10 +242,8 @@ static bool is_directory_secure(const char* path) { // this is either a regular file or some other type of file, // any of which are unexpected and therefore insecure. // - if (PrintMiscellaneous && Verbose) { - warning("%s is not a directory, file attributes = " - INTPTR_FORMAT "\n", path, fa); - } + log_debug(perf)("%s is not a directory, file attributes : " + INTPTR_FORMAT, path, fa); return false; } } @@ -492,11 +479,9 @@ static void remove_file(const char* dirname, const char* filename) { strcat(path, filename); if (::unlink(path) == OS_ERR) { - if (PrintMiscellaneous && Verbose) { - if (errno != ENOENT) { - warning("Could not unlink shared memory backing" - " store file %s : %s\n", path, os::strerror(errno)); - } + if (errno != ENOENT) { + log_debug(perf)("could not unlink shared memory backing store file %s : %s", + path, os::strerror(errno)); } } @@ -515,20 +500,16 @@ static bool is_alive(int pid) { HANDLE ph = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); if (ph == nullptr) { // the process does not exist. - if (PrintMiscellaneous && Verbose) { - DWORD lastError = GetLastError(); - if (lastError != ERROR_INVALID_PARAMETER) { - warning("OpenProcess failed: %d\n", GetLastError()); - } + DWORD lastError = GetLastError(); + if (lastError != ERROR_INVALID_PARAMETER) { + log_debug(perf)("OpenProcess failed: %d", lastError); } return false; } DWORD exit_status; if (!GetExitCodeProcess(ph, &exit_status)) { - if (PrintMiscellaneous && Verbose) { - warning("GetExitCodeProcess failed: %d\n", GetLastError()); - } + log_debug(perf)("GetExitCodeProcess failed: %d", GetLastError()); CloseHandle(ph); return false; } @@ -545,17 +526,13 @@ static bool is_filesystem_secure(const char* path) { char fs_type[MAX_PATH]; if (PerfBypassFileSystemCheck) { - if (PrintMiscellaneous && Verbose) { - warning("bypassing file system criteria checks for %s\n", path); - } + log_debug(perf)("bypassing file system criteria checks for %s", path); return true; } char* first_colon = strchr((char *)path, ':'); if (first_colon == nullptr) { - if (PrintMiscellaneous && Verbose) { - warning("expected device specifier in path: %s\n", path); - } + log_debug(perf)("expected device specifier in path: %s", path); return false; } @@ -576,29 +553,22 @@ static bool is_filesystem_secure(const char* path) { if (!GetVolumeInformation(root_path, nullptr, 0, nullptr, &maxpath, &flags, fs_type, MAX_PATH)) { // we can't get information about the volume, so assume unsafe. - if (PrintMiscellaneous && Verbose) { - warning("could not get device information for %s: " - " path = %s: lasterror = %d\n", - root_path, path, GetLastError()); - } + log_debug(perf)("could not get device information for %s: path = %s: lasterror = %d", + root_path, path, GetLastError()); return false; } if ((flags & FS_PERSISTENT_ACLS) == 0) { // file system doesn't support ACLs, declare file system unsafe - if (PrintMiscellaneous && Verbose) { - warning("file system type %s on device %s does not support" - " ACLs\n", fs_type, root_path); - } + log_debug(perf)("file system type %s on device %s does not support ACLs", + fs_type, root_path); return false; } if ((flags & FS_VOL_IS_COMPRESSED) != 0) { // file system is compressed, declare file system unsafe - if (PrintMiscellaneous && Verbose) { - warning("file system type %s on device %s is compressed\n", - fs_type, root_path); - } + log_debug(perf)("file system type %s on device %s is compressed", + fs_type, root_path); return false; } @@ -704,9 +674,7 @@ static HANDLE create_file_mapping(const char* name, HANDLE fh, LPSECURITY_ATTRIB name); /* LPCTSTR name for object */ if (fmh == nullptr) { - if (PrintMiscellaneous && Verbose) { - warning("CreateFileMapping failed, lasterror = %d\n", GetLastError()); - } + log_debug(perf)("CreateFileMapping failed, lasterror = %d", GetLastError()); return nullptr; } @@ -717,9 +685,7 @@ static HANDLE create_file_mapping(const char* name, HANDLE fh, LPSECURITY_ATTRIB // the other processes either exit or close their mapping objects // and/or mapped views of this mapping object. // - if (PrintMiscellaneous && Verbose) { - warning("file mapping already exists, lasterror = %d\n", GetLastError()); - } + log_debug(perf)("file mapping already exists, lasterror = %d", GetLastError()); CloseHandle(fmh); return nullptr; @@ -783,9 +749,7 @@ static PSID get_user_sid(HANDLE hProcess) { // get the process token if (!OpenProcessToken(hProcess, TOKEN_READ, &hAccessToken)) { - if (PrintMiscellaneous && Verbose) { - warning("OpenProcessToken failure: lasterror = %d \n", GetLastError()); - } + log_debug(perf)("OpenProcessToken failure: lasterror = %d", GetLastError()); return nullptr; } @@ -795,10 +759,8 @@ static PSID get_user_sid(HANDLE hProcess) { if (!GetTokenInformation(hAccessToken, TokenUser, nullptr, rsize, &rsize)) { DWORD lasterror = GetLastError(); if (lasterror != ERROR_INSUFFICIENT_BUFFER) { - if (PrintMiscellaneous && Verbose) { - warning("GetTokenInformation failure: lasterror = %d," - " rsize = %d\n", lasterror, rsize); - } + log_debug(perf)("GetTokenInformation failure: lasterror = %d, rsize = %d", + lasterror, rsize); CloseHandle(hAccessToken); return nullptr; } @@ -808,10 +770,8 @@ static PSID get_user_sid(HANDLE hProcess) { // get the user token information if (!GetTokenInformation(hAccessToken, TokenUser, token_buf, rsize, &rsize)) { - if (PrintMiscellaneous && Verbose) { - warning("GetTokenInformation failure: lasterror = %d," - " rsize = %d\n", GetLastError(), rsize); - } + log_debug(perf)("GetTokenInformation failure: lasterror = %d, rsize = %d", + GetLastError(), rsize); FREE_C_HEAP_ARRAY(char, token_buf); CloseHandle(hAccessToken); return nullptr; @@ -821,10 +781,8 @@ static PSID get_user_sid(HANDLE hProcess) { PSID pSID = NEW_C_HEAP_ARRAY(char, nbytes, mtInternal); if (!CopySid(nbytes, pSID, token_buf->User.Sid)) { - if (PrintMiscellaneous && Verbose) { - warning("GetTokenInformation failure: lasterror = %d," - " rsize = %d\n", GetLastError(), rsize); - } + log_debug(perf)("GetTokenInformation failure: lasterror = %d, rsize = %d", + GetLastError(), rsize); FREE_C_HEAP_ARRAY(char, token_buf); FREE_C_HEAP_ARRAY(char, pSID); CloseHandle(hAccessToken); @@ -866,10 +824,8 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, // retrieve any existing access control list. if (!GetSecurityDescriptorDacl(pSD, &exists, &oldACL, &isdefault)) { - if (PrintMiscellaneous && Verbose) { - warning("GetSecurityDescriptor failure: lasterror = %d \n", - GetLastError()); - } + log_debug(perf)("GetSecurityDescriptor failure: lasterror = %d", + GetLastError()); return false; } @@ -886,10 +842,8 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, if (!GetAclInformation(oldACL, &aclinfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation)) { - if (PrintMiscellaneous && Verbose) { - warning("GetAclInformation failure: lasterror = %d \n", GetLastError()); - return false; - } + log_debug(perf)("GetAclInformation failure: lasterror = %d", GetLastError()); + return false; } } else { aclinfo.AceCount = 0; // assume null DACL @@ -914,9 +868,7 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, newACL = (PACL) NEW_C_HEAP_ARRAY(char, newACLsize, mtInternal); if (!InitializeAcl(newACL, newACLsize, ACL_REVISION)) { - if (PrintMiscellaneous && Verbose) { - warning("InitializeAcl failure: lasterror = %d \n", GetLastError()); - } + log_debug(perf)("InitializeAcl failure: lasterror = %d", GetLastError()); FREE_C_HEAP_ARRAY(char, newACL); return false; } @@ -927,9 +879,7 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, while (ace_index < aclinfo.AceCount) { LPVOID ace; if (!GetAce(oldACL, ace_index, &ace)) { - if (PrintMiscellaneous && Verbose) { - warning("InitializeAcl failure: lasterror = %d \n", GetLastError()); - } + log_debug(perf)("InitializeAcl failure: lasterror = %d", GetLastError()); FREE_C_HEAP_ARRAY(char, newACL); return false; } @@ -954,9 +904,7 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, if (matches == 0) { if (!AddAce(newACL, ACL_REVISION, MAXDWORD, ace, ((PACE_HEADER)ace)->AceSize)) { - if (PrintMiscellaneous && Verbose) { - warning("AddAce failure: lasterror = %d \n", GetLastError()); - } + log_debug(perf)("AddAce failure: lasterror = %d", GetLastError()); FREE_C_HEAP_ARRAY(char, newACL); return false; } @@ -969,10 +917,8 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, for (int i = 0; i < ace_count; i++) { if (!AddAccessAllowedAce(newACL, ACL_REVISION, aces[i].mask, aces[i].pSid)) { - if (PrintMiscellaneous && Verbose) { - warning("AddAccessAllowedAce failure: lasterror = %d \n", - GetLastError()); - } + log_debug(perf)("AddAccessAllowedAce failure: lasterror = %d", + GetLastError()); FREE_C_HEAP_ARRAY(char, newACL); return false; } @@ -985,17 +931,13 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, while (ace_index < aclinfo.AceCount) { LPVOID ace; if (!GetAce(oldACL, ace_index, &ace)) { - if (PrintMiscellaneous && Verbose) { - warning("InitializeAcl failure: lasterror = %d \n", GetLastError()); - } + log_debug(perf)("InitializeAcl failure: lasterror = %d", GetLastError()); FREE_C_HEAP_ARRAY(char, newACL); return false; } if (!AddAce(newACL, ACL_REVISION, MAXDWORD, ace, ((PACE_HEADER)ace)->AceSize)) { - if (PrintMiscellaneous && Verbose) { - warning("AddAce failure: lasterror = %d \n", GetLastError()); - } + log_debug(perf)("AddAce failure: lasterror = %d", GetLastError()); FREE_C_HEAP_ARRAY(char, newACL); return false; } @@ -1005,10 +947,7 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, // add the new ACL to the security descriptor. if (!SetSecurityDescriptorDacl(pSD, TRUE, newACL, FALSE)) { - if (PrintMiscellaneous && Verbose) { - warning("SetSecurityDescriptorDacl failure:" - " lasterror = %d \n", GetLastError()); - } + log_debug(perf)("SetSecurityDescriptorDacl failure: lasterror = %d", GetLastError()); FREE_C_HEAP_ARRAY(char, newACL); return false; } @@ -1025,10 +964,7 @@ static bool add_allow_aces(PSECURITY_DESCRIPTOR pSD, // protected prevents that. if (!_SetSecurityDescriptorControl(pSD, SE_DACL_PROTECTED, SE_DACL_PROTECTED)) { - if (PrintMiscellaneous && Verbose) { - warning("SetSecurityDescriptorControl failure:" - " lasterror = %d \n", GetLastError()); - } + log_debug(perf)("SetSecurityDescriptorControl failure: lasterror = %d", GetLastError()); FREE_C_HEAP_ARRAY(char, newACL); return false; } @@ -1057,10 +993,7 @@ static LPSECURITY_ATTRIBUTES make_security_attr(ace_data_t aces[], int count) { // initialize the security descriptor if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { - if (PrintMiscellaneous && Verbose) { - warning("InitializeSecurityDescriptor failure: " - "lasterror = %d \n", GetLastError()); - } + log_debug(perf)("InitializeSecurityDescriptor failure: lasterror = %d", GetLastError()); free_security_desc(pSD); return nullptr; } @@ -1113,11 +1046,7 @@ static LPSECURITY_ATTRIBUTES make_user_everybody_admin_security_attr( SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &administratorsSid)) { - - if (PrintMiscellaneous && Verbose) { - warning("AllocateAndInitializeSid failure: " - "lasterror = %d \n", GetLastError()); - } + log_debug(perf)("AllocateAndInitializeSid failure: lasterror = %d", GetLastError()); return nullptr; } @@ -1131,11 +1060,7 @@ static LPSECURITY_ATTRIBUTES make_user_everybody_admin_security_attr( if (!AllocateAndInitializeSid( &SIDAuthEverybody, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &everybodySid)) { - - if (PrintMiscellaneous && Verbose) { - warning("AllocateAndInitializeSid failure: " - "lasterror = %d \n", GetLastError()); - } + log_debug(perf)("AllocateAndInitializeSid failure: lasterror = %d", GetLastError()); return nullptr; } @@ -1236,9 +1161,7 @@ static bool make_user_tmp_dir(const char* dirname) { // if (!is_directory_secure(dirname)) { // directory is not secure - if (PrintMiscellaneous && Verbose) { - warning("%s directory is insecure\n", dirname); - } + log_debug(perf)("%s directory is insecure", dirname); free_security_attr(pDirSA); return false; } @@ -1249,16 +1172,11 @@ static bool make_user_tmp_dir(const char* dirname) { // DACLs might fix the corrupted the DACLs. SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION; if (!SetFileSecurity(dirname, secInfo, pDirSA->lpSecurityDescriptor)) { - if (PrintMiscellaneous && Verbose) { - lasterror = GetLastError(); - warning("SetFileSecurity failed for %s directory. lasterror %d \n", - dirname, lasterror); - } + lasterror = GetLastError(); + log_debug(perf)("SetFileSecurity failed for %s directory. lasterror = %d", dirname, lasterror); } } else { - if (PrintMiscellaneous && Verbose) { - warning("CreateDirectory failed: %d\n", GetLastError()); - } + log_debug(perf)("CreateDirectory failed: %d", GetLastError()); free_security_attr(pDirSA); return false; } @@ -1325,9 +1243,7 @@ static HANDLE create_sharedmem_resources(const char* dirname, const char* filena if (fh == INVALID_HANDLE_VALUE) { DWORD lasterror = GetLastError(); - if (PrintMiscellaneous && Verbose) { - warning("could not create file %s: %d\n", filename, lasterror); - } + log_debug(perf)("could not create file %s: %d", filename, lasterror); free_security_attr(lpSmoSA); return nullptr; } @@ -1353,10 +1269,8 @@ static HANDLE create_sharedmem_resources(const char* dirname, const char* filena struct stat statbuf; int ret_code = ::stat(filename, &statbuf); if (ret_code == OS_ERR) { - if (PrintMiscellaneous && Verbose) { - warning("Could not get status information from file %s: %s\n", - filename, os::strerror(errno)); - } + log_debug(perf)("could not get status information from file %s: %s", + filename, os::strerror(errno)); CloseHandle(fmh); CloseHandle(fh); fh = nullptr; @@ -1369,9 +1283,7 @@ static HANDLE create_sharedmem_resources(const char* dirname, const char* filena // call it when we observe the size as zero (0). if (statbuf.st_size == 0 && FlushFileBuffers(fh) != TRUE) { DWORD lasterror = GetLastError(); - if (PrintMiscellaneous && Verbose) { - warning("could not flush file %s: %d\n", filename, lasterror); - } + log_debug(perf)("could not flush file %s: %d", filename, lasterror); CloseHandle(fmh); CloseHandle(fh); fh = nullptr; @@ -1402,10 +1314,8 @@ static HANDLE open_sharedmem_object(const char* objectname, DWORD ofm_access, TR if (fmh == nullptr) { DWORD lasterror = GetLastError(); - if (PrintMiscellaneous && Verbose) { - warning("OpenFileMapping failed for shared memory object %s:" - " lasterror = %d\n", objectname, lasterror); - } + log_debug(perf)("OpenFileMapping failed for shared memory object %s:" + " lasterror = %d", objectname, lasterror); THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), err_msg("Could not open PerfMemory, error %d", lasterror), INVALID_HANDLE_VALUE); @@ -1485,9 +1395,7 @@ static char* mapping_create_shared(size_t size) { (DWORD)size); /* DWORD Number of bytes to map */ if (mapAddress == nullptr) { - if (PrintMiscellaneous && Verbose) { - warning("MapViewOfFile failed, lasterror = %d\n", GetLastError()); - } + log_debug(perf)("MapViewOfFile failed, lasterror = %d", GetLastError()); CloseHandle(sharedmem_fileMapHandle); sharedmem_fileMapHandle = nullptr; return nullptr; @@ -1551,20 +1459,14 @@ static size_t sharedmem_filesize(const char* filename, TRAPS) { // inconsistencies // if (::stat(filename, &statbuf) == OS_ERR) { - if (PrintMiscellaneous && Verbose) { - warning("stat %s failed: %s\n", filename, os::strerror(errno)); - } + log_debug(perf)("stat %s failed: %s", filename, os::strerror(errno)); THROW_MSG_0(vmSymbols::java_io_IOException(), "Could not determine PerfMemory size"); } if ((statbuf.st_size == 0) || (statbuf.st_size % os::vm_page_size() != 0)) { - if (PrintMiscellaneous && Verbose) { - warning("unexpected file size: size = %zu\n", - statbuf.st_size); - } - THROW_MSG_0(vmSymbols::java_io_IOException(), - "Invalid PerfMemory size"); + log_debug(perf)("unexpected file size: size = %zu", statbuf.st_size); + THROW_MSG_0(vmSymbols::java_io_IOException(), "Invalid PerfMemory size"); } return statbuf.st_size; @@ -1637,9 +1539,7 @@ static void open_file_mapping(int vmid, char** addrp, size_t* sizep, TRAPS) { size); /* DWORD Number of bytes to map */ if (mapAddress == nullptr) { - if (PrintMiscellaneous && Verbose) { - warning("MapViewOfFile failed, lasterror = %d\n", GetLastError()); - } + log_debug(perf)("MapViewOfFile failed, lasterror = %d", GetLastError()); CloseHandle(fmh); THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), "Could not map PerfMemory"); @@ -1708,9 +1608,7 @@ void PerfMemory::create_memory_region(size_t size) { // creation of the shared memory region failed, attempt // to create a contiguous, non-shared memory region instead. // - if (PrintMiscellaneous && Verbose) { - warning("Reverting to non-shared PerfMemory region.\n"); - } + log_debug(perf)("Reverting to non-shared PerfMemory region."); FLAG_SET_ERGO(PerfDisableSharedMem, true); _start = create_standard_memory(size); } diff --git a/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.inline.hpp b/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.inline.hpp index 794aa12155b17..568b6e3938ee7 100644 --- a/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.inline.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.inline.hpp @@ -26,10 +26,17 @@ #define OS_CPU_WINDOWS_AARCH64_OS_WINDOWS_AARCH64_INLINE_HPP #include "runtime/os.hpp" +#include "os_windows.hpp" inline bool os::register_code_area(char *low, char *high) { // Using Vectored Exception Handling return true; } +#define HAVE_PLATFORM_PRINT_NATIVE_STACK 1 +inline bool os::platform_print_native_stack(outputStream* st, const void* context, + char *buf, int buf_size, address& lastpc) { + return os::win32::platform_print_native_stack(st, context, buf, buf_size, lastpc); +} + #endif // OS_CPU_WINDOWS_AARCH64_OS_WINDOWS_AARCH64_INLINE_HPP diff --git a/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp b/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp index c188919595c9d..53f9647983248 100644 --- a/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp +++ b/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp @@ -197,98 +197,6 @@ bool handle_FLT_exception(struct _EXCEPTION_POINTERS* exceptionInfo) { } #endif -#ifdef HAVE_PLATFORM_PRINT_NATIVE_STACK -/* - * Windows/x64 does not use stack frames the way expected by Java: - * [1] in most cases, there is no frame pointer. All locals are addressed via RSP - * [2] in rare cases, when alloca() is used, a frame pointer is used, but this may - * not be RBP. - * See http://msdn.microsoft.com/en-us/library/ew5tede7.aspx - * - * So it's not possible to print the native stack using the - * while (...) {... fr = os::get_sender_for_C_frame(&fr); } - * loop in vmError.cpp. We need to roll our own loop. - */ -bool os::win32::platform_print_native_stack(outputStream* st, const void* context, - char *buf, int buf_size, address& lastpc) -{ - CONTEXT ctx; - if (context != nullptr) { - memcpy(&ctx, context, sizeof(ctx)); - } else { - RtlCaptureContext(&ctx); - } - - st->print_cr("Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)"); - - STACKFRAME stk; - memset(&stk, 0, sizeof(stk)); - stk.AddrStack.Offset = ctx.Rsp; - stk.AddrStack.Mode = AddrModeFlat; - stk.AddrFrame.Offset = ctx.Rbp; - stk.AddrFrame.Mode = AddrModeFlat; - stk.AddrPC.Offset = ctx.Rip; - stk.AddrPC.Mode = AddrModeFlat; - - // Ensure we consider dynamically loaded dll's - SymbolEngine::refreshModuleList(); - - int count = 0; - address lastpc_internal = 0; - while (count++ < StackPrintLimit) { - intptr_t* sp = (intptr_t*)stk.AddrStack.Offset; - intptr_t* fp = (intptr_t*)stk.AddrFrame.Offset; // NOT necessarily the same as ctx.Rbp! - address pc = (address)stk.AddrPC.Offset; - - if (pc != nullptr) { - if (count == 2 && lastpc_internal == pc) { - // Skip it -- StackWalk64() may return the same PC - // (but different SP) on the first try. - } else { - // Don't try to create a frame(sp, fp, pc) -- on WinX64, stk.AddrFrame - // may not contain what Java expects, and may cause the frame() constructor - // to crash. Let's just print out the symbolic address. - frame::print_C_frame(st, buf, buf_size, pc); - // print source file and line, if available - char buf[128]; - int line_no; - if (SymbolEngine::get_source_info(pc, buf, sizeof(buf), &line_no)) { - st->print(" (%s:%d)", buf, line_no); - } else { - st->print(" (no source info available)"); - } - st->cr(); - } - lastpc_internal = pc; - } - - PVOID p = WindowsDbgHelp::symFunctionTableAccess64(GetCurrentProcess(), stk.AddrPC.Offset); - if (!p) { - // StackWalk64() can't handle this PC. Calling StackWalk64 again may cause crash. - lastpc = lastpc_internal; - break; - } - - BOOL result = WindowsDbgHelp::stackWalk64( - IMAGE_FILE_MACHINE_AMD64, // __in DWORD MachineType, - GetCurrentProcess(), // __in HANDLE hProcess, - GetCurrentThread(), // __in HANDLE hThread, - &stk, // __inout LP STACKFRAME64 StackFrame, - &ctx); // __inout PVOID ContextRecord, - - if (!result) { - break; - } - } - if (count > StackPrintLimit) { - st->print_cr("......"); - } - st->cr(); - - return true; -} -#endif // HAVE_PLATFORM_PRINT_NATIVE_STACK - address os::fetch_frame_from_context(const void* ucVoid, intptr_t** ret_sp, intptr_t** ret_fp) { diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp index 6cc3a81c2ae1a..8b4e60dece2d8 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.cpp @@ -225,7 +225,38 @@ void AOTConstantPoolResolver::preresolve_field_and_method_cp_entries(JavaThread* Bytecodes::Code raw_bc = bcs.raw_code(); switch (raw_bc) { case Bytecodes::_getfield: + // no-fast bytecode + case Bytecodes::_nofast_getfield: + // fast bytecodes + case Bytecodes::_fast_agetfield: + case Bytecodes::_fast_bgetfield: + case Bytecodes::_fast_cgetfield: + case Bytecodes::_fast_dgetfield: + case Bytecodes::_fast_fgetfield: + case Bytecodes::_fast_igetfield: + case Bytecodes::_fast_lgetfield: + case Bytecodes::_fast_sgetfield: + raw_bc = Bytecodes::_getfield; + maybe_resolve_fmi_ref(ik, m, raw_bc, bcs.get_index_u2(), preresolve_list, THREAD); + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; // just ignore + } + break; + case Bytecodes::_putfield: + // no-fast bytecode + case Bytecodes::_nofast_putfield: + // fast bytecodes + case Bytecodes::_fast_aputfield: + case Bytecodes::_fast_bputfield: + case Bytecodes::_fast_zputfield: + case Bytecodes::_fast_cputfield: + case Bytecodes::_fast_dputfield: + case Bytecodes::_fast_fputfield: + case Bytecodes::_fast_iputfield: + case Bytecodes::_fast_lputfield: + case Bytecodes::_fast_sputfield: + raw_bc = Bytecodes::_putfield; maybe_resolve_fmi_ref(ik, m, raw_bc, bcs.get_index_u2(), preresolve_list, THREAD); if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; // just ignore diff --git a/src/hotspot/share/cds/aotMapLogger.cpp b/src/hotspot/share/cds/aotMapLogger.cpp index b0e410b5cf1bb..151c15048c28e 100644 --- a/src/hotspot/share/cds/aotMapLogger.cpp +++ b/src/hotspot/share/cds/aotMapLogger.cpp @@ -135,12 +135,14 @@ class AOTMapLogger::RuntimeGatherArchivedMetaspaceObjs : public UniqueMetaspaceC virtual bool do_unique_ref(Ref* ref, bool read_only) { ArchivedObjInfo info; - info._src_addr = ref->obj(); - info._buffered_addr = ref->obj(); - info._requested_addr = ref->obj(); - info._bytes = ref->size() * BytesPerWord; - info._type = ref->msotype(); - _objs.append(info); + if (AOTMetaspace::in_aot_cache(ref->obj())) { + info._src_addr = ref->obj(); + info._buffered_addr = ref->obj(); + info._requested_addr = ref->obj(); + info._bytes = ref->size() * BytesPerWord; + info._type = ref->msotype(); + _objs.append(info); + } return true; // keep iterating } diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index dfe74acd6c1e5..a9bbc398736d3 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -127,6 +127,14 @@ void FinalImageRecipes::record_recipes_for_constantpool() { } if (cp_indices.length() > 0) { + LogStreamHandle(Trace, aot, resolve) log; + if (log.is_enabled()) { + log.print("ConstantPool entries for %s to be pre-resolved:", k->external_name()); + for (int i = 0; i < cp_indices.length(); i++) { + log.print(" %d", cp_indices.at(i)); + } + log.print("\n"); + } tmp_cp_recipes.append(ArchiveUtils::archive_array(&cp_indices)); } else { tmp_cp_recipes.append(nullptr); diff --git a/src/hotspot/share/classfile/stackMapTable.cpp b/src/hotspot/share/classfile/stackMapTable.cpp index 9e02956aceb6c..85fb4de868658 100644 --- a/src/hotspot/share/classfile/stackMapTable.cpp +++ b/src/hotspot/share/classfile/stackMapTable.cpp @@ -132,8 +132,16 @@ bool StackMapTable::match_stackmap( } void StackMapTable::check_jump_target( - StackMapFrame* frame, int32_t target, TRAPS) const { + StackMapFrame* frame, int bci, int offset, TRAPS) const { ErrorContext ctx; + // Jump targets must be within the method and the method size is limited. See JVMS 4.11 + int min_offset = -1 * max_method_code_size; + if (offset < min_offset || offset > max_method_code_size) { + frame->verifier()->verify_error(ErrorContext::bad_stackmap(bci, frame), + "Illegal target of jump or branch (bci %d + offset %d)", bci, offset); + return; + } + int target = bci + offset; bool match = match_stackmap( frame, target, true, false, &ctx, CHECK_VERIFY(frame->verifier())); if (!match || (target < 0 || target >= _code_length)) { diff --git a/src/hotspot/share/classfile/stackMapTable.hpp b/src/hotspot/share/classfile/stackMapTable.hpp index 6d4c0ce36c086..9b46fa89345d9 100644 --- a/src/hotspot/share/classfile/stackMapTable.hpp +++ b/src/hotspot/share/classfile/stackMapTable.hpp @@ -67,7 +67,7 @@ class StackMapTable : public StackObj { // Check jump instructions. Make sure there are no uninitialized // instances on backward branch. - void check_jump_target(StackMapFrame* frame, int32_t target, TRAPS) const; + void check_jump_target(StackMapFrame* frame, int bci, int offset, TRAPS) const; // The following methods are only used inside this class. diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 9b93e283362fd..38dba1d3d5fbb 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -781,7 +781,6 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) { // Merge with the next instruction { - int target; VerificationType type, type2; VerificationType atype; @@ -1606,9 +1605,8 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) { case Bytecodes::_ifle: current_frame.pop_stack( VerificationType::integer_type(), CHECK_VERIFY(this)); - target = bcs.dest(); stackmap_table.check_jump_target( - ¤t_frame, target, CHECK_VERIFY(this)); + ¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this)); no_control_flow = false; break; case Bytecodes::_if_acmpeq : case Bytecodes::_if_acmpne : @@ -1619,19 +1617,16 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) { case Bytecodes::_ifnonnull : current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); - target = bcs.dest(); stackmap_table.check_jump_target - (¤t_frame, target, CHECK_VERIFY(this)); + (¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this)); no_control_flow = false; break; case Bytecodes::_goto : - target = bcs.dest(); stackmap_table.check_jump_target( - ¤t_frame, target, CHECK_VERIFY(this)); + ¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this)); no_control_flow = true; break; case Bytecodes::_goto_w : - target = bcs.dest_w(); stackmap_table.check_jump_target( - ¤t_frame, target, CHECK_VERIFY(this)); + ¤t_frame, bcs.bci(), bcs.get_offset_s4(), CHECK_VERIFY(this)); no_control_flow = true; break; case Bytecodes::_tableswitch : case Bytecodes::_lookupswitch : @@ -2280,15 +2275,14 @@ void ClassVerifier::verify_switch( } } } - int target = bci + default_offset; - stackmap_table->check_jump_target(current_frame, target, CHECK_VERIFY(this)); + stackmap_table->check_jump_target(current_frame, bci, default_offset, CHECK_VERIFY(this)); for (int i = 0; i < keys; i++) { // Because check_jump_target() may safepoint, the bytecode could have // moved, which means 'aligned_bcp' is no good and needs to be recalculated. aligned_bcp = align_up(bcs->bcp() + 1, jintSize); - target = bci + (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize); + int offset = (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize); stackmap_table->check_jump_target( - current_frame, target, CHECK_VERIFY(this)); + current_frame, bci, offset, CHECK_VERIFY(this)); } NOT_PRODUCT(aligned_bcp = nullptr); // no longer valid at this point } diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp index 16cae714cb976..f3d411e34ba51 100644 --- a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp +++ b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp @@ -52,7 +52,7 @@ jint EpsilonHeap::initialize() { initialize_reserved_region(heap_rs); _space = new ContiguousSpace(); - _space->initialize(committed_region, /* clear_space = */ true, /* mangle_space = */ true); + _space->initialize(committed_region, /* clear_space = */ true); // Precompute hot fields _max_tlab_size = MIN2(CollectedHeap::max_tlab_size(), align_object_size(EpsilonMaxTLABSize / HeapWordSize)); diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.cpp b/src/hotspot/share/gc/g1/g1BarrierSet.cpp index ab7d6febf4cf7..622651ce0d8d1 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.cpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.cpp @@ -111,7 +111,7 @@ void G1BarrierSet::write_ref_array_pre(narrowOop* dst, size_t count, bool dest_u } } -void G1BarrierSet::write_region(JavaThread* thread, MemRegion mr) { +void G1BarrierSet::write_region(MemRegion mr) { if (mr.is_empty()) { return; } diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.hpp index 1e5c111a65217..58a70ed6a6076 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.hpp @@ -99,8 +99,7 @@ class G1BarrierSet: public CardTableBarrierSet { template void write_ref_field_pre(T* field); - inline void write_region(MemRegion mr); - void write_region(JavaThread* thread, MemRegion mr); + virtual void write_region(MemRegion mr); template void write_ref_field_post(T* field); diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp index 0888fc589375a..ffba561f11f3d 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp @@ -68,10 +68,6 @@ inline void G1BarrierSet::write_ref_field_pre(T* field) { enqueue(field); } -inline void G1BarrierSet::write_region(MemRegion mr) { - write_region(JavaThread::current(), mr); -} - template inline void G1BarrierSet::write_ref_field_post(T* field) { volatile CardValue* byte = _card_table->byte_for(field); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 485caa9f6c0e2..31142a09bc330 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -118,6 +118,7 @@ #include "utilities/globalDefinitions.hpp" #include "utilities/stack.inline.hpp" +uintx G1CollectedHeap::_gc_overhead_counter = 0; size_t G1CollectedHeap::_humongous_object_threshold_in_words = 0; // INVARIANTS/NOTES @@ -403,21 +404,25 @@ HeapWord* G1CollectedHeap::allocate_new_tlab(size_t min_size, assert_heap_not_locked_and_not_at_safepoint(); assert(!is_humongous(requested_size), "we do not allow humongous TLABs"); - return attempt_allocation(min_size, requested_size, actual_size); + // Do not allow a GC because we are allocating a new TLAB to avoid an issue + // with UseGCOverheadLimit: although this GC would return null if the overhead + // limit would be exceeded, but it would likely free at least some space. + // So the subsequent outside-TLAB allocation could be successful anyway and + // the indication that the overhead limit had been exceeded swallowed. + return attempt_allocation(min_size, requested_size, actual_size, false /* allow_gc */); } -HeapWord* -G1CollectedHeap::mem_allocate(size_t word_size) { +HeapWord* G1CollectedHeap::mem_allocate(size_t word_size) { assert_heap_not_locked_and_not_at_safepoint(); if (is_humongous(word_size)) { return attempt_allocation_humongous(word_size); } size_t dummy = 0; - return attempt_allocation(word_size, word_size, &dummy); + return attempt_allocation(word_size, word_size, &dummy, true /* allow_gc */); } -HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_size) { +HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_size, bool allow_gc) { ResourceMark rm; // For retrieving the thread names in log messages. // Make sure you read the note in attempt_allocation_humongous(). @@ -444,6 +449,8 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_ result = _allocator->attempt_allocation_locked(node_index, word_size); if (result != nullptr) { return result; + } else if (!allow_gc) { + return nullptr; } // Read the GC count while still holding the Heap_lock. @@ -461,8 +468,20 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_ log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating %zu words", Thread::current()->name(), word_size); + if (is_shutting_down()) { + stall_for_vm_shutdown(); + return nullptr; + } + + // Was the gc-overhead reached inside the safepoint? If so, this mutator + // should return null even when unsuccessfully scheduling a collection as well + // for global consistency. + if (gc_overhead_limit_exceeded()) { + return nullptr; + } + // We can reach here if we were unsuccessful in scheduling a collection (because - // another thread beat us to it). In this case immeditealy retry the allocation + // another thread beat us to it). In this case immediately retry the allocation // attempt because another thread successfully performed a collection and possibly // reclaimed enough space. The first attempt (without holding the Heap_lock) is // here and the follow-on attempt will be at the start of the next loop @@ -479,11 +498,6 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_ log_warning(gc, alloc)("%s: Retried allocation %u times for %zu words", Thread::current()->name(), try_count, word_size); } - - if (is_shutting_down()) { - stall_for_vm_shutdown(); - return nullptr; - } } ShouldNotReachHere(); @@ -612,7 +626,8 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion range) { inline HeapWord* G1CollectedHeap::attempt_allocation(size_t min_word_size, size_t desired_word_size, - size_t* actual_word_size) { + size_t* actual_word_size, + bool allow_gc) { assert_heap_not_locked_and_not_at_safepoint(); assert(!is_humongous(desired_word_size), "attempt_allocation() should not " "be called for humongous allocation requests"); @@ -624,7 +639,7 @@ inline HeapWord* G1CollectedHeap::attempt_allocation(size_t min_word_size, if (result == nullptr) { *actual_word_size = desired_word_size; - result = attempt_allocation_slow(node_index, desired_word_size); + result = attempt_allocation_slow(node_index, desired_word_size, allow_gc); } assert_heap_not_locked(); @@ -707,6 +722,17 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) { log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating %zu", Thread::current()->name(), word_size); + if (is_shutting_down()) { + stall_for_vm_shutdown(); + return nullptr; + } + + // Was the gc-overhead reached inside the safepoint? If so, this mutator + // should return null as well for global consistency. + if (gc_overhead_limit_exceeded()) { + return nullptr; + } + // We can reach here if we were unsuccessful in scheduling a collection (because // another thread beat us to it). // Humongous object allocation always needs a lock, so we wait for the retry @@ -718,11 +744,6 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) { log_warning(gc, alloc)("%s: Retried allocation %u times for %zu words", Thread::current()->name(), try_count, word_size); } - - if (is_shutting_down()) { - stall_for_vm_shutdown(); - return nullptr; - } } ShouldNotReachHere(); @@ -948,25 +969,58 @@ void G1CollectedHeap::resize_heap_after_young_collection(size_t allocation_word_ phase_times()->record_resize_heap_time((Ticks::now() - start).seconds() * 1000.0); } +void G1CollectedHeap::update_gc_overhead_limit_exceeded() { + assert(SafepointSynchronize::is_at_safepoint(), "precondition"); + + if (UseGCOverheadLimit) { + bool little_mutator_time = (_policy->analytics()->long_term_gc_time_ratio() * 100) >= GCTimeLimit; + double free_space_percent = percent_of(num_available_regions() * G1HeapRegion::GrainBytes, max_capacity()); + bool little_free_space = free_space_percent < GCHeapFreeLimit; + + log_debug(gc)("GC Overhead Limit: GC Time %f Free Space %f Counter %zu", + (_policy->analytics()->long_term_gc_time_ratio() * 100), + free_space_percent, + _gc_overhead_counter); + + if (little_mutator_time && little_free_space) { + _gc_overhead_counter++; + return; + } else { + _gc_overhead_counter = 0; + } + } +} + +bool G1CollectedHeap::gc_overhead_limit_exceeded() { + return _gc_overhead_counter >= GCOverheadLimitThreshold; +} + HeapWord* G1CollectedHeap::satisfy_failed_allocation_helper(size_t word_size, bool do_gc, bool maximal_compaction, bool expect_null_mutator_alloc_region) { - // Let's attempt the allocation first. - HeapWord* result = - attempt_allocation_at_safepoint(word_size, - expect_null_mutator_alloc_region); - if (result != nullptr) { - return result; - } + // Skip allocation if GC overhead has been exceeded to let the mutator run into + // an OOME. It can either exit "gracefully" or try to free up memory asap. + // For the latter situation, keep running GCs. If the mutator frees up enough + // memory quickly enough, the overhead(s) will go below the threshold(s) again + // and the VM may continue running. + if (!gc_overhead_limit_exceeded()) { + // Let's attempt the allocation first. + HeapWord* result = + attempt_allocation_at_safepoint(word_size, + expect_null_mutator_alloc_region); + if (result != nullptr) { + return result; + } - // In a G1 heap, we're supposed to keep allocation from failing by - // incremental pauses. Therefore, at least for now, we'll favor - // expansion over collection. (This might change in the future if we can - // do something smarter than full collection to satisfy a failed alloc.) - result = expand_and_allocate(word_size); - if (result != nullptr) { - return result; + // In a G1 heap, we're supposed to keep allocation from failing by + // incremental pauses. Therefore, at least for now, we'll favor + // expansion over collection. (This might change in the future if we can + // do something smarter than full collection to satisfy a failed alloc.) + result = expand_and_allocate(word_size); + if (result != nullptr) { + return result; + } } if (do_gc) { @@ -990,6 +1044,10 @@ HeapWord* G1CollectedHeap::satisfy_failed_allocation_helper(size_t word_size, HeapWord* G1CollectedHeap::satisfy_failed_allocation(size_t word_size) { assert_at_safepoint_on_vm_thread(); + // Update GC overhead limits after the initial garbage collection leading to this + // allocation attempt. + update_gc_overhead_limit_exceeded(); + // Attempts to allocate followed by Full GC. HeapWord* result = satisfy_failed_allocation_helper(word_size, @@ -1021,6 +1079,10 @@ HeapWord* G1CollectedHeap::satisfy_failed_allocation(size_t word_size) { return result; } + if (gc_overhead_limit_exceeded()) { + log_info(gc)("GC Overhead Limit exceeded too often (%zu).", GCOverheadLimitThreshold); + } + // What else? We might try synchronous finalization later. If the total // space available is large enough for the allocation, then a more // complete compaction phase than we've tried so far might be diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 7e3f8a3028568..9b56b6e2788b4 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -169,6 +169,17 @@ class G1CollectedHeap : public CollectedHeap { friend class G1CheckRegionAttrTableClosure; private: + // GC Overhead Limit functionality related members. + // + // The goal is to return null for allocations prematurely (before really going + // OOME) in case both GC CPU usage (>= GCTimeLimit) and not much available free + // memory (<= GCHeapFreeLimit) so that applications can exit gracefully or try + // to keep running by easing off memory. + static uintx _gc_overhead_counter; // The amount of successive times we were over the limits. + + void update_gc_overhead_limit_exceeded(); + static bool gc_overhead_limit_exceeded(); + G1ServiceThread* _service_thread; G1ServiceTask* _periodic_gc_task; G1MonotonicArenaFreeMemoryTask* _free_arena_memory_task; @@ -439,18 +450,14 @@ class G1CollectedHeap : public CollectedHeap { // // * If either call cannot satisfy the allocation request using the // current allocating region, they will try to get a new one. If - // this fails, they will attempt to do an evacuation pause and - // retry the allocation. - // - // * If all allocation attempts fail, even after trying to schedule - // an evacuation pause, allocate_new_tlab() will return null, - // whereas mem_allocate() will attempt a heap expansion and/or - // schedule a Full GC. + // this fails, (only) mem_allocate() will attempt to do an evacuation + // pause and retry the allocation. Allocate_new_tlab() will return null, + // deferring to the following mem_allocate(). // // * We do not allow humongous-sized TLABs. So, allocate_new_tlab // should never be called with word_size being humongous. All // humongous allocation requests should go to mem_allocate() which - // will satisfy them with a special path. + // will satisfy them in a special path. HeapWord* allocate_new_tlab(size_t min_size, size_t requested_size, @@ -463,12 +470,13 @@ class G1CollectedHeap : public CollectedHeap { // should only be used for non-humongous allocations. inline HeapWord* attempt_allocation(size_t min_word_size, size_t desired_word_size, - size_t* actual_word_size); - + size_t* actual_word_size, + bool allow_gc); // Second-level mutator allocation attempt: take the Heap_lock and // retry the allocation attempt, potentially scheduling a GC - // pause. This should only be used for non-humongous allocations. - HeapWord* attempt_allocation_slow(uint node_index, size_t word_size); + // pause if allow_gc is set. This should only be used for non-humongous + // allocations. + HeapWord* attempt_allocation_slow(uint node_index, size_t word_size, bool allow_gc); // Takes the Heap_lock and attempts a humongous allocation. It can // potentially schedule a GC pause. diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp index df4312ebd7554..36412ce5efe95 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp @@ -37,21 +37,11 @@ #include "runtime/threadSMR.hpp" #include "utilities/align.hpp" -MutableNUMASpace::MutableNUMASpace(size_t alignment) : MutableSpace(alignment), _must_use_large_pages(false) { +MutableNUMASpace::MutableNUMASpace(size_t page_size) : MutableSpace(page_size) { _lgrp_spaces = new (mtGC) GrowableArray(0, mtGC); - _page_size = os::vm_page_size(); _adaptation_cycles = 0; _samples_count = 0; -#ifdef LINUX - // Changing the page size can lead to freeing of memory. When using large pages - // and the memory has been both reserved and committed, Linux does not support - // freeing parts of it. - if (UseLargePages && !os::can_commit_large_page_memory()) { - _must_use_large_pages = true; - } -#endif // LINUX - size_t lgrp_limit = os::numa_get_groups_num(); uint *lgrp_ids = NEW_C_HEAP_ARRAY(uint, lgrp_limit, mtGC); size_t lgrp_num = os::numa_get_leaf_groups(lgrp_ids, lgrp_limit); @@ -60,7 +50,7 @@ MutableNUMASpace::MutableNUMASpace(size_t alignment) : MutableSpace(alignment), lgrp_spaces()->reserve(checked_cast(lgrp_num)); // Add new spaces for the new nodes for (size_t i = 0; i < lgrp_num; i++) { - lgrp_spaces()->append(new LGRPSpace(lgrp_ids[i], alignment)); + lgrp_spaces()->append(new LGRPSpace(lgrp_ids[i], page_size)); } FREE_C_HEAP_ARRAY(uint, lgrp_ids); @@ -128,7 +118,10 @@ MutableNUMASpace::LGRPSpace *MutableNUMASpace::lgrp_space_for_thread(Thread* thr return space->lgrp_id() == (uint)lgrp_id; }); - assert(lgrp_spaces_index != -1, "must have created spaces for all lgrp_ids"); + if (lgrp_spaces_index == -1) { + // Running on a CPU with no memory; pick another CPU based on %. + lgrp_spaces_index = lgrp_id % lgrp_spaces()->length(); + } return lgrp_spaces()->at(lgrp_spaces_index); } @@ -146,22 +139,19 @@ size_t MutableNUMASpace::unsafe_max_tlab_alloc(Thread *thr) const { // Bias region towards the first-touching lgrp. Set the right page sizes. void MutableNUMASpace::bias_region(MemRegion mr, uint lgrp_id) { - HeapWord *start = align_up(mr.start(), page_size()); - HeapWord *end = align_down(mr.end(), page_size()); - if (end > start) { - MemRegion aligned_region(start, end); - assert((intptr_t)aligned_region.start() % page_size() == 0 && - (intptr_t)aligned_region.byte_size() % page_size() == 0, "Bad alignment"); - assert(region().contains(aligned_region), "Sanity"); - // First we tell the OS which page size we want in the given range. The underlying - // large page can be broken down if we require small pages. - const size_t os_align = UseLargePages ? page_size() : os::vm_page_size(); - os::realign_memory((char*)aligned_region.start(), aligned_region.byte_size(), os_align); - // Then we uncommit the pages in the range. - os::disclaim_memory((char*)aligned_region.start(), aligned_region.byte_size()); - // And make them local/first-touch biased. - os::numa_make_local((char*)aligned_region.start(), aligned_region.byte_size(), checked_cast(lgrp_id)); + assert(is_aligned(mr.start(), page_size()), "precondition"); + assert(is_aligned(mr.end(), page_size()), "precondition"); + + if (mr.is_empty()) { + return; } + // First we tell the OS which page size we want in the given range. The underlying + // large page can be broken down if we require small pages. + os::realign_memory((char*) mr.start(), mr.byte_size(), page_size()); + // Then we uncommit the pages in the range. + os::disclaim_memory((char*) mr.start(), mr.byte_size()); + // And make them local/first-touch biased. + os::numa_make_local((char*)mr.start(), mr.byte_size(), checked_cast(lgrp_id)); } // Update space layout. Perform adaptation. @@ -210,14 +200,15 @@ size_t MutableNUMASpace::current_chunk_size(int i) { // Return the default chunk size by equally diving the space. // page_size() aligned. size_t MutableNUMASpace::default_chunk_size() { - return base_space_size() / lgrp_spaces()->length() * page_size(); + // The number of pages may not be evenly divided. + return align_down(capacity_in_bytes() / lgrp_spaces()->length(), page_size()); } // Produce a new chunk size. page_size() aligned. // This function is expected to be called on sequence of i's from 0 to // lgrp_spaces()->length(). size_t MutableNUMASpace::adaptive_chunk_size(int i, size_t limit) { - size_t pages_available = base_space_size(); + size_t pages_available = capacity_in_bytes() / page_size(); for (int j = 0; j < i; j++) { pages_available -= align_down(current_chunk_size(j), page_size()) / page_size(); } @@ -263,20 +254,13 @@ size_t MutableNUMASpace::adaptive_chunk_size(int i, size_t limit) { // |----bottom_region--|---intersection---|------top_region------| void MutableNUMASpace::select_tails(MemRegion new_region, MemRegion intersection, MemRegion* bottom_region, MemRegion *top_region) { + assert(is_aligned(new_region.start(), page_size()), "precondition"); + assert(is_aligned(new_region.end(), page_size()), "precondition"); + assert(is_aligned(intersection.start(), page_size()), "precondition"); + assert(is_aligned(intersection.end(), page_size()), "precondition"); + // Is there bottom? if (new_region.start() < intersection.start()) { // Yes - // Try to coalesce small pages into a large one. - if (UseLargePages && page_size() >= alignment()) { - HeapWord* p = align_up(intersection.start(), alignment()); - if (new_region.contains(p) - && pointer_delta(p, new_region.start(), sizeof(char)) >= alignment()) { - if (intersection.contains(p)) { - intersection = MemRegion(p, intersection.end()); - } else { - intersection = MemRegion(p, p); - } - } - } *bottom_region = MemRegion(new_region.start(), intersection.start()); } else { *bottom_region = MemRegion(); @@ -284,18 +268,6 @@ void MutableNUMASpace::select_tails(MemRegion new_region, MemRegion intersection // Is there top? if (intersection.end() < new_region.end()) { // Yes - // Try to coalesce small pages into a large one. - if (UseLargePages && page_size() >= alignment()) { - HeapWord* p = align_down(intersection.end(), alignment()); - if (new_region.contains(p) - && pointer_delta(new_region.end(), p, sizeof(char)) >= alignment()) { - if (intersection.contains(p)) { - intersection = MemRegion(intersection.start(), p); - } else { - intersection = MemRegion(p, p); - } - } - } *top_region = MemRegion(intersection.end(), new_region.end()); } else { *top_region = MemRegion(); @@ -309,6 +281,8 @@ void MutableNUMASpace::initialize(MemRegion mr, WorkerThreads* pretouch_workers) { assert(clear_space, "Reallocation will destroy data!"); assert(lgrp_spaces()->length() > 0, "There should be at least one space"); + assert(is_aligned(mr.start(), page_size()), "precondition"); + assert(is_aligned(mr.end(), page_size()), "precondition"); MemRegion old_region = region(), new_region; set_bottom(mr.start()); @@ -316,37 +290,22 @@ void MutableNUMASpace::initialize(MemRegion mr, // Must always clear the space clear(SpaceDecorator::DontMangle); - // Compute chunk sizes - size_t prev_page_size = page_size(); - set_page_size(alignment()); - HeapWord* rounded_bottom = align_up(bottom(), page_size()); - HeapWord* rounded_end = align_down(end(), page_size()); - size_t base_space_size_pages = pointer_delta(rounded_end, rounded_bottom, sizeof(char)) / page_size(); - - // Try small pages if the chunk size is too small - if (base_space_size_pages / lgrp_spaces()->length() == 0 - && page_size() > os::vm_page_size()) { - // Changing the page size below can lead to freeing of memory. So we fail initialization. - if (_must_use_large_pages) { - vm_exit_during_initialization("Failed initializing NUMA with large pages. Too small heap size"); - } - set_page_size(os::vm_page_size()); - rounded_bottom = align_up(bottom(), page_size()); - rounded_end = align_down(end(), page_size()); - base_space_size_pages = pointer_delta(rounded_end, rounded_bottom, sizeof(char)) / page_size(); + size_t num_pages = mr.byte_size() / page_size(); + + if (num_pages < (size_t)lgrp_spaces()->length()) { + log_warning(gc)("Degraded NUMA config: #os-pages (%zu) < #CPU (%d); space-size: %zu, page-size: %zu", + num_pages, lgrp_spaces()->length(), mr.byte_size(), page_size()); + + // Keep only the first few CPUs. + lgrp_spaces()->trunc_to((int)num_pages); } - guarantee(base_space_size_pages / lgrp_spaces()->length() > 0, "Space too small"); - set_base_space_size(base_space_size_pages); // Handle space resize MemRegion top_region, bottom_region; if (!old_region.equals(region())) { - new_region = MemRegion(rounded_bottom, rounded_end); + new_region = mr; MemRegion intersection = new_region.intersection(old_region); - if (intersection.start() == nullptr || - intersection.end() == nullptr || - prev_page_size > page_size()) { // If the page size got smaller we have to change - // the page size preference for the whole space. + if (intersection.is_empty()) { intersection = MemRegion(new_region.start(), new_region.start()); } select_tails(new_region, intersection, &bottom_region, &top_region); @@ -393,19 +352,18 @@ void MutableNUMASpace::initialize(MemRegion mr, if (i == 0) { // Bottom chunk if (i != lgrp_spaces()->length() - 1) { - new_region = MemRegion(bottom(), rounded_bottom + (chunk_byte_size >> LogHeapWordSize)); + new_region = MemRegion(bottom(), chunk_byte_size >> LogHeapWordSize); } else { new_region = MemRegion(bottom(), end()); } - } else - if (i < lgrp_spaces()->length() - 1) { // Middle chunks - MutableSpace *ps = lgrp_spaces()->at(i - 1)->space(); - new_region = MemRegion(ps->end(), - ps->end() + (chunk_byte_size >> LogHeapWordSize)); - } else { // Top chunk - MutableSpace *ps = lgrp_spaces()->at(i - 1)->space(); - new_region = MemRegion(ps->end(), end()); - } + } else if (i < lgrp_spaces()->length() - 1) { // Middle chunks + MutableSpace* ps = lgrp_spaces()->at(i - 1)->space(); + new_region = MemRegion(ps->end(), + chunk_byte_size >> LogHeapWordSize); + } else { // Top chunk + MutableSpace* ps = lgrp_spaces()->at(i - 1)->space(); + new_region = MemRegion(ps->end(), end()); + } guarantee(region().contains(new_region), "Region invariant"); @@ -432,9 +390,8 @@ void MutableNUMASpace::initialize(MemRegion mr, // Clear space (set top = bottom) but never mangle. s->initialize(new_region, SpaceDecorator::Clear, SpaceDecorator::DontMangle, MutableSpace::DontSetupPages); - - set_adaptation_cycles(samples_count()); } + set_adaptation_cycles(samples_count()); } // Set the top of the whole space. diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp index dc37b10292ad6..0285037659268 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp @@ -80,8 +80,8 @@ class MutableNUMASpace : public MutableSpace { SpaceStats _space_stats; public: - LGRPSpace(uint l, size_t alignment) : _lgrp_id(l), _allocation_failed(false) { - _space = new MutableSpace(alignment); + LGRPSpace(uint l, size_t page_size) : _lgrp_id(l), _allocation_failed(false) { + _space = new MutableSpace(page_size); _alloc_rate = new AdaptiveWeightedAverage(NUMAChunkResizeWeight); } ~LGRPSpace() { @@ -117,24 +117,14 @@ class MutableNUMASpace : public MutableSpace { }; GrowableArray* _lgrp_spaces; - size_t _page_size; unsigned _adaptation_cycles, _samples_count; - bool _must_use_large_pages; - - void set_page_size(size_t psz) { _page_size = psz; } - size_t page_size() const { return _page_size; } - unsigned adaptation_cycles() { return _adaptation_cycles; } void set_adaptation_cycles(int v) { _adaptation_cycles = v; } unsigned samples_count() { return _samples_count; } void increment_samples_count() { ++_samples_count; } - size_t _base_space_size; - void set_base_space_size(size_t v) { _base_space_size = v; } - size_t base_space_size() const { return _base_space_size; } - // Bias region towards the lgrp. void bias_region(MemRegion mr, uint lgrp_id); @@ -154,7 +144,7 @@ class MutableNUMASpace : public MutableSpace { public: GrowableArray* lgrp_spaces() const { return _lgrp_spaces; } - MutableNUMASpace(size_t alignment); + MutableNUMASpace(size_t page_size); virtual ~MutableNUMASpace(); // Space initialization. virtual void initialize(MemRegion mr, diff --git a/src/hotspot/share/gc/parallel/mutableSpace.cpp b/src/hotspot/share/gc/parallel/mutableSpace.cpp index 71fddf2c4dad9..a8f47a387e35b 100644 --- a/src/hotspot/share/gc/parallel/mutableSpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableSpace.cpp @@ -34,30 +34,26 @@ #include "utilities/align.hpp" #include "utilities/macros.hpp" -MutableSpace::MutableSpace(size_t alignment) : +MutableSpace::MutableSpace(size_t page_size) : _last_setup_region(), - _alignment(alignment), + _page_size(page_size), _bottom(nullptr), _top(nullptr), - _end(nullptr) -{ - assert(MutableSpace::alignment() % os::vm_page_size() == 0, - "Space should be aligned"); -} + _end(nullptr) {} -void MutableSpace::numa_setup_pages(MemRegion mr, size_t page_size, bool clear_space) { - if (!mr.is_empty()) { - HeapWord *start = align_up(mr.start(), page_size); - HeapWord *end = align_down(mr.end(), page_size); - if (end > start) { - size_t size = pointer_delta(end, start, sizeof(char)); - if (clear_space) { - // Prefer page reallocation to migration. - os::disclaim_memory((char*)start, size); - } - os::numa_make_global((char*)start, size); - } +void MutableSpace::numa_setup_pages(MemRegion mr, bool clear_space) { + assert(is_aligned(mr.start(), page_size()), "precondition"); + assert(is_aligned(mr.end(), page_size()), "precondition"); + + if (mr.is_empty()) { + return; } + + if (clear_space) { + // Prefer page reallocation to migration. + os::disclaim_memory((char*) mr.start(), mr.byte_size()); + } + os::numa_make_global((char*) mr.start(), mr.byte_size()); } void MutableSpace::initialize(MemRegion mr, @@ -105,20 +101,17 @@ void MutableSpace::initialize(MemRegion mr, } assert(mr.contains(head) && mr.contains(tail), "Sanity"); - size_t page_size = alignment(); - if (UseNUMA) { - numa_setup_pages(head, page_size, clear_space); - numa_setup_pages(tail, page_size, clear_space); + numa_setup_pages(head, clear_space); + numa_setup_pages(tail, clear_space); } if (AlwaysPreTouch) { - size_t pretouch_page_size = UseLargePages ? page_size : os::vm_page_size(); PretouchTask::pretouch("ParallelGC PreTouch head", (char*)head.start(), (char*)head.end(), - pretouch_page_size, pretouch_workers); + page_size(), pretouch_workers); PretouchTask::pretouch("ParallelGC PreTouch tail", (char*)tail.start(), (char*)tail.end(), - pretouch_page_size, pretouch_workers); + page_size(), pretouch_workers); } // Remember where we stopped so that we can continue later. diff --git a/src/hotspot/share/gc/parallel/mutableSpace.hpp b/src/hotspot/share/gc/parallel/mutableSpace.hpp index d09a2b2df89b7..785bfe272287a 100644 --- a/src/hotspot/share/gc/parallel/mutableSpace.hpp +++ b/src/hotspot/share/gc/parallel/mutableSpace.hpp @@ -51,17 +51,20 @@ class MutableSpace: public CHeapObj { // The last region which page had been setup to be interleaved. MemRegion _last_setup_region; - size_t _alignment; + size_t _page_size; HeapWord* _bottom; HeapWord* volatile _top; HeapWord* _end; - void numa_setup_pages(MemRegion mr, size_t page_size, bool clear_space); + void numa_setup_pages(MemRegion mr, bool clear_space); void set_last_setup_region(MemRegion mr) { _last_setup_region = mr; } MemRegion last_setup_region() const { return _last_setup_region; } - public: +protected: + size_t page_size() const { return _page_size; } + +public: virtual ~MutableSpace() = default; MutableSpace(size_t page_size); @@ -77,8 +80,6 @@ class MutableSpace: public CHeapObj { HeapWord* volatile* top_addr() { return &_top; } HeapWord** end_addr() { return &_end; } - size_t alignment() { return _alignment; } - MemRegion region() const { return MemRegion(bottom(), end()); } size_t capacity_in_bytes() const { return capacity_in_words() * HeapWordSize; } diff --git a/src/hotspot/share/gc/parallel/objectStartArray.cpp b/src/hotspot/share/gc/parallel/objectStartArray.cpp index d120c71d2fa2c..255ee0c56ec42 100644 --- a/src/hotspot/share/gc/parallel/objectStartArray.cpp +++ b/src/hotspot/share/gc/parallel/objectStartArray.cpp @@ -47,7 +47,10 @@ ObjectStartArray::ObjectStartArray(MemRegion covered_region) // Do not use large-pages for the backing store. The one large page region // will be used for the heap proper. - ReservedSpace backing_store = MemoryReserver::reserve(bytes_to_reserve, mtGC); + ReservedSpace backing_store = MemoryReserver::reserve(bytes_to_reserve, + os::vm_allocation_granularity(), + os::vm_page_size(), + mtGC); if (!backing_store.is_reserved()) { vm_exit_during_initialization("Could not reserve space for ObjectStartArray"); } diff --git a/src/hotspot/share/gc/parallel/parallelArguments.cpp b/src/hotspot/share/gc/parallel/parallelArguments.cpp index 780185952b4cb..629690a6258db 100644 --- a/src/hotspot/share/gc/parallel/parallelArguments.cpp +++ b/src/hotspot/share/gc/parallel/parallelArguments.cpp @@ -66,11 +66,6 @@ void ParallelArguments::initialize() { } } - // True in product build, since tests using debug build often stress GC - if (FLAG_IS_DEFAULT(UseGCOverheadLimit)) { - FLAG_SET_DEFAULT(UseGCOverheadLimit, trueInProduct); - } - if (InitialSurvivorRatio < MinSurvivorRatio) { if (FLAG_IS_CMDLINE(InitialSurvivorRatio)) { if (FLAG_IS_CMDLINE(MinSurvivorRatio)) { @@ -103,15 +98,10 @@ void ParallelArguments::initialize() { FullGCForwarding::initialize_flags(heap_reserved_size_bytes()); } -// The alignment used for spaces in young gen and old gen -static size_t default_space_alignment() { - return 64 * K * HeapWordSize; -} - void ParallelArguments::initialize_alignments() { // Initialize card size before initializing alignments CardTable::initialize_card_size(); - SpaceAlignment = default_space_alignment(); + SpaceAlignment = ParallelScavengeHeap::default_space_alignment(); HeapAlignment = compute_heap_alignment(); } @@ -123,12 +113,23 @@ void ParallelArguments::initialize_heap_flags_and_sizes_one_pass() { void ParallelArguments::initialize_heap_flags_and_sizes() { initialize_heap_flags_and_sizes_one_pass(); + if (!UseLargePages) { + ParallelScavengeHeap::set_desired_page_size(os::vm_page_size()); + return; + } + + // If using large-page, need to update SpaceAlignment so that spaces are page-size aligned. const size_t min_pages = 4; // 1 for eden + 1 for each survivor + 1 for old const size_t page_sz = os::page_size_for_region_aligned(MinHeapSize, min_pages); + ParallelScavengeHeap::set_desired_page_size(page_sz); + + if (page_sz == os::vm_page_size()) { + log_warning(gc, heap)("MinHeapSize (%zu) must be large enough for 4 * page-size; Disabling UseLargePages for heap", MinHeapSize); + return; + } - // Can a page size be something else than a power of two? - assert(is_power_of_2((intptr_t)page_sz), "must be a power of 2"); - size_t new_alignment = align_up(page_sz, SpaceAlignment); + // Space is largepage-aligned. + size_t new_alignment = page_sz; if (new_alignment != SpaceAlignment) { SpaceAlignment = new_alignment; // Redo everything from the start diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index eb1552e3db613..f1baa4c4ff733 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -61,11 +61,18 @@ PSYoungGen* ParallelScavengeHeap::_young_gen = nullptr; PSOldGen* ParallelScavengeHeap::_old_gen = nullptr; PSAdaptiveSizePolicy* ParallelScavengeHeap::_size_policy = nullptr; GCPolicyCounters* ParallelScavengeHeap::_gc_policy_counters = nullptr; +size_t ParallelScavengeHeap::_desired_page_size = 0; jint ParallelScavengeHeap::initialize() { const size_t reserved_heap_size = ParallelArguments::heap_reserved_size_bytes(); - ReservedHeapSpace heap_rs = Universe::reserve_heap(reserved_heap_size, HeapAlignment); + assert(_desired_page_size != 0, "Should be initialized"); + ReservedHeapSpace heap_rs = Universe::reserve_heap(reserved_heap_size, HeapAlignment, _desired_page_size); + // Adjust SpaceAlignment based on actually used large page size. + if (UseLargePages) { + SpaceAlignment = MAX2(heap_rs.page_size(), default_space_alignment()); + } + assert(is_aligned(SpaceAlignment, heap_rs.page_size()), "inv"); trace_actual_reserved_page_size(reserved_heap_size, heap_rs); @@ -367,6 +374,13 @@ bool ParallelScavengeHeap::check_gc_overhead_limit() { bool little_mutator_time = _size_policy->mutator_time_percent() * 100 < (100 - GCTimeLimit); bool little_free_space = check_gc_heap_free_limit(_young_gen->free_in_bytes(), _young_gen->capacity_in_bytes()) && check_gc_heap_free_limit( _old_gen->free_in_bytes(), _old_gen->capacity_in_bytes()); + + log_debug(gc)("GC Overhead Limit: GC Time %f Free Space Young %f Old %f Counter %zu", + (100 - _size_policy->mutator_time_percent()), + percent_of(_young_gen->free_in_bytes(), _young_gen->capacity_in_bytes()), + percent_of(_old_gen->free_in_bytes(), _young_gen->capacity_in_bytes()), + _gc_overhead_counter); + if (little_mutator_time && little_free_space) { _gc_overhead_counter++; if (_gc_overhead_counter >= GCOverheadLimitThreshold) { @@ -419,7 +433,7 @@ HeapWord* ParallelScavengeHeap::satisfy_failed_allocation(size_t size, bool is_t } if (check_gc_overhead_limit()) { - log_info(gc)("GCOverheadLimitThreshold %zu reached.", GCOverheadLimitThreshold); + log_info(gc)("GC Overhead Limit exceeded too often (%zu).", GCOverheadLimitThreshold); return nullptr; } diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index bf777bda29e04..962a3c4b15b17 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp @@ -76,6 +76,9 @@ class ParallelScavengeHeap : public CollectedHeap { static PSAdaptiveSizePolicy* _size_policy; static GCPolicyCounters* _gc_policy_counters; + // At startup, calculate the desired OS page-size based on heap size and large-page flags. + static size_t _desired_page_size; + GCMemoryManager* _young_manager; GCMemoryManager* _old_manager; @@ -85,7 +88,7 @@ class ParallelScavengeHeap : public CollectedHeap { WorkerThreads _workers; - uint _gc_overhead_counter; + uintx _gc_overhead_counter; bool _is_heap_almost_full; @@ -128,6 +131,18 @@ class ParallelScavengeHeap : public CollectedHeap { _gc_overhead_counter(0), _is_heap_almost_full(false) {} + // The alignment used for spaces in young gen and old gen + constexpr static size_t default_space_alignment() { + constexpr size_t alignment = 64 * K * HeapWordSize; + static_assert(is_power_of_2(alignment), "inv"); + return alignment; + } + + static void set_desired_page_size(size_t page_size) { + assert(is_power_of_2(page_size), "precondition"); + _desired_page_size = page_size; + } + Name kind() const override { return CollectedHeap::Parallel; } diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.hpp b/src/hotspot/share/gc/parallel/psCompactionManager.hpp index b013238a9f8cd..613361c703910 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.hpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.hpp @@ -120,7 +120,6 @@ class ParCompactionManager : public CHeapObj { static RegionTaskQueueSet* region_task_queues() { return _region_task_queues; } inline PSMarkTaskQueue* marking_stack() { return &_marking_stack; } - inline void push(PartialArrayState* stat); void push_objArray(oop obj); // To collect per-region live-words in a worker local cache in order to diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp b/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp index 2c0b8480726ab..75f54caf89e28 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp @@ -60,10 +60,6 @@ inline void ParCompactionManager::push(oop obj) { marking_stack()->push(ScannerTask(obj)); } -inline void ParCompactionManager::push(PartialArrayState* stat) { - marking_stack()->push(ScannerTask(stat)); -} - void ParCompactionManager::push_region(size_t index) { #ifdef ASSERT diff --git a/src/hotspot/share/gc/parallel/psOldGen.cpp b/src/hotspot/share/gc/parallel/psOldGen.cpp index 89f22b72b6995..2d4b0698ad0c8 100644 --- a/src/hotspot/share/gc/parallel/psOldGen.cpp +++ b/src/hotspot/share/gc/parallel/psOldGen.cpp @@ -96,7 +96,7 @@ void PSOldGen::initialize_work() { // ObjectSpace stuff // - _object_space = new MutableSpace(virtual_space()->alignment()); + _object_space = new MutableSpace(virtual_space()->page_size()); object_space()->initialize(committed_mr, SpaceDecorator::Clear, SpaceDecorator::Mangle, diff --git a/src/hotspot/share/gc/parallel/psVirtualspace.cpp b/src/hotspot/share/gc/parallel/psVirtualspace.cpp index 3be90b370d186..f4b24fa51af77 100644 --- a/src/hotspot/share/gc/parallel/psVirtualspace.cpp +++ b/src/hotspot/share/gc/parallel/psVirtualspace.cpp @@ -29,8 +29,8 @@ #include "utilities/align.hpp" PSVirtualSpace::PSVirtualSpace(ReservedSpace rs, size_t alignment) : - _alignment(alignment) -{ + _alignment(alignment), + _page_size(rs.page_size()) { set_reserved(rs); set_committed(reserved_low_addr(), reserved_low_addr()); DEBUG_ONLY(verify()); @@ -88,7 +88,8 @@ bool PSVirtualSpace::shrink_by(size_t bytes) { #ifndef PRODUCT void PSVirtualSpace::verify() const { - assert(is_aligned(_alignment, os::vm_page_size()), "bad alignment"); + assert(is_aligned(_page_size, os::vm_page_size()), "bad alignment"); + assert(is_aligned(_alignment, _page_size), "inv"); assert(is_aligned(reserved_low_addr(), _alignment), "bad reserved_low_addr"); assert(is_aligned(reserved_high_addr(), _alignment), "bad reserved_high_addr"); assert(is_aligned(committed_low_addr(), _alignment), "bad committed_low_addr"); diff --git a/src/hotspot/share/gc/parallel/psVirtualspace.hpp b/src/hotspot/share/gc/parallel/psVirtualspace.hpp index a54a513a11753..ca94f4d83b65f 100644 --- a/src/hotspot/share/gc/parallel/psVirtualspace.hpp +++ b/src/hotspot/share/gc/parallel/psVirtualspace.hpp @@ -41,6 +41,9 @@ class PSVirtualSpace : public CHeapObj { // ReservedSpace passed to initialize() must be aligned to this value. const size_t _alignment; + // OS page size used. If using Transparent Huge Pages, it's the desired large page-size. + const size_t _page_size; + // Reserved area char* _reserved_low_addr; char* _reserved_high_addr; @@ -68,6 +71,7 @@ class PSVirtualSpace : public CHeapObj { // Accessors (all sizes are bytes). size_t alignment() const { return _alignment; } + size_t page_size() const { return _page_size; } char* reserved_low_addr() const { return _reserved_low_addr; } char* reserved_high_addr() const { return _reserved_high_addr; } char* committed_low_addr() const { return _committed_low_addr; } diff --git a/src/hotspot/share/gc/parallel/psYoungGen.cpp b/src/hotspot/share/gc/parallel/psYoungGen.cpp index c26fdf4740caa..b2cce11398d91 100644 --- a/src/hotspot/share/gc/parallel/psYoungGen.cpp +++ b/src/hotspot/share/gc/parallel/psYoungGen.cpp @@ -83,12 +83,12 @@ void PSYoungGen::initialize_work() { } if (UseNUMA) { - _eden_space = new MutableNUMASpace(virtual_space()->alignment()); + _eden_space = new MutableNUMASpace(virtual_space()->page_size()); } else { - _eden_space = new MutableSpace(virtual_space()->alignment()); + _eden_space = new MutableSpace(virtual_space()->page_size()); } - _from_space = new MutableSpace(virtual_space()->alignment()); - _to_space = new MutableSpace(virtual_space()->alignment()); + _from_space = new MutableSpace(virtual_space()->page_size()); + _to_space = new MutableSpace(virtual_space()->page_size()); // Generation Counters - generation 0, 3 subspaces _gen_counters = new GenerationCounters("new", 0, 3, min_gen_size(), diff --git a/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp b/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp index fa019aa5b4295..f5e7375fca1dd 100644 --- a/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp +++ b/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp @@ -40,6 +40,7 @@ /* Parallel GC fields */ \ /**********************/ \ nonstatic_field(PSVirtualSpace, _alignment, const size_t) \ + nonstatic_field(PSVirtualSpace, _page_size, const size_t) \ nonstatic_field(PSVirtualSpace, _reserved_low_addr, char*) \ nonstatic_field(PSVirtualSpace, _reserved_high_addr, char*) \ nonstatic_field(PSVirtualSpace, _committed_low_addr, char*) \ diff --git a/src/hotspot/share/gc/serial/defNewGeneration.cpp b/src/hotspot/share/gc/serial/defNewGeneration.cpp index aef896182c01a..413d80bebf4ec 100644 --- a/src/hotspot/share/gc/serial/defNewGeneration.cpp +++ b/src/hotspot/share/gc/serial/defNewGeneration.cpp @@ -297,9 +297,9 @@ void DefNewGeneration::init_spaces() { MemRegion edenMR((HeapWord*)eden_start, (HeapWord*)eden_end); // Reset the spaces for their new regions. - from()->initialize(fromMR, from()->is_empty(), SpaceDecorator::Mangle); - to()->initialize(toMR, true, SpaceDecorator::Mangle); - eden()->initialize(edenMR, true, SpaceDecorator::Mangle); + from()->initialize(fromMR, from()->is_empty()); + to()->initialize(toMR, true); + eden()->initialize(edenMR, true); post_resize(); } @@ -340,7 +340,7 @@ void DefNewGeneration::expand_eden_by(size_t delta_bytes) { } MemRegion eden_mr{eden()->bottom(), (HeapWord*)_virtual_space.high()}; - eden()->initialize(eden_mr, eden()->is_empty(), SpaceDecorator::Mangle); + eden()->initialize(eden_mr, eden()->is_empty()); post_resize(); } diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.cpp b/src/hotspot/share/gc/serial/tenuredGeneration.cpp index a28a8c8e1cb92..f68847ed1a6b9 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.cpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.cpp @@ -314,7 +314,7 @@ TenuredGeneration::TenuredGeneration(ReservedSpace rs, HeapWord* bottom = (HeapWord*) _virtual_space.low(); HeapWord* end = (HeapWord*) _virtual_space.high(); _the_space = new ContiguousSpace(); - _the_space->initialize(MemRegion(bottom, end), SpaceDecorator::Clear, SpaceDecorator::Mangle); + _the_space->initialize(MemRegion(bottom, end), SpaceDecorator::Clear); // If we don't shrink the heap in steps, '_shrink_factor' is always 100%. _shrink_factor = ShrinkHeapInSteps ? 0 : 100; _capacity_at_prologue = 0; diff --git a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp index e97da234d1637..a5646c303f38a 100644 --- a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp +++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp @@ -61,10 +61,6 @@ class CardTableBarrierSet: public ModRefBarrierSet { CardTable* card_table() const { return _card_table; } - void write_region(JavaThread* thread, MemRegion mr) { - write_region(mr); - } - // Record a reference update. Note that these versions are precise! // The scanning code has to handle the fact that the write barrier may be // either precise or imprecise. We make non-virtual inline variants of diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index 956bffde15625..6f754dbc39d8b 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.hpp @@ -357,7 +357,7 @@ "Initial ratio of young generation/survivor space size") \ range(3, max_uintx) \ \ - product(bool, UseGCOverheadLimit, true, \ + product(bool, UseGCOverheadLimit, falseInDebug, \ "Use policy to limit of proportion of time spent in GC " \ "before an OutOfMemory error is thrown") \ \ diff --git a/src/hotspot/share/gc/shared/modRefBarrierSet.hpp b/src/hotspot/share/gc/shared/modRefBarrierSet.hpp index 15ac797111835..c078d151233d1 100644 --- a/src/hotspot/share/gc/shared/modRefBarrierSet.hpp +++ b/src/hotspot/share/gc/shared/modRefBarrierSet.hpp @@ -53,8 +53,6 @@ class ModRefBarrierSet: public BarrierSet { // Causes all refs in "mr" to be assumed to be modified (by this JavaThread). virtual void write_region(MemRegion mr) = 0; - // Causes all refs in "mr" to be assumed to be modified by the given JavaThread. - virtual void write_region(JavaThread* thread, MemRegion mr) = 0; // Operations on arrays, or general regions (e.g., for "clone") may be // optimized by some barriers. diff --git a/src/hotspot/share/gc/shared/space.cpp b/src/hotspot/share/gc/shared/space.cpp index 1d15fbc3fa9e5..011a0f5cfd83c 100644 --- a/src/hotspot/share/gc/shared/space.cpp +++ b/src/hotspot/share/gc/shared/space.cpp @@ -44,8 +44,7 @@ ContiguousSpace::ContiguousSpace(): _top(nullptr) {} void ContiguousSpace::initialize(MemRegion mr, - bool clear_space, - bool mangle_space) { + bool clear_space) { HeapWord* bottom = mr.start(); HeapWord* end = mr.end(); assert(Universe::on_page_boundary(bottom) && Universe::on_page_boundary(end), @@ -55,7 +54,7 @@ void ContiguousSpace::initialize(MemRegion mr, if (clear_space) { clear(SpaceDecorator::DontMangle); } - if (ZapUnusedHeapArea && mangle_space) { + if (ZapUnusedHeapArea) { mangle_unused_area(); } } diff --git a/src/hotspot/share/gc/shared/space.hpp b/src/hotspot/share/gc/shared/space.hpp index 75dd3f998d63e..7f2887275b3c8 100644 --- a/src/hotspot/share/gc/shared/space.hpp +++ b/src/hotspot/share/gc/shared/space.hpp @@ -101,7 +101,7 @@ class ContiguousSpace: public CHeapObj { // any purpose. The "mr" arguments gives the bounds of the space, and // the "clear_space" argument should be true unless the memory in "mr" is // known to be zeroed. - void initialize(MemRegion mr, bool clear_space, bool mangle_space); + void initialize(MemRegion mr, bool clear_space); // The "clear" method must be called on a region that may have // had allocation performed in it, but is now to be considered empty. diff --git a/src/hotspot/share/interpreter/abstractInterpreter.cpp b/src/hotspot/share/interpreter/abstractInterpreter.cpp index 640e3ab3fff93..b6a2255b4686c 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.cpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp @@ -258,7 +258,8 @@ bool AbstractInterpreter::is_not_reached(const methodHandle& method, int bci) { case Bytecodes::_invokedynamic: { assert(invoke_bc.has_index_u4(code), "sanity"); int method_index = invoke_bc.get_index_u4(code); - return cpool->resolved_indy_entry_at(method_index)->is_resolved(); + bool is_resolved = cpool->resolved_indy_entry_at(method_index)->is_resolved(); + return !is_resolved; } case Bytecodes::_invokevirtual: // fall-through case Bytecodes::_invokeinterface: // fall-through diff --git a/src/hotspot/share/interpreter/bytecodeStream.hpp b/src/hotspot/share/interpreter/bytecodeStream.hpp index 89d97053b45e2..412951691c536 100644 --- a/src/hotspot/share/interpreter/bytecodeStream.hpp +++ b/src/hotspot/share/interpreter/bytecodeStream.hpp @@ -100,8 +100,23 @@ class BaseBytecodeStream: StackObj { void set_next_bci(int bci) { assert(0 <= bci && bci <= method()->code_size(), "illegal bci"); _next_bci = bci; } // Bytecode-specific attributes - int dest() const { return bci() + bytecode().get_offset_s2(raw_code()); } - int dest_w() const { return bci() + bytecode().get_offset_s4(raw_code()); } + int get_offset_s2() const { return bytecode().get_offset_s2(raw_code()); } + int get_offset_s4() const { return bytecode().get_offset_s4(raw_code()); } + + // These methods are not safe to use before or during verification as they may + // have large offsets and cause overflows + int dest() const { + int min_offset = -1 * max_method_code_size; + int offset = bytecode().get_offset_s2(raw_code()); + guarantee(offset >= min_offset && offset <= max_method_code_size, "must be"); + return bci() + offset; + } + int dest_w() const { + int min_offset = -1 * max_method_code_size; + int offset = bytecode().get_offset_s4(raw_code()); + guarantee(offset >= min_offset && offset <= max_method_code_size, "must be"); + return bci() + offset; + } // One-byte indices. u1 get_index_u1() const { assert_raw_index_size(1); return *(jubyte*)(bcp()+1); } diff --git a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp index 5163bc7f6a5cb..309ae961808d5 100644 --- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp +++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp @@ -458,6 +458,7 @@ const char* JfrEmergencyDump::chunk_path(const char* repository_path) { */ static void release_locks(Thread* thread) { assert(thread != nullptr, "invariant"); + assert(!thread->is_Java_thread() || JavaThread::cast(thread)->thread_state() == _thread_in_vm, "invariant"); #ifdef ASSERT Mutex* owned_lock = thread->owned_locks(); @@ -519,13 +520,14 @@ static void release_locks(Thread* thread) { class JavaThreadInVMAndNative : public StackObj { private: - JavaThread* const _jt; + JavaThread* _jt; JavaThreadState _original_state; public: - JavaThreadInVMAndNative(Thread* t) : _jt(t->is_Java_thread() ? JavaThread::cast(t) : nullptr), + JavaThreadInVMAndNative(Thread* t) : _jt(nullptr), _original_state(_thread_max_state) { - if (_jt != nullptr) { + if (t != nullptr && t->is_Java_thread()) { + _jt = JavaThread::cast(t); _original_state = _jt->thread_state(); if (_original_state != _thread_in_vm) { _jt->set_thread_state(_thread_in_vm); @@ -535,6 +537,7 @@ class JavaThreadInVMAndNative : public StackObj { ~JavaThreadInVMAndNative() { if (_original_state != _thread_max_state) { + assert(_jt != nullptr, "invariant"); _jt->set_thread_state(_original_state); } } @@ -574,11 +577,13 @@ static bool guard_reentrancy() { Thread* const thread = Thread::current_or_null_safe(); const traceid tid = thread != nullptr ? JFR_JVM_THREAD_ID(thread) : max_julong; if (AtomicAccess::cmpxchg(&_jfr_shutdown_tid, shutdown_tid, tid) != shutdown_tid) { + JavaThreadInVMAndNative jtivm(thread); if (thread != nullptr) { - JavaThreadInVMAndNative jtivm(thread); release_locks(thread); } log_info(jfr, system)("A jfr emergency dump is already in progress, waiting for thread id " UINT64_FORMAT_X, AtomicAccess::load(&_jfr_shutdown_tid)); + // Transition to a safe safepoint state for the infinite sleep. A nop for non-java threads. + jtivm.transition_to_native(); os::infinite_sleep(); // stay here until we exit normally or crash. ShouldNotReachHere(); } diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index 424e43c5e83ce..756619bff3386 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -955,7 +955,7 @@ void Universe::initialize_tlab() { } } -ReservedHeapSpace Universe::reserve_heap(size_t heap_size, size_t alignment) { +ReservedHeapSpace Universe::reserve_heap(size_t heap_size, size_t alignment, size_t desired_page_size) { assert(alignment <= Arguments::conservative_max_heap_alignment(), "actual alignment %zu must be within maximum heap alignment %zu", @@ -966,15 +966,21 @@ ReservedHeapSpace Universe::reserve_heap(size_t heap_size, size_t alignment) { assert(!UseCompressedOops || (total_reserved <= (OopEncodingHeapMax - os::vm_page_size())), "heap size is too big for compressed oops"); - size_t page_size = os::vm_page_size(); - if (UseLargePages && is_aligned(alignment, os::large_page_size())) { - page_size = os::large_page_size(); + size_t page_size; + if (desired_page_size == 0) { + if (UseLargePages) { + page_size = os::large_page_size(); + } else { + page_size = os::vm_page_size(); + } } else { // Parallel is the only collector that might opt out of using large pages // for the heap. - assert(!UseLargePages || UseParallelGC , "Wrong alignment to use large pages"); + assert(UseParallelGC , "only Parallel"); + // Use caller provided value. + page_size = desired_page_size; } - + assert(is_aligned(heap_size, page_size), "inv"); // Now create the space. ReservedHeapSpace rhs = HeapReserver::reserve(total_reserved, alignment, page_size, AllocateHeapAt); diff --git a/src/hotspot/share/memory/universe.hpp b/src/hotspot/share/memory/universe.hpp index 3b1f2523ed845..37ca965062e80 100644 --- a/src/hotspot/share/memory/universe.hpp +++ b/src/hotspot/share/memory/universe.hpp @@ -315,7 +315,7 @@ class Universe: AllStatic { DEBUG_ONLY(static bool is_in_heap_or_null(const void* p) { return p == nullptr || is_in_heap(p); }) // Reserve Java heap and determine CompressedOops mode - static ReservedHeapSpace reserve_heap(size_t heap_size, size_t alignment); + static ReservedHeapSpace reserve_heap(size_t heap_size, size_t alignment, size_t desired_page_size = 0); // Global OopStorages static OopStorage* vm_weak(); diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 5d5c05482156b..b072c7f26ec53 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -538,18 +538,23 @@ void ConstantPool::remove_resolved_klass_if_non_deterministic(int cp_index) { assert(ArchiveBuilder::current()->is_in_buffer_space(this), "must be"); assert(tag_at(cp_index).is_klass(), "must be resolved"); - Klass* k = resolved_klass_at(cp_index); bool can_archive; + Klass* k = nullptr; - if (k == nullptr) { - // We'd come here if the referenced class has been excluded via - // SystemDictionaryShared::is_excluded_class(). As a result, ArchiveBuilder - // has cleared the resolved_klasses()->at(...) pointer to null. Thus, we - // need to revert the tag to JVM_CONSTANT_UnresolvedClass. + if (CDSConfig::is_dumping_preimage_static_archive()) { can_archive = false; } else { - ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(this); - can_archive = AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index); + k = resolved_klass_at(cp_index); + if (k == nullptr) { + // We'd come here if the referenced class has been excluded via + // SystemDictionaryShared::is_excluded_class(). As a result, ArchiveBuilder + // has cleared the resolved_klasses()->at(...) pointer to null. Thus, we + // need to revert the tag to JVM_CONSTANT_UnresolvedClass. + can_archive = false; + } else { + ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(this); + can_archive = AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index); + } } if (!can_archive) { diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index 941ceac8de12b..f60229dbfffc2 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -430,26 +430,25 @@ void ConstantPoolCache::remove_resolved_field_entries_if_non_deterministic() { bool archived = false; bool resolved = rfi->is_resolved(Bytecodes::_getfield) || rfi->is_resolved(Bytecodes::_putfield); - if (resolved && AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) { + if (resolved && !CDSConfig::is_dumping_preimage_static_archive() + && AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) { rfi->mark_and_relocate(); archived = true; } else { rfi->remove_unshareable_info(); } - if (resolved) { - LogStreamHandle(Trace, aot, resolve) log; - if (log.is_enabled()) { - ResourceMark rm; - int klass_cp_index = cp->uncached_klass_ref_index_at(cp_index); - Symbol* klass_name = cp->klass_name_at(klass_cp_index); - Symbol* name = cp->uncached_name_ref_at(cp_index); - Symbol* signature = cp->uncached_signature_ref_at(cp_index); - log.print("%s field CP entry [%3d]: %s => %s.%s:%s", - (archived ? "archived" : "reverted"), - cp_index, - cp->pool_holder()->name()->as_C_string(), - klass_name->as_C_string(), name->as_C_string(), signature->as_C_string()); - } + LogStreamHandle(Trace, aot, resolve) log; + if (log.is_enabled()) { + ResourceMark rm; + int klass_cp_index = cp->uncached_klass_ref_index_at(cp_index); + Symbol* klass_name = cp->klass_name_at(klass_cp_index); + Symbol* name = cp->uncached_name_ref_at(cp_index); + Symbol* signature = cp->uncached_signature_ref_at(cp_index); + log.print("%s field CP entry [%3d]: %s => %s.%s:%s", + (archived ? "archived" : "reverted"), + cp_index, + cp->pool_holder()->name()->as_C_string(), + klass_name->as_C_string(), name->as_C_string(), signature->as_C_string()); } ArchiveBuilder::alloc_stats()->record_field_cp_entry(archived, resolved && !archived); } @@ -470,32 +469,31 @@ void ConstantPoolCache::remove_resolved_method_entries_if_non_deterministic() { // Just for safety -- this should not happen, but do not archive if we ever see this. resolved &= !(rme->is_resolved(Bytecodes::_invokestatic)); - if (resolved && can_archive_resolved_method(src_cp, rme)) { + if (resolved && !CDSConfig::is_dumping_preimage_static_archive() + && can_archive_resolved_method(src_cp, rme)) { rme->mark_and_relocate(src_cp); archived = true; } else { rme->remove_unshareable_info(); } - if (resolved) { - LogStreamHandle(Trace, aot, resolve) log; - if (log.is_enabled()) { - ResourceMark rm; - int klass_cp_index = cp->uncached_klass_ref_index_at(cp_index); - Symbol* klass_name = cp->klass_name_at(klass_cp_index); - Symbol* name = cp->uncached_name_ref_at(cp_index); - Symbol* signature = cp->uncached_signature_ref_at(cp_index); - log.print("%s%s method CP entry [%3d]: %s %s.%s:%s", - (archived ? "archived" : "reverted"), - (rme->is_resolved(Bytecodes::_invokeinterface) ? " interface" : ""), - cp_index, - cp->pool_holder()->name()->as_C_string(), - klass_name->as_C_string(), name->as_C_string(), signature->as_C_string()); - if (archived) { - Klass* resolved_klass = cp->resolved_klass_at(klass_cp_index); - log.print(" => %s%s", - resolved_klass->name()->as_C_string(), - (rme->is_resolved(Bytecodes::_invokestatic) ? " *** static" : "")); - } + LogStreamHandle(Trace, aot, resolve) log; + if (log.is_enabled()) { + ResourceMark rm; + int klass_cp_index = cp->uncached_klass_ref_index_at(cp_index); + Symbol* klass_name = cp->klass_name_at(klass_cp_index); + Symbol* name = cp->uncached_name_ref_at(cp_index); + Symbol* signature = cp->uncached_signature_ref_at(cp_index); + log.print("%s%s method CP entry [%3d]: %s %s.%s:%s", + (archived ? "archived" : "reverted"), + (rme->is_resolved(Bytecodes::_invokeinterface) ? " interface" : ""), + cp_index, + cp->pool_holder()->name()->as_C_string(), + klass_name->as_C_string(), name->as_C_string(), signature->as_C_string()); + if (archived) { + Klass* resolved_klass = cp->resolved_klass_at(klass_cp_index); + log.print(" => %s%s", + resolved_klass->name()->as_C_string(), + (rme->is_resolved(Bytecodes::_invokestatic) ? " *** static" : "")); } ArchiveBuilder::alloc_stats()->record_method_cp_entry(archived, resolved && !archived); } @@ -510,29 +508,28 @@ void ConstantPoolCache::remove_resolved_indy_entries_if_non_deterministic() { int cp_index = rei->constant_pool_index(); bool archived = false; bool resolved = rei->is_resolved(); - if (resolved && AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) { + if (resolved && !CDSConfig::is_dumping_preimage_static_archive() + && AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) { rei->mark_and_relocate(); archived = true; } else { rei->remove_unshareable_info(); } - if (resolved) { - LogStreamHandle(Trace, aot, resolve) log; - if (log.is_enabled()) { - ResourceMark rm; - int bsm = cp->bootstrap_method_ref_index_at(cp_index); - int bsm_ref = cp->method_handle_index_at(bsm); - Symbol* bsm_name = cp->uncached_name_ref_at(bsm_ref); - Symbol* bsm_signature = cp->uncached_signature_ref_at(bsm_ref); - Symbol* bsm_klass = cp->klass_name_at(cp->uncached_klass_ref_index_at(bsm_ref)); - log.print("%s indy CP entry [%3d]: %s (%d)", - (archived ? "archived" : "reverted"), - cp_index, cp->pool_holder()->name()->as_C_string(), i); - log.print(" %s %s.%s:%s", (archived ? "=>" : " "), bsm_klass->as_C_string(), - bsm_name->as_C_string(), bsm_signature->as_C_string()); - } - ArchiveBuilder::alloc_stats()->record_indy_cp_entry(archived, resolved && !archived); + LogStreamHandle(Trace, aot, resolve) log; + if (log.is_enabled()) { + ResourceMark rm; + int bsm = cp->bootstrap_method_ref_index_at(cp_index); + int bsm_ref = cp->method_handle_index_at(bsm); + Symbol* bsm_name = cp->uncached_name_ref_at(bsm_ref); + Symbol* bsm_signature = cp->uncached_signature_ref_at(bsm_ref); + Symbol* bsm_klass = cp->klass_name_at(cp->uncached_klass_ref_index_at(bsm_ref)); + log.print("%s indy CP entry [%3d]: %s (%d)", + (archived ? "archived" : "reverted"), + cp_index, cp->pool_holder()->name()->as_C_string(), i); + log.print(" %s %s.%s:%s", (archived ? "=>" : " "), bsm_klass->as_C_string(), + bsm_name->as_C_string(), bsm_signature->as_C_string()); } + ArchiveBuilder::alloc_stats()->record_indy_cp_entry(archived, resolved && !archived); } } diff --git a/src/hotspot/share/opto/chaitin.hpp b/src/hotspot/share/opto/chaitin.hpp index 22cc4259c6fd8..b477c54fcae49 100644 --- a/src/hotspot/share/opto/chaitin.hpp +++ b/src/hotspot/share/opto/chaitin.hpp @@ -128,7 +128,7 @@ class LRG : public ResourceObj { // count of bits in the current mask. int get_invalid_mask_size() const { return _mask_size; } const RegMask &mask() const { return _mask; } - void set_mask( const RegMask &rm ) { _mask = rm; DEBUG_ONLY(_msize_valid=0;)} + void set_mask(const RegMask& rm) { _mask.assignFrom(rm); DEBUG_ONLY(_msize_valid = 0;) } void init_mask(Arena* arena) { new (&_mask) RegMask(arena); } void and_with( const RegMask &rm ) { _mask.and_with(rm); DEBUG_ONLY(_msize_valid=0;)} void subtract( const RegMask &rm ) { _mask.subtract(rm); DEBUG_ONLY(_msize_valid=0;)} diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index 823745ea8e7fd..06ba1856941a3 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -1668,10 +1668,10 @@ Node *DivModINode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; if (proj->_con == div_proj_num) { - rm = match->divI_proj_mask(); + rm.assignFrom(match->divI_proj_mask()); } else { assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm = match->modI_proj_mask(); + rm.assignFrom(match->modI_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } @@ -1683,10 +1683,10 @@ Node *DivModLNode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; if (proj->_con == div_proj_num) { - rm = match->divL_proj_mask(); + rm.assignFrom(match->divL_proj_mask()); } else { assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm = match->modL_proj_mask(); + rm.assignFrom(match->modL_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } @@ -1721,10 +1721,10 @@ Node* UDivModINode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; if (proj->_con == div_proj_num) { - rm = match->divI_proj_mask(); + rm.assignFrom(match->divI_proj_mask()); } else { assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm = match->modI_proj_mask(); + rm.assignFrom(match->modI_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } @@ -1736,10 +1736,10 @@ Node* UDivModLNode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; if (proj->_con == div_proj_num) { - rm = match->divL_proj_mask(); + rm.assignFrom(match->divL_proj_mask()); } else { assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm = match->modL_proj_mask(); + rm.assignFrom(match->modL_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index cbf0666c00e4e..a148b167ee301 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -1296,9 +1296,8 @@ void ConnectionGraph::reduce_phi(PhiNode* ophi, GrowableArray &alloc_wo castpps.push(use); } else if (use->is_AddP() || use->is_Cmp()) { others.push(use); - } else if (use->is_SafePoint()) { - // processed later } else { + // Safepoints to be processed later; other users aren't expected here assert(use->is_SafePoint(), "Unexpected user of reducible Phi %d -> %d:%s:%d", ophi->_idx, use->_idx, use->Name(), use->outcnt()); } } diff --git a/src/hotspot/share/opto/ifg.cpp b/src/hotspot/share/opto/ifg.cpp index 681d2f28cb16a..1480e806f76a7 100644 --- a/src/hotspot/share/opto/ifg.cpp +++ b/src/hotspot/share/opto/ifg.cpp @@ -729,7 +729,7 @@ void PhaseChaitin::remove_bound_register_from_interfering_live_ranges(LRG& lrg, } // Remove bound register(s) from 'l's choices - old = interfering_lrg.mask(); + old.assignFrom(interfering_lrg.mask()); uint old_size = interfering_lrg.mask_size(); // Remove the bits from LRG 'mask' from LRG 'l' so 'l' no @@ -738,7 +738,7 @@ void PhaseChaitin::remove_bound_register_from_interfering_live_ranges(LRG& lrg, assert(!interfering_lrg._is_vector || !interfering_lrg._fat_proj, "sanity"); if (interfering_lrg.num_regs() > 1 && !interfering_lrg._fat_proj) { - r2mask = mask; + r2mask.assignFrom(mask); // Leave only aligned set of bits. r2mask.smear_to_sets(interfering_lrg.num_regs()); // It includes vector case. diff --git a/src/hotspot/share/opto/loopUnswitch.cpp b/src/hotspot/share/opto/loopUnswitch.cpp index b40a0492df511..f79afee31039c 100644 --- a/src/hotspot/share/opto/loopUnswitch.cpp +++ b/src/hotspot/share/opto/loopUnswitch.cpp @@ -522,7 +522,9 @@ IfTrueNode* PhaseIdealLoop::create_new_if_for_multiversion(IfTrueNode* multivers // Hook region into slow_path, in stead of the multiversion_slow_proj. // This also moves all other dependencies of the multiversion_slow_proj to the region. - _igvn.replace_node(multiversion_slow_proj, region); + // The lazy_replace ensures that any get_ctrl that used to have multiversion_slow_proj + // as their control are forwarded to the new region node as their control. + lazy_replace(multiversion_slow_proj, region); return new_if_true; } diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index 7621fc1bb3e83..c63cefe7ac201 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -195,7 +195,7 @@ void Matcher::match( ) { OptoRegPair regs = return_value(ireg); // And mask for same - _return_value_mask = RegMask(regs.first()); + _return_value_mask.assignFrom(RegMask(regs.first())); if( OptoReg::is_valid(regs.second()) ) _return_value_mask.insert(regs.second()); } @@ -422,11 +422,11 @@ static RegMask *init_input_masks( uint size, RegMask &ret_adr, RegMask &fp ) { new (rms + i) RegMask(Compile::current()->comp_arena()); } // Do all the pre-defined register masks - rms[TypeFunc::Control ] = RegMask::EMPTY; - rms[TypeFunc::I_O ] = RegMask::EMPTY; - rms[TypeFunc::Memory ] = RegMask::EMPTY; - rms[TypeFunc::ReturnAdr] = ret_adr; - rms[TypeFunc::FramePtr ] = fp; + rms[TypeFunc::Control ].assignFrom(RegMask::EMPTY); + rms[TypeFunc::I_O ].assignFrom(RegMask::EMPTY); + rms[TypeFunc::Memory ].assignFrom(RegMask::EMPTY); + rms[TypeFunc::ReturnAdr].assignFrom(ret_adr); + rms[TypeFunc::FramePtr ].assignFrom(fp); return rms; } @@ -488,44 +488,44 @@ void Matcher::init_first_stack_mask() { assert(aligned_stack_mask.is_infinite_stack(), "should be infinite stack"); RegMask scalable_stack_mask(aligned_stack_mask, C->comp_arena()); - *idealreg2spillmask[Op_RegP] = *idealreg2regmask[Op_RegP]; + idealreg2spillmask[Op_RegP]->assignFrom(*idealreg2regmask[Op_RegP]); #ifdef _LP64 - *idealreg2spillmask[Op_RegN] = *idealreg2regmask[Op_RegN]; - idealreg2spillmask[Op_RegN]->or_with(C->FIRST_STACK_mask()); - idealreg2spillmask[Op_RegP]->or_with(aligned_stack_mask); + idealreg2spillmask[Op_RegN]->assignFrom(*idealreg2regmask[Op_RegN]); + idealreg2spillmask[Op_RegN]->or_with(C->FIRST_STACK_mask()); + idealreg2spillmask[Op_RegP]->or_with(aligned_stack_mask); #else idealreg2spillmask[Op_RegP]->or_with(C->FIRST_STACK_mask()); #endif - *idealreg2spillmask[Op_RegI] = *idealreg2regmask[Op_RegI]; - idealreg2spillmask[Op_RegI]->or_with(C->FIRST_STACK_mask()); - *idealreg2spillmask[Op_RegL] = *idealreg2regmask[Op_RegL]; - idealreg2spillmask[Op_RegL]->or_with(aligned_stack_mask); - *idealreg2spillmask[Op_RegF] = *idealreg2regmask[Op_RegF]; - idealreg2spillmask[Op_RegF]->or_with(C->FIRST_STACK_mask()); - *idealreg2spillmask[Op_RegD] = *idealreg2regmask[Op_RegD]; - idealreg2spillmask[Op_RegD]->or_with(aligned_stack_mask); + idealreg2spillmask[Op_RegI]->assignFrom(*idealreg2regmask[Op_RegI]); + idealreg2spillmask[Op_RegI]->or_with(C->FIRST_STACK_mask()); + idealreg2spillmask[Op_RegL]->assignFrom(*idealreg2regmask[Op_RegL]); + idealreg2spillmask[Op_RegL]->or_with(aligned_stack_mask); + idealreg2spillmask[Op_RegF]->assignFrom(*idealreg2regmask[Op_RegF]); + idealreg2spillmask[Op_RegF]->or_with(C->FIRST_STACK_mask()); + idealreg2spillmask[Op_RegD]->assignFrom(*idealreg2regmask[Op_RegD]); + idealreg2spillmask[Op_RegD]->or_with(aligned_stack_mask); if (Matcher::has_predicated_vectors()) { - *idealreg2spillmask[Op_RegVectMask] = *idealreg2regmask[Op_RegVectMask]; - idealreg2spillmask[Op_RegVectMask]->or_with(aligned_stack_mask); + idealreg2spillmask[Op_RegVectMask]->assignFrom(*idealreg2regmask[Op_RegVectMask]); + idealreg2spillmask[Op_RegVectMask]->or_with(aligned_stack_mask); } else { - *idealreg2spillmask[Op_RegVectMask] = RegMask::EMPTY; + idealreg2spillmask[Op_RegVectMask]->assignFrom(RegMask::EMPTY); } if (Matcher::vector_size_supported(T_BYTE,4)) { - *idealreg2spillmask[Op_VecS] = *idealreg2regmask[Op_VecS]; - idealreg2spillmask[Op_VecS]->or_with(C->FIRST_STACK_mask()); + idealreg2spillmask[Op_VecS]->assignFrom(*idealreg2regmask[Op_VecS]); + idealreg2spillmask[Op_VecS]->or_with(C->FIRST_STACK_mask()); } else { - *idealreg2spillmask[Op_VecS] = RegMask::EMPTY; + idealreg2spillmask[Op_VecS]->assignFrom(RegMask::EMPTY); } if (Matcher::vector_size_supported(T_FLOAT,2)) { // For VecD we need dual alignment and 8 bytes (2 slots) for spills. // RA guarantees such alignment since it is needed for Double and Long values. - *idealreg2spillmask[Op_VecD] = *idealreg2regmask[Op_VecD]; - idealreg2spillmask[Op_VecD]->or_with(aligned_stack_mask); + idealreg2spillmask[Op_VecD]->assignFrom(*idealreg2regmask[Op_VecD]); + idealreg2spillmask[Op_VecD]->or_with(aligned_stack_mask); } else { - *idealreg2spillmask[Op_VecD] = RegMask::EMPTY; + idealreg2spillmask[Op_VecD]->assignFrom(RegMask::EMPTY); } if (Matcher::vector_size_supported(T_FLOAT,4)) { @@ -541,12 +541,12 @@ void Matcher::init_first_stack_mask() { aligned_stack_mask.remove(in); in = OptoReg::add(in, -1); } - aligned_stack_mask.clear_to_sets(RegMask::SlotsPerVecX); - assert(aligned_stack_mask.is_infinite_stack(), "should be infinite stack"); - *idealreg2spillmask[Op_VecX] = *idealreg2regmask[Op_VecX]; - idealreg2spillmask[Op_VecX]->or_with(aligned_stack_mask); + aligned_stack_mask.clear_to_sets(RegMask::SlotsPerVecX); + assert(aligned_stack_mask.is_infinite_stack(), "should be infinite stack"); + idealreg2spillmask[Op_VecX]->assignFrom(*idealreg2regmask[Op_VecX]); + idealreg2spillmask[Op_VecX]->or_with(aligned_stack_mask); } else { - *idealreg2spillmask[Op_VecX] = RegMask::EMPTY; + idealreg2spillmask[Op_VecX]->assignFrom(RegMask::EMPTY); } if (Matcher::vector_size_supported(T_FLOAT,8)) { @@ -556,12 +556,12 @@ void Matcher::init_first_stack_mask() { aligned_stack_mask.remove(in); in = OptoReg::add(in, -1); } - aligned_stack_mask.clear_to_sets(RegMask::SlotsPerVecY); - assert(aligned_stack_mask.is_infinite_stack(), "should be infinite stack"); - *idealreg2spillmask[Op_VecY] = *idealreg2regmask[Op_VecY]; - idealreg2spillmask[Op_VecY]->or_with(aligned_stack_mask); + aligned_stack_mask.clear_to_sets(RegMask::SlotsPerVecY); + assert(aligned_stack_mask.is_infinite_stack(), "should be infinite stack"); + idealreg2spillmask[Op_VecY]->assignFrom(*idealreg2regmask[Op_VecY]); + idealreg2spillmask[Op_VecY]->or_with(aligned_stack_mask); } else { - *idealreg2spillmask[Op_VecY] = RegMask::EMPTY; + idealreg2spillmask[Op_VecY]->assignFrom(RegMask::EMPTY); } if (Matcher::vector_size_supported(T_FLOAT,16)) { @@ -571,12 +571,12 @@ void Matcher::init_first_stack_mask() { aligned_stack_mask.remove(in); in = OptoReg::add(in, -1); } - aligned_stack_mask.clear_to_sets(RegMask::SlotsPerVecZ); - assert(aligned_stack_mask.is_infinite_stack(), "should be infinite stack"); - *idealreg2spillmask[Op_VecZ] = *idealreg2regmask[Op_VecZ]; - idealreg2spillmask[Op_VecZ]->or_with(aligned_stack_mask); + aligned_stack_mask.clear_to_sets(RegMask::SlotsPerVecZ); + assert(aligned_stack_mask.is_infinite_stack(), "should be infinite stack"); + idealreg2spillmask[Op_VecZ]->assignFrom(*idealreg2regmask[Op_VecZ]); + idealreg2spillmask[Op_VecZ]->or_with(aligned_stack_mask); } else { - *idealreg2spillmask[Op_VecZ] = RegMask::EMPTY; + idealreg2spillmask[Op_VecZ]->assignFrom(RegMask::EMPTY); } if (Matcher::supports_scalable_vector()) { @@ -593,7 +593,7 @@ void Matcher::init_first_stack_mask() { // For RegVectMask scalable_stack_mask.clear_to_sets(scalable_predicate_reg_slots()); assert(scalable_stack_mask.is_infinite_stack(), "should be infinite stack"); - *idealreg2spillmask[Op_RegVectMask] = *idealreg2regmask[Op_RegVectMask]; + idealreg2spillmask[Op_RegVectMask]->assignFrom(*idealreg2regmask[Op_RegVectMask]); idealreg2spillmask[Op_RegVectMask]->or_with(scalable_stack_mask); } @@ -605,12 +605,12 @@ void Matcher::init_first_stack_mask() { } // For VecA - scalable_stack_mask.clear_to_sets(RegMask::SlotsPerVecA); - assert(scalable_stack_mask.is_infinite_stack(), "should be infinite stack"); - *idealreg2spillmask[Op_VecA] = *idealreg2regmask[Op_VecA]; - idealreg2spillmask[Op_VecA]->or_with(scalable_stack_mask); + scalable_stack_mask.clear_to_sets(RegMask::SlotsPerVecA); + assert(scalable_stack_mask.is_infinite_stack(), "should be infinite stack"); + idealreg2spillmask[Op_VecA]->assignFrom(*idealreg2regmask[Op_VecA]); + idealreg2spillmask[Op_VecA]->or_with(scalable_stack_mask); } else { - *idealreg2spillmask[Op_VecA] = RegMask::EMPTY; + idealreg2spillmask[Op_VecA]->assignFrom(RegMask::EMPTY); } if (UseFPUForSpilling) { @@ -639,20 +639,20 @@ void Matcher::init_first_stack_mask() { // Make up debug masks. Any spill slot plus callee-save (SOE) registers. // Caller-save (SOC, AS) registers are assumed to be trashable by the various // inline-cache fixup routines. - *idealreg2debugmask [Op_RegN] = *idealreg2spillmask[Op_RegN]; - *idealreg2debugmask [Op_RegI] = *idealreg2spillmask[Op_RegI]; - *idealreg2debugmask [Op_RegL] = *idealreg2spillmask[Op_RegL]; - *idealreg2debugmask [Op_RegF] = *idealreg2spillmask[Op_RegF]; - *idealreg2debugmask [Op_RegD] = *idealreg2spillmask[Op_RegD]; - *idealreg2debugmask [Op_RegP] = *idealreg2spillmask[Op_RegP]; - *idealreg2debugmask [Op_RegVectMask] = *idealreg2spillmask[Op_RegVectMask]; - - *idealreg2debugmask [Op_VecA] = *idealreg2spillmask[Op_VecA]; - *idealreg2debugmask [Op_VecS] = *idealreg2spillmask[Op_VecS]; - *idealreg2debugmask [Op_VecD] = *idealreg2spillmask[Op_VecD]; - *idealreg2debugmask [Op_VecX] = *idealreg2spillmask[Op_VecX]; - *idealreg2debugmask [Op_VecY] = *idealreg2spillmask[Op_VecY]; - *idealreg2debugmask [Op_VecZ] = *idealreg2spillmask[Op_VecZ]; + idealreg2debugmask[Op_RegN]->assignFrom(*idealreg2spillmask[Op_RegN]); + idealreg2debugmask[Op_RegI]->assignFrom(*idealreg2spillmask[Op_RegI]); + idealreg2debugmask[Op_RegL]->assignFrom(*idealreg2spillmask[Op_RegL]); + idealreg2debugmask[Op_RegF]->assignFrom(*idealreg2spillmask[Op_RegF]); + idealreg2debugmask[Op_RegD]->assignFrom(*idealreg2spillmask[Op_RegD]); + idealreg2debugmask[Op_RegP]->assignFrom(*idealreg2spillmask[Op_RegP]); + idealreg2debugmask[Op_RegVectMask]->assignFrom(*idealreg2spillmask[Op_RegVectMask]); + + idealreg2debugmask[Op_VecA]->assignFrom(*idealreg2spillmask[Op_VecA]); + idealreg2debugmask[Op_VecS]->assignFrom(*idealreg2spillmask[Op_VecS]); + idealreg2debugmask[Op_VecD]->assignFrom(*idealreg2spillmask[Op_VecD]); + idealreg2debugmask[Op_VecX]->assignFrom(*idealreg2spillmask[Op_VecX]); + idealreg2debugmask[Op_VecY]->assignFrom(*idealreg2spillmask[Op_VecY]); + idealreg2debugmask[Op_VecZ]->assignFrom(*idealreg2spillmask[Op_VecZ]); // Prevent stub compilations from attempting to reference // callee-saved (SOE) registers from debug info @@ -702,8 +702,9 @@ void Matcher::Fixup_Save_On_Entry( ) { RegMask *ret_rms = init_input_masks( ret_edge_cnt + soe_cnt, _return_addr_mask, c_frame_ptr_mask ); // Returns have 0 or 1 returned values depending on call signature. // Return register is specified by return_value in the AD file. - if (ret_edge_cnt > TypeFunc::Parms) - ret_rms[TypeFunc::Parms+0] = _return_value_mask; + if (ret_edge_cnt > TypeFunc::Parms) { + ret_rms[TypeFunc::Parms + 0].assignFrom(_return_value_mask); + } // Input RegMask array shared by all ForwardExceptions uint forw_exc_edge_cnt = TypeFunc::Parms; @@ -715,7 +716,7 @@ void Matcher::Fixup_Save_On_Entry( ) { // Rethrow takes exception oop only, but in the argument 0 slot. OptoReg::Name reg = find_receiver(); if (reg >= 0) { - reth_rms[TypeFunc::Parms] = mreg2regmask[reg]; + reth_rms[TypeFunc::Parms].assignFrom(mreg2regmask[reg]); #ifdef _LP64 // Need two slots for ptrs in 64-bit land reth_rms[TypeFunc::Parms].insert(OptoReg::add(OptoReg::Name(reg), 1)); @@ -737,8 +738,8 @@ void Matcher::Fixup_Save_On_Entry( ) { for( i=1; i < root->req(); i++ ) { MachReturnNode *m = root->in(i)->as_MachReturn(); if( m->ideal_Opcode() == Op_TailCall ) { - tail_call_rms[TypeFunc::Parms+0] = m->MachNode::in_RegMask(TypeFunc::Parms+0); - tail_call_rms[TypeFunc::Parms+1] = m->MachNode::in_RegMask(TypeFunc::Parms+1); + tail_call_rms[TypeFunc::Parms + 0].assignFrom(m->MachNode::in_RegMask(TypeFunc::Parms + 0)); + tail_call_rms[TypeFunc::Parms + 1].assignFrom(m->MachNode::in_RegMask(TypeFunc::Parms + 1)); break; } } @@ -750,8 +751,8 @@ void Matcher::Fixup_Save_On_Entry( ) { for( i=1; i < root->req(); i++ ) { MachReturnNode *m = root->in(i)->as_MachReturn(); if( m->ideal_Opcode() == Op_TailJump ) { - tail_jump_rms[TypeFunc::Parms+0] = m->MachNode::in_RegMask(TypeFunc::Parms+0); - tail_jump_rms[TypeFunc::Parms+1] = m->MachNode::in_RegMask(TypeFunc::Parms+1); + tail_jump_rms[TypeFunc::Parms + 0].assignFrom(m->MachNode::in_RegMask(TypeFunc::Parms + 0)); + tail_jump_rms[TypeFunc::Parms + 1].assignFrom(m->MachNode::in_RegMask(TypeFunc::Parms + 1)); break; } } @@ -784,14 +785,14 @@ void Matcher::Fixup_Save_On_Entry( ) { if( is_save_on_entry(i) ) { // Add the save-on-entry to the mask array - ret_rms [ ret_edge_cnt] = mreg2regmask[i]; - reth_rms [ reth_edge_cnt] = mreg2regmask[i]; - tail_call_rms[tail_call_edge_cnt] = mreg2regmask[i]; - tail_jump_rms[tail_jump_edge_cnt] = mreg2regmask[i]; - forw_exc_rms [ forw_exc_edge_cnt] = mreg2regmask[i]; + ret_rms [ ret_edge_cnt].assignFrom(mreg2regmask[i]); + reth_rms [ reth_edge_cnt].assignFrom(mreg2regmask[i]); + tail_call_rms[tail_call_edge_cnt].assignFrom(mreg2regmask[i]); + tail_jump_rms[tail_jump_edge_cnt].assignFrom(mreg2regmask[i]); + forw_exc_rms [ forw_exc_edge_cnt].assignFrom(mreg2regmask[i]); // Halts need the SOE registers, but only in the stack as debug info. // A just-prior uncommon-trap or deoptimization will use the SOE regs. - halt_rms [ halt_edge_cnt] = *idealreg2spillmask[_register_save_type[i]]; + halt_rms [ halt_edge_cnt].assignFrom(*idealreg2spillmask[_register_save_type[i]]); Node *mproj; @@ -815,12 +816,12 @@ void Matcher::Fixup_Save_On_Entry( ) { _register_save_type[i-1] == Op_RegF && _register_save_type[i ] == Op_RegF && is_save_on_entry(i-1) ) { - ret_rms [ ret_edge_cnt] = RegMask::EMPTY; - reth_rms [ reth_edge_cnt] = RegMask::EMPTY; - tail_call_rms[tail_call_edge_cnt] = RegMask::EMPTY; - tail_jump_rms[tail_jump_edge_cnt] = RegMask::EMPTY; - forw_exc_rms [ forw_exc_edge_cnt] = RegMask::EMPTY; - halt_rms [ halt_edge_cnt] = RegMask::EMPTY; + ret_rms [ ret_edge_cnt].assignFrom(RegMask::EMPTY); + reth_rms [ reth_edge_cnt].assignFrom(RegMask::EMPTY); + tail_call_rms[tail_call_edge_cnt].assignFrom(RegMask::EMPTY); + tail_jump_rms[tail_jump_edge_cnt].assignFrom(RegMask::EMPTY); + forw_exc_rms [ forw_exc_edge_cnt].assignFrom(RegMask::EMPTY); + halt_rms [ halt_edge_cnt].assignFrom(RegMask::EMPTY); mproj = C->top(); } // Is this a RegI low half of a RegL? Double up 2 adjacent RegI's @@ -843,12 +844,12 @@ void Matcher::Fixup_Save_On_Entry( ) { _register_save_type[i-1] == Op_RegI && _register_save_type[i ] == Op_RegI && is_save_on_entry(i-1) ) { - ret_rms [ ret_edge_cnt] = RegMask::EMPTY; - reth_rms [ reth_edge_cnt] = RegMask::EMPTY; - tail_call_rms[tail_call_edge_cnt] = RegMask::EMPTY; - tail_jump_rms[tail_jump_edge_cnt] = RegMask::EMPTY; - forw_exc_rms [ forw_exc_edge_cnt] = RegMask::EMPTY; - halt_rms [ halt_edge_cnt] = RegMask::EMPTY; + ret_rms [ ret_edge_cnt].assignFrom(RegMask::EMPTY); + reth_rms [ reth_edge_cnt].assignFrom(RegMask::EMPTY); + tail_call_rms[tail_call_edge_cnt].assignFrom(RegMask::EMPTY); + tail_jump_rms[tail_jump_edge_cnt].assignFrom(RegMask::EMPTY); + forw_exc_rms [ forw_exc_edge_cnt].assignFrom(RegMask::EMPTY); + halt_rms [ halt_edge_cnt].assignFrom(RegMask::EMPTY); mproj = C->top(); } else { // Make a projection for it off the Start @@ -875,7 +876,7 @@ void Matcher::init_spill_mask( Node *ret ) { if( idealreg2regmask[Op_RegI] ) return; // One time only init OptoReg::c_frame_pointer = c_frame_pointer(); - c_frame_ptr_mask = RegMask(c_frame_pointer()); + c_frame_ptr_mask.assignFrom(RegMask(c_frame_pointer())); #ifdef _LP64 // pointers are twice as big c_frame_ptr_mask.insert(OptoReg::add(c_frame_pointer(), 1)); @@ -1240,8 +1241,8 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { } // Do all the pre-defined non-Empty register masks - msfpt->_in_rms[TypeFunc::ReturnAdr] = _return_addr_mask; - msfpt->_in_rms[TypeFunc::FramePtr ] = c_frame_ptr_mask; + msfpt->_in_rms[TypeFunc::ReturnAdr].assignFrom(_return_addr_mask); + msfpt->_in_rms[TypeFunc::FramePtr ].assignFrom(c_frame_ptr_mask); // Place first outgoing argument can possibly be put. OptoReg::Name begin_out_arg_area = OptoReg::add(_new_SP, C->out_preserve_stack_slots()); diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index 0b609b70ab5e9..e4396b423ac0e 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -408,14 +408,14 @@ class Matcher : public PhaseTransform { static int inline_cache_reg_encode(); // Register for DIVI projection of divmodI - static RegMask divI_proj_mask(); + static const RegMask& divI_proj_mask(); // Register for MODI projection of divmodI - static RegMask modI_proj_mask(); + static const RegMask& modI_proj_mask(); // Register for DIVL projection of divmodL - static RegMask divL_proj_mask(); + static const RegMask& divL_proj_mask(); // Register for MODL projection of divmodL - static RegMask modL_proj_mask(); + static const RegMask& modL_proj_mask(); // Use hardware DIV instruction when it is faster than // a code which use multiply for division by constant. diff --git a/src/hotspot/share/opto/mempointer.cpp b/src/hotspot/share/opto/mempointer.cpp index a63ba8ef7010b..68abaffe6429a 100644 --- a/src/hotspot/share/opto/mempointer.cpp +++ b/src/hotspot/share/opto/mempointer.cpp @@ -112,7 +112,7 @@ void MemPointerParser::canonicalize_raw_summands() { } } // Keep summands with non-zero scale. - if (!scaleI.is_zero() && !scaleL.is_NaN()) { + if (!scaleI.is_zero() && !scaleL.is_zero()) { _raw_summands.at_put(pos_put++, MemPointerRawSummand(variable, scaleI, scaleL, int_group)); } } diff --git a/src/hotspot/share/opto/postaloc.cpp b/src/hotspot/share/opto/postaloc.cpp index 8eb9167921bad..c961340e71acf 100644 --- a/src/hotspot/share/opto/postaloc.cpp +++ b/src/hotspot/share/opto/postaloc.cpp @@ -767,7 +767,7 @@ void PhaseChaitin::post_allocate_copy_removal() { if (!is_adjacent) { // Nearly always adjacent // Sparc occasionally has non-adjacent pairs. // Find the actual other value - RegMask tmp = lrgs(lidx).mask(); + RegMask tmp(lrgs(lidx).mask()); tmp.remove(nreg); nreg_lo = tmp.find_first_elem(); } diff --git a/src/hotspot/share/opto/regmask.hpp b/src/hotspot/share/opto/regmask.hpp index 832499d951d91..453fbb45d33b7 100644 --- a/src/hotspot/share/opto/regmask.hpp +++ b/src/hotspot/share/opto/regmask.hpp @@ -299,39 +299,6 @@ class RegMask { } } - // Make us a copy of src - void copy(const RegMask& src) { - assert(_offset == src._offset, "offset mismatch"); - _hwm = src._hwm; - _lwm = src._lwm; - - // Copy base mask - memcpy(_rm_word, src._rm_word, sizeof(uintptr_t) * RM_SIZE_IN_WORDS); - _infinite_stack = src._infinite_stack; - - // Copy extension - if (src._rm_word_ext != nullptr) { - assert(src._rm_size_in_words > RM_SIZE_IN_WORDS, "sanity"); - assert(_original_ext_address == &_rm_word_ext, "clone sanity check"); - grow(src._rm_size_in_words, false); - memcpy(_rm_word_ext, src._rm_word_ext, - sizeof(uintptr_t) * (src._rm_size_in_words - RM_SIZE_IN_WORDS)); - } - - // If the source is smaller than us, we need to set the gap according to - // the sources infinite_stack flag. - if (src._rm_size_in_words < _rm_size_in_words) { - int value = 0; - if (src.is_infinite_stack()) { - value = 0xFF; - _hwm = rm_word_max_index(); - } - set_range(src._rm_size_in_words, value, _rm_size_in_words - src._rm_size_in_words); - } - - assert(valid_watermarks(), "post-condition"); - } - // Make the watermarks as tight as possible. void trim_watermarks() { if (_hwm < _lwm) { @@ -453,21 +420,52 @@ class RegMask { } explicit RegMask(OptoReg::Name reg) : RegMask(reg, nullptr) {} - // ---------------------------------------- - // Deep copying constructors and assignment - // ---------------------------------------- + // Make us represent the same set of registers as src. + void assignFrom(const RegMask& src) { + assert(_offset == src._offset, "offset mismatch"); + _hwm = src._hwm; + _lwm = src._lwm; + + // Copy base mask + memcpy(_rm_word, src._rm_word, sizeof(uintptr_t) * RM_SIZE_IN_WORDS); + _infinite_stack = src._infinite_stack; + + // Copy extension + if (src._rm_word_ext != nullptr) { + assert(src._rm_size_in_words > RM_SIZE_IN_WORDS, "sanity"); + assert(_original_ext_address == &_rm_word_ext, "clone sanity check"); + grow(src._rm_size_in_words, false); + memcpy(_rm_word_ext, src._rm_word_ext, + sizeof(uintptr_t) * (src._rm_size_in_words - RM_SIZE_IN_WORDS)); + } + // If the source is smaller than us, we need to set the gap according to + // the sources infinite_stack flag. + if (src._rm_size_in_words < _rm_size_in_words) { + int value = 0; + if (src.is_infinite_stack()) { + value = 0xFF; + _hwm = rm_word_max_index(); + } + set_range(src._rm_size_in_words, value, _rm_size_in_words - src._rm_size_in_words); + } + + assert(valid_watermarks(), "post-condition"); + } + + // Construct from other register mask (deep copy) and register an arena + // for potential register mask extension. Passing nullptr as arena disables + // extension. RegMask(const RegMask& rm, Arena* arena) : _arena(arena), _rm_size_in_words(RM_SIZE_IN_WORDS), _offset(rm._offset) { - copy(rm); + assignFrom(rm); } - RegMask(const RegMask& rm) : RegMask(rm, nullptr) {} + // Copy constructor (deep copy). By default does not allow extension. + explicit RegMask(const RegMask& rm) : RegMask(rm, nullptr) {} - RegMask& operator=(const RegMask& rm) { - copy(rm); - return *this; - } + // Disallow copy assignment (use assignFrom instead) + RegMask& operator=(const RegMask&) = delete; // ---------------- // End deep copying diff --git a/src/hotspot/share/prims/jvmtiAgentList.cpp b/src/hotspot/share/prims/jvmtiAgentList.cpp index 8da5b75be4611..41fc9c0f3594c 100644 --- a/src/hotspot/share/prims/jvmtiAgentList.cpp +++ b/src/hotspot/share/prims/jvmtiAgentList.cpp @@ -196,6 +196,11 @@ void JvmtiAgentList::load_xrun_agents() { // Invokes Agent_OnAttach for agents loaded dynamically during runtime. void JvmtiAgentList::load_agent(const char* agent_name, bool is_absolute_path, const char* options, outputStream* st) { + if (JvmtiEnvBase::get_phase() != JVMTI_PHASE_LIVE) { + st->print_cr("Dynamic agent loading is only permitted in the live phase"); + return; + } + JvmtiAgent* const agent = new JvmtiAgent(agent_name, options, is_absolute_path, /* dynamic agent */ true); if (agent->load(st)) { add(agent); diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index 853c6554022f9..daafcaea61bfe 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -1804,10 +1804,11 @@ void Deoptimization::deoptimize(JavaThread* thread, frame fr, DeoptReason reason deoptimize_single_frame(thread, fr, reason); } -#if INCLUDE_JVMCI -address Deoptimization::deoptimize_for_missing_exception_handler(nmethod* nm) { +address Deoptimization::deoptimize_for_missing_exception_handler(nmethod* nm, bool make_not_entrant) { // there is no exception handler for this pc => deoptimize - nm->make_not_entrant(nmethod::InvalidationReason::MISSING_EXCEPTION_HANDLER); + if (make_not_entrant) { + nm->make_not_entrant(nmethod::InvalidationReason::MISSING_EXCEPTION_HANDLER); + } // Use Deoptimization::deoptimize for all of its side-effects: // gathering traps statistics, logging... @@ -1821,6 +1822,15 @@ address Deoptimization::deoptimize_for_missing_exception_handler(nmethod* nm) { frame runtime_frame = thread->last_frame(); frame caller_frame = runtime_frame.sender(®_map); assert(caller_frame.cb()->as_nmethod_or_null() == nm, "expect top frame compiled method"); + + Deoptimization::deoptimize(thread, caller_frame, Deoptimization::Reason_not_compiled_exception_handler); + + if (!nm->is_compiled_by_jvmci()) { + return SharedRuntime::deopt_blob()->unpack_with_exception_in_tls(); + } + +#if INCLUDE_JVMCI + // JVMCI support vframe* vf = vframe::new_vframe(&caller_frame, ®_map, thread); compiledVFrame* cvf = compiledVFrame::cast(vf); ScopeDesc* imm_scope = cvf->scope(); @@ -1836,16 +1846,15 @@ address Deoptimization::deoptimize_for_missing_exception_handler(nmethod* nm) { } } - Deoptimization::deoptimize(thread, caller_frame, Deoptimization::Reason_not_compiled_exception_handler); MethodData* trap_mdo = get_method_data(thread, methodHandle(thread, nm->method()), true); if (trap_mdo != nullptr) { trap_mdo->inc_trap_count(Deoptimization::Reason_not_compiled_exception_handler); } +#endif return SharedRuntime::deopt_blob()->unpack_with_exception_in_tls(); } -#endif void Deoptimization::deoptimize_frame_internal(JavaThread* thread, intptr_t* id, DeoptReason reason) { assert(thread == Thread::current() || @@ -2748,10 +2757,10 @@ const char* Deoptimization::_trap_reason_name[] = { "unstable_if", "unstable_fused_if", "receiver_constraint", + "not_compiled_exception_handler", "short_running_loop" JVMCI_ONLY("_or_aliasing"), #if INCLUDE_JVMCI "transfer_to_interpreter", - "not_compiled_exception_handler", "unresolved", "jsr_mismatch", #endif diff --git a/src/hotspot/share/runtime/deoptimization.hpp b/src/hotspot/share/runtime/deoptimization.hpp index 5d97e2056adcc..d168d9c8af682 100644 --- a/src/hotspot/share/runtime/deoptimization.hpp +++ b/src/hotspot/share/runtime/deoptimization.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -117,11 +117,11 @@ class Deoptimization : AllStatic { Reason_unstable_if, // a branch predicted always false was taken Reason_unstable_fused_if, // fused two ifs that had each one untaken branch. One is now taken. Reason_receiver_constraint, // receiver subtype check failed + Reason_not_compiled_exception_handler, // missing compiled exception handler Reason_short_running_long_loop, // profile reports loop runs for small number of iterations #if INCLUDE_JVMCI Reason_aliasing = Reason_short_running_long_loop, // optimistic assumption about aliasing failed Reason_transfer_to_interpreter, // explicit transferToInterpreter() - Reason_not_compiled_exception_handler, Reason_unresolved, Reason_jsr_mismatch, #endif @@ -184,8 +184,8 @@ class Deoptimization : AllStatic { // Deoptimizes a frame lazily. Deopt happens on return to the frame. static void deoptimize(JavaThread* thread, frame fr, DeoptReason reason = Reason_constraint); + static address deoptimize_for_missing_exception_handler(nmethod* nm, bool make_not_entrant); #if INCLUDE_JVMCI - static address deoptimize_for_missing_exception_handler(nmethod* nm); static oop get_cached_box(AutoBoxObjectValue* bv, frame* fr, RegisterMap* reg_map, bool& cache_init_error, TRAPS); #endif diff --git a/src/hotspot/share/runtime/perfMemory.cpp b/src/hotspot/share/runtime/perfMemory.cpp index a75a41e95a94c..9594149333e6e 100644 --- a/src/hotspot/share/runtime/perfMemory.cpp +++ b/src/hotspot/share/runtime/perfMemory.cpp @@ -114,9 +114,7 @@ void PerfMemory::initialize() { // the warning is issued only in debug mode in order to avoid // additional output to the stdout or stderr output streams. // - if (PrintMiscellaneous && Verbose) { - warning("Could not create PerfData Memory region, reverting to malloc"); - } + log_debug(perf)("could not create PerfData Memory region, reverting to malloc"); _prologue = NEW_C_HEAP_OBJ(PerfDataPrologue, mtInternal); } @@ -250,10 +248,7 @@ char* PerfMemory::get_perfdata_file_path() { if(!Arguments::copy_expand_pid(PerfDataSaveFile, strlen(PerfDataSaveFile), dest_file, JVM_MAXPATHLEN)) { FREE_C_HEAP_ARRAY(char, dest_file); - if (PrintMiscellaneous && Verbose) { - warning("Invalid performance data file path name specified, "\ - "fall back to a default name"); - } + log_debug(perf)("invalid performance data file path name specified, fall back to a default name"); } else { return dest_file; } diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index efc47dd11c643..35bc3f5f1beaf 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -791,7 +791,8 @@ address SharedRuntime::compute_compiled_exc_handler(nmethod* nm, address ret_pc, if (t != nullptr) { return nm->code_begin() + t->pco(); } else { - return Deoptimization::deoptimize_for_missing_exception_handler(nm); + bool make_not_entrant = true; + return Deoptimization::deoptimize_for_missing_exception_handler(nm, make_not_entrant); } } #endif // INCLUDE_JVMCI @@ -847,6 +848,15 @@ address SharedRuntime::compute_compiled_exc_handler(nmethod* nm, address ret_pc, ExceptionHandlerTable table(nm); HandlerTableEntry *t = table.entry_for(catch_pco, handler_bci, scope_depth); + + // If the compiler did not anticipate a recursive exception, resulting in an exception + // thrown from the catch bci, then the compiled exception handler might be missing. + // This is rare. Just deoptimize and let the interpreter handle it. + if (t == nullptr && recursive_exception_occurred) { + bool make_not_entrant = false; + return Deoptimization::deoptimize_for_missing_exception_handler(nm, make_not_entrant); + } + if (t == nullptr && (nm->is_compiled_by_c1() || handler_bci != -1)) { // Allow abbreviated catch tables. The idea is to allow a method // to materialize its exceptions without committing to the exact diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp index 93cd92b3a32df..f4ff51504f0e5 100644 --- a/src/hotspot/share/runtime/sharedRuntime.hpp +++ b/src/hotspot/share/runtime/sharedRuntime.hpp @@ -384,10 +384,6 @@ class SharedRuntime: AllStatic { // deopt blob static void generate_deopt_blob(void); - static bool handle_ic_miss_helper_internal(Handle receiver, nmethod* caller_nm, const frame& caller_frame, - methodHandle callee_method, Bytecodes::Code bc, CallInfo& call_info, - bool& needs_ic_stub_refill, TRAPS); - public: static DeoptimizationBlob* deopt_blob(void) { return _deopt_blob; } @@ -549,7 +545,6 @@ class SharedRuntime: AllStatic { // A compiled caller has just called the interpreter, but compiled code // exists. Patch the caller so he no longer calls into the interpreter. static void fixup_callers_callsite(Method* moop, address ret_pc); - static bool should_fixup_call_destination(address destination, address entry_point, address caller_pc, Method* moop, CodeBlob* cb); // Slow-path Locking and Unlocking static void complete_monitor_locking_C(oopDesc* obj, BasicLock* lock, JavaThread* current); diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 8dc4b660f9133..85f921ef3e33c 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1581,8 +1581,8 @@ declare_constant(Deoptimization::Reason_unstable_if) \ declare_constant(Deoptimization::Reason_unstable_fused_if) \ declare_constant(Deoptimization::Reason_receiver_constraint) \ + declare_constant(Deoptimization::Reason_not_compiled_exception_handler) \ NOT_ZERO(JVMCI_ONLY(declare_constant(Deoptimization::Reason_transfer_to_interpreter))) \ - NOT_ZERO(JVMCI_ONLY(declare_constant(Deoptimization::Reason_not_compiled_exception_handler))) \ NOT_ZERO(JVMCI_ONLY(declare_constant(Deoptimization::Reason_unresolved))) \ NOT_ZERO(JVMCI_ONLY(declare_constant(Deoptimization::Reason_jsr_mismatch))) \ declare_constant(Deoptimization::Reason_tenured) \ diff --git a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java index d317557cbb19c..f1da102236a34 100644 --- a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java +++ b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java @@ -1448,8 +1448,8 @@ public AbstractStringBuilder insert(int dstOffset, CharSequence s, shift(currValue, coder, count, dstOffset, len); count += len; // Coder of CharSequence may be a mismatch, requiring the value array to be inflated - byte[] newValue = (s instanceof String str) - ? putStringAt(currValue, coder, count, dstOffset, str, start, end) + byte[] newValue = (s instanceof String str && str.length() == len) + ? putStringAt(currValue, coder, count, dstOffset, str) : putCharsAt(currValue, coder, count, dstOffset, s, start, end); if (currValue != newValue) { this.coder = UTF16; @@ -1928,10 +1928,10 @@ private static byte[] inflateIfNeededFor(byte[] value, int count, byte coder, by * @param index the index to insert the string * @param str the string */ - private static byte[] putStringAt(byte[] value, byte coder, int count, int index, String str, int off, int end) { + private static byte[] putStringAt(byte[] value, byte coder, int count, int index, String str) { byte[] newValue = inflateIfNeededFor(value, count, coder, str.coder()); coder = (newValue == value) ? coder : UTF16; - str.getBytes(newValue, off, index, coder, end - off); + str.getBytes(newValue, 0, index, coder, str.length()); return newValue; } 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 24b55600954d2..18aa6f29f1f09 100644 --- a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java +++ b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java @@ -25,18 +25,26 @@ package java.lang.runtime; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassHierarchyResolver; +import java.lang.classfile.Opcode; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; import java.lang.invoke.ConstantCallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.invoke.StringConcatFactory; import java.lang.invoke.TypeDescriptor; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Objects; +import static java.lang.classfile.ClassFile.ACC_STATIC; +import static java.lang.constant.ConstantDescs.*; import static java.util.Objects.requireNonNull; /** @@ -58,15 +66,18 @@ private ObjectMethods() { } private static final MethodHandle TRUE = MethodHandles.constant(boolean.class, true); private static final MethodHandle ZERO = MethodHandles.zero(int.class); private static final MethodHandle CLASS_IS_INSTANCE; - private static final MethodHandle OBJECTS_EQUALS; - private static final MethodHandle OBJECTS_HASHCODE; - private static final MethodHandle OBJECTS_TOSTRING; + private static final MethodHandle IS_NULL; + private static final MethodHandle IS_ARG0_NULL; + private static final MethodHandle IS_ARG1_NULL; private static final MethodHandle OBJECT_EQ; private static final MethodHandle HASH_COMBINER; + private static final MethodType MT_OBJECT_BOOLEAN = MethodType.methodType(boolean.class, Object.class); + private static final MethodType MT_INT = MethodType.methodType(int.class); + private static final MethodTypeDesc MTD_OBJECT_BOOLEAN = MethodTypeDesc.of(CD_boolean, CD_Object); + private static final MethodTypeDesc MTD_INT = MethodTypeDesc.of(CD_int); private static final HashMap, MethodHandle> primitiveEquals = new HashMap<>(); private static final HashMap, MethodHandle> primitiveHashers = new HashMap<>(); - private static final HashMap, MethodHandle> primitiveToString = new HashMap<>(); static { try { @@ -76,12 +87,12 @@ private ObjectMethods() { } CLASS_IS_INSTANCE = publicLookup.findVirtual(Class.class, "isInstance", MethodType.methodType(boolean.class, Object.class)); - OBJECTS_EQUALS = publicLookup.findStatic(Objects.class, "equals", - MethodType.methodType(boolean.class, Object.class, Object.class)); - OBJECTS_HASHCODE = publicLookup.findStatic(Objects.class, "hashCode", - MethodType.methodType(int.class, Object.class)); - OBJECTS_TOSTRING = publicLookup.findStatic(Objects.class, "toString", - MethodType.methodType(String.class, Object.class)); + + var objectsIsNull = publicLookup.findStatic(Objects.class, "isNull", + MethodType.methodType(boolean.class, Object.class)); + IS_NULL = objectsIsNull; + IS_ARG0_NULL = MethodHandles.dropArguments(objectsIsNull, 1, Object.class); + IS_ARG1_NULL = MethodHandles.dropArguments(objectsIsNull, 0, Object.class); OBJECT_EQ = lookup.findStatic(OBJECT_METHODS_CLASS, "eq", MethodType.methodType(boolean.class, Object.class, Object.class)); @@ -121,23 +132,6 @@ private ObjectMethods() { } MethodType.methodType(int.class, double.class))); primitiveHashers.put(boolean.class, lookup.findStatic(Boolean.class, "hashCode", MethodType.methodType(int.class, boolean.class))); - - primitiveToString.put(byte.class, lookup.findStatic(Byte.class, "toString", - MethodType.methodType(String.class, byte.class))); - primitiveToString.put(short.class, lookup.findStatic(Short.class, "toString", - MethodType.methodType(String.class, short.class))); - primitiveToString.put(char.class, lookup.findStatic(Character.class, "toString", - MethodType.methodType(String.class, char.class))); - primitiveToString.put(int.class, lookup.findStatic(Integer.class, "toString", - MethodType.methodType(String.class, int.class))); - primitiveToString.put(long.class, lookup.findStatic(Long.class, "toString", - MethodType.methodType(String.class, long.class))); - primitiveToString.put(float.class, lookup.findStatic(Float.class, "toString", - MethodType.methodType(String.class, float.class))); - primitiveToString.put(double.class, lookup.findStatic(Double.class, "toString", - MethodType.methodType(String.class, double.class))); - primitiveToString.put(boolean.class, lookup.findStatic(Boolean.class, "toString", - MethodType.methodType(String.class, boolean.class))); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); @@ -159,24 +153,41 @@ private static int hashCombiner(int x, int y) { private static boolean eq(boolean a, boolean b) { return a == b; } /** Get the method handle for combining two values of a given type */ - private static MethodHandle equalator(Class clazz) { - return (clazz.isPrimitive() - ? primitiveEquals.get(clazz) - : OBJECTS_EQUALS.asType(MethodType.methodType(boolean.class, clazz, clazz))); + private static MethodHandle equalator(MethodHandles.Lookup lookup, Class clazz) throws Throwable { + if (clazz.isPrimitive()) + return primitiveEquals.get(clazz); + MethodType mt = MethodType.methodType(boolean.class, clazz, clazz); + return MethodHandles.guardWithTest(IS_ARG0_NULL.asType(mt), + IS_ARG1_NULL.asType(mt), + lookup.findVirtual(clazz, "equals", MT_OBJECT_BOOLEAN).asType(mt)); } /** Get the hasher for a value of a given type */ - private static MethodHandle hasher(Class clazz) { - return (clazz.isPrimitive() - ? primitiveHashers.get(clazz) - : OBJECTS_HASHCODE.asType(MethodType.methodType(int.class, clazz))); + private static MethodHandle hasher(MethodHandles.Lookup lookup, Class clazz) throws Throwable { + if (clazz.isPrimitive()) + return primitiveHashers.get(clazz); + MethodType mt = MethodType.methodType(int.class, clazz); + return MethodHandles.guardWithTest(IS_NULL.asType(MethodType.methodType(boolean.class, clazz)), + MethodHandles.dropArguments(MethodHandles.zero(int.class), 0, clazz), + lookup.findVirtual(clazz, "hashCode", MT_INT).asType(mt)); } - /** Get the stringifier for a value of a given type */ - private static MethodHandle stringifier(Class clazz) { - return (clazz.isPrimitive() - ? primitiveToString.get(clazz) - : OBJECTS_TOSTRING.asType(MethodType.methodType(String.class, clazz))); + // If this type must be a monomorphic receiver, that is, one that has no + // subtypes in the JVM. For example, Object-typed fields may have a more + // specific one type at runtime and thus need optimizations. + private static boolean isMonomorphic(Class type) { + // Includes primitives and final classes, but not arrays. + // All array classes are reported to be final, but Object[] can have subtypes like String[] + return Modifier.isFinal(type.getModifiers()) && !type.isArray(); + } + + private static String specializerClassName(Class targetClass, String kind) { + String name = targetClass.getName(); + if (targetClass.isHidden()) { + // use the original class name + name = name.replace('/', '_'); + } + return name + "$$" + kind + "Specializer"; } /** @@ -185,8 +196,8 @@ private static MethodHandle stringifier(Class clazz) { * @param getters the list of getters * @return the method handle */ - private static MethodHandle makeEquals(Class receiverClass, - List getters) { + private static MethodHandle makeEquals(MethodHandles.Lookup lookup, Class receiverClass, + List getters) throws Throwable { MethodType rr = MethodType.methodType(boolean.class, receiverClass, receiverClass); MethodType ro = MethodType.methodType(boolean.class, receiverClass, Object.class); MethodHandle instanceFalse = MethodHandles.dropArguments(FALSE, 0, receiverClass, Object.class); // (RO)Z @@ -195,8 +206,70 @@ private static MethodHandle makeEquals(Class receiverClass, MethodHandle isInstance = MethodHandles.dropArguments(CLASS_IS_INSTANCE.bindTo(receiverClass), 0, receiverClass); // (RO)Z MethodHandle accumulator = MethodHandles.dropArguments(TRUE, 0, receiverClass, receiverClass); // (RR)Z - for (MethodHandle getter : getters) { - MethodHandle equalator = equalator(getter.type().returnType()); // (TT)Z + int size = getters.size(); + MethodHandle[] equalators = new MethodHandle[size]; + boolean hasPolymorphism = false; + for (int i = 0; i < size; i++) { + var getter = getters.get(i); + var type = getter.type().returnType(); + if (isMonomorphic(type)) { + equalators[i] = equalator(lookup, type); + } else { + hasPolymorphism = true; + } + } + + // Currently, hotspot does not support polymorphic inlining. + // As a result, if we have a MethodHandle to Object.equals, + // it does not enjoy separate profiles like individual invokevirtuals, + // and we must spin bytecode to accomplish separate profiling. + if (hasPolymorphism) { + String[] names = new String[size]; + + var classFileContext = ClassFile.of(ClassFile.ClassHierarchyResolverOption.of(ClassHierarchyResolver.ofClassLoading(lookup))); + var bytes = classFileContext.build(ClassDesc.of(specializerClassName(lookup.lookupClass(), "Equalator")), clb -> { + for (int i = 0; i < size; i++) { + if (equalators[i] == null) { + var name = "equalator".concat(Integer.toString(i)); + names[i] = name; + var type = getters.get(i).type().returnType(); + boolean isInterface = type.isInterface(); + var typeDesc = type.describeConstable().orElseThrow(); + clb.withMethodBody(name, MethodTypeDesc.of(CD_boolean, typeDesc, typeDesc), ACC_STATIC, cob -> { + var nonNullPath = cob.newLabel(); + var fail = cob.newLabel(); + cob.aload(0) + .ifnonnull(nonNullPath) + .aload(1) + .ifnonnull(fail) + .iconst_1() // arg0 null, arg1 null + .ireturn() + .labelBinding(fail) + .iconst_0() // arg0 null, arg1 non-null + .ireturn() + .labelBinding(nonNullPath) + .aload(0) // arg0.equals(arg1) - bytecode subject to customized profiling + .aload(1) + .invoke(isInterface ? Opcode.INVOKEINTERFACE : Opcode.INVOKEVIRTUAL, typeDesc, "equals", MTD_OBJECT_BOOLEAN, isInterface) + .ireturn(); + }); + } + } + }); + + var specializerLookup = lookup.defineHiddenClass(bytes, true, MethodHandles.Lookup.ClassOption.STRONG); + + for (int i = 0; i < size; i++) { + if (equalators[i] == null) { + var type = getters.get(i).type().returnType(); + equalators[i] = specializerLookup.findStatic(specializerLookup.lookupClass(), names[i], MethodType.methodType(boolean.class, type, type)); + } + } + } + + for (int i = 0; i < size; i++) { + var getter = getters.get(i); + MethodHandle equalator = equalators[i]; // (TT)Z MethodHandle thisFieldEqual = MethodHandles.filterArguments(equalator, 0, getter, getter); // (RR)Z accumulator = MethodHandles.guardWithTest(thisFieldEqual, accumulator, instanceFalse.asType(rr)); } @@ -212,13 +285,68 @@ private static MethodHandle makeEquals(Class receiverClass, * @param getters the list of getters * @return the method handle */ - private static MethodHandle makeHashCode(Class receiverClass, - List getters) { + private static MethodHandle makeHashCode(MethodHandles.Lookup lookup, Class receiverClass, + List getters) throws Throwable { MethodHandle accumulator = MethodHandles.dropArguments(ZERO, 0, receiverClass); // (R)I + int size = getters.size(); + MethodHandle[] hashers = new MethodHandle[size]; + boolean hasPolymorphism = false; + for (int i = 0; i < size; i++) { + var getter = getters.get(i); + var type = getter.type().returnType(); + if (isMonomorphic(type)) { + hashers[i] = hasher(lookup, type); + } else { + hasPolymorphism = true; + } + } + + // Currently, hotspot does not support polymorphic inlining. + // As a result, if we have a MethodHandle to Object.hashCode, + // it does not enjoy separate profiles like individual invokevirtuals, + // and we must spin bytecode to accomplish separate profiling. + if (hasPolymorphism) { + String[] names = new String[size]; + + var classFileContext = ClassFile.of(ClassFile.ClassHierarchyResolverOption.of(ClassHierarchyResolver.ofClassLoading(lookup))); + var bytes = classFileContext.build(ClassDesc.of(specializerClassName(lookup.lookupClass(), "Hasher")), clb -> { + for (int i = 0; i < size; i++) { + if (hashers[i] == null) { + var name = "hasher".concat(Integer.toString(i)); + names[i] = name; + var type = getters.get(i).type().returnType(); + boolean isInterface = type.isInterface(); + var typeDesc = type.describeConstable().orElseThrow(); + clb.withMethodBody(name, MethodTypeDesc.of(CD_int, typeDesc), ACC_STATIC, cob -> { + var nonNullPath = cob.newLabel(); + cob.aload(0) + .ifnonnull(nonNullPath) + .iconst_0() // null hash is 0 + .ireturn() + .labelBinding(nonNullPath) + .aload(0) // arg0.hashCode() - bytecode subject to customized profiling + .invoke(isInterface ? Opcode.INVOKEINTERFACE : Opcode.INVOKEVIRTUAL, typeDesc, "hashCode", MTD_INT, isInterface) + .ireturn(); + }); + } + } + }); + + var specializerLookup = lookup.defineHiddenClass(bytes, true, MethodHandles.Lookup.ClassOption.STRONG); + + for (int i = 0; i < size; i++) { + if (hashers[i] == null) { + var type = getters.get(i).type().returnType(); + hashers[i] = specializerLookup.findStatic(specializerLookup.lookupClass(), names[i], MethodType.methodType(int.class, type)); + } + } + } + // @@@ Use loop combinator instead? - for (MethodHandle getter : getters) { - MethodHandle hasher = hasher(getter.type().returnType()); // (T)I + for (int i = 0; i < size; i++) { + var getter = getters.get(i); + MethodHandle hasher = hashers[i]; // (T)I MethodHandle hashThisField = MethodHandles.filterArguments(hasher, 0, getter); // (R)I MethodHandle combineHashes = MethodHandles.filterArguments(HASH_COMBINER, 0, accumulator, hashThisField); // (RR)I accumulator = MethodHandles.permuteArguments(combineHashes, accumulator.type(), 0, 0); // adapt (R)I to (RR)I @@ -403,12 +531,12 @@ public static Object bootstrap(MethodHandles.Lookup lookup, String methodName, T case "equals" -> { if (methodType != null && !methodType.equals(MethodType.methodType(boolean.class, recordClass, Object.class))) throw new IllegalArgumentException("Bad method type: " + methodType); - yield makeEquals(recordClass, getterList); + yield makeEquals(lookup, recordClass, getterList); } case "hashCode" -> { if (methodType != null && !methodType.equals(MethodType.methodType(int.class, recordClass))) throw new IllegalArgumentException("Bad method type: " + methodType); - yield makeHashCode(recordClass, getterList); + yield makeHashCode(lookup, recordClass, getterList); } case "toString" -> { if (methodType != null && !methodType.equals(MethodType.methodType(String.class, recordClass))) diff --git a/src/java.base/share/classes/java/nio/channels/GatheringByteChannel.java b/src/java.base/share/classes/java/nio/channels/GatheringByteChannel.java index 4e3b0cf136d2a..e2e97562dee79 100644 --- a/src/java.base/share/classes/java/nio/channels/GatheringByteChannel.java +++ b/src/java.base/share/classes/java/nio/channels/GatheringByteChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -76,11 +76,14 @@ public interface GatheringByteChannel * the final position of each updated buffer, except the last updated * buffer, is guaranteed to be equal to that buffer's limit. * - *

Unless otherwise specified, a write operation will return only after + *

For many types of channels, a write operation will return only after * writing all of the r requested bytes. Some types of channels, * depending upon their state, may write only some of the bytes or possibly - * none at all. A socket channel in non-blocking mode, for example, cannot - * write any more bytes than are free in the socket's output buffer. + * none at all. A socket channel in {@linkplain + * SelectableChannel#isBlocking non-blocking mode}, for example, cannot + * write any more bytes than are free in the socket's output buffer. The + * write method may need to be invoked more than once to ensure that all + * {@linkplain ByteBuffer#hasRemaining remaining} bytes are written. * *

This method may be invoked at any time. If another thread has * already initiated a write operation upon this channel, however, then an diff --git a/src/java.base/share/classes/java/nio/channels/WritableByteChannel.java b/src/java.base/share/classes/java/nio/channels/WritableByteChannel.java index ef8efa5037c42..5284c72b37b00 100644 --- a/src/java.base/share/classes/java/nio/channels/WritableByteChannel.java +++ b/src/java.base/share/classes/java/nio/channels/WritableByteChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -65,11 +65,14 @@ public interface WritableByteChannel * Upon return the buffer's position will be equal to * p {@code +} n; its limit will not have changed. * - *

Unless otherwise specified, a write operation will return only after + *

For many types of channels, a write operation will return only after * writing all of the r requested bytes. Some types of channels, * depending upon their state, may write only some of the bytes or possibly - * none at all. A socket channel in non-blocking mode, for example, cannot - * write any more bytes than are free in the socket's output buffer. + * none at all. A socket channel in {@linkplain + * SelectableChannel#isBlocking non-blocking mode}, for example, cannot + * write any more bytes than are free in the socket's output buffer. The + * write method may need to be invoked more than once to ensure that all + * {@linkplain ByteBuffer#hasRemaining remaining} bytes are written. * *

This method may be invoked at any time. If another thread has * already initiated a write operation upon this channel, however, then an diff --git a/src/java.base/share/classes/java/time/Duration.java b/src/java.base/share/classes/java/time/Duration.java index 23577a8a63495..7b3289a1f5957 100644 --- a/src/java.base/share/classes/java/time/Duration.java +++ b/src/java.base/share/classes/java/time/Duration.java @@ -138,6 +138,37 @@ public final class Duration * Constant for a duration of zero. */ public static final Duration ZERO = new Duration(0, 0); + /** + * The minimum supported {@code Duration}, which is {@link Long#MIN_VALUE} + * seconds. + * + * @apiNote This constant represents the smallest possible instance of + * {@code Duration}. Since {@code Duration} is directed, the smallest + * possible duration is negative. + * + * The constant is intended to be used as a sentinel value or in tests. + * Care should be taken when performing arithmetic on {@code MIN} as there + * is a high risk that {@link ArithmeticException} or {@link DateTimeException} + * will be thrown. + * + * @since 26 + */ + public static final Duration MIN = new Duration(Long.MIN_VALUE, 0); + /** + * The maximum supported {@code Duration}, which is {@link Long#MAX_VALUE} + * seconds and {@code 999,999,999} nanoseconds. + * + * @apiNote This constant represents the largest possible instance of + * {@code Duration}. + * + * The constant is intended to be used as a sentinel value or in tests. + * Care should be taken when performing arithmetic on {@code MAX} as there + * is a high risk that {@link ArithmeticException} or {@link DateTimeException} + * will be thrown. + * + * @since 26 + */ + public static final Duration MAX = new Duration(Long.MAX_VALUE, 999_999_999); /** * Serialization version. */ diff --git a/src/java.base/share/classes/java/time/temporal/ChronoUnit.java b/src/java.base/share/classes/java/time/temporal/ChronoUnit.java index 8f94e061d4d48..6e944b296daa8 100644 --- a/src/java.base/share/classes/java/time/temporal/ChronoUnit.java +++ b/src/java.base/share/classes/java/time/temporal/ChronoUnit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -184,10 +184,9 @@ public enum ChronoUnit implements TemporalUnit { * Artificial unit that represents the concept of forever. * This is primarily used with {@link TemporalField} to represent unbounded fields * such as the year or era. - * The estimated duration of this unit is artificially defined as the largest duration - * supported by {@link Duration}. + * The estimated duration of this unit is artificially defined as {@link Duration#MAX}. */ - FOREVER("Forever", Duration.ofSeconds(Long.MAX_VALUE, 999_999_999)); + FOREVER("Forever", Duration.MAX); private final String name; private final Duration duration; diff --git a/src/java.base/share/classes/sun/security/util/DerValue.java b/src/java.base/share/classes/sun/security/util/DerValue.java index 19e7083180b2e..ec8b482b07dc3 100644 --- a/src/java.base/share/classes/sun/security/util/DerValue.java +++ b/src/java.base/share/classes/sun/security/util/DerValue.java @@ -859,6 +859,22 @@ public String getUniversalString() throws IOException { return readStringInternal(tag_UniversalString, new UTF_32BE()); } + /** + * Checks that the BMPString does not contain any surrogate characters, + * which are outside the Basic Multilingual Plane. + * + * @throws IOException if illegal characters are detected + */ + public void validateBMPString() throws IOException { + String bmpString = getBMPString(); + for (int i = 0; i < bmpString.length(); i++) { + if (Character.isSurrogate(bmpString.charAt(i))) { + throw new IOException( + "Illegal character in BMPString, index: " + i); + } + } + } + /** * Reads the ASN.1 NULL value */ diff --git a/src/java.base/share/classes/sun/security/x509/AVA.java b/src/java.base/share/classes/sun/security/x509/AVA.java index 915421c76f2f4..214ae71828846 100644 --- a/src/java.base/share/classes/sun/security/x509/AVA.java +++ b/src/java.base/share/classes/sun/security/x509/AVA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -28,10 +28,13 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.Reader; +import java.nio.charset.Charset; import java.text.Normalizer; import java.util.*; +import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.nio.charset.StandardCharsets.UTF_16BE; import sun.security.util.*; import sun.security.pkcs.PKCS9Attribute; @@ -589,6 +592,10 @@ private static boolean trailingSpace(Reader in) throws IOException { throw new IOException("AVA, extra bytes = " + derval.data.available()); } + + if (value.tag == DerValue.tag_BMPString) { + value.validateBMPString(); + } } AVA(DerInputStream in) throws IOException { @@ -713,7 +720,8 @@ public String toRFC2253String(Map oidMap) { * NOTE: this implementation only emits DirectoryStrings of the * types returned by isDerString(). */ - String valStr = new String(value.getDataBytes(), UTF_8); + String valStr = + new String(value.getDataBytes(), getCharset(value, false)); /* * 2.4 (cont): If the UTF-8 string does not have any of the @@ -832,7 +840,8 @@ public String toRFC2253CanonicalString() { * NOTE: this implementation only emits DirectoryStrings of the * types returned by isDerString(). */ - String valStr = new String(value.getDataBytes(), UTF_8); + String valStr = + new String(value.getDataBytes(), getCharset(value, true)); /* * 2.4 (cont): If the UTF-8 string does not have any of the @@ -927,6 +936,39 @@ private static boolean isDerString(DerValue value, boolean canonical) { } } + /* + * Returns the charset that should be used to decode each DN string type. + * + * This method ensures that multi-byte (UTF8String and BMPString) types + * are decoded using the correct charset and the String forms represent + * the correct characters. For 8-bit ASCII-based types (PrintableString + * and IA5String), we return ISO_8859_1 rather than ASCII, so that the + * complete range of characters can be represented, as many certificates + * do not comply with the Internationalized Domain Name ACE format. + * + * NOTE: this method only supports DirectoryStrings of the types returned + * by isDerString(). + */ + private static Charset getCharset(DerValue value, boolean canonical) { + if (canonical) { + return switch (value.tag) { + case DerValue.tag_PrintableString -> ISO_8859_1; + case DerValue.tag_UTF8String -> UTF_8; + default -> throw new Error("unexpected tag: " + value.tag); + }; + } + + return switch (value.tag) { + case DerValue.tag_PrintableString, + DerValue.tag_T61String, + DerValue.tag_IA5String, + DerValue.tag_GeneralString -> ISO_8859_1; + case DerValue.tag_BMPString -> UTF_16BE; + case DerValue.tag_UTF8String -> UTF_8; + default -> throw new Error("unexpected tag: " + value.tag); + }; + } + boolean hasRFC2253Keyword() { return AVAKeyword.hasKeyword(oid, RFC2253); } diff --git a/src/java.base/share/classes/sun/security/x509/AlgorithmId.java b/src/java.base/share/classes/sun/security/x509/AlgorithmId.java index 7d525a9add7bf..8d2c761a011cf 100644 --- a/src/java.base/share/classes/sun/security/x509/AlgorithmId.java +++ b/src/java.base/share/classes/sun/security/x509/AlgorithmId.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -127,10 +127,35 @@ public AlgorithmId(ObjectIdentifier oid, AlgorithmParameters algparams) { public AlgorithmId(ObjectIdentifier oid, DerValue params) throws IOException { this.algid = oid; - if (params != null) { - encodedParams = params.toByteArray(); - decodeParams(); + + if (params == null) { + this.encodedParams = null; + this.algParams = null; + return; + } + + /* + * If the parameters field explicitly contains an ASN.1 NULL, treat it as + * "no parameters" rather than storing a literal NULL encoding. + * + * This canonicalization ensures consistent encoding/decoding behavior: + * - Algorithms that omit parameters and those that encode explicit NULL + * are treated equivalently (encodedParams == null). + */ + if (params.tag == DerValue.tag_Null) { + if (params.length() != 0) { + throw new IOException("Invalid ASN.1 NULL in AlgorithmId parameters: " + + "non-zero length"); + } + // Canonicalize to "no parameters" representation for consistency + this.encodedParams = null; + this.algParams = null; + return; } + + // Normal case: non-NULL params -> store and decode + this.encodedParams = params.toByteArray(); + decodeParams(); } protected void decodeParams() throws IOException { @@ -163,38 +188,10 @@ public void encode(DerOutputStream out) { bytes.putOID(algid); if (encodedParams == null) { - // MessageDigest algorithms usually have a NULL parameters even - // if most RFCs suggested absent. - // RSA key and signature algorithms requires the NULL parameters - // to be present, see A.1 and A.2.4 of RFC 8017. - if (algid.equals(RSAEncryption_oid) - || algid.equals(MD2_oid) - || algid.equals(MD5_oid) - || algid.equals(SHA_oid) - || algid.equals(SHA224_oid) - || algid.equals(SHA256_oid) - || algid.equals(SHA384_oid) - || algid.equals(SHA512_oid) - || algid.equals(SHA512_224_oid) - || algid.equals(SHA512_256_oid) - || algid.equals(SHA3_224_oid) - || algid.equals(SHA3_256_oid) - || algid.equals(SHA3_384_oid) - || algid.equals(SHA3_512_oid) - || algid.equals(SHA1withRSA_oid) - || algid.equals(SHA224withRSA_oid) - || algid.equals(SHA256withRSA_oid) - || algid.equals(SHA384withRSA_oid) - || algid.equals(SHA512withRSA_oid) - || algid.equals(SHA512$224withRSA_oid) - || algid.equals(SHA512$256withRSA_oid) - || algid.equals(MD2withRSA_oid) - || algid.equals(MD5withRSA_oid) - || algid.equals(SHA3_224withRSA_oid) - || algid.equals(SHA3_256withRSA_oid) - || algid.equals(SHA3_384withRSA_oid) - || algid.equals(SHA3_512withRSA_oid)) { + if (OIDS_REQUIRING_NULL.contains(algid.toString())) { bytes.putNull(); + } else { + // Parameters omitted } } else { bytes.writeBytes(encodedParams); @@ -646,30 +643,54 @@ private static ConcurrentHashMap collectOIDAliases() { public static final ObjectIdentifier MGF1_oid = ObjectIdentifier.of(KnownOIDs.MGF1); - public static final ObjectIdentifier SHA1withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA1withRSA); - public static final ObjectIdentifier SHA224withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA224withRSA); - public static final ObjectIdentifier SHA256withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA256withRSA); - public static final ObjectIdentifier SHA384withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA384withRSA); - public static final ObjectIdentifier SHA512withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA512withRSA); - public static final ObjectIdentifier SHA512$224withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA512$224withRSA); - public static final ObjectIdentifier SHA512$256withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA512$256withRSA); - public static final ObjectIdentifier MD2withRSA_oid = - ObjectIdentifier.of(KnownOIDs.MD2withRSA); - public static final ObjectIdentifier MD5withRSA_oid = - ObjectIdentifier.of(KnownOIDs.MD5withRSA); - public static final ObjectIdentifier SHA3_224withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA3_224withRSA); - public static final ObjectIdentifier SHA3_256withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA3_256withRSA); - public static final ObjectIdentifier SHA3_384withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA3_384withRSA); - public static final ObjectIdentifier SHA3_512withRSA_oid = - ObjectIdentifier.of(KnownOIDs.SHA3_512withRSA); + /* Set of OIDs that must explicitly encode a NULL parameter in AlgorithmIdentifier. + * References: + - RFC 8017 (PKCS #1) ยงA.1, ยงA.2.4: RSA key and signature algorithms + - RFC 9879 (HMAC) ยง4: HMAC algorithm identifiers + - RFC 9688 (HMAC with SHA-3) ยง4.3: HMAC-SHA3 algorithms MUST omit parameters + */ + private static final Set OIDS_REQUIRING_NULL = Set.of( + // MessageDigest algorithms usually have a NULL parameters even + // if most RFCs suggested absent. + KnownOIDs.MD2.value(), + KnownOIDs.MD5.value(), + KnownOIDs.SHA_1.value(), + KnownOIDs.SHA_224.value(), + KnownOIDs.SHA_256.value(), + KnownOIDs.SHA_384.value(), + KnownOIDs.SHA_512.value(), + KnownOIDs.SHA_512$224.value(), + KnownOIDs.SHA_512$256.value(), + KnownOIDs.SHA3_224.value(), + KnownOIDs.SHA3_256.value(), + KnownOIDs.SHA3_384.value(), + KnownOIDs.SHA3_512.value(), + + //--- RSA key and signature algorithms (RFC 8017 ยงA.1, ยงA.2.4) + KnownOIDs.RSA.value(), + KnownOIDs.SHA1withRSA.value(), + KnownOIDs.SHA224withRSA.value(), + KnownOIDs.SHA256withRSA.value(), + KnownOIDs.SHA384withRSA.value(), + KnownOIDs.SHA512withRSA.value(), + KnownOIDs.SHA512$224withRSA.value(), + KnownOIDs.SHA512$256withRSA.value(), + KnownOIDs.MD2withRSA.value(), + KnownOIDs.MD5withRSA.value(), + KnownOIDs.SHA3_224withRSA.value(), + KnownOIDs.SHA3_256withRSA.value(), + KnownOIDs.SHA3_384withRSA.value(), + KnownOIDs.SHA3_512withRSA.value(), + + // HMACs per RFC 9879 (Section 4): these require explicit NULL parameters + // Note: HMAC-SHA3 algorithms (RFC 9688 ยง4.3) MUST omit parameters, + // so they are intentionally excluded from this list. + KnownOIDs.HmacSHA1.value(), + KnownOIDs.HmacSHA224.value(), + KnownOIDs.HmacSHA256.value(), + KnownOIDs.HmacSHA384.value(), + KnownOIDs.HmacSHA512.value(), + KnownOIDs.HmacSHA512$224.value(), + KnownOIDs.HmacSHA512$256.value() + ); } diff --git a/src/java.base/share/native/libverify/check_code.c b/src/java.base/share/native/libverify/check_code.c index 7266ac8f93c26..32df102dcb3a9 100644 --- a/src/java.base/share/native/libverify/check_code.c +++ b/src/java.base/share/native/libverify/check_code.c @@ -395,7 +395,8 @@ static jboolean is_superclass(context_type *, fullinfo_type); static void initialize_exception_table(context_type *); static int instruction_length(unsigned char *iptr, unsigned char *end); -static jboolean isLegalTarget(context_type *, int offset); +static jboolean isLegalOffset(context_type *, int bci, int offset); +static jboolean isLegalTarget(context_type *, int target); static void verify_constant_pool_type(context_type *, int, unsigned); static void initialize_dataflow(context_type *); @@ -1154,9 +1155,9 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) case JVM_OPC_goto: { /* Set the ->operand to be the instruction number of the target. */ int jump = (((signed char)(code[offset+1])) << 8) + code[offset+2]; - int target = offset + jump; - if (!isLegalTarget(context, target)) + if (!isLegalOffset(context, offset, jump)) CCerror(context, "Illegal target of jump or branch"); + int target = offset + jump; this_idata->operand.i = code_data[target]; break; } @@ -1170,9 +1171,9 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) int jump = (((signed char)(code[offset+1])) << 24) + (code[offset+2] << 16) + (code[offset+3] << 8) + (code[offset + 4]); - int target = offset + jump; - if (!isLegalTarget(context, target)) + if (!isLegalOffset(context, offset, jump)) CCerror(context, "Illegal target of jump or branch"); + int target = offset + jump; this_idata->operand.i = code_data[target]; break; } @@ -1211,13 +1212,16 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) } } saved_operand = NEW(int, keys + 2); - if (!isLegalTarget(context, offset + _ck_ntohl(lpc[0]))) + int jump = _ck_ntohl(lpc[0]); + if (!isLegalOffset(context, offset, jump)) CCerror(context, "Illegal default target in switch"); - saved_operand[keys + 1] = code_data[offset + _ck_ntohl(lpc[0])]; + int target = offset + jump; + saved_operand[keys + 1] = code_data[target]; for (k = keys, lptr = &lpc[3]; --k >= 0; lptr += delta) { - int target = offset + _ck_ntohl(lptr[0]); - if (!isLegalTarget(context, target)) + jump = _ck_ntohl(lptr[0]); + if (!isLegalOffset(context, offset, jump)) CCerror(context, "Illegal branch in tableswitch"); + target = offset + jump; saved_operand[k + 1] = code_data[target]; } saved_operand[0] = keys + 1; /* number of successors */ @@ -1746,11 +1750,24 @@ static int instruction_length(unsigned char *iptr, unsigned char *end) /* Given the target of a branch, make sure that it's a legal target. */ static jboolean -isLegalTarget(context_type *context, int offset) +isLegalTarget(context_type *context, int target) +{ + int code_length = context->code_length; + int *code_data = context->code_data; + return (target >= 0 && target < code_length && code_data[target] >= 0); +} + +/* Given a bci and offset, make sure the offset is valid and the target is legal */ +static jboolean +isLegalOffset(context_type *context, int bci, int offset) { int code_length = context->code_length; int *code_data = context->code_data; - return (offset >= 0 && offset < code_length && code_data[offset] >= 0); + int max_offset = 65535; // JVMS 4.11 + int min_offset = -65535; + if (offset < min_offset || offset > max_offset) return JNI_FALSE; + int target = bci + offset; + return (target >= 0 && target < code_length && code_data[target] >= 0); } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java index f28ae2a93264b..3a2578b3e0bc4 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java @@ -76,19 +76,20 @@ protected void paintBackground(Graphics g, JMenuItem menuItem, super.paintBackground(g, menuItem, bgColor); } - /** - * Paint MenuItem. - */ + @Override protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon, Icon arrowIcon, Color background, Color foreground, int defaultTextIconGap) { if (WindowsMenuItemUI.isVistaPainting()) { - WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, - arrowIcon, background, foreground, - disabledForeground, acceleratorSelectionForeground, - acceleratorForeground, defaultTextIconGap, - menuItem, getPropertyPrefix()); + WindowsMenuItemUI.paintMenuItem(accessor, g, c, + checkIcon, arrowIcon, + background, foreground, + disabledForeground, + acceleratorSelectionForeground, + acceleratorForeground, + defaultTextIconGap, + menuItem, getPropertyPrefix()); return; } super.paintMenuItem(g, c, checkIcon, arrowIcon, background, diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java index a9b09085ad1a6..041bdb5adaaf5 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java @@ -29,16 +29,11 @@ import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; -import java.awt.Insets; import java.awt.Rectangle; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.util.Enumeration; -import javax.swing.AbstractButton; -import javax.swing.ButtonGroup; import javax.swing.ButtonModel; -import javax.swing.DefaultButtonModel; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JMenu; @@ -132,27 +127,6 @@ public void propertyChange(PropertyChangeEvent e) { menuItem.addPropertyChangeListener(changeListener); } - protected void installDefaults() { - super.installDefaults(); - String prefix = getPropertyPrefix(); - - if (acceleratorSelectionForeground == null || - acceleratorSelectionForeground instanceof UIResource) { - acceleratorSelectionForeground = - UIManager.getColor(prefix + ".acceleratorSelectionForeground"); - } - if (acceleratorForeground == null || - acceleratorForeground instanceof UIResource) { - acceleratorForeground = - UIManager.getColor(prefix + ".acceleratorForeground"); - } - if (disabledForeground == null || - disabledForeground instanceof UIResource) { - disabledForeground = - UIManager.getColor(prefix + ".disabledForeground"); - } - } - /** * {@inheritDoc} */ @@ -165,15 +139,19 @@ protected void uninstallListeners() { changeListener = null; } + @Override protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon, Icon arrowIcon, Color background, Color foreground, int defaultTextIconGap) { if (WindowsMenuItemUI.isVistaPainting()) { - WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, - arrowIcon, background, foreground, - disabledForeground, acceleratorSelectionForeground, - acceleratorForeground, defaultTextIconGap, menuItem, + WindowsMenuItemUI.paintMenuItem(accessor, g, c, + checkIcon, arrowIcon, + background, foreground, + disabledForeground, + acceleratorSelectionForeground, + acceleratorForeground, + defaultTextIconGap, menuItem, getPropertyPrefix()); return; } @@ -182,12 +160,16 @@ protected void paintMenuItem(Graphics g, JComponent c, } static void paintMenuItem(WindowsMenuItemUIAccessor accessor, Graphics g, - JComponent c, Icon checkIcon, Icon arrowIcon, + JComponent c, + Icon checkIcon, Icon arrowIcon, Color background, Color foreground, Color disabledForeground, Color acceleratorSelectionForeground, Color acceleratorForeground, - int defaultTextIconGap, JMenuItem menuItem, String prefix) { + int defaultTextIconGap, JMenuItem menuItem, + String prefix) { + assert c == menuItem : "menuItem passed as 'c' must be the same"; + // Save original graphics font and color Font holdf = g.getFont(); Color holdc = g.getColor(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java index 754b394d4ac11..130b09227cc20 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java @@ -131,18 +131,20 @@ protected void installDefaults() { hotTrackingOn = (obj instanceof Boolean) ? (Boolean)obj : true; } - /** - * Paint MenuItem. - */ + @Override protected void paintMenuItem(Graphics g, JComponent c, - Icon checkIcon, Icon arrowIcon, - Color background, Color foreground, - int defaultTextIconGap) { + Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap) { + assert c == menuItem : "menuItem passed as 'c' must be the same"; if (WindowsMenuItemUI.isVistaPainting()) { - WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, arrowIcon, + WindowsMenuItemUI.paintMenuItem(accessor, g, c, + checkIcon, arrowIcon, background, foreground, - disabledForeground, acceleratorSelectionForeground, - acceleratorForeground, defaultTextIconGap, menuItem, + disabledForeground, + acceleratorSelectionForeground, + acceleratorForeground, + defaultTextIconGap, menuItem, getPropertyPrefix()); return; } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java index 06ef5db23a1b2..78768c29ab3b7 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java @@ -76,19 +76,20 @@ protected void paintBackground(Graphics g, JMenuItem menuItem, super.paintBackground(g, menuItem, bgColor); } - /** - * Paint MenuItem. - */ + @Override protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon, Icon arrowIcon, Color background, Color foreground, int defaultTextIconGap) { if (WindowsMenuItemUI.isVistaPainting()) { - WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, - arrowIcon, background, foreground, - disabledForeground, acceleratorSelectionForeground, - acceleratorForeground, defaultTextIconGap, - menuItem, getPropertyPrefix()); + WindowsMenuItemUI.paintMenuItem(accessor, g, c, + checkIcon, arrowIcon, + background, foreground, + disabledForeground, + acceleratorSelectionForeground, + acceleratorForeground, + defaultTextIconGap, + menuItem, getPropertyPrefix()); return; } super.paintMenuItem(g, c, checkIcon, arrowIcon, background, diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java index b14d76d8dbadb..a02506cff5c76 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java @@ -174,15 +174,20 @@ private static boolean hostnameVerificationDisabledValue() { public static final int SLICE_THRESHOLD = 32; /** - * Allocated buffer size. Must never be higher than 16K. But can be lower - * if smaller allocation units preferred. HTTP/2 mandates that all - * implementations support frame payloads of at least 16K. + * The capacity of ephemeral {@link ByteBuffer}s allocated to pass data to and from the client. + * It is ensured to have a value between 1 and 2^14 (16,384). */ - private static final int DEFAULT_BUFSIZE = 16 * 1024; - public static final int BUFSIZE = getIntegerNetProperty( - "jdk.httpclient.bufsize", DEFAULT_BUFSIZE - ); + "jdk.httpclient.bufsize", 1, + // We cap at 2^14 (16,384) for two main reasons: + // - The initial frame size is 2^14 (RFC 9113) + // - SSL record layer fragments data in chunks of 2^14 bytes or less (RFC 5246) + 1 << 14, + // We choose 2^14 (16,384) as the default, because: + // 1. It maximizes throughput within the limits described above + // 2. It is small enough to not create a GC bottleneck when it is partially filled + 1 << 14, + true); public static final BiPredicate ACCEPT_ALL = (x,y) -> true; diff --git a/src/java.net.http/share/classes/module-info.java b/src/java.net.http/share/classes/module-info.java index 392385136b084..48f23953ad088 100644 --- a/src/java.net.http/share/classes/module-info.java +++ b/src/java.net.http/share/classes/module-info.java @@ -48,7 +48,9 @@ * depending on the context. These restrictions cannot be overridden by this property. * *

  • {@systemProperty jdk.httpclient.bufsize} (default: 16384 bytes or 16 kB)
    - * The size to use for internal allocated buffers in bytes. + * The capacity of internal ephemeral buffers allocated to pass data to and from the + * client, in bytes. Valid values are in the range [1, 2^14 (16384)]. + * If an invalid value is provided, the default value is used. *

  • *
  • {@systemProperty jdk.httpclient.connectionPoolSize} (default: 0)
    * The maximum number of connections to keep in the HTTP/1.1 keep alive cache. A value of 0 diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/DocumentBuilderFactoryImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/DocumentBuilderFactoryImpl.java index 385b8e294395d..bc8e93b4f0b92 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/DocumentBuilderFactoryImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/DocumentBuilderFactoryImpl.java @@ -41,7 +41,7 @@ /** * @author Rajiv Mordani * @author Edwin Goei - * @LastModified: May 2025 + * @LastModified: June 2025 */ public class DocumentBuilderFactoryImpl extends DocumentBuilderFactory { /** These are DocumentBuilderFactory attributes not DOM attributes */ @@ -59,11 +59,24 @@ public class DocumentBuilderFactoryImpl extends DocumentBuilderFactory { XMLSecurityManager fSecurityManager; XMLSecurityPropertyManager fSecurityPropertyMgr; + /** + * Creates a new {@code DocumentBuilderFactory} instance. + */ public DocumentBuilderFactoryImpl() { + this(null, null); + } + + /** + * Creates a new {@code DocumentBuilderFactory} instance with a {@code XMLSecurityManager} + * and {@code XMLSecurityPropertyManager}. + * @param xsm the {@code XMLSecurityManager} + * @param xspm the {@code XMLSecurityPropertyManager} + */ + public DocumentBuilderFactoryImpl(XMLSecurityManager xsm, XMLSecurityPropertyManager xspm) { JdkXmlConfig config = JdkXmlConfig.getInstance(false); // security (property) managers updated with current system properties - fSecurityManager = config.getXMLSecurityManager(true); - fSecurityPropertyMgr = config.getXMLSecurityPropertyManager(true); + fSecurityManager = (xsm == null) ? config.getXMLSecurityManager(true) : xsm; + fSecurityPropertyMgr = (xspm == null) ? config.getXMLSecurityPropertyManager(true) : xspm; } /** diff --git a/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java index 1288f1dbac32a..2f4d2ade54530 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java @@ -35,7 +35,7 @@ * * @author Ramesh Mandava * - * @LastModified: May 2025 + * @LastModified: June 2025 */ public class XPathFactoryImpl extends XPathFactory { @@ -72,6 +72,7 @@ public class XPathFactoryImpl extends XPathFactory { * The XML security manager */ private XMLSecurityManager _xmlSecMgr; + private XMLSecurityPropertyManager _xmlSecPropMgr; /** * javax.xml.xpath.XPathFactory implementation. @@ -80,6 +81,7 @@ public XPathFactoryImpl() { JdkXmlConfig config = JdkXmlConfig.getInstance(false); _xmlSecMgr = config.getXMLSecurityManager(true); _featureManager = config.getXMLFeatures(true); + _xmlSecPropMgr = config.getXMLSecurityPropertyManager(true); } /** @@ -129,7 +131,7 @@ public boolean isObjectModelSupported(String objectModel) { */ public javax.xml.xpath.XPath newXPath() { return new XPathImpl(xPathVariableResolver, xPathFunctionResolver, - !_isNotSecureProcessing, _featureManager, _xmlSecMgr); + !_isNotSecureProcessing, _featureManager, _xmlSecMgr, _xmlSecPropMgr); } /** @@ -183,6 +185,7 @@ public void setFeature(String name, boolean value) if (value && _featureManager != null) { _featureManager.setFeature(JdkXmlFeatures.XmlFeature.ENABLE_EXTENSION_FUNCTION, JdkProperty.State.FSP, false); + _xmlSecMgr.setSecureProcessing(value); } // all done processing feature @@ -338,8 +341,7 @@ public void setProperty(String name, String value) { throw new NullPointerException(fmsg); } - if (_xmlSecMgr != null && - _xmlSecMgr.setLimit(name, JdkProperty.State.APIPROPERTY, value)) { + if (JdkXmlUtils.setProperty(_xmlSecMgr, _xmlSecPropMgr, name, value)) { return; } diff --git a/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java index 53099ad078ec4..c2faf90ce2e1e 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java @@ -36,6 +36,7 @@ import jdk.xml.internal.JdkXmlConfig; import jdk.xml.internal.JdkXmlFeatures; import jdk.xml.internal.XMLSecurityManager; +import jdk.xml.internal.XMLSecurityPropertyManager; import org.w3c.dom.Document; import org.xml.sax.InputSource; @@ -50,7 +51,7 @@ * New methods: evaluateExpression * Refactored to share code with XPathExpressionImpl. * - * @LastModified: May 2025 + * @LastModified: June 2025 */ public class XPathImpl extends XPathImplUtil implements javax.xml.xpath.XPath { @@ -62,12 +63,13 @@ public class XPathImpl extends XPathImplUtil implements javax.xml.xpath.XPath { XPathImpl(XPathVariableResolver vr, XPathFunctionResolver fr) { this(vr, fr, false, JdkXmlConfig.getInstance(false).getXMLFeatures(false), - JdkXmlConfig.getInstance(false).getXMLSecurityManager(false)); + JdkXmlConfig.getInstance(false).getXMLSecurityManager(false), + JdkXmlConfig.getInstance(false).getXMLSecurityPropertyManager(false)); } XPathImpl(XPathVariableResolver vr, XPathFunctionResolver fr, boolean featureSecureProcessing, JdkXmlFeatures featureManager, - XMLSecurityManager xmlSecMgr) { + XMLSecurityManager xmlSecMgr, XMLSecurityPropertyManager xmlSecPropMgr) { this.origVariableResolver = this.variableResolver = vr; this.origFunctionResolver = this.functionResolver = fr; this.featureSecureProcessing = featureSecureProcessing; @@ -75,6 +77,7 @@ public class XPathImpl extends XPathImplUtil implements javax.xml.xpath.XPath { overrideDefaultParser = featureManager.getFeature( JdkXmlFeatures.XmlFeature.JDK_OVERRIDE_PARSER); this.xmlSecMgr = xmlSecMgr; + this.xmlSecPropMgr = xmlSecPropMgr; } diff --git a/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java b/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java index a92090900facb..3de72f3f68bb2 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java @@ -31,6 +31,7 @@ import com.sun.org.apache.xpath.internal.objects.XObject; import com.sun.org.apache.xpath.internal.res.XPATHErrorResources; import java.io.IOException; +import javax.xml.XMLConstants; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -44,6 +45,7 @@ import jdk.xml.internal.JdkXmlFeatures; import jdk.xml.internal.JdkXmlUtils; import jdk.xml.internal.XMLSecurityManager; +import jdk.xml.internal.XMLSecurityPropertyManager; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.traversal.NodeIterator; @@ -54,7 +56,7 @@ * This class contains several utility methods used by XPathImpl and * XPathExpressionImpl * - * @LastModified: Apr 2025 + * @LastModified: June 2025 */ class XPathImplUtil { XPathFunctionResolver functionResolver; @@ -67,6 +69,7 @@ class XPathImplUtil { boolean featureSecureProcessing = false; JdkXmlFeatures featureManager; XMLSecurityManager xmlSecMgr; + XMLSecurityPropertyManager xmlSecPropMgr; /** * Evaluate an XPath context using the internal XPath engine @@ -128,7 +131,12 @@ Document getDocument(InputSource source) // // so we really have to create a fresh DocumentBuilder every time we need one // - KK - DocumentBuilderFactory dbf = JdkXmlUtils.getDOMFactory(overrideDefaultParser); + DocumentBuilderFactory dbf = JdkXmlUtils.getDOMFactory( + overrideDefaultParser, xmlSecMgr, xmlSecPropMgr); + if (xmlSecMgr != null && xmlSecMgr.isSecureProcessingSet()) { + dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, + xmlSecMgr.isSecureProcessing()); + } return dbf.newDocumentBuilder().parse(source); } catch (ParserConfigurationException | SAXException | IOException e) { throw new XPathExpressionException (e); diff --git a/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java b/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java index 93b63a746f15e..9e718b264e447 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java +++ b/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java @@ -445,6 +445,20 @@ public static Document getDOMDocument() { * @return a DocumentBuilderFactory instance. */ public static DocumentBuilderFactory getDOMFactory(boolean overrideDefaultParser) { + return getDOMFactory(overrideDefaultParser, null, null); + } + + /** + * {@return a DocumentBuilderFactory instance} + * + * @param overrideDefaultParser a flag indicating whether the system-default + * implementation may be overridden. If the system property of the + * DOM factory ID is set, override is always allowed. + * @param xsm XMLSecurityManager + * @param xspm XMLSecurityPropertyManager + */ + public static DocumentBuilderFactory getDOMFactory(boolean overrideDefaultParser, + XMLSecurityManager xsm, XMLSecurityPropertyManager xspm) { boolean override = overrideDefaultParser; String spDOMFactory = SecuritySupport.getJAXPSystemProperty(DOM_FACTORY_ID); @@ -453,7 +467,7 @@ public static DocumentBuilderFactory getDOMFactory(boolean overrideDefaultParser } DocumentBuilderFactory dbf = !override - ? new DocumentBuilderFactoryImpl() + ? new DocumentBuilderFactoryImpl(xsm, xspm) : DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); // false is the default setting. This step here is for compatibility diff --git a/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java b/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java index 5ca4073e20f2f..a1687c420c3ff 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java +++ b/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java @@ -244,6 +244,12 @@ public static enum Processor { */ boolean secureProcessing; + /** + * Flag indicating the secure processing is set explicitly through factories' + * setFeature method and then the setSecureProcessing method + */ + boolean secureProcessingSet; + /** * States that determine if properties are set explicitly */ @@ -340,6 +346,7 @@ private NotFoundAction toActionType(String resolve) { * Setting FEATURE_SECURE_PROCESSING explicitly */ public void setSecureProcessing(boolean secure) { + secureProcessingSet = true; secureProcessing = secure; for (Limit limit : Limit.values()) { if (secure) { @@ -358,6 +365,15 @@ public boolean isSecureProcessing() { return secureProcessing; } + /** + * Returns the state indicating whether the Secure Processing is set explicitly, + * via factories' setFeature and then this class' setSecureProcessing method. + * @return the state indicating whether the Secure Processing is set explicitly + */ + public boolean isSecureProcessingSet() { + return secureProcessingSet; + } + /** * Finds a limit's new name with the given property name. * @param propertyName the property name specified diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ExhaustivenessComputer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ExhaustivenessComputer.java new file mode 100644 index 0000000000000..7a8067ce983f8 --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ExhaustivenessComputer.java @@ -0,0 +1,604 @@ +/* + * Copyright (c) 1999, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.tools.javac.comp; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import com.sun.tools.javac.code.*; +import com.sun.tools.javac.tree.*; +import com.sun.tools.javac.util.*; + +import com.sun.tools.javac.code.Symbol.*; +import com.sun.tools.javac.tree.JCTree.*; + +import com.sun.tools.javac.code.Kinds.Kind; +import com.sun.tools.javac.code.Type.TypeVar; +import java.util.Arrays; +import java.util.Iterator; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static java.util.stream.Collectors.groupingBy; + +/** A class to compute exhaustiveness of set of switch cases. + * + *

    This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public class ExhaustivenessComputer { + protected static final Context.Key exhaustivenessKey = new Context.Key<>(); + + private final Symtab syms; + private final Types types; + private final Check chk; + private final Infer infer; + + public static ExhaustivenessComputer instance(Context context) { + ExhaustivenessComputer instance = context.get(exhaustivenessKey); + if (instance == null) + instance = new ExhaustivenessComputer(context); + return instance; + } + + @SuppressWarnings("this-escape") + protected ExhaustivenessComputer(Context context) { + context.put(exhaustivenessKey, this); + syms = Symtab.instance(context); + types = Types.instance(context); + chk = Check.instance(context); + infer = Infer.instance(context); + } + + public boolean exhausts(JCExpression selector, List cases) { + Set patternSet = new HashSet<>(); + Map> enum2Constants = new HashMap<>(); + Set booleanLiterals = new HashSet<>(Set.of(0, 1)); + for (JCCase c : cases) { + if (!TreeInfo.unguardedCase(c)) + continue; + + for (var l : c.labels) { + if (l instanceof JCPatternCaseLabel patternLabel) { + for (Type component : components(selector.type)) { + patternSet.add(makePatternDescription(component, patternLabel.pat)); + } + } else if (l instanceof JCConstantCaseLabel constantLabel) { + if (types.unboxedTypeOrType(selector.type).hasTag(TypeTag.BOOLEAN)) { + Object value = ((JCLiteral) constantLabel.expr).value; + booleanLiterals.remove(value); + } else { + Symbol s = TreeInfo.symbol(constantLabel.expr); + if (s != null && s.isEnum()) { + enum2Constants.computeIfAbsent(s.owner, x -> { + Set result = new HashSet<>(); + s.owner.members() + .getSymbols(sym -> sym.kind == Kind.VAR && sym.isEnum()) + .forEach(result::add); + return result; + }).remove(s); + } + } + } + } + } + + if (types.unboxedTypeOrType(selector.type).hasTag(TypeTag.BOOLEAN) && booleanLiterals.isEmpty()) { + return true; + } + + for (Entry> e : enum2Constants.entrySet()) { + if (e.getValue().isEmpty()) { + patternSet.add(new BindingPattern(e.getKey().type)); + } + } + Set patterns = patternSet; + boolean useHashes = true; + try { + boolean repeat = true; + while (repeat) { + Set updatedPatterns; + updatedPatterns = reduceBindingPatterns(selector.type, patterns); + updatedPatterns = reduceNestedPatterns(updatedPatterns, useHashes); + updatedPatterns = reduceRecordPatterns(updatedPatterns); + updatedPatterns = removeCoveredRecordPatterns(updatedPatterns); + repeat = !updatedPatterns.equals(patterns); + if (checkCovered(selector.type, patterns)) { + return true; + } + if (!repeat) { + //there may be situation like: + //class B permits S1, S2 + //patterns: R(S1, B), R(S2, S2) + //this might be joined to R(B, S2), as B could be rewritten to S2 + //but hashing in reduceNestedPatterns will not allow that + //disable the use of hashing, and use subtyping in + //reduceNestedPatterns to handle situations like this: + repeat = useHashes; + useHashes = false; + } else { + //if a reduction happened, make sure hashing in reduceNestedPatterns + //is enabled, as the hashing speeds up the process significantly: + useHashes = true; + } + patterns = updatedPatterns; + } + return checkCovered(selector.type, patterns); + } catch (CompletionFailure cf) { + chk.completionError(selector.pos(), cf); + return true; //error recovery + } + } + + private boolean checkCovered(Type seltype, Iterable patterns) { + for (Type seltypeComponent : components(seltype)) { + for (PatternDescription pd : patterns) { + if(isBpCovered(seltypeComponent, pd)) { + return true; + } + } + } + return false; + } + + private List components(Type seltype) { + return switch (seltype.getTag()) { + case CLASS -> { + if (seltype.isCompound()) { + if (seltype.isIntersection()) { + yield ((Type.IntersectionClassType) seltype).getComponents() + .stream() + .flatMap(t -> components(t).stream()) + .collect(List.collector()); + } + yield List.nil(); + } + yield List.of(types.erasure(seltype)); + } + case TYPEVAR -> components(((TypeVar) seltype).getUpperBound()); + default -> List.of(types.erasure(seltype)); + }; + } + + /* In a set of patterns, search for a sub-set of binding patterns that + * in combination exhaust their sealed supertype. If such a sub-set + * is found, it is removed, and replaced with a binding pattern + * for the sealed supertype. + */ + private Set reduceBindingPatterns(Type selectorType, Set patterns) { + Set existingBindings = patterns.stream() + .filter(pd -> pd instanceof BindingPattern) + .map(pd -> ((BindingPattern) pd).type.tsym) + .collect(Collectors.toSet()); + + for (PatternDescription pdOne : patterns) { + if (pdOne instanceof BindingPattern bpOne) { + Set toAdd = new HashSet<>(); + + for (Type sup : types.directSupertypes(bpOne.type)) { + ClassSymbol clazz = (ClassSymbol) types.erasure(sup).tsym; + + clazz.complete(); + + if (clazz.isSealed() && clazz.isAbstract() && + //if a binding pattern for clazz already exists, no need to analyze it again: + !existingBindings.contains(clazz)) { + ListBuffer bindings = new ListBuffer<>(); + //do not reduce to types unrelated to the selector type: + Type clazzErasure = types.erasure(clazz.type); + if (components(selectorType).stream() + .map(types::erasure) + .noneMatch(c -> types.isSubtype(clazzErasure, c))) { + continue; + } + + Set permitted = allPermittedSubTypes(clazz, csym -> { + Type instantiated; + if (csym.type.allparams().isEmpty()) { + instantiated = csym.type; + } else { + instantiated = infer.instantiatePatternType(selectorType, csym); + } + + return instantiated != null && types.isCastable(selectorType, instantiated); + }); + + for (PatternDescription pdOther : patterns) { + if (pdOther instanceof BindingPattern bpOther) { + Set currentPermittedSubTypes = + allPermittedSubTypes(bpOther.type.tsym, s -> true); + + PERMITTED: for (Iterator it = permitted.iterator(); it.hasNext();) { + Symbol perm = it.next(); + + for (Symbol currentPermitted : currentPermittedSubTypes) { + if (types.isSubtype(types.erasure(currentPermitted.type), + types.erasure(perm.type))) { + it.remove(); + continue PERMITTED; + } + } + if (types.isSubtype(types.erasure(perm.type), + types.erasure(bpOther.type))) { + it.remove(); + } + } + } + } + + if (permitted.isEmpty()) { + toAdd.add(new BindingPattern(clazz.type)); + } + } + } + + if (!toAdd.isEmpty()) { + Set newPatterns = new HashSet<>(patterns); + newPatterns.addAll(toAdd); + return newPatterns; + } + } + } + return patterns; + } + + private Set allPermittedSubTypes(TypeSymbol root, Predicate accept) { + Set permitted = new HashSet<>(); + List permittedSubtypesClosure = baseClasses(root); + + while (permittedSubtypesClosure.nonEmpty()) { + ClassSymbol current = permittedSubtypesClosure.head; + + permittedSubtypesClosure = permittedSubtypesClosure.tail; + + current.complete(); + + if (current.isSealed() && current.isAbstract()) { + for (Type t : current.getPermittedSubclasses()) { + ClassSymbol csym = (ClassSymbol) t.tsym; + + if (accept.test(csym)) { + permittedSubtypesClosure = permittedSubtypesClosure.prepend(csym); + permitted.add(csym); + } + } + } + } + + return permitted; + } + + private List baseClasses(TypeSymbol root) { + if (root instanceof ClassSymbol clazz) { + return List.of(clazz); + } else if (root instanceof TypeVariableSymbol tvar) { + ListBuffer result = new ListBuffer<>(); + for (Type bound : tvar.getBounds()) { + result.appendList(baseClasses(bound.tsym)); + } + return result.toList(); + } else { + return List.nil(); + } + } + + /* Among the set of patterns, find sub-set of patterns such: + * $record($prefix$, $nested, $suffix$) + * Where $record, $prefix$ and $suffix$ is the same for each pattern + * in the set, and the patterns only differ in one "column" in + * the $nested pattern. + * Then, the set of $nested patterns is taken, and passed recursively + * to reduceNestedPatterns and to reduceBindingPatterns, to + * simplify the pattern. If that succeeds, the original found sub-set + * of patterns is replaced with a new set of patterns of the form: + * $record($prefix$, $resultOfReduction, $suffix$) + * + * useHashes: when true, patterns will be subject to exact equivalence; + * when false, two binding patterns will be considered equivalent + * if one of them is more generic than the other one; + * when false, the processing will be significantly slower, + * as pattern hashes cannot be used to speed up the matching process + */ + private Set reduceNestedPatterns(Set patterns, + boolean useHashes) { + /* implementation note: + * finding a sub-set of patterns that only differ in a single + * column is time-consuming task, so this method speeds it up by: + * - group the patterns by their record class + * - for each column (nested pattern) do: + * -- group patterns by their hash + * -- in each such by-hash group, find sub-sets that only differ in + * the chosen column, and then call reduceBindingPatterns and reduceNestedPatterns + * on patterns in the chosen column, as described above + */ + var groupByRecordClass = + patterns.stream() + .filter(pd -> pd instanceof RecordPattern) + .map(pd -> (RecordPattern) pd) + .collect(groupingBy(pd -> (ClassSymbol) pd.recordType.tsym)); + + for (var e : groupByRecordClass.entrySet()) { + int nestedPatternsCount = e.getKey().getRecordComponents().size(); + Set current = new HashSet<>(e.getValue()); + + for (int mismatchingCandidate = 0; + mismatchingCandidate < nestedPatternsCount; + mismatchingCandidate++) { + int mismatchingCandidateFin = mismatchingCandidate; + var groupEquivalenceCandidates = + current + .stream() + //error recovery, ignore patterns with incorrect number of nested patterns: + .filter(pd -> pd.nested.length == nestedPatternsCount) + .collect(groupingBy(pd -> useHashes ? pd.hashCode(mismatchingCandidateFin) : 0)); + for (var candidates : groupEquivalenceCandidates.values()) { + var candidatesArr = candidates.toArray(RecordPattern[]::new); + + for (int firstCandidate = 0; + firstCandidate < candidatesArr.length; + firstCandidate++) { + RecordPattern rpOne = candidatesArr[firstCandidate]; + ListBuffer join = new ListBuffer<>(); + + join.append(rpOne); + + NEXT_PATTERN: for (int nextCandidate = 0; + nextCandidate < candidatesArr.length; + nextCandidate++) { + if (firstCandidate == nextCandidate) { + continue; + } + + RecordPattern rpOther = candidatesArr[nextCandidate]; + if (rpOne.recordType.tsym == rpOther.recordType.tsym) { + for (int i = 0; i < rpOne.nested.length; i++) { + if (i != mismatchingCandidate) { + if (!rpOne.nested[i].equals(rpOther.nested[i])) { + if (useHashes || + //when not using hashes, + //check if rpOne.nested[i] is + //a subtype of rpOther.nested[i]: + !(rpOne.nested[i] instanceof BindingPattern bpOne) || + !(rpOther.nested[i] instanceof BindingPattern bpOther) || + !types.isSubtype(types.erasure(bpOne.type), types.erasure(bpOther.type))) { + continue NEXT_PATTERN; + } + } + } + } + join.append(rpOther); + } + } + + var nestedPatterns = join.stream().map(rp -> rp.nested[mismatchingCandidateFin]).collect(Collectors.toSet()); + var updatedPatterns = reduceNestedPatterns(nestedPatterns, useHashes); + + updatedPatterns = reduceRecordPatterns(updatedPatterns); + updatedPatterns = removeCoveredRecordPatterns(updatedPatterns); + updatedPatterns = reduceBindingPatterns(rpOne.fullComponentTypes()[mismatchingCandidateFin], updatedPatterns); + + if (!nestedPatterns.equals(updatedPatterns)) { + if (useHashes) { + current.removeAll(join); + } + + for (PatternDescription nested : updatedPatterns) { + PatternDescription[] newNested = + Arrays.copyOf(rpOne.nested, rpOne.nested.length); + newNested[mismatchingCandidateFin] = nested; + current.add(new RecordPattern(rpOne.recordType(), + rpOne.fullComponentTypes(), + newNested)); + } + } + } + } + } + + if (!current.equals(new HashSet<>(e.getValue()))) { + Set result = new HashSet<>(patterns); + result.removeAll(e.getValue()); + result.addAll(current); + return result; + } + } + return patterns; + } + + /* In the set of patterns, find those for which, given: + * $record($nested1, $nested2, ...) + * all the $nestedX pattern cover the given record component, + * and replace those with a simple binding pattern over $record. + */ + private Set reduceRecordPatterns(Set patterns) { + var newPatterns = new HashSet(); + boolean modified = false; + for (PatternDescription pd : patterns) { + if (pd instanceof RecordPattern rpOne) { + PatternDescription reducedPattern = reduceRecordPattern(rpOne); + if (reducedPattern != rpOne) { + newPatterns.add(reducedPattern); + modified = true; + continue; + } + } + newPatterns.add(pd); + } + return modified ? newPatterns : patterns; + } + + private PatternDescription reduceRecordPattern(PatternDescription pattern) { + if (pattern instanceof RecordPattern rpOne) { + Type[] componentType = rpOne.fullComponentTypes(); + //error recovery, ignore patterns with incorrect number of nested patterns: + if (componentType.length != rpOne.nested.length) { + return pattern; + } + PatternDescription[] reducedNestedPatterns = null; + boolean covered = true; + for (int i = 0; i < componentType.length; i++) { + PatternDescription newNested = reduceRecordPattern(rpOne.nested[i]); + if (newNested != rpOne.nested[i]) { + if (reducedNestedPatterns == null) { + reducedNestedPatterns = Arrays.copyOf(rpOne.nested, rpOne.nested.length); + } + reducedNestedPatterns[i] = newNested; + } + + covered &= checkCovered(componentType[i], List.of(newNested)); + } + if (covered) { + return new BindingPattern(rpOne.recordType); + } else if (reducedNestedPatterns != null) { + return new RecordPattern(rpOne.recordType, rpOne.fullComponentTypes(), reducedNestedPatterns); + } + } + return pattern; + } + + private Set removeCoveredRecordPatterns(Set patterns) { + Set existingBindings = patterns.stream() + .filter(pd -> pd instanceof BindingPattern) + .map(pd -> ((BindingPattern) pd).type.tsym) + .collect(Collectors.toSet()); + Set result = new HashSet<>(patterns); + + for (Iterator it = result.iterator(); it.hasNext();) { + PatternDescription pd = it.next(); + if (pd instanceof RecordPattern rp && existingBindings.contains(rp.recordType.tsym)) { + it.remove(); + } + } + + return result; + } + + private boolean isBpCovered(Type componentType, PatternDescription newNested) { + if (newNested instanceof BindingPattern bp) { + Type seltype = types.erasure(componentType); + Type pattype = types.erasure(bp.type); + + return seltype.isPrimitive() ? + types.isUnconditionallyExact(seltype, pattype) : + (bp.type.isPrimitive() && types.isUnconditionallyExact(types.unboxedType(seltype), bp.type)) || types.isSubtype(seltype, pattype); + } + return false; + } + + sealed interface PatternDescription { } + public PatternDescription makePatternDescription(Type selectorType, JCPattern pattern) { + if (pattern instanceof JCBindingPattern binding) { + Type type = !selectorType.isPrimitive() && types.isSubtype(selectorType, binding.type) + ? selectorType : binding.type; + return new BindingPattern(type); + } else if (pattern instanceof JCRecordPattern record) { + Type[] componentTypes; + + if (!record.type.isErroneous()) { + componentTypes = ((ClassSymbol) record.type.tsym).getRecordComponents() + .map(r -> types.memberType(record.type, r)) + .toArray(s -> new Type[s]); + } + else { + componentTypes = record.nested.map(t -> types.createErrorType(t.type)).toArray(s -> new Type[s]);; + } + + PatternDescription[] nestedDescriptions = + new PatternDescription[record.nested.size()]; + int i = 0; + for (List it = record.nested; + it.nonEmpty(); + it = it.tail, i++) { + Type componentType = i < componentTypes.length ? componentTypes[i] + : syms.errType; + nestedDescriptions[i] = makePatternDescription(types.erasure(componentType), it.head); + } + return new RecordPattern(record.type, componentTypes, nestedDescriptions); + } else if (pattern instanceof JCAnyPattern) { + return new BindingPattern(selectorType); + } else { + throw Assert.error(); + } + } + record BindingPattern(Type type) implements PatternDescription { + @Override + public int hashCode() { + return type.tsym.hashCode(); + } + @Override + public boolean equals(Object o) { + return o instanceof BindingPattern other && + type.tsym == other.type.tsym; + } + @Override + public String toString() { + return type.tsym + " _"; + } + } + record RecordPattern(Type recordType, int _hashCode, Type[] fullComponentTypes, PatternDescription... nested) implements PatternDescription { + + public RecordPattern(Type recordType, Type[] fullComponentTypes, PatternDescription[] nested) { + this(recordType, hashCode(-1, recordType, nested), fullComponentTypes, nested); + } + + @Override + public int hashCode() { + return _hashCode; + } + + @Override + public boolean equals(Object o) { + return o instanceof RecordPattern other && + recordType.tsym == other.recordType.tsym && + Arrays.equals(nested, other.nested); + } + + public int hashCode(int excludeComponent) { + return hashCode(excludeComponent, recordType, nested); + } + + public static int hashCode(int excludeComponent, Type recordType, PatternDescription... nested) { + int hash = 5; + hash = 41 * hash + recordType.tsym.hashCode(); + for (int i = 0; i < nested.length; i++) { + if (i != excludeComponent) { + hash = 41 * hash + nested[i].hashCode(); + } + } + return hash; + } + @Override + public String toString() { + return recordType.tsym + "(" + Arrays.stream(nested) + .map(pd -> pd.toString()) + .collect(Collectors.joining(", ")) + ")"; + } + } +} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index 3bbd007c66a65..e74aed6a35703 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -27,11 +27,7 @@ package com.sun.tools.javac.comp; -import java.util.Map; -import java.util.Map.Entry; import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; import java.util.function.Consumer; import com.sun.source.tree.LambdaExpressionTree.BodyKind; @@ -51,20 +47,12 @@ import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Flags.BLOCK; -import com.sun.tools.javac.code.Kinds.Kind; import static com.sun.tools.javac.code.Kinds.Kind.*; -import com.sun.tools.javac.code.Type.TypeVar; import static com.sun.tools.javac.code.TypeTag.BOOLEAN; import static com.sun.tools.javac.code.TypeTag.VOID; import com.sun.tools.javac.resources.CompilerProperties.Fragments; import static com.sun.tools.javac.tree.JCTree.Tag.*; import com.sun.tools.javac.util.JCDiagnostic.Fragment; -import java.util.Arrays; -import java.util.Iterator; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import static java.util.stream.Collectors.groupingBy; /** This pass implements dataflow analysis for Java programs though * different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that @@ -213,8 +201,8 @@ public class Flow { private TreeMaker make; private final Resolve rs; private final JCDiagnostic.Factory diags; + private final ExhaustivenessComputer exhaustiveness; private Env attrEnv; - private final Infer infer; public static Flow instance(Context context) { Flow instance = context.get(flowKey); @@ -336,10 +324,9 @@ protected Flow(Context context) { syms = Symtab.instance(context); types = Types.instance(context); chk = Check.instance(context); - infer = Infer.instance(context); rs = Resolve.instance(context); diags = JCDiagnostic.Factory.instance(context); - Source source = Source.instance(context); + exhaustiveness = ExhaustivenessComputer.instance(context); } /** @@ -709,7 +696,7 @@ public void visitSwitch(JCSwitch tree) { tree.isExhaustive = tree.hasUnconditionalPattern || TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases); if (exhaustiveSwitch) { - tree.isExhaustive |= exhausts(tree.selector, tree.cases); + tree.isExhaustive |= exhaustiveness.exhausts(tree.selector, tree.cases); if (!tree.isExhaustive) { log.error(tree, Errors.NotExhaustiveStatement); } @@ -748,7 +735,7 @@ public void visitSwitchExpression(JCSwitchExpression tree) { TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases)) { tree.isExhaustive = true; } else { - tree.isExhaustive = exhausts(tree.selector, tree.cases); + tree.isExhaustive = exhaustiveness.exhausts(tree.selector, tree.cases); } if (!tree.isExhaustive) { @@ -758,429 +745,6 @@ public void visitSwitchExpression(JCSwitchExpression tree) { alive = alive.or(resolveYields(tree, prevPendingExits)); } - private boolean exhausts(JCExpression selector, List cases) { - Set patternSet = new HashSet<>(); - Map> enum2Constants = new HashMap<>(); - Set booleanLiterals = new HashSet<>(Set.of(0, 1)); - for (JCCase c : cases) { - if (!TreeInfo.unguardedCase(c)) - continue; - - for (var l : c.labels) { - if (l instanceof JCPatternCaseLabel patternLabel) { - for (Type component : components(selector.type)) { - patternSet.add(makePatternDescription(component, patternLabel.pat)); - } - } else if (l instanceof JCConstantCaseLabel constantLabel) { - if (types.unboxedTypeOrType(selector.type).hasTag(TypeTag.BOOLEAN)) { - Object value = ((JCLiteral) constantLabel.expr).value; - booleanLiterals.remove(value); - } else { - Symbol s = TreeInfo.symbol(constantLabel.expr); - if (s != null && s.isEnum()) { - enum2Constants.computeIfAbsent(s.owner, x -> { - Set result = new HashSet<>(); - s.owner.members() - .getSymbols(sym -> sym.kind == Kind.VAR && sym.isEnum()) - .forEach(result::add); - return result; - }).remove(s); - } - } - } - } - } - - if (types.unboxedTypeOrType(selector.type).hasTag(TypeTag.BOOLEAN) && booleanLiterals.isEmpty()) { - return true; - } - - for (Entry> e : enum2Constants.entrySet()) { - if (e.getValue().isEmpty()) { - patternSet.add(new BindingPattern(e.getKey().type)); - } - } - Set patterns = patternSet; - boolean useHashes = true; - try { - boolean repeat = true; - while (repeat) { - Set updatedPatterns; - updatedPatterns = reduceBindingPatterns(selector.type, patterns); - updatedPatterns = reduceNestedPatterns(updatedPatterns, useHashes); - updatedPatterns = reduceRecordPatterns(updatedPatterns); - updatedPatterns = removeCoveredRecordPatterns(updatedPatterns); - repeat = !updatedPatterns.equals(patterns); - if (checkCovered(selector.type, patterns)) { - return true; - } - if (!repeat) { - //there may be situation like: - //class B permits S1, S2 - //patterns: R(S1, B), R(S2, S2) - //this might be joined to R(B, S2), as B could be rewritten to S2 - //but hashing in reduceNestedPatterns will not allow that - //disable the use of hashing, and use subtyping in - //reduceNestedPatterns to handle situations like this: - repeat = useHashes; - useHashes = false; - } else { - //if a reduction happened, make sure hashing in reduceNestedPatterns - //is enabled, as the hashing speeds up the process significantly: - useHashes = true; - } - patterns = updatedPatterns; - } - return checkCovered(selector.type, patterns); - } catch (CompletionFailure cf) { - chk.completionError(selector.pos(), cf); - return true; //error recovery - } - } - - private boolean checkCovered(Type seltype, Iterable patterns) { - for (Type seltypeComponent : components(seltype)) { - for (PatternDescription pd : patterns) { - if(isBpCovered(seltypeComponent, pd)) { - return true; - } - } - } - return false; - } - - private List components(Type seltype) { - return switch (seltype.getTag()) { - case CLASS -> { - if (seltype.isCompound()) { - if (seltype.isIntersection()) { - yield ((Type.IntersectionClassType) seltype).getComponents() - .stream() - .flatMap(t -> components(t).stream()) - .collect(List.collector()); - } - yield List.nil(); - } - yield List.of(types.erasure(seltype)); - } - case TYPEVAR -> components(((TypeVar) seltype).getUpperBound()); - default -> List.of(types.erasure(seltype)); - }; - } - - /* In a set of patterns, search for a sub-set of binding patterns that - * in combination exhaust their sealed supertype. If such a sub-set - * is found, it is removed, and replaced with a binding pattern - * for the sealed supertype. - */ - private Set reduceBindingPatterns(Type selectorType, Set patterns) { - Set existingBindings = patterns.stream() - .filter(pd -> pd instanceof BindingPattern) - .map(pd -> ((BindingPattern) pd).type.tsym) - .collect(Collectors.toSet()); - - for (PatternDescription pdOne : patterns) { - if (pdOne instanceof BindingPattern bpOne) { - Set toAdd = new HashSet<>(); - - for (Type sup : types.directSupertypes(bpOne.type)) { - ClassSymbol clazz = (ClassSymbol) types.erasure(sup).tsym; - - clazz.complete(); - - if (clazz.isSealed() && clazz.isAbstract() && - //if a binding pattern for clazz already exists, no need to analyze it again: - !existingBindings.contains(clazz)) { - ListBuffer bindings = new ListBuffer<>(); - //do not reduce to types unrelated to the selector type: - Type clazzErasure = types.erasure(clazz.type); - if (components(selectorType).stream() - .map(types::erasure) - .noneMatch(c -> types.isSubtype(clazzErasure, c))) { - continue; - } - - Set permitted = allPermittedSubTypes(clazz, csym -> { - Type instantiated; - if (csym.type.allparams().isEmpty()) { - instantiated = csym.type; - } else { - instantiated = infer.instantiatePatternType(selectorType, csym); - } - - return instantiated != null && types.isCastable(selectorType, instantiated); - }); - - for (PatternDescription pdOther : patterns) { - if (pdOther instanceof BindingPattern bpOther) { - Set currentPermittedSubTypes = - allPermittedSubTypes(bpOther.type.tsym, s -> true); - - PERMITTED: for (Iterator it = permitted.iterator(); it.hasNext();) { - Symbol perm = it.next(); - - for (Symbol currentPermitted : currentPermittedSubTypes) { - if (types.isSubtype(types.erasure(currentPermitted.type), - types.erasure(perm.type))) { - it.remove(); - continue PERMITTED; - } - } - if (types.isSubtype(types.erasure(perm.type), - types.erasure(bpOther.type))) { - it.remove(); - } - } - } - } - - if (permitted.isEmpty()) { - toAdd.add(new BindingPattern(clazz.type)); - } - } - } - - if (!toAdd.isEmpty()) { - Set newPatterns = new HashSet<>(patterns); - newPatterns.addAll(toAdd); - return newPatterns; - } - } - } - return patterns; - } - - private Set allPermittedSubTypes(TypeSymbol root, Predicate accept) { - Set permitted = new HashSet<>(); - List permittedSubtypesClosure = baseClasses(root); - - while (permittedSubtypesClosure.nonEmpty()) { - ClassSymbol current = permittedSubtypesClosure.head; - - permittedSubtypesClosure = permittedSubtypesClosure.tail; - - current.complete(); - - if (current.isSealed() && current.isAbstract()) { - for (Type t : current.getPermittedSubclasses()) { - ClassSymbol csym = (ClassSymbol) t.tsym; - - if (accept.test(csym)) { - permittedSubtypesClosure = permittedSubtypesClosure.prepend(csym); - permitted.add(csym); - } - } - } - } - - return permitted; - } - - private List baseClasses(TypeSymbol root) { - if (root instanceof ClassSymbol clazz) { - return List.of(clazz); - } else if (root instanceof TypeVariableSymbol tvar) { - ListBuffer result = new ListBuffer<>(); - for (Type bound : tvar.getBounds()) { - result.appendList(baseClasses(bound.tsym)); - } - return result.toList(); - } else { - return List.nil(); - } - } - - /* Among the set of patterns, find sub-set of patterns such: - * $record($prefix$, $nested, $suffix$) - * Where $record, $prefix$ and $suffix$ is the same for each pattern - * in the set, and the patterns only differ in one "column" in - * the $nested pattern. - * Then, the set of $nested patterns is taken, and passed recursively - * to reduceNestedPatterns and to reduceBindingPatterns, to - * simplify the pattern. If that succeeds, the original found sub-set - * of patterns is replaced with a new set of patterns of the form: - * $record($prefix$, $resultOfReduction, $suffix$) - * - * useHashes: when true, patterns will be subject to exact equivalence; - * when false, two binding patterns will be considered equivalent - * if one of them is more generic than the other one; - * when false, the processing will be significantly slower, - * as pattern hashes cannot be used to speed up the matching process - */ - private Set reduceNestedPatterns(Set patterns, - boolean useHashes) { - /* implementation note: - * finding a sub-set of patterns that only differ in a single - * column is time-consuming task, so this method speeds it up by: - * - group the patterns by their record class - * - for each column (nested pattern) do: - * -- group patterns by their hash - * -- in each such by-hash group, find sub-sets that only differ in - * the chosen column, and then call reduceBindingPatterns and reduceNestedPatterns - * on patterns in the chosen column, as described above - */ - var groupByRecordClass = - patterns.stream() - .filter(pd -> pd instanceof RecordPattern) - .map(pd -> (RecordPattern) pd) - .collect(groupingBy(pd -> (ClassSymbol) pd.recordType.tsym)); - - for (var e : groupByRecordClass.entrySet()) { - int nestedPatternsCount = e.getKey().getRecordComponents().size(); - Set current = new HashSet<>(e.getValue()); - - for (int mismatchingCandidate = 0; - mismatchingCandidate < nestedPatternsCount; - mismatchingCandidate++) { - int mismatchingCandidateFin = mismatchingCandidate; - var groupEquivalenceCandidates = - current - .stream() - //error recovery, ignore patterns with incorrect number of nested patterns: - .filter(pd -> pd.nested.length == nestedPatternsCount) - .collect(groupingBy(pd -> useHashes ? pd.hashCode(mismatchingCandidateFin) : 0)); - for (var candidates : groupEquivalenceCandidates.values()) { - var candidatesArr = candidates.toArray(RecordPattern[]::new); - - for (int firstCandidate = 0; - firstCandidate < candidatesArr.length; - firstCandidate++) { - RecordPattern rpOne = candidatesArr[firstCandidate]; - ListBuffer join = new ListBuffer<>(); - - join.append(rpOne); - - NEXT_PATTERN: for (int nextCandidate = 0; - nextCandidate < candidatesArr.length; - nextCandidate++) { - if (firstCandidate == nextCandidate) { - continue; - } - - RecordPattern rpOther = candidatesArr[nextCandidate]; - if (rpOne.recordType.tsym == rpOther.recordType.tsym) { - for (int i = 0; i < rpOne.nested.length; i++) { - if (i != mismatchingCandidate) { - if (!rpOne.nested[i].equals(rpOther.nested[i])) { - if (useHashes || - //when not using hashes, - //check if rpOne.nested[i] is - //a subtype of rpOther.nested[i]: - !(rpOne.nested[i] instanceof BindingPattern bpOne) || - !(rpOther.nested[i] instanceof BindingPattern bpOther) || - !types.isSubtype(types.erasure(bpOne.type), types.erasure(bpOther.type))) { - continue NEXT_PATTERN; - } - } - } - } - join.append(rpOther); - } - } - - var nestedPatterns = join.stream().map(rp -> rp.nested[mismatchingCandidateFin]).collect(Collectors.toSet()); - var updatedPatterns = reduceNestedPatterns(nestedPatterns, useHashes); - - updatedPatterns = reduceRecordPatterns(updatedPatterns); - updatedPatterns = removeCoveredRecordPatterns(updatedPatterns); - updatedPatterns = reduceBindingPatterns(rpOne.fullComponentTypes()[mismatchingCandidateFin], updatedPatterns); - - if (!nestedPatterns.equals(updatedPatterns)) { - if (useHashes) { - current.removeAll(join); - } - - for (PatternDescription nested : updatedPatterns) { - PatternDescription[] newNested = - Arrays.copyOf(rpOne.nested, rpOne.nested.length); - newNested[mismatchingCandidateFin] = nested; - current.add(new RecordPattern(rpOne.recordType(), - rpOne.fullComponentTypes(), - newNested)); - } - } - } - } - } - - if (!current.equals(new HashSet<>(e.getValue()))) { - Set result = new HashSet<>(patterns); - result.removeAll(e.getValue()); - result.addAll(current); - return result; - } - } - return patterns; - } - - /* In the set of patterns, find those for which, given: - * $record($nested1, $nested2, ...) - * all the $nestedX pattern cover the given record component, - * and replace those with a simple binding pattern over $record. - */ - private Set reduceRecordPatterns(Set patterns) { - var newPatterns = new HashSet(); - boolean modified = false; - for (PatternDescription pd : patterns) { - if (pd instanceof RecordPattern rpOne) { - PatternDescription reducedPattern = reduceRecordPattern(rpOne); - if (reducedPattern != rpOne) { - newPatterns.add(reducedPattern); - modified = true; - continue; - } - } - newPatterns.add(pd); - } - return modified ? newPatterns : patterns; - } - - private PatternDescription reduceRecordPattern(PatternDescription pattern) { - if (pattern instanceof RecordPattern rpOne) { - Type[] componentType = rpOne.fullComponentTypes(); - //error recovery, ignore patterns with incorrect number of nested patterns: - if (componentType.length != rpOne.nested.length) { - return pattern; - } - PatternDescription[] reducedNestedPatterns = null; - boolean covered = true; - for (int i = 0; i < componentType.length; i++) { - PatternDescription newNested = reduceRecordPattern(rpOne.nested[i]); - if (newNested != rpOne.nested[i]) { - if (reducedNestedPatterns == null) { - reducedNestedPatterns = Arrays.copyOf(rpOne.nested, rpOne.nested.length); - } - reducedNestedPatterns[i] = newNested; - } - - covered &= checkCovered(componentType[i], List.of(newNested)); - } - if (covered) { - return new BindingPattern(rpOne.recordType); - } else if (reducedNestedPatterns != null) { - return new RecordPattern(rpOne.recordType, rpOne.fullComponentTypes(), reducedNestedPatterns); - } - } - return pattern; - } - - private Set removeCoveredRecordPatterns(Set patterns) { - Set existingBindings = patterns.stream() - .filter(pd -> pd instanceof BindingPattern) - .map(pd -> ((BindingPattern) pd).type.tsym) - .collect(Collectors.toSet()); - Set result = new HashSet<>(patterns); - - for (Iterator it = result.iterator(); it.hasNext();) { - PatternDescription pd = it.next(); - if (pd instanceof RecordPattern rp && existingBindings.contains(rp.recordType.tsym)) { - it.remove(); - } - } - - return result; - } - public void visitTry(JCTry tree) { ListBuffer prevPendingExits = pendingExits; pendingExits = new ListBuffer<>(); @@ -1326,18 +890,6 @@ public void analyzeTree(Env env, JCTree tree, TreeMaker make) { } } - private boolean isBpCovered(Type componentType, PatternDescription newNested) { - if (newNested instanceof BindingPattern bp) { - Type seltype = types.erasure(componentType); - Type pattype = types.erasure(bp.type); - - return seltype.isPrimitive() ? - types.isUnconditionallyExact(seltype, pattype) : - (bp.type.isPrimitive() && types.isUnconditionallyExact(types.unboxedType(seltype), bp.type)) || types.isSubtype(seltype, pattype); - } - return false; - } - /** * This pass implements the second step of the dataflow analysis, namely * the exception analysis. This is to ensure that every checked exception that is @@ -3473,93 +3025,4 @@ public static Liveness from(boolean value) { } } - sealed interface PatternDescription { } - public PatternDescription makePatternDescription(Type selectorType, JCPattern pattern) { - if (pattern instanceof JCBindingPattern binding) { - Type type = !selectorType.isPrimitive() && types.isSubtype(selectorType, binding.type) - ? selectorType : binding.type; - return new BindingPattern(type); - } else if (pattern instanceof JCRecordPattern record) { - Type[] componentTypes; - - if (!record.type.isErroneous()) { - componentTypes = ((ClassSymbol) record.type.tsym).getRecordComponents() - .map(r -> types.memberType(record.type, r)) - .toArray(s -> new Type[s]); - } - else { - componentTypes = record.nested.map(t -> types.createErrorType(t.type)).toArray(s -> new Type[s]);; - } - - PatternDescription[] nestedDescriptions = - new PatternDescription[record.nested.size()]; - int i = 0; - for (List it = record.nested; - it.nonEmpty(); - it = it.tail, i++) { - Type componentType = i < componentTypes.length ? componentTypes[i] - : syms.errType; - nestedDescriptions[i] = makePatternDescription(types.erasure(componentType), it.head); - } - return new RecordPattern(record.type, componentTypes, nestedDescriptions); - } else if (pattern instanceof JCAnyPattern) { - return new BindingPattern(selectorType); - } else { - throw Assert.error(); - } - } - record BindingPattern(Type type) implements PatternDescription { - @Override - public int hashCode() { - return type.tsym.hashCode(); - } - @Override - public boolean equals(Object o) { - return o instanceof BindingPattern other && - type.tsym == other.type.tsym; - } - @Override - public String toString() { - return type.tsym + " _"; - } - } - record RecordPattern(Type recordType, int _hashCode, Type[] fullComponentTypes, PatternDescription... nested) implements PatternDescription { - - public RecordPattern(Type recordType, Type[] fullComponentTypes, PatternDescription[] nested) { - this(recordType, hashCode(-1, recordType, nested), fullComponentTypes, nested); - } - - @Override - public int hashCode() { - return _hashCode; - } - - @Override - public boolean equals(Object o) { - return o instanceof RecordPattern other && - recordType.tsym == other.recordType.tsym && - Arrays.equals(nested, other.nested); - } - - public int hashCode(int excludeComponent) { - return hashCode(excludeComponent, recordType, nested); - } - - public static int hashCode(int excludeComponent, Type recordType, PatternDescription... nested) { - int hash = 5; - hash = 41 * hash + recordType.tsym.hashCode(); - for (int i = 0; i < nested.length; i++) { - if (i != excludeComponent) { - hash = 41 * hash + nested[i].hashCode(); - } - } - return hash; - } - @Override - public String toString() { - return recordType.tsym + "(" + Arrays.stream(nested) - .map(pd -> pd.toString()) - .collect(Collectors.joining(", ")) + ")"; - } - } } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java index f4ce5337e2fc7..cae034c96132a 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java @@ -258,7 +258,13 @@ public Frame sender(RegisterMap regMap, CodeBlob cb) { } if (cb != null) { - return cb.isUpcallStub() ? senderForUpcallStub(map, (UpcallStub)cb) : senderForCompiledFrame(map, cb); + if (cb.isUpcallStub()) { + return senderForUpcallStub(map, (UpcallStub)cb); + } else if (cb.isContinuationStub()) { + return senderForContinuationStub(map, cb); + } else { + return senderForCompiledFrame(map, cb); + } } // Must be native-compiled frame, i.e. the marshaling code for native @@ -331,6 +337,15 @@ private Frame senderForInterpreterFrame(PPC64RegisterMap map) { return new PPC64Frame(sp, unextendedSP, getLink(), getSenderPC()); } + private Frame senderForContinuationStub(PPC64RegisterMap map, CodeBlob cb) { + var contEntry = map.getThread().getContEntry(); + + Address sp = contEntry.getEntrySP(); + Address pc = contEntry.getEntryPC(); + Address fp = contEntry.getEntryFP(); + + return new PPC64Frame(sp, fp, pc); + } private Frame senderForCompiledFrame(PPC64RegisterMap map, CodeBlob cb) { if (DEBUG) { diff --git a/src/jdk.jconsole/share/classes/sun/tools/jconsole/ProxyClient.java b/src/jdk.jconsole/share/classes/sun/tools/jconsole/ProxyClient.java index ec2290421a4bd..0ff2d0e366588 100644 --- a/src/jdk.jconsole/share/classes/sun/tools/jconsole/ProxyClient.java +++ b/src/jdk.jconsole/share/classes/sun/tools/jconsole/ProxyClient.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -34,6 +34,7 @@ import static java.lang.management.ManagementFactory.*; import java.lang.ref.WeakReference; import java.lang.reflect.*; +import java.net.URI; import java.rmi.*; import java.rmi.registry.*; import java.rmi.server.*; @@ -133,7 +134,24 @@ private ProxyClient(String url, this.advancedUrl = url; this.connectionName = getConnectionName(url, userName); this.displayName = connectionName; - setParameters(new JMXServiceURL(url), userName, password); + JMXServiceURL jmxServiceURL = new JMXServiceURL(url); + setParameters(jmxServiceURL, userName, password); + if ("rmi".equals(jmxServiceURL.getProtocol())) { + String path = jmxServiceURL.getURLPath(); + if (path.startsWith("/jndi/")) { + int end = path.indexOf(';'); + if (end < 0) end = path.length(); + String registryURIStr = path.substring(6, end); + URI registryURI = URI.create(registryURIStr); + if ("rmi".equals(registryURI.getScheme()) + && "/jmxrmi".equals(registryURI.getPath())) { + this.registryHostName = registryURI.getHost(); + this.registryPort = registryURI.getPort(); + this.vmConnector = true; + checkSslConfig(); + } + } + } } private ProxyClient(LocalVirtualMachine lvm) throws IOException { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java index 46dab564ac0fb..c8db9aef169e5 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java @@ -257,6 +257,11 @@ private void processOrdered(Dispatcher c) throws IOException { } for (int i = 0; i < index; i++) { c.dispatch(sortedCache[i]); + sortedCache[i] = null; + } + // Shrink array + if (index > 100_000 && 4 * index < sortedCache.length) { + sortedCache = new RecordedEvent[2 * index]; } onFlush(); return; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java index 56d9fe01d790c..82712fcbedc49 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java @@ -127,6 +127,10 @@ private void processOrdered(Dispatcher c) throws IOException { cacheSorted[index++] = event; } dispatchOrdered(c, index); + if (index > 100_000 && 4 * index < cacheSorted.length) { + cacheSorted = new RecordedEvent[2 * index]; + } + onFlush(); index = 0; } } @@ -136,8 +140,8 @@ private void dispatchOrdered(Dispatcher c, int index) { Arrays.sort(cacheSorted, 0, index, EVENT_COMPARATOR); for (int i = 0; i < index; i++) { c.dispatch(cacheSorted[i]); + cacheSorted[i] = null; } - onFlush(); } private void processUnordered(Dispatcher c) throws IOException { diff --git a/src/jdk.jlink/share/man/jlink.md b/src/jdk.jlink/share/man/jlink.md index 74f2d119c6980..dc256af43b5f1 100644 --- a/src/jdk.jlink/share/man/jlink.md +++ b/src/jdk.jlink/share/man/jlink.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2017, 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 @@ -279,8 +279,6 @@ Suggested providers: java.smartcardio provides java.security.Provider used by java.base java.xml.crypto provides java.security.Provider used by java.base jdk.crypto.cryptoki provides java.security.Provider used by java.base - jdk.crypto.ec provides java.security.Provider used by java.base - jdk.crypto.mscapi provides java.security.Provider used by java.base jdk.security.jgss provides java.security.Provider used by java.base ``` diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java index 571f163f682bb..1345597e3527b 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java @@ -106,7 +106,7 @@ private void sign(MacApplication app, MacBundle appImage) throws CodesignExcepti throw new IllegalArgumentException(); } - app = normalizeAppImageLayout(app); + app = copyWithUnresolvedAppImageLayout(app); final var fileFilter = new SignFilter(app, appImage); @@ -243,7 +243,7 @@ static Codesigners create(CodesignConfig signingCfg) { } } - private static MacApplication normalizeAppImageLayout(MacApplication app) { + private static MacApplication copyWithUnresolvedAppImageLayout(MacApplication app) { switch (app.imageLayout()) { case MacApplicationLayout macLayout -> { return MacApplicationBuilder.overrideAppImageLayout(app, APPLICATION_LAYOUT); diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBaseInstallerBundler.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBaseInstallerBundler.java index f46b5a328fd85..5c912728c32d5 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBaseInstallerBundler.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBaseInstallerBundler.java @@ -26,14 +26,8 @@ package jdk.jpackage.internal; import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE; -import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE_FILE; -import static jdk.jpackage.internal.StandardBundlerParam.SIGN_BUNDLE; -import java.nio.file.Files; -import java.nio.file.Path; -import java.text.MessageFormat; import java.util.Map; -import java.util.Optional; import jdk.jpackage.internal.model.ConfigException; public abstract class MacBaseInstallerBundler extends AbstractBundler { @@ -44,26 +38,7 @@ public MacBaseInstallerBundler() { protected void validateAppImageAndBundeler( Map params) throws ConfigException { - if (PREDEFINED_APP_IMAGE.fetchFrom(params) != null) { - Path applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(params); - if (new MacAppImageFileExtras(PREDEFINED_APP_IMAGE_FILE.fetchFrom(params)).signed()) { - var appLayout = ApplicationLayoutUtils.PLATFORM_APPLICATION_LAYOUT.resolveAt(applicationImage); - if (!Files.exists( - PackageFile.getPathInAppImage(appLayout))) { - Log.info(MessageFormat.format(I18N.getString( - "warning.per.user.app.image.signed"), - PackageFile.getPathInAppImage(appLayout))); - } - } else { - if (Optional.ofNullable( - SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.FALSE)) { - // if signing bundle with app-image, warn user if app-image - // is not already signed. - Log.info(MessageFormat.format(I18N.getString( - "warning.unsigned.app.image"), getID())); - } - } - } else { + if (PREDEFINED_APP_IMAGE.fetchFrom(params) == null) { appImageBundler.validate(params); } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java index 7a881c846ff46..e2d8750e39c93 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java @@ -99,7 +99,7 @@ private static MacApplication createMacApplication( // AppImageFile assumes the main launcher start up info is available when // it is constructed from Application instance. // This happens when jpackage signs predefined app image. - final var mainLauncherStartupInfo = new MainLauncherStartupInfo(PREDEFINED_APP_IMAGE_FILE.fetchFrom(params).getMainClass()); + final var mainLauncherStartupInfo = new MainLauncherStartupInfo(superAppBuilder.mainLauncherClassName().orElseThrow()); final var launchers = superAppBuilder.launchers().orElseThrow(); final var mainLauncher = ApplicationBuilder.overrideLauncherStartupInfo(launchers.mainLauncher(), mainLauncherStartupInfo); superAppBuilder.launchers(new ApplicationLaunchers(MacLauncher.create(mainLauncher), launchers.additionalLaunchers())); @@ -122,7 +122,7 @@ private static MacApplication createMacApplication( final boolean appStore; if (hasPredefinedAppImage(params) && PACKAGE_TYPE.findIn(params).filter(Predicate.isEqual("app-image")).isEmpty()) { - final var appImageFileExtras = new MacAppImageFileExtras(PREDEFINED_APP_IMAGE_FILE.fetchFrom(params)); + final var appImageFileExtras = new MacAppImageFileExtras(superAppBuilder.externalApplication().orElseThrow()); sign = appImageFileExtras.signed(); appStore = appImageFileExtras.appStore(); } else { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java index cf5c6a934f733..9576f6a6a996e 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java @@ -24,8 +24,10 @@ */ package jdk.jpackage.internal; +import static jdk.jpackage.internal.MacPackagingPipeline.APPLICATION_LAYOUT; import static jdk.jpackage.internal.MacPackagingPipeline.LayoutUtils.packagerLayout; +import java.nio.file.Files; import java.util.Objects; import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.MacApplication; @@ -57,7 +59,21 @@ MacPackage create() throws ConfigException { .installedPackageLayout(pkg.installedPackageLayout()); pkg = pkgBuilder.create(); - return MacPackage.create(pkg, new MacPackageMixin.Stub(pkg.predefinedAppImage().map(v -> predefinedAppImageSigned))); + + var macPkg = MacPackage.create(pkg, new MacPackageMixin.Stub(pkg.predefinedAppImage().map(v -> predefinedAppImageSigned))); + validatePredefinedAppImage(macPkg); + return macPkg; + } + + private static void validatePredefinedAppImage(MacPackage pkg) { + if (pkg.predefinedAppImageSigned().orElse(false)) { + pkg.predefinedAppImage().ifPresent(predefinedAppImage -> { + var thePackageFile = PackageFile.getPathInAppImage(APPLICATION_LAYOUT); + if (!Files.exists(predefinedAppImage.resolve(thePackageFile))) { + Log.info(I18N.format("warning.per.user.app.image.signed", thePackageFile)); + } + }); + } } private final PackageBuilder pkgBuilder; diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackageBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackageBuilder.java index 131650aebb516..663b8b16265a0 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackageBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackageBuilder.java @@ -43,7 +43,9 @@ MacPkgPackageBuilder signingBuilder(SigningIdentityBuilder v) { } MacPkgPackage create() throws ConfigException { - return MacPkgPackage.create(pkgBuilder.create(), new MacPkgPackageMixin.Stub(createSigningConfig())); + var pkg = MacPkgPackage.create(pkgBuilder.create(), new MacPkgPackageMixin.Stub(createSigningConfig())); + validatePredefinedAppImage(pkg); + return pkg; } private Optional createSigningConfig() throws ConfigException { @@ -56,6 +58,14 @@ private Optional createSigningConfig() throws ConfigException } } + private static void validatePredefinedAppImage(MacPkgPackage pkg) { + if (!pkg.predefinedAppImageSigned().orElse(false) && pkg.sign()) { + pkg.predefinedAppImage().ifPresent(predefinedAppImage -> { + Log.info(I18N.format("warning.unsigned.app.image", "pkg")); + }); + } + } + private final MacPackageBuilder pkgBuilder; private SigningIdentityBuilder signingBuilder; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java index 141a1b5155f15..9b5ed5b3b0817 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java @@ -86,6 +86,9 @@ ApplicationBuilder runtimeBuilder(RuntimeBuilder v) { ApplicationBuilder initFromExternalApplication(ExternalApplication app, Function mapper) { + + externalApp = Objects.requireNonNull(app); + if (version == null) { version = app.getAppVersion(); } @@ -112,6 +115,19 @@ Optional launchers() { return Optional.ofNullable(launchers); } + Optional externalApplication() { + return Optional.ofNullable(externalApp); + } + + Optional mainLauncherClassName() { + return launchers() + .map(ApplicationLaunchers::mainLauncher) + .flatMap(Launcher::startupInfo) + .map(LauncherStartupInfo::qualifiedClassName).or(() -> { + return externalApplication().map(ExternalApplication::getMainClass); + }); + } + ApplicationBuilder appImageLayout(AppImageLayout v) { appImageLayout = v; return this; @@ -208,6 +224,7 @@ String copyright() { private String vendor; private String copyright; private Path srcDir; + private ExternalApplication externalApp; private List contentDirs; private AppImageLayout appImageLayout; private RuntimeBuilder runtimeBuilder; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java index 13c7a78b502ff..427051719bb71 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java @@ -25,8 +25,6 @@ package jdk.jpackage.internal; -import java.io.BufferedReader; -import java.io.InputStreamReader; import java.io.IOException; import java.io.PrintStream; import java.nio.file.Files; @@ -45,33 +43,13 @@ final class IOUtils { public static void copyFile(Path sourceFile, Path destFile) throws IOException { - Files.createDirectories(getParent(destFile)); + Files.createDirectories(destFile.getParent()); Files.copy(sourceFile, destFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); } - public static boolean exists(Path path) { - if (path == null) { - return false; - } - - return Files.exists(path); - } - - // run "launcher paramfile" in the directory where paramfile is kept - public static void run(String launcher, Path paramFile) - throws IOException { - if (IOUtils.exists(paramFile)) { - ProcessBuilder pb = - new ProcessBuilder(launcher, - getFileName(paramFile).toString()); - pb = pb.directory(getParent(paramFile).toFile()); - exec(pb); - } - } - public static void exec(ProcessBuilder pb) throws IOException { exec(pb, false, null, false, Executor.INFINITE_TIMEOUT); @@ -83,21 +61,6 @@ public static void exec(ProcessBuilder pb, long timeout) exec(pb, false, null, false, timeout); } - // See JDK-8236282 - // Reading output from some processes (currently known "hdiutil attach") - // might hang even if process already exited. Only possible workaround found - // in "hdiutil attach" case is to redirect the output to a temp file and then - // read this file back. - public static void exec(ProcessBuilder pb, boolean writeOutputToFile) - throws IOException { - exec(pb, false, null, writeOutputToFile, Executor.INFINITE_TIMEOUT); - } - - static void exec(ProcessBuilder pb, boolean testForPresenceOnly, - PrintStream consumer) throws IOException { - exec(pb, testForPresenceOnly, consumer, false, Executor.INFINITE_TIMEOUT); - } - static void exec(ProcessBuilder pb, boolean testForPresenceOnly, PrintStream consumer, boolean writeOutputToFile, long timeout) throws IOException { @@ -127,51 +90,6 @@ static void exec(ProcessBuilder pb, boolean testForPresenceOnly, } } - public static int getProcessOutput(List result, String... args) - throws IOException, InterruptedException { - - ProcessBuilder pb = new ProcessBuilder(args); - - final Process p = pb.start(); - - List list = new ArrayList<>(); - - final BufferedReader in = - new BufferedReader(new InputStreamReader(p.getInputStream())); - final BufferedReader err = - new BufferedReader(new InputStreamReader(p.getErrorStream())); - - Thread t = new Thread(() -> { - try { - String line; - while ((line = in.readLine()) != null) { - list.add(line); - } - } catch (IOException ioe) { - Log.verbose(ioe); - } - - try { - String line; - while ((line = err.readLine()) != null) { - Log.error(line); - } - } catch (IOException ioe) { - Log.verbose(ioe); - } - }); - t.setDaemon(true); - t.start(); - - int ret = p.waitFor(); - Log.verbose(pb.command(), list, ret, IOUtils.getPID(p)); - - result.clear(); - result.addAll(list); - - return ret; - } - static void writableOutputDir(Path outdir) throws PackagerException { if (!Files.isDirectory(outdir)) { try { @@ -188,28 +106,6 @@ static void writableOutputDir(Path outdir) throws PackagerException { } } - public static Path getParent(Path p) { - Path parent = p.getParent(); - if (parent == null) { - IllegalArgumentException iae = - new IllegalArgumentException(p.toString()); - Log.verbose(iae); - throw iae; - } - return parent; - } - - public static Path getFileName(Path p) { - Path filename = p.getFileName(); - if (filename == null) { - IllegalArgumentException iae = - new IllegalArgumentException(p.toString()); - Log.verbose(iae); - throw iae; - } - return filename; - } - public static long getPID(Process p) { try { return p.pid(); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java index b43acffbafbfd..2273d385936e1 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java @@ -23,8 +23,10 @@ * questions. */ package jdk.jpackage.internal; +import static jdk.jpackage.internal.model.RuntimeBuilder.getDefaultModulePath; import java.io.File; +import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.module.Configuration; @@ -32,6 +34,7 @@ import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; import java.lang.module.ResolvedModule; +import java.nio.file.Files; import java.nio.file.Path; import java.text.MessageFormat; import java.util.ArrayList; @@ -93,6 +96,52 @@ static RuntimeBuilder createJLinkRuntimeBuilder(List modulePath, Set + * Returns the specified path list if "java.base" module can be found in one of + * the paths from the specified path list. + *

    + * Returns a new path list created from the specified path list with the path of + * "java.base" module in the current runtime appended otherwise. + * + * @param modulePath the path list where to look up for "java.base" module + * @return the path list that includes location of "java.base" module + */ + static List ensureBaseModuleInModulePath(List modulePath) { + if (modulePath.stream().anyMatch(path -> { + return Files.isRegularFile(path.resolve("java.base.jmod")); + })) { + return modulePath; + } else { + // There is no "java.base.jmod" file in the `modulePath` path list. + // Pick items from the default module path list that are not yet + // in the `modulePath` path list and append them to it. + + var missingDefaultModulePath = getDefaultModulePath(); + + if (!modulePath.isEmpty()) { + missingDefaultModulePath.stream().filter(defaultPath -> { + return modulePath.stream().anyMatch(path -> { + try { + return Files.isSameFile(path, defaultPath); + } catch (IOException ex) { + // Assume `defaultPath` path doesn't exist in `modulePath` list. + return false; + } + }); + }).toList(); + } + + if (missingDefaultModulePath.isEmpty()) { + return modulePath; + } else { + return Stream.of(modulePath, missingDefaultModulePath).flatMap(Collection::stream).toList(); + } + } + } + private static List createJLinkCmdline(List modulePath, Set addModules, Set limitModules, List options, List startupInfos) throws ConfigException { List launcherModules = startupInfos.stream().map(si -> { @@ -230,5 +279,5 @@ private static class LazyLoad { static final ToolProvider JLINK_TOOL = ToolProvider.findFirst( "jlink").orElseThrow(); - }; + } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OverridableResource.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OverridableResource.java index 0099491fc7309..73fc36d78acce 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OverridableResource.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OverridableResource.java @@ -31,6 +31,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -180,6 +181,15 @@ OverridableResource setExternal(File v) { return setExternal(toPath(v)); } + Source probe() { + try { + return saveToStream(null); + } catch (IOException ex) { + // Should never happen. + throw new UncheckedIOException(ex); + } + } + Source saveToStream(OutputStream dest) throws IOException { if (dest == null) { return sendToConsumer(null); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java index 6b89bb3ee65f5..2b35a6830f870 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java @@ -43,7 +43,6 @@ import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.ExternalApplication; import static jdk.jpackage.internal.ApplicationLayoutUtils.PLATFORM_APPLICATION_LAYOUT; -import static jdk.jpackage.internal.model.RuntimeBuilder.getDefaultModulePath; /** * Standard bundler parameters. @@ -56,7 +55,6 @@ */ final class StandardBundlerParam { - private static final String JAVABASEJMOD = "java.base.jmod"; private static final String DEFAULT_VERSION = "1.0"; private static final String DEFAULT_RELEASE = "1"; private static final String[] DEFAULT_JLINK_OPTIONS = { @@ -415,47 +413,14 @@ final class StandardBundlerParam { new BundlerParamInfo<>( Arguments.CLIOptions.MODULE_PATH.getId(), (Class>) (Object)List.class, - p -> getDefaultModulePath(), + p -> JLinkRuntimeBuilder.ensureBaseModuleInModulePath(List.of()), (s, p) -> { List modulePath = Stream.of(s.split(File.pathSeparator)) .map(Path::of) .toList(); - Path javaBasePath = findPathOfModule(modulePath, JAVABASEJMOD); - - // Add the default JDK module path to the module path. - if (javaBasePath == null) { - List jdkModulePath = getDefaultModulePath(); - - if (jdkModulePath != null) { - modulePath = Stream.concat(modulePath.stream(), - jdkModulePath.stream()).toList(); - javaBasePath = findPathOfModule(modulePath, JAVABASEJMOD); - } - } - - if (javaBasePath == null || - !Files.exists(javaBasePath)) { - Log.error(String.format(I18N.getString( - "warning.no.jdk.modules.found"))); - } - - return modulePath; + return JLinkRuntimeBuilder.ensureBaseModuleInModulePath(modulePath); }); - // Returns the path to the JDK modules in the user defined module path. - private static Path findPathOfModule( List modulePath, String moduleName) { - - for (Path path : modulePath) { - Path moduleNamePath = path.resolve(moduleName); - - if (Files.exists(moduleNamePath)) { - return path; - } - } - - return null; - } - static final BundlerParamInfo MODULE = new BundlerParamInfo<>( Arguments.CLIOptions.MODULE.getId(), diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ApplicationLaunchers.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ApplicationLaunchers.java index af52696a54677..ce2925d75e41c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ApplicationLaunchers.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ApplicationLaunchers.java @@ -57,12 +57,12 @@ public List asList() { }).orElseGet(List::of); } - public static Optional fromList(List launchers) { + public static Optional fromList(List launchers) { if (launchers == null || launchers.isEmpty()) { return Optional.empty(); } else { return Optional.of(new ApplicationLaunchers(launchers.getFirst(), - launchers.subList(1, launchers.size()))); + List.copyOf(launchers.subList(1, launchers.size())))); } } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java index c989bcc8915a5..a0f5f077c700c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java @@ -24,6 +24,7 @@ */ package jdk.jpackage.internal.model; +import java.lang.module.ModuleFinder; import java.nio.file.Path; import java.util.List; @@ -46,7 +47,13 @@ public interface RuntimeBuilder { void create(AppImageLayout appImageLayout) throws PackagerException; /** - * Gets the default set of paths where to find Java modules. + * Gets the default set of paths where jlink should look up for system Java + * modules. + * + *

    + * These paths are for {@code jlink} command. Using them with + * {@link ModuleFinder#of(Path...)} may not work as expected: attempt to find + * "java.base" module in these paths will fail. * * @return the default set of paths where to find Java modules */ diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties index 3aa0c69dbd5be..2a81b1c102cb2 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties @@ -78,8 +78,6 @@ error.blocked.option=jlink option [{0}] is not permitted in --jlink-options error.no.name=Name not specified with --name and cannot infer one from app-image error.no.name.advice=Specify name with --name -warning.no.jdk.modules.found=Warning: No JDK Modules found - error.foreign-app-image=Error: Missing .jpackage.xml file in app-image dir "{0}" error.invalid-app-image=Error: app-image dir "{0}" generated by another jpackage version or malformed "{1}" file diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/IdentityWrapper.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/IdentityWrapper.java new file mode 100644 index 0000000000000..0a7a8cc8d4b24 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/IdentityWrapper.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.util; + +import java.util.Objects; + +/** + * Object wrapper implementing {@link Object#equals(Object)} such that it + * returns {@code true} only when the argument is another instance of this class + * wrapping the same object. + *

    + * The class guarantees that {@link Object#equals(Object)} and + * {@link Object#hashCode()} methods of the wrapped object will never be called + * inside of the class methods. + * + * @param the type of the wrapped value + */ +public final class IdentityWrapper { + + public IdentityWrapper(T value) { + this.value = Objects.requireNonNull(value); + } + + public T value() { + return value; + } + + @Override + public int hashCode() { + return System.identityHashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + var other = (IdentityWrapper) obj; + return value == other.value; + } + + @Override + public String toString() { + return String.format("Identity[%s]", value); + } + + private final T value; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Result.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Result.java index 7bd6408183a41..8a61acafe777c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Result.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Result.java @@ -4,7 +4,9 @@ * * 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. + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java index 63be18a5ee8ae..0dff5c26ae279 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java @@ -248,7 +248,7 @@ enum Id { String of(Path path) { if (this == Folder && KNOWN_DIRS.contains(path)) { - return IOUtils.getFileName(path).toString(); + return path.getFileName().toString(); } String result = of(path, prefix, name()); @@ -525,7 +525,7 @@ private String addShortcutComponent(XMLStreamWriter xml, Path launcherPath, } String launcherBasename = PathUtils.replaceSuffix( - IOUtils.getFileName(launcherPath), "").toString(); + launcherPath.getFileName(), "").toString(); Path shortcutPath = folder.getPath(this).resolve(launcherBasename); return addComponent(xml, shortcutPath, Component.Shortcut, unused -> { @@ -712,7 +712,7 @@ public void createDirectory(final Path dir) throws IOException { xml.writeAttribute("Id", Id.Folder.of(dir.getParent())); xml.writeStartElement("Directory"); xml.writeAttribute("Id", Id.Folder.of(dir)); - xml.writeAttribute("Name", IOUtils.getFileName(dir).toString()); + xml.writeAttribute("Name", dir.getFileName().toString()); xml.writeEndElement(); xml.writeEndElement(); } @@ -818,7 +818,7 @@ private void addIcons(XMLStreamWriter xml) throws appImagePathGroup.transform(installedAppImagePathGroup, new PathGroup.TransformHandler() { @Override public void copyFile(Path src, Path dst) throws IOException { - if (IOUtils.getFileName(src).toString().endsWith(".ico")) { + if (src.getFileName().toString().endsWith(".ico")) { icoFiles.add(Map.entry(src, dst)); } } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixPipeline.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixPipeline.java index be15b2028771a..4016019286203 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixPipeline.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixPipeline.java @@ -241,7 +241,7 @@ private void buildMsiWix3(Path msi) throws IOException { lightCmdline.addAll(lightOptions); wixObjs.stream().map(Path::toString).forEach(lightCmdline::add); - Files.createDirectories(IOUtils.getParent(msi)); + Files.createDirectories(msi.getParent()); execute(lightCmdline); } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixSourceConverter.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixSourceConverter.java index 8f98df060ac92..b1ad973b9ed54 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixSourceConverter.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixSourceConverter.java @@ -145,7 +145,7 @@ Status applyTo(OverridableResource resource, Path resourceSaveAsFile) throws IOE newProxyInstance(XMLStreamWriter.class.getClassLoader(), new Class[]{XMLStreamWriter.class}, new NamespaceCleaner(nc. getPrefixToUri(), outputFactory.createXMLStreamWriter(outXml))))); - Files.createDirectories(IOUtils.getParent(resourceSaveAsFile)); + Files.createDirectories(resourceSaveAsFile.getParent()); Files.copy(new ByteArrayInputStream(outXml.toByteArray()), resourceSaveAsFile, StandardCopyOption.REPLACE_EXISTING); } catch (TransformerException | XMLStreamException ex) { diff --git a/test/docs/ProblemList.txt b/test/docs/ProblemList.txt index 914ae21d49fae..83693eacbd111 100644 --- a/test/docs/ProblemList.txt +++ b/test/docs/ProblemList.txt @@ -1,6 +1,6 @@ ########################################################################### # -# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2024, 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 @@ -39,3 +39,5 @@ # More than one label is allowed but must be on the same line. # ############################################################################# + +jdk/javadoc/doccheck/checks/jdkCheckLinks.java 8370249 generic-all diff --git a/test/hotspot/gtest/aarch64/aarch64-asmtest.py b/test/hotspot/gtest/aarch64/aarch64-asmtest.py index bf4f2111999bd..48b19acaa059b 100644 --- a/test/hotspot/gtest/aarch64/aarch64-asmtest.py +++ b/test/hotspot/gtest/aarch64/aarch64-asmtest.py @@ -2143,6 +2143,10 @@ def generate(kind, names): ["facge", "__ sve_fac(Assembler::GE, p1, __ H, p2, z4, z5);", "facge\tp1.h, p2/z, z4.h, z5.h"], ["facge", "__ sve_fac(Assembler::GE, p1, __ S, p2, z4, z5);", "facge\tp1.s, p2/z, z4.s, z5.s"], ["facge", "__ sve_fac(Assembler::GE, p1, __ D, p2, z4, z5);", "facge\tp1.d, p2/z, z4.d, z5.d"], + ["splice", "__ sve_splice(z0, __ B, p0, z1);", "splice\tz0.b, p0, z0.b, z1.b"], + ["splice", "__ sve_splice(z0, __ H, p0, z1);", "splice\tz0.h, p0, z0.h, z1.h"], + ["splice", "__ sve_splice(z0, __ S, p0, z1);", "splice\tz0.s, p0, z0.s, z1.s"], + ["splice", "__ sve_splice(z0, __ D, p0, z1);", "splice\tz0.d, p0, z0.d, z1.d"], # SVE2 instructions ["histcnt", "__ sve_histcnt(z16, __ S, p0, z16, z16);", "histcnt\tz16.s, p0/z, z16.s, z16.s"], ["histcnt", "__ sve_histcnt(z17, __ D, p0, z17, z17);", "histcnt\tz17.d, p0/z, z17.d, z17.d"], diff --git a/test/hotspot/gtest/aarch64/asmtest.out.h b/test/hotspot/gtest/aarch64/asmtest.out.h index 352ea33750ee0..34a5f8ca68e94 100644 --- a/test/hotspot/gtest/aarch64/asmtest.out.h +++ b/test/hotspot/gtest/aarch64/asmtest.out.h @@ -1156,6 +1156,10 @@ __ sve_fac(Assembler::GE, p1, __ H, p2, z4, z5); // facge p1.h, p2/z, z4.h, z5.h __ sve_fac(Assembler::GE, p1, __ S, p2, z4, z5); // facge p1.s, p2/z, z4.s, z5.s __ sve_fac(Assembler::GE, p1, __ D, p2, z4, z5); // facge p1.d, p2/z, z4.d, z5.d + __ sve_splice(z0, __ B, p0, z1); // splice z0.b, p0, z0.b, z1.b + __ sve_splice(z0, __ H, p0, z1); // splice z0.h, p0, z0.h, z1.h + __ sve_splice(z0, __ S, p0, z1); // splice z0.s, p0, z0.s, z1.s + __ sve_splice(z0, __ D, p0, z1); // splice z0.d, p0, z0.d, z1.d __ sve_histcnt(z16, __ S, p0, z16, z16); // histcnt z16.s, p0/z, z16.s, z16.s __ sve_histcnt(z17, __ D, p0, z17, z17); // histcnt z17.d, p0/z, z17.d, z17.d @@ -1445,30 +1449,30 @@ 0x9101a1a0, 0xb10a5cc8, 0xd10810aa, 0xf10fd061, 0x120cb166, 0x321764bc, 0x52174681, 0x720c0227, 0x9241018e, 0xb25a2969, 0xd278b411, 0xf26aad01, - 0x14000000, 0x17ffffd7, 0x140004b7, 0x94000000, - 0x97ffffd4, 0x940004b4, 0x3400000a, 0x34fffa2a, - 0x3400962a, 0x35000008, 0x35fff9c8, 0x350095c8, - 0xb400000b, 0xb4fff96b, 0xb400956b, 0xb500001d, - 0xb5fff91d, 0xb500951d, 0x10000013, 0x10fff8b3, - 0x100094b3, 0x90000013, 0x36300016, 0x3637f836, - 0x36309436, 0x3758000c, 0x375ff7cc, 0x375893cc, + 0x14000000, 0x17ffffd7, 0x140004bb, 0x94000000, + 0x97ffffd4, 0x940004b8, 0x3400000a, 0x34fffa2a, + 0x340096aa, 0x35000008, 0x35fff9c8, 0x35009648, + 0xb400000b, 0xb4fff96b, 0xb40095eb, 0xb500001d, + 0xb5fff91d, 0xb500959d, 0x10000013, 0x10fff8b3, + 0x10009533, 0x90000013, 0x36300016, 0x3637f836, + 0x363094b6, 0x3758000c, 0x375ff7cc, 0x3758944c, 0x128313a0, 0x528a32c7, 0x7289173b, 0x92ab3acc, 0xd2a0bf94, 0xf2c285e8, 0x9358722f, 0x330e652f, 0x53067f3b, 0x93577c53, 0xb34a1aac, 0xd35a4016, 0x13946c63, 0x93c3dbc8, 0x54000000, 0x54fff5a0, - 0x540091a0, 0x54000001, 0x54fff541, 0x54009141, - 0x54000002, 0x54fff4e2, 0x540090e2, 0x54000002, - 0x54fff482, 0x54009082, 0x54000003, 0x54fff423, - 0x54009023, 0x54000003, 0x54fff3c3, 0x54008fc3, - 0x54000004, 0x54fff364, 0x54008f64, 0x54000005, - 0x54fff305, 0x54008f05, 0x54000006, 0x54fff2a6, - 0x54008ea6, 0x54000007, 0x54fff247, 0x54008e47, - 0x54000008, 0x54fff1e8, 0x54008de8, 0x54000009, - 0x54fff189, 0x54008d89, 0x5400000a, 0x54fff12a, - 0x54008d2a, 0x5400000b, 0x54fff0cb, 0x54008ccb, - 0x5400000c, 0x54fff06c, 0x54008c6c, 0x5400000d, - 0x54fff00d, 0x54008c0d, 0x5400000e, 0x54ffefae, - 0x54008bae, 0x5400000f, 0x54ffef4f, 0x54008b4f, + 0x54009220, 0x54000001, 0x54fff541, 0x540091c1, + 0x54000002, 0x54fff4e2, 0x54009162, 0x54000002, + 0x54fff482, 0x54009102, 0x54000003, 0x54fff423, + 0x540090a3, 0x54000003, 0x54fff3c3, 0x54009043, + 0x54000004, 0x54fff364, 0x54008fe4, 0x54000005, + 0x54fff305, 0x54008f85, 0x54000006, 0x54fff2a6, + 0x54008f26, 0x54000007, 0x54fff247, 0x54008ec7, + 0x54000008, 0x54fff1e8, 0x54008e68, 0x54000009, + 0x54fff189, 0x54008e09, 0x5400000a, 0x54fff12a, + 0x54008daa, 0x5400000b, 0x54fff0cb, 0x54008d4b, + 0x5400000c, 0x54fff06c, 0x54008cec, 0x5400000d, + 0x54fff00d, 0x54008c8d, 0x5400000e, 0x54ffefae, + 0x54008c2e, 0x5400000f, 0x54ffef4f, 0x54008bcf, 0xd40658e1, 0xd4014d22, 0xd4046543, 0xd4273f60, 0xd44cad80, 0xd503201f, 0xd503203f, 0xd503205f, 0xd503209f, 0xd50320bf, 0xd503219f, 0xd50323bf, @@ -1689,7 +1693,8 @@ 0x05a14c00, 0x05e14c00, 0x05304001, 0x05314001, 0x05a18610, 0x05e18610, 0x0420bc31, 0x05271e11, 0x6545e891, 0x6585e891, 0x65c5e891, 0x6545c891, - 0x6585c891, 0x65c5c891, 0x45b0c210, 0x45f1c231, + 0x6585c891, 0x65c5c891, 0x052c8020, 0x056c8020, + 0x05ac8020, 0x05ec8020, 0x45b0c210, 0x45f1c231, 0x1e601000, 0x1e603000, 0x1e621000, 0x1e623000, 0x1e641000, 0x1e643000, 0x1e661000, 0x1e663000, 0x1e681000, 0x1e683000, 0x1e6a1000, 0x1e6a3000, diff --git a/test/hotspot/gtest/opto/test_regmask.cpp b/test/hotspot/gtest/opto/test_regmask.cpp index 55dc020b6d066..f367ca4bef42b 100644 --- a/test/hotspot/gtest/opto/test_regmask.cpp +++ b/test/hotspot/gtest/opto/test_regmask.cpp @@ -523,8 +523,8 @@ TEST_VM_ASSERT_MSG(RegMask, offset_mismatch, ".*offset mismatch") { RegMask rm2; rm1.set_infinite_stack(true); rm1.rollover(); - // Cannot copy with different offsets - rm2 = rm1; + // Cannot assign with different offsets + rm2.assignFrom(rm1); } #endif @@ -1241,8 +1241,8 @@ TEST_VM(RegMask, random_copy) { // Randomly initialize source randomize(src); - // Copy source to destination - dst = src; + // Set destination to source + dst.assignFrom(src); // Check equality bool passed = src.gtest_equals(dst); diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index f02ba70ba874e..b1c03553dfc4f 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -133,7 +133,7 @@ containers/docker/TestJcmdWithSideCar.java 8341518 linux-x64 # :hotspot_serviceability # 8239062 and 8270326 only affects macosx-x64,macosx-aarch64 -serviceability/sa/sadebugd/DebugdConnectTest.java 8239062,8270326,8344261 generic-all +serviceability/sa/sadebugd/DebugdConnectTest.java 8239062,8270326 generic-all serviceability/sa/TestRevPtrsForInvokeDynamic.java 8241235 generic-all serviceability/jvmti/vthread/GetThreadStateMountedTest/GetThreadStateMountedTest.java 8318090,8318729 generic-all @@ -153,10 +153,6 @@ serviceability/sa/ClhsdbThreadContext.java 8356704 windows-x64 serviceability/jvmti/stress/StackTrace/NotSuspended/GetStackTraceNotSuspendedStressTest.java 8315980 linux-all,windows-x64 -serviceability/sa/JhsdbThreadInfoTest.java 8344261 generic-all -serviceability/sa/TestJhsdbJstackLock.java 8344261 generic-all -serviceability/attach/RemovingUnixDomainSocketTest.java 8344261 generic-all - ############################################################################# # :hotspot_misc diff --git a/test/hotspot/jtreg/compiler/c2/TestBit.java b/test/hotspot/jtreg/compiler/c2/TestBit.java index b1186a85cae15..01769470d78c0 100644 --- a/test/hotspot/jtreg/compiler/c2/TestBit.java +++ b/test/hotspot/jtreg/compiler/c2/TestBit.java @@ -33,7 +33,7 @@ * @library /test/lib / * * @requires vm.flagless - * @requires os.arch=="aarch64" | os.arch=="amd64" | os.arch == "ppc64" | os.arch == "ppc64le" | os.arch == "riscv64" + * @requires os.arch == "aarch64" | os.arch == "amd64" | os.arch == "x86_64" | os.arch == "ppc64" | os.arch == "ppc64le" | os.arch == "riscv64" * @requires vm.debug == true & vm.compiler2.enabled * * @run driver compiler.c2.TestBit diff --git a/test/hotspot/jtreg/compiler/c2/irTests/RotateLeftNodeIntIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/RotateLeftNodeIntIdealizationTests.java index e3651129daaf3..f5225e8173c3e 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/RotateLeftNodeIntIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/RotateLeftNodeIntIdealizationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -31,7 +31,7 @@ * @summary Test that Ideal transformations of RotateLeftNode* are being performed as expected. * @library /test/lib / * @run driver compiler.c2.irTests.RotateLeftNodeIntIdealizationTests - * @requires os.arch == "x86_64" | os.arch == "aarch64" | (os.arch == "riscv64" & vm.cpu.features ~= ".*zbb.*") + * @requires os.arch == "amd64" | os.arch == "x86_64" | os.arch == "aarch64" | (os.arch == "riscv64" & vm.cpu.features ~= ".*zbb.*") */ public class RotateLeftNodeIntIdealizationTests { diff --git a/test/hotspot/jtreg/compiler/c2/irTests/RotateLeftNodeLongIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/RotateLeftNodeLongIdealizationTests.java index 190f08d348ced..b28d2f6dc8be5 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/RotateLeftNodeLongIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/RotateLeftNodeLongIdealizationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -31,7 +31,7 @@ * @summary Test that Ideal transformations of RotateLeftNode* are being performed as expected. * @library /test/lib / * @run driver compiler.c2.irTests.RotateLeftNodeLongIdealizationTests - * @requires os.arch == "x86_64" | os.arch == "aarch64" | (os.arch == "riscv64" & vm.cpu.features ~= ".*zbb.*") + * @requires os.arch == "amd64" | os.arch == "x86_64" | os.arch == "aarch64" | (os.arch == "riscv64" & vm.cpu.features ~= ".*zbb.*") */ public class RotateLeftNodeLongIdealizationTests { diff --git a/test/hotspot/jtreg/compiler/exceptions/IllegalAccessInCatch.jasm b/test/hotspot/jtreg/compiler/exceptions/IllegalAccessInCatch.jasm new file mode 100644 index 0000000000000..beeffa69a971e --- /dev/null +++ b/test/hotspot/jtreg/compiler/exceptions/IllegalAccessInCatch.jasm @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +super class IllegalAccessInCatch + version 52:0 +{ + /* + static int test() { + try { + return 1 / 0; + } catch (jdk.internal.agent.AgentConfigurationError e1) { + try { + return 0; + } catch (IllegalAccessError e2) { + return 1; + } + } + } + */ + static Method test:"()I" + stack 2 locals 1 + { + iconst_1; + iconst_0; + try t0; + idiv; + endtry t0; + ireturn; + catch t0 jdk/internal/agent/AgentConfigurationError; // loadable but not accessible from unnamed module + stack_frame_type full; + stack_map class java/lang/Throwable; + try t1; + iconst_0; + ireturn; + endtry t1; + catch t1 java/lang/IllegalAccessError; + stack_frame_type full; + stack_map class java/lang/Throwable; + iconst_1; + ireturn; + } +} diff --git a/test/hotspot/jtreg/compiler/exceptions/TestAccessErrorInCatch.java b/test/hotspot/jtreg/compiler/exceptions/TestAccessErrorInCatch.java new file mode 100644 index 0000000000000..46541d760169f --- /dev/null +++ b/test/hotspot/jtreg/compiler/exceptions/TestAccessErrorInCatch.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8367002 + * @summary Compilers might not generate handlers for recursive exceptions + * + * @compile IllegalAccessInCatch.jasm + * @run main/othervm -Xbatch + * -XX:CompileCommand=compileonly,IllegalAccessInCatch*::test + * -XX:-TieredCompilation + * TestAccessErrorInCatch + * @run main/othervm -Xbatch + * -XX:CompileCommand=compileonly,IllegalAccessInCatch*::test + * -XX:TieredStopAtLevel=3 + * TestAccessErrorInCatch + */ + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import java.nio.file.Files; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class TestAccessErrorInCatch { + + public static void main(String[] args) throws Throwable { + Path TEST_CLASSES_DIR = FileSystems.getDefault().getPath(System.getProperty("test.classes")); + byte[] bytes = Files.readAllBytes(TEST_CLASSES_DIR.resolve("IllegalAccessInCatch.class")); + + var l = MethodHandles.lookup().defineHiddenClass(bytes, true); + Class anonClass = l.lookupClass(); + MethodHandle mh = l.findStatic(anonClass, "test", MethodType.methodType(int.class)); + for (int i = 0; i < 16_000; i++) { + invoke(mh); + } + System.out.println(invoke(mh)); + } + + private static int invoke(MethodHandle mh) throws Throwable { + return (int) mh.invokeExact(); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 99a289476ec4d..f0f7aaf383615 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -2840,6 +2840,36 @@ public class IRNode { vectorNode(EXPAND_BITS_VL, "ExpandBitsV", TYPE_LONG); } + public static final String COMPRESS_VB = VECTOR_PREFIX + "COMPRESS_VB" + POSTFIX; + static { + vectorNode(COMPRESS_VB, "CompressV", TYPE_BYTE); + } + + public static final String COMPRESS_VS = VECTOR_PREFIX + "COMPRESS_VS" + POSTFIX; + static { + vectorNode(COMPRESS_VS, "CompressV", TYPE_SHORT); + } + + public static final String COMPRESS_VI = VECTOR_PREFIX + "COMPRESS_VI" + POSTFIX; + static { + vectorNode(COMPRESS_VI, "CompressV", TYPE_INT); + } + + public static final String COMPRESS_VL = VECTOR_PREFIX + "COMPRESS_VL" + POSTFIX; + static { + vectorNode(COMPRESS_VL, "CompressV", TYPE_LONG); + } + + public static final String COMPRESS_VF = VECTOR_PREFIX + "COMPRESS_VF" + POSTFIX; + static { + vectorNode(COMPRESS_VF, "CompressV", TYPE_FLOAT); + } + + public static final String COMPRESS_VD = VECTOR_PREFIX + "COMPRESS_VD" + POSTFIX; + static { + vectorNode(COMPRESS_VD, "CompressV", TYPE_DOUBLE); + } + public static final String EXPAND_VB = VECTOR_PREFIX + "EXPAND_VB" + POSTFIX; static { vectorNode(EXPAND_VB, "ExpandV", TYPE_BYTE); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java index c05124edcd78c..daa2b9765f8e0 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java @@ -106,6 +106,7 @@ public class IREncodingPrinter { "avx512_fp16", "avx512_vnni", "avx512_vbmi", + "avx512_vbmi2", "avx10_2", "bmi2", // AArch64 diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java index 30e1f1c061950..62e474ecb2c67 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java @@ -112,7 +112,10 @@ * memory and split ranges. But we could alternate between same memory * and split ranges, and then different memory but overlapping ranges. * This would also be never aliasing. - * + * - Generate cases that would catch bugs like JDK-8369902: + * - Large long constants, or scales. Probably only possible for MemorySegment. + * - Large number of invar, and reuse of invar so that they could cancle + * to zero, and need to be filtered out. */ public class TestAliasingFuzzer { private static final Random RANDOM = Utils.getRandomInstance(); diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestDoNotFilterNaNSummands.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestDoNotFilterNaNSummands.java new file mode 100644 index 0000000000000..93a1f0b56a8b8 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestDoNotFilterNaNSummands.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8369902 + * @summary Bug in MemPointerParser::canonicalize_raw_summands let to wrong result, because a + * NaN summand was filtered out, instead of making the MemPointer / VPointer invalid. + * @run main/othervm + * -XX:+IgnoreUnrecognizedVMOptions + * -XX:CompileCommand=compileonly,*TestDoNotFilterNaNSummands::test + * -Xbatch + * compiler.loopopts.superword.TestDoNotFilterNaNSummands + * @run main compiler.loopopts.superword.TestDoNotFilterNaNSummands + */ + +package compiler.loopopts.superword; + +// This was the test found by the fuzzer. If you are looking for a simpler example with the same issue, +// please look at TestMemorySegmentFilterSummands::test2. +public class TestDoNotFilterNaNSummands { + static final int N = 100; + static int zero = 0; + + static int[] test() { + int x = -4; + int aI[] = new int[N]; + for (int k = 0; k < N; k++) { + // Note that x is always "-4", and N is a compile time constant. The modulo "%" + // gets optimized with magic numbers and shift/mul/sub trick, in the long domain, + // which somehow creates some large long constant that cannot be represented + // as an int. + int idx = (x >>> 1) % N; + // This is the CountedLoop that we may try to auto vectorize. + // We have a linear access (i) and a constant index access (idx), which eventually + // cross, so there is aliasing. If there is vectorization with an aliasing runtime + // check, this check must fail. + for (int i = 1; i < 63; i++) { + aI[i] = 2; + // The MemPointer / VPointer for the accesses below contain a large constant + // long constant offset that cannot be represented as an int, so the scaleL + // NoOverflowInt becomes NaN. In MemPointerParser::canonicalize_raw_summands + // we are supposed to filter out zero summands, but since we WRONGLY filtered + // out NaNs instead, this summand got filtered out, and later we did not detect + // that the MemPointer contains a NaN. Instead, we just get a "valid" looking + // VPointer, and generate runtime checks that are missing the long constant + // offset, leading to wrong decisions, and hence vectorization even though + // we have aliasing. This means that the accesses from above and below get + // reordered in an illegal way, leading to wrong results. + aI[idx] += 1; + } + for (int i = 0; i < 100; i++) { + // It is a no-op, but the compiler can't know statically that zero=0. + // Seems to be required in the graph, no idea why. + x >>= zero; + } + } + return aI; + } + + // Use the sum as an easy way to compare the results. + public static int sum(int[] aI) { + int sum = 0; + for (int i = 0; i < aI.length; i++) { sum += aI[i]; } + return sum; + } + + public static void main(String[] args) { + // Run once, hopefully before compilation, so get interpreter results. + int[] aIG = test(); + int gold = sum(aIG); + + // Repeat execution, until eventually compilation happens, compare + // compiler results to interpreter results. + for (int k = 0; k < 1000; k++) { + int[] aI = test(); + int val = sum(aI); + if (gold != val) { + System.out.println("Detected wrong result, printing values of arrays:"); + for (int i = 0; i < aI.length; i++) { + System.out.println("at " + i + ": " + aIG[i] + " vs " + aI[i]); + } + throw new RuntimeException("wrong result: " + gold + " " + val); + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentFilterSummands.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentFilterSummands.java new file mode 100644 index 0000000000000..355d8d5383ccd --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentFilterSummands.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.loopopts.superword; + +import java.lang.foreign.*; +import java.util.Set; + +import compiler.lib.ir_framework.*; +import compiler.lib.verify.*; + +/* + * @test + * @bug 8369902 + * @summary Bug in MemPointerParser::canonicalize_raw_summands let to wrong results or assert. + * @library /test/lib / + * @run driver compiler.loopopts.superword.TestMemorySegmentFilterSummands + */ + +public class TestMemorySegmentFilterSummands { + + static long init = 1000; + static long limit = 9000; + + static long invar0 = 0; + static long invar1 = 0; + static long invar2 = 0; + static long invar3 = 0; + static long invar4 = 0; + static long invarX = 0; + + public static final long BIG = 0x200000000L; + public static long big = -BIG; + + static MemorySegment a1 = Arena.ofAuto().allocate(10_000); + static MemorySegment b1 = Arena.ofAuto().allocate(10_000); + static { + for (long i = init; i < limit; i++) { + a1.set(ValueLayout.JAVA_BYTE, i, (byte)((i & 0xf) + 1)); + } + } + + static MemorySegment a2 = MemorySegment.ofArray(new byte[40_000]); + static MemorySegment b2 = a2; + + public static void main(String[] args) { + TestFramework f = new TestFramework(); + f.addFlags("-XX:+IgnoreUnrecognizedVMOptions"); + f.addCrossProductScenarios(Set.of("-XX:-AlignVector", "-XX:+AlignVector"), + Set.of("-XX:-ShortRunningLongLoop", "-XX:+ShortRunningLoop")); + f.start(); + } + + @Test + @IR(counts = {IRNode.STORE_VECTOR, "> 0", + IRNode.LOAD_VECTOR_B, "> 0", + ".*multiversion.*", "= 0"}, // AutoVectorization Predicate SUFFICES, there is no aliasing + phase = CompilePhase.PRINT_IDEAL, + applyIfPlatform = {"64-bit", "true"}, + applyIf = {"AlignVector", "false"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + public static void test1() { + long invar = 0; + invar += invarX; // cancles out with above + invar += invar0; + invar += invar1; + invar += invar2; + invar += invar3; + invar += invar4; + invar -= invarX; // cancles out with above + // invar contains a raw summand for invarX, which has a scaleL=0. It needs to be filtered out. + // The two occurances of invarX are conveniently put in a long chain, so that IGVN cannot see + // that they cancle out, so that they are not optimized out before loop-opts. + for (long i = init; i < limit; i++) { + byte v = a1.get(ValueLayout.JAVA_BYTE, i + invar); + b1.set(ValueLayout.JAVA_BYTE, i + invar, v); + } + } + + @Check(test = "test1") + static void check1() { + Verify.checkEQ(a1, b1); + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + // This test could in principle show vectorization, but it would probably need to do some special + // tricks to only vectorize around the overlap. Still, it could happen that at some point we end + // up multiversioning, and having a vectorized loop that is never entered. + // + // For now, the long constant BIG leads to an invalid VPointer, which means we do not vectorize. + static void test2() { + // At runtime, "BIG + big" is zero. But BIG is a long constant that cannot be represented as + // an int, and so the scaleL NoOverflowInt is a NaN. We should not filter it out from the summands, + // but instead make the MemPointer / VPointer invalid, which prevents vectorization. + long adr = 4L * 5000 + BIG + big; + + for (long i = init; i < limit; i++) { + // The reference to a2 iterates linearly, while the reference to "b2" stays at the same adr. + // But the two alias: in the middle of the "a2" range it crosses over "b2" adr, so the + // aliasing runtime check (if we generate one) should fail. But if "BIG" is just filtered + // out from the summands, we instead just create a runtime check without it, which leads + // to a wrong answer, and the check does not fail, and we get wrong results. + a2.set(ValueLayout.JAVA_INT_UNALIGNED, 4L * i, 0); + int v = b2.get(ValueLayout.JAVA_INT_UNALIGNED, adr); + b2.set(ValueLayout.JAVA_INT_UNALIGNED, adr, v + 1); + } + } + + @Check(test = "test2") + static void check2() { + int s = 0; + for (long i = init; i < limit; i++) { + s += a2.get(ValueLayout.JAVA_INT_UNALIGNED, 4L * i); + } + if (s != 4000) { + throw new RuntimeException("wrong value"); + } + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestMultiversionSlowProjReplacementAndGetCtrl.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestMultiversionSlowProjReplacementAndGetCtrl.java new file mode 100644 index 0000000000000..6d2249cc15cf8 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestMultiversionSlowProjReplacementAndGetCtrl.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8369898 + * @summary Bug in PhaseIdealLoop::create_new_if_for_multiversion, that messed up the + * _loop_or_ctrl data structure while doing SuperWord for a first loop, and + * then get_ctrl asserted for a second loop that was also SuperWord-ed in the + * same loop-opts-phase. + * @run main/othervm + * -XX:CompileCommand=compileonly,*TestMultiversionSlowProjReplacementAndGetCtrl::test + * -XX:CompileCommand=exclude,*TestMultiversionSlowProjReplacementAndGetCtrl::dontinline + * -XX:-TieredCompilation + * -Xbatch + * compiler.loopopts.superword.TestMultiversionSlowProjReplacementAndGetCtrl + * @run main compiler.loopopts.superword.TestMultiversionSlowProjReplacementAndGetCtrl + */ + +package compiler.loopopts.superword; + +public class TestMultiversionSlowProjReplacementAndGetCtrl { + static final int N = 400; + + static void dontinline() {} + + static long test() { + int x = 0; + int arrayI[] = new int[N]; + byte[] arrayB = new byte[N]; + dontinline(); + // CallStaticJava for dontinline + // -> memory Proj + // -> it is used in both the k-indexed and j-indexed loops by their loads/stores. + for (int k = 8; k < 92; ++k) { + // Loop here is multiversioned, and eventually we insert an aliasing runtime check. + // This means that a StoreN (with mem input Proj from above) has its ctrl changed + // from the old multiversion_if_proj to a new region. We have to be careful to update + // the _loop_or_ctrl side-table so that get_ctrl for StoreN is sane. + // + // Below is some nested loop material I could not reduce further. Maybe because + // of loop-opts phase timing. Because we have to SuperWord the k-indexed loop + // above in the same loop-opts-phase as the j-indexed loop below, so that they + // have a shared _loop_or_ctrl data structure. + int y = 6; + while (--y > 0) {} + for (long i = 1; i < 6; i++) { + // I suspect that it is the two array references below that are SuperWord-ed, + // and since we do not manage to statically prove they cannot overlap, we add + // a speculative runtime check, i.e. multiversioning in this case. + arrayI[0] += 1; + arrayI[k] = 0; + try { + x = 2 / k % y; + } catch (ArithmeticException a_e) { + } + } + } + long sum = 0; + for (int j = 0; j < arrayB.length; j++) { + // Load below has mem input from Proj below dontinline + // We look up to the mem input (Proj), and down to uses + // that are Stores, checking in_bb on them, which calls + // get_ctrl on that StoreN from the other loop above. + sum += arrayB[j]; + } + return sum; + } + + public static void main(String[] strArr) { + for (int i = 0; i < 1_000; i++) { + test(); + } + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorCompressTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorCompressTest.java new file mode 100644 index 0000000000000..7ab60885ad2f5 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorCompressTest.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * 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.generators.*; +import compiler.lib.ir_framework.*; +import jdk.incubator.vector.*; +import jdk.test.lib.Asserts; + +/** + * @test + * @bug 8366333 + * @key randomness + * @library /test/lib / + * @summary IR test for VectorAPI compress + * @modules jdk.incubator.vector + * + * @run driver compiler.vectorapi.VectorCompressTest + */ + +public class VectorCompressTest { + static final VectorSpecies B_SPECIES = ByteVector.SPECIES_MAX; + static final VectorSpecies S_SPECIES = ShortVector.SPECIES_MAX; + static final VectorSpecies I_SPECIES = IntVector.SPECIES_MAX; + static final VectorSpecies F_SPECIES = FloatVector.SPECIES_MAX; + static final VectorSpecies L_SPECIES = LongVector.SPECIES_MAX; + static final VectorSpecies D_SPECIES = DoubleVector.SPECIES_MAX; + static final int LENGTH = 512; + static final Generators RD = Generators.G; + static byte[] ba, bb; + static short[] sa, sb; + static int[] ia, ib; + static long[] la, lb; + static float[] fa, fb; + static double[] da, db; + static boolean[] ma; + + static { + ba = new byte[LENGTH]; + bb = new byte[LENGTH]; + sa = new short[LENGTH]; + sb = new short[LENGTH]; + ia = new int[LENGTH]; + ib = new int[LENGTH]; + la = new long[LENGTH]; + lb = new long[LENGTH]; + fa = new float[LENGTH]; + fb = new float[LENGTH]; + da = new double[LENGTH]; + db = new double[LENGTH]; + ma = new boolean[LENGTH]; + + Generator iGen = RD.ints(); + Generator lGen = RD.longs(); + Generator fGen = RD.floats(); + Generator dGen = RD.doubles(); + + for (int i = 0; i < LENGTH; i++) { + ba[i] = iGen.next().byteValue(); + sa[i] = iGen.next().shortValue(); + ma[i] = iGen.next() % 2 == 0; + } + RD.fill(iGen, ia); + RD.fill(lGen, la); + RD.fill(fGen, fa); + RD.fill(dGen, da); + } + + @DontInline + static void verifyVectorCompressByte(int vlen) { + int index = 0; + for (int i = 0; i < vlen; i++) { + if (ma[i]) { + Asserts.assertEquals(ba[i], bb[index++]); + } + } + for (int i = index; i < vlen; i++) { + Asserts.assertEquals((byte)0, bb[i]); + } + } + + @DontInline + static void verifyVectorCompressShort(int vlen) { + int index = 0; + for (int i = 0; i < vlen; i++) { + if (ma[i]) { + Asserts.assertEquals(sa[i], sb[index++]); + } + } + for (int i = index; i < vlen; i++) { + Asserts.assertEquals((short)0, sb[i]); + } + } + + @DontInline + static void verifyVectorCompressInteger(int vlen) { + int index = 0; + for (int i = 0; i < vlen; i++) { + if (ma[i]) { + Asserts.assertEquals(ia[i], ib[index++]); + } + } + for (int i = index; i < vlen; i++) { + Asserts.assertEquals(0, ib[i]); + } + } + + @DontInline + static void verifyVectorCompressLong(int vlen) { + int index = 0; + for (int i = 0; i < vlen; i++) { + if (ma[i]) { + Asserts.assertEquals(la[i], lb[index++]); + } + } + for (int i = index; i < vlen; i++) { + Asserts.assertEquals(0L, lb[i]); + } + } + + @DontInline + static void verifyVectorCompressFloat(int vlen) { + int index = 0; + for (int i = 0; i < vlen; i++) { + if (ma[i]) { + Asserts.assertEquals(fa[i], fb[index++]); + } + } + for (int i = index; i < vlen; i++) { + Asserts.assertEquals(0.0f, fb[i]); + } + } + + @DontInline + static void verifyVectorCompressDouble(int vlen) { + int index = 0; + for (int i = 0; i < vlen; i++) { + if (ma[i]) { + Asserts.assertEquals(da[i], db[index++]); + } + } + for (int i = index; i < vlen; i++) { + Asserts.assertEquals(0.0, db[i]); + } + } + + @Test + @IR(counts = { IRNode.COMPRESS_VB, "= 1" }, + applyIfCPUFeature = { "sve", "true" }) + @IR(counts = { IRNode.COMPRESS_VB, "= 1" }, + applyIfCPUFeatureAnd = {"avx512_vbmi2", "true", "avx512vl", "true"}) + public static void testVectorCompressByte() { + ByteVector av = ByteVector.fromArray(B_SPECIES, ba, 0); + VectorMask m = VectorMask.fromArray(B_SPECIES, ma, 0); + av.compress(m).intoArray(bb, 0); + verifyVectorCompressByte(B_SPECIES.length()); + } + + @Test + @IR(counts = { IRNode.COMPRESS_VS, "= 1" }, + applyIfCPUFeature = { "sve", "true" }) + @IR(counts = { IRNode.COMPRESS_VS, "= 1" }, + applyIfCPUFeatureAnd = {"avx512_vbmi2", "true", "avx512vl", "true"}) + public static void testVectorCompressShort() { + ShortVector av = ShortVector.fromArray(S_SPECIES, sa, 0); + VectorMask m = VectorMask.fromArray(S_SPECIES, ma, 0); + av.compress(m).intoArray(sb, 0); + verifyVectorCompressShort(S_SPECIES.length()); + } + + @Test + @IR(counts = { IRNode.COMPRESS_VI, "= 1" }, + applyIfCPUFeature = { "sve", "true" }) + @IR(counts = { IRNode.COMPRESS_VI, "= 1" }, + applyIfCPUFeatureAnd = {"avx512f", "true", "avx512vl", "true"}) + public static void testVectorCompressInt() { + IntVector av = IntVector.fromArray(I_SPECIES, ia, 0); + VectorMask m = VectorMask.fromArray(I_SPECIES, ma, 0); + av.compress(m).intoArray(ib, 0); + verifyVectorCompressInteger(I_SPECIES.length()); + } + + @Test + @IR(counts = { IRNode.COMPRESS_VL, "= 1" }, + applyIfCPUFeature = { "sve", "true" }) + @IR(counts = { IRNode.COMPRESS_VL, "= 1" }, + applyIfCPUFeatureAnd = {"avx512f", "true", "avx512vl", "true"}) + public static void testVectorCompressLong() { + LongVector av = LongVector.fromArray(L_SPECIES, la, 0); + VectorMask m = VectorMask.fromArray(L_SPECIES, ma, 0); + av.compress(m).intoArray(lb, 0); + verifyVectorCompressLong(L_SPECIES.length()); + } + + @Test + @IR(counts = { IRNode.COMPRESS_VF, "= 1" }, + applyIfCPUFeature = { "sve", "true" }) + @IR(counts = { IRNode.COMPRESS_VF, "= 1" }, + applyIfCPUFeatureAnd = {"avx512f", "true", "avx512vl", "true"}) + public static void testVectorCompressFloat() { + FloatVector av = FloatVector.fromArray(F_SPECIES, fa, 0); + VectorMask m = VectorMask.fromArray(F_SPECIES, ma, 0); + av.compress(m).intoArray(fb, 0); + verifyVectorCompressFloat(F_SPECIES.length()); + } + + @Test + @IR(counts = { IRNode.COMPRESS_VD, "= 1" }, + applyIfCPUFeature = { "sve", "true" }) + @IR(counts = { IRNode.COMPRESS_VD, "= 1" }, + applyIfCPUFeatureAnd = {"avx512f", "true", "avx512vl", "true"}) + public static void testVectorCompressDouble() { + DoubleVector av = DoubleVector.fromArray(D_SPECIES, da, 0); + VectorMask m = VectorMask.fromArray(D_SPECIES, ma, 0); + av.compress(m).intoArray(db, 0); + verifyVectorCompressDouble(D_SPECIES.length()); + } + + public static void main(String[] args) { + TestFramework testFramework = new TestFramework(); + testFramework.setDefaultWarmup(10000) + .addFlags("--add-modules=jdk.incubator.vector") + .start(); + } +} diff --git a/test/hotspot/jtreg/gc/TestUseGCOverheadLimit.java b/test/hotspot/jtreg/gc/TestUseGCOverheadLimit.java new file mode 100644 index 0000000000000..1c1be1599f5c9 --- /dev/null +++ b/test/hotspot/jtreg/gc/TestUseGCOverheadLimit.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package gc; + +/* + * @test id=Parallel + * @requires vm.gc.Parallel + * @requires !vm.debug + * @summary Verifies that the UseGCOverheadLimit functionality works in Parallel GC. + * @library /test/lib + * @run driver gc.TestUseGCOverheadLimit Parallel + */ + +/* + * @test id=G1 + * @requires vm.gc.G1 + * @requires !vm.debug + * @summary Verifies that the UseGCOverheadLimit functionality works in G1 GC. + * @library /test/lib + * @run driver gc.TestUseGCOverheadLimit G1 + */ + +import java.util.Arrays; +import java.util.stream.Stream; + +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestUseGCOverheadLimit { + public static void main(String args[]) throws Exception { + String[] parallelArgs = { + "-XX:+UseParallelGC", + "-XX:NewSize=122m", + "-XX:SurvivorRatio=99", + "-XX:GCHeapFreeLimit=10" + }; + String[] g1Args = { + "-XX:+UseG1GC", + "-XX:GCHeapFreeLimit=5" + }; + + String[] selectedArgs = args[0].equals("G1") ? g1Args : parallelArgs; + + final String[] commonArgs = { + "-XX:-UseCompactObjectHeaders", // Object sizes are calculated such that the heap is tight. + "-XX:ParallelGCThreads=1", // Make GCs take longer. + "-XX:+UseGCOverheadLimit", + "-Xlog:gc=debug", + "-XX:GCTimeLimit=90", // Ease the CPU requirement a little. + "-Xmx128m", + Allocating.class.getName() + }; + + String[] vmArgs = Stream.concat(Arrays.stream(selectedArgs), Arrays.stream(commonArgs)).toArray(String[]::new); + OutputAnalyzer output = ProcessTools.executeLimitedTestJava(vmArgs); + output.shouldNotHaveExitValue(0); + + System.out.println(output.getStdout()); + + Asserts.assertTrue(output.getStdout().indexOf("GC Overhead Limit exceeded too often (5).") != -1, + "Could not find indication that we failed because of GC overhead limit."); + } + + static class Allocating { + public static void main(String[] args) { + Object[] cache = new Object[1024 * 1024 * 2]; + + // Allocate random objects, keeping around most of the data. + for (int i = 0; i < 1024* 1024 * 30; i++) { + Object[] obj = new Object[10]; + cache[i % cache.length] = obj; + } + + System.out.println(cache); + } + } +} diff --git a/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java b/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java index f9f94107f3712..106295960fd0c 100644 --- a/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java +++ b/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java @@ -14,7 +14,7 @@ * * 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 021MALLOC_SIZE-1301 USA. + * 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 diff --git a/test/hotspot/jtreg/serviceability/attach/EarlyDynamicLoad/EarlyDynamicLoad.java b/test/hotspot/jtreg/serviceability/attach/EarlyDynamicLoad/EarlyDynamicLoad.java new file mode 100644 index 0000000000000..cb1596da08cc4 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/attach/EarlyDynamicLoad/EarlyDynamicLoad.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.tools.attach.VirtualMachine; +import com.sun.tools.attach.AgentLoadException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterAll; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.TimeUnit; +import jdk.test.lib.dcmd.PidJcmdExecutor; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.Utils; + +/* + * @test EarlyDynamicLoad + * @summary Test that dynamic attach fails gracefully when the JVM is not in live phase. + * @requires vm.jvmti + * @library /test/lib + * @run junit EarlyDynamicLoad + */ +public class EarlyDynamicLoad { + private static final String EXPECTED_MESSAGE = "Dynamic agent loading is only permitted in the live phase"; + + private static Process child; + + @BeforeAll + static void startAndWaitChild() throws Exception { + child = ProcessTools.createTestJavaProcessBuilder( + "-XX:+StartAttachListener", + "-agentpath:" + Utils.TEST_NATIVE_PATH + File.separator + System.mapLibraryName("EarlyDynamicLoad"), + "--version").start(); + + // Wait until the process enters VMStartCallback + try (InputStream is = child.getInputStream()) { + is.read(); + } + } + + @AfterAll + static void stopChild() throws Exception { + try (OutputStream os = child.getOutputStream()) { + os.write(0); + } + + if (!child.waitFor(5, TimeUnit.SECONDS)) { + child.destroyForcibly(); + throw new AssertionError("Timed out while waiting child process to complete"); + } + + OutputAnalyzer analyzer = new OutputAnalyzer(child); + analyzer.shouldHaveExitValue(0); + analyzer.stderrShouldBeEmpty(); + } + + @Test + public void virtualMachine() throws Exception { + try { + VirtualMachine vm = VirtualMachine.attach(String.valueOf(child.pid())); + vm.loadAgent("some.jar"); + vm.detach(); + throw new AssertionError("Should have failed with AgentLoadException"); + } catch(AgentLoadException exception) { + if (!exception.getMessage().contains(EXPECTED_MESSAGE)) { + throw new AssertionError("Unexpected error message", exception); + } + } + } + + @Test + public void jcmd() throws Exception { + PidJcmdExecutor executor = new PidJcmdExecutor(String.valueOf(child.pid())); + OutputAnalyzer out = executor.execute("JVMTI.agent_load some.jar"); + + out.shouldHaveExitValue(0); + out.stdoutShouldContain(EXPECTED_MESSAGE); + } +} diff --git a/test/hotspot/jtreg/serviceability/attach/EarlyDynamicLoad/libEarlyDynamicLoad.cpp b/test/hotspot/jtreg/serviceability/attach/EarlyDynamicLoad/libEarlyDynamicLoad.cpp new file mode 100644 index 0000000000000..3991926306e4f --- /dev/null +++ b/test/hotspot/jtreg/serviceability/attach/EarlyDynamicLoad/libEarlyDynamicLoad.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include + +extern "C" { + +static void JNICALL VMStartCallback(jvmtiEnv* jvmti, JNIEnv* env) { + putchar('1'); + fflush(stdout); + getchar(); +} + +JNIEXPORT int Agent_OnLoad(JavaVM* vm, char* options, void* reserved) { + jvmtiEnv* jvmti; + if (vm->GetEnv((void**) &jvmti, JVMTI_VERSION_1_0) != JVMTI_ERROR_NONE) { + fprintf(stderr, "JVMTI error occurred during GetEnv\n"); + return JNI_ERR; + } + + jvmtiEventCallbacks callbacks; + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.VMStart = VMStartCallback; + + if (jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks)) != JVMTI_ERROR_NONE) { + fprintf(stderr, "JVMTI error occurred during SetEventCallbacks\n"); + return JNI_ERR; + } + if (jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_START, nullptr) != JVMTI_ERROR_NONE) { + fprintf(stderr, "JVMTI error occurred during SetEventNotificationMode\n"); + return JNI_ERR; + } + + return JNI_OK; +} + +} diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackWithVirtualThread.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackWithVirtualThread.java index acea00a190b15..6d7921c7ed8b4 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackWithVirtualThread.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackWithVirtualThread.java @@ -37,7 +37,6 @@ * @test * @bug 8369505 * @requires vm.hasSA - * @requires (os.arch == "amd64" | os.arch == "x86_64" | os.arch == "aarch64" | os.arch == "riscv64") * @library /test/lib * @run driver TestJhsdbJstackWithVirtualThread */ diff --git a/test/hotspot/jtreg/vmTestbase/gc/vector/CircularListLow/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/vector/CircularListLow/TestDescription.java index 252e3a2d40f92..68fe6779e997d 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/vector/CircularListLow/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/vector/CircularListLow/TestDescription.java @@ -31,6 +31,5 @@ * * @library /vmTestbase * /test/lib - * @requires vm.gc != "Serial" * @run main/othervm/timeout=480 gc.vector.SimpleGC.SimpleGC -ms low -gp circularList(low) */ diff --git a/test/hotspot/jtreg/vmTestbase/gc/vector/LinearListLow/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/vector/LinearListLow/TestDescription.java index bd094045abf0f..8ae86af035db6 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/vector/LinearListLow/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/vector/LinearListLow/TestDescription.java @@ -31,6 +31,5 @@ * * @library /vmTestbase * /test/lib - * @requires vm.gc != "Serial" * @run main/othervm gc.vector.SimpleGC.SimpleGC -ms low -gp linearList(low) */ diff --git a/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/common/PerformChecksHelper.java b/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/common/PerformChecksHelper.java index 3d57bfb6ea9ee..e660dac4fd57f 100644 --- a/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/common/PerformChecksHelper.java +++ b/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/common/PerformChecksHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -136,8 +136,17 @@ private void callMethods(Class clazz) } } } + } catch (InvocationTargetException ite) { + Throwable cause = ite.getCause(); + if (cause != null && (cause instanceof OutOfMemoryError) && cause.getMessage().contains("Metaspace")) { + // avoid string concatenation, which may create more classes. + System.out.println("Got OOME in metaspace in PerformChecksHelper.callMethods(Class clazz). "); + System.out.println("This is possible with -triggerUnloadingByFillingMetaspace"); + } else { + throw ite; + } } catch (OutOfMemoryError e) { - if (e.getMessage().trim().toLowerCase().contains("metaspace")) { + if (e.getMessage().contains("Metaspace")) { // avoid string concatenation, which may create more classes. System.out.println("Got OOME in metaspace in PerformChecksHelper.callMethods(Class clazz). "); System.out.println("This is possible with -triggerUnloadingByFillingMetaspace"); diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index faf4c5e7a9471..1f6bea97407a1 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -676,7 +676,6 @@ javax/swing/AbstractButton/6711682/bug6711682.java 8060765 windows-all,macosx-al javax/swing/JFileChooser/6396844/TwentyThousandTest.java 8198003 generic-all javax/swing/JFileChooser/8194044/FileSystemRootTest.java 8327236 windows-all javax/swing/JPopupMenu/6800513/bug6800513.java 7184956 macosx-all -javax/swing/JTabbedPane/4624207/bug4624207.java 8064922 macosx-all javax/swing/SwingUtilities/TestBadBreak/TestBadBreak.java 8160720 generic-all javax/swing/JFileChooser/bug6798062.java 8146446 windows-all javax/swing/JPopupMenu/4870644/bug4870644.java 8194130 macosx-all,linux-all @@ -743,8 +742,6 @@ sun/tools/jstatd/TestJstatdRmiPort.java 8251259,8293577 jdk/incubator/vector/ShortMaxVectorTests.java 8306592 generic-i586 jdk/incubator/vector/LoadJsvmlTest.java 8305390 windows-x64 -sun/misc/SunMiscSignalTest.java 8370207 generic-all - ############################################################################ # jdk_jfr diff --git a/test/jdk/com/sun/java/swing/plaf/windows/MenuItem/MenuItemAcceleratorColor.java b/test/jdk/com/sun/java/swing/plaf/windows/MenuItem/MenuItemAcceleratorColor.java new file mode 100644 index 0000000000000..f098be4fdbd6a --- /dev/null +++ b/test/jdk/com/sun/java/swing/plaf/windows/MenuItem/MenuItemAcceleratorColor.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.Box; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.KeyStroke; +import javax.swing.UIManager; + +import static javax.swing.BorderFactory.createEmptyBorder; + +/* + * @test id=windows + * @bug 8348760 8365375 8365389 8365625 + * @requires (os.family == "windows") + * @summary Verify that Windows Look & Feel allows changing + * accelerator colors + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuItemAcceleratorColor + */ + +/* + * @test id=classic + * @bug 8348760 8365375 8365389 8365625 + * @requires (os.family == "windows") + * @summary Verify that Windows Classic Look & Feel allows changing + * accelerator colors + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuItemAcceleratorColor classic + */ +public final class MenuItemAcceleratorColor { + private static final String INSTRUCTIONS = + "Click the Menu to open it.\n" + + "\n" + + "Verify that the first and the last menu items render " + + "their accelerators using the default colors, the color " + + "should match that of the menu item itself in regular and " + + "selected states.\n" + + "\n" + + "Verify that the second menu item renders its accelerator " + + "with green and that the color changes to red when selected.\n" + + "\n" + + "Verify that the third menu item renders its accelerator " + + "with magenta and yellow correspondingly.\n" + + "\n" + + "Verify that only the fifth menu item renders its accelerator " + + "with blue; both the fourth and sixth should render their " + + "accelerator with a shade of gray.\n" + + "\n" + + "If the above conditions are satisfied, press the Pass button; " + + "otherwise, press the Fail button."; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel((args.length > 0 && "classic".equals(args[0])) + ? "com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel" + : "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .rows(20) + .columns(60) + .testUI(MenuItemAcceleratorColor::createUI) + .build() + .awaitAndCheck(); + } + + private static Box createInfoPanel() { + Box box = Box.createVerticalBox(); + box.add(new JLabel("Look and Feel: " + + UIManager.getLookAndFeel() + .getName())); + box.add(new JLabel("Java version: " + + System.getProperty("java.runtime.version"))); + return box; + } + + private static JFrame createUI() { + JPanel content = new JPanel(new BorderLayout()); + content.setBorder(createEmptyBorder(8, 8, 8, 8)); + content.add(createInfoPanel(), + BorderLayout.SOUTH); + + JFrame frame = new JFrame("Accelerator colors in Windows L&F"); + frame.setJMenuBar(createMenuBar()); + frame.add(content, BorderLayout.CENTER); + frame.setSize(350, 370); + return frame; + } + + private static JMenuBar createMenuBar() { + JMenuItem first = new JMenuItem("First menu item"); + first.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, + InputEvent.CTRL_DOWN_MASK)); + + // Modify colors for accelerator rendering + Color acceleratorForeground = UIManager.getColor("MenuItem.acceleratorForeground"); + Color acceleratorSelectionForeground = UIManager.getColor("MenuItem.acceleratorSelectionForeground"); + UIManager.put("MenuItem.acceleratorForeground", Color.GREEN); + UIManager.put("MenuItem.acceleratorSelectionForeground", Color.RED); + + JMenuItem second = new JMenuItem("Second menu item"); + second.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, + InputEvent.SHIFT_DOWN_MASK + | InputEvent.CTRL_DOWN_MASK)); + + UIManager.put("MenuItem.acceleratorForeground", Color.MAGENTA); + UIManager.put("MenuItem.acceleratorSelectionForeground", Color.YELLOW); + JMenuItem third = new JMenuItem("Third menu item"); + third.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, + InputEvent.ALT_DOWN_MASK)); + + // Restore colors + UIManager.put("MenuItem.acceleratorForeground", acceleratorForeground); + UIManager.put("MenuItem.acceleratorSelectionForeground", acceleratorSelectionForeground); + + + // Disabled foreground + JMenuItem fourth = new JMenuItem("Fourth menu item"); + fourth.setEnabled(false); + fourth.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, + InputEvent.CTRL_DOWN_MASK)); + + Color disabledForeground = UIManager.getColor("MenuItem.disabledForeground"); + UIManager.put("MenuItem.disabledForeground", Color.BLUE); + + JMenuItem fifth = new JMenuItem("Fifth menu item"); + fifth.setEnabled(false); + fifth.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, + InputEvent.CTRL_DOWN_MASK + | InputEvent.SHIFT_DOWN_MASK)); + + // Restore disabled foreground + UIManager.put("MenuItem.disabledForeground", disabledForeground); + + JMenuItem sixth = new JMenuItem("Sixth menu item"); + sixth.setEnabled(false); + sixth.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, + InputEvent.CTRL_DOWN_MASK + | InputEvent.ALT_DOWN_MASK)); + + + JMenuItem quit = new JMenuItem("Quit"); + quit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, + InputEvent.CTRL_DOWN_MASK)); + + JMenu menu = new JMenu("Menu"); + menu.add(first); + menu.add(second); + menu.add(third); + menu.addSeparator(); + menu.add(fourth); + menu.add(fifth); + menu.add(sixth); + menu.addSeparator(); + menu.add(quit); + + JMenuBar menuBar = new JMenuBar(); + menuBar.add(menu); + + return menuBar; + } +} diff --git a/test/jdk/java/awt/color/ICC_Profile/SerializedFormSize.java b/test/jdk/java/awt/color/ICC_Profile/SerializedFormSize.java new file mode 100644 index 0000000000000..bb8d7a0ab88e5 --- /dev/null +++ b/test/jdk/java/awt/color/ICC_Profile/SerializedFormSize.java @@ -0,0 +1,72 @@ +/* + * 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.color.ColorSpace; +import java.awt.color.ICC_Profile; +import java.io.ByteArrayOutputStream; +import java.io.ObjectOutputStream; + +/** + * @test + * @bug 8369032 + * @summary Checks the size of the serialized ICC_Profile for standard and + * non-standard profiles. + */ +public final class SerializedFormSize { + + private static final ICC_Profile[] PROFILES = { + ICC_Profile.getInstance(ColorSpace.CS_sRGB), + ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB), + ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ), + ICC_Profile.getInstance(ColorSpace.CS_PYCC), + ICC_Profile.getInstance(ColorSpace.CS_GRAY) + }; + + public static void main(String[] args) throws Exception { + for (ICC_Profile profile : PROFILES) { + byte[] data = profile.getData(); + int dataSize = data.length; + int min = 3; // At least version, name and data fields + int max = 200; // Small enough to confirm no data saved + + // Standard profile: should serialize to a small size, no data + test(profile, min, max); + // Non-standard profile: includes full data, but only once + test(ICC_Profile.getInstance(data), dataSize, dataSize + max); + } + } + + private static void test(ICC_Profile p, int min, int max) throws Exception { + try (var bos = new ByteArrayOutputStream(); + var oos = new ObjectOutputStream(bos)) + { + oos.writeObject(p); + int size = bos.size(); + if (size < min || size > max) { + System.err.println("Expected: >= " + min + " and <= " + max); + System.err.println("Actual: " + size); + throw new RuntimeException("Wrong size"); + } + } + } +} diff --git a/test/jdk/java/awt/print/PrinterJob/PageRanges.java b/test/jdk/java/awt/print/PrinterJob/PageRanges.java index accde99ae9565..e80330bae6ceb 100644 --- a/test/jdk/java/awt/print/PrinterJob/PageRanges.java +++ b/test/jdk/java/awt/print/PrinterJob/PageRanges.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -21,58 +21,63 @@ * questions. */ -/** +/* * @test * @bug 6575331 * @key printer * @summary The specified pages should be printed. - * @run main/manual=yesno PageRanges + * @library /java/awt/regtesthelpers + * @library /test/lib + * @build PassFailJFrame + * @build jtreg.SkippedException + * @run main/manual PageRanges */ -import java.awt.*; -import java.awt.print.*; +import java.awt.Graphics; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import jtreg.SkippedException; public class PageRanges implements Printable { - - static String[] instr = { - "This test prints two jobs, and tests that the specified range", - "of pages is printed. You must have a printer installed for this test.", - "In the first dialog, select a page range of 2 to 3, and press OK", - "In the second dialog, select ALL, to print all pages (in total 5 pages).", - "Collect the two print outs and confirm the jobs printed correctly", - }; + private static final String INSTRUCTIONS = """ + This test prints two jobs and tests that the specified range + of pages is printed. + In the first dialog, select a page range of 2 to 3, and press OK. + In the second dialog, select ALL, to print all pages (in total 5 pages). + Collect the two print outs and confirm the jobs are printed correctly. + """; public static void main(String args[]) throws Exception { - for (int i=0;i= 5) { return NO_SUCH_PAGE; } g.drawString("Page : " + (pi+1), 200, 200); - return PAGE_EXISTS; } } diff --git a/test/jdk/java/awt/print/PrinterJob/PolylinePrintingTest.java b/test/jdk/java/awt/print/PrinterJob/PolylinePrintingTest.java index 7d8568c01f98c..c9dcdfdd450f1 100644 --- a/test/jdk/java/awt/print/PrinterJob/PolylinePrintingTest.java +++ b/test/jdk/java/awt/print/PrinterJob/PolylinePrintingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -21,31 +21,56 @@ * questions. */ -/** +/* + * @test * @bug 8041902 * @key printer * @summary Test printing of wide poly lines. - * @run main/manual=yesno PolylinePrintingTest + * @library /java/awt/regtesthelpers + * @library /test/lib + * @build PassFailJFrame + * @build jtreg.SkippedException + * @run main/manual PolylinePrintingTest */ -import java.awt.Dialog; -import java.awt.Frame; -import java.awt.TextArea; import java.awt.BasicStroke; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.Path2D; import java.awt.print.PageFormat; -import java.awt.print.Paper; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; +import jtreg.SkippedException; public class PolylinePrintingTest implements Printable { + private static final String INSTRUCTIONS = """ + Press OK in the print dialog and collect the printed page. + Passing test : Output should show two identical chevrons. + Failing test : The line joins will appear different. + """; + + public static void main(String[] args) throws Exception { + PrinterJob job = PrinterJob.getPrinterJob(); + if (job.getPrintService() == null) { + throw new SkippedException("Printer not configured or available."); + } + + PassFailJFrame passFailJFrame = PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(45) + .build(); + + job.setPrintable(new PolylinePrintingTest()); + if (job.printDialog()) { + job.print(); + } + + passFailJFrame.awaitAndCheck(); + } public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { - if (pageIndex > 0) { return NO_SUCH_PAGE; } @@ -66,7 +91,6 @@ public int print(Graphics graphics, PageFormat pageFormat, private void drawPolylineGOOD(Graphics2D g2d, int[] x2Points, int[] y2Points) { - Path2D polyline = new Path2D.Float(Path2D.WIND_EVEN_ODD, x2Points.length); @@ -83,141 +107,4 @@ private void drawPolylineBAD(Graphics2D g, int[] xp, int[] yp) { g.translate(0, offset); g.drawPolyline(xp, yp, xp.length); } - - public PolylinePrintingTest() throws PrinterException { - PrinterJob job = PrinterJob.getPrinterJob(); - PageFormat pf = job.defaultPage(); - Paper p = pf.getPaper(); - p.setImageableArea(0,0,p.getWidth(), p.getHeight()); - pf.setPaper(p); - job.setPrintable(this, pf); - if (job.printDialog()) { - job.print(); - } - } - - public static void main(String[] args) throws PrinterException { - String[] instructions = { - "You must have a printer available to perform this test.", - "OK the print dialog, and collect the printed page.", - "Passing test : Output should show two identical chevrons.", - "Failing test : The line joins will appear different." - }; - Sysout.createDialog(); - Sysout.printInstructions(instructions); - new PolylinePrintingTest(); - } } - -class Sysout { - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog { - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - show(); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - } - -}// TestDialog class - diff --git a/test/jdk/java/net/httpclient/BufferSize1Test.java b/test/jdk/java/net/httpclient/BufferSize1Test.java new file mode 100644 index 0000000000000..842dc06a630ac --- /dev/null +++ b/test/jdk/java/net/httpclient/BufferSize1Test.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.httpclient.test.lib.common.HttpServerAdapters; +import jdk.internal.net.http.common.Utils; +import jdk.test.lib.net.SimpleSSLContext; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpClient.Version; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +import static java.net.http.HttpClient.Builder.NO_PROXY; +import static java.net.http.HttpClient.Version.HTTP_1_1; +import static java.net.http.HttpClient.Version.HTTP_2; +import static java.net.http.HttpClient.Version.HTTP_3; +import static java.net.http.HttpOption.H3_DISCOVERY; +import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * @test id + * @bug 8367976 + * @summary Verifies that setting the `jdk.httpclient.bufsize` system property + * to its lowest possible value, 1, does not wedge the client + * @library /test/jdk/java/net/httpclient/lib + * /test/lib + * @run junit/othervm -Djdk.httpclient.bufsize=1 BufferSize1Test + */ + +class BufferSize1Test implements HttpServerAdapters { + + @BeforeAll + static void verifyBufferSize() { + assertEquals(1, Utils.BUFSIZE); + } + + static Object[][] testArgs() { + return new Object[][]{ + {HTTP_1_1, false}, + {HTTP_1_1, true}, + {HTTP_2, false}, + {HTTP_2, true}, + {HTTP_3, true} + }; + } + + @ParameterizedTest + @MethodSource("testArgs") + void test(Version version, boolean secure) throws Exception { + + // Create the server + var sslContext = secure || HTTP_3.equals(version) ? new SimpleSSLContext().get() : null; + try (var server = switch (version) { + case HTTP_1_1, HTTP_2 -> HttpTestServer.create(version, sslContext); + case HTTP_3 -> HttpTestServer.create(HTTP_3_URI_ONLY, sslContext); + }) { + + // Add the handler and start the server + var serverHandlerPath = "/" + BufferSize1Test.class.getSimpleName(); + server.addHandler(new HttpTestEchoHandler(), serverHandlerPath); + server.start(); + + // Create the client + try (var client = createClient(version, sslContext)) { + + // Create the request with body to ensure that `ByteBuffer`s + // will be used throughout the entire end-to-end interaction. + byte[] requestBodyBytes = "body".repeat(1000).getBytes(StandardCharsets.US_ASCII); + var request = createRequest(sslContext, server, serverHandlerPath, version, requestBodyBytes); + + // Execute and verify the request. + // Do it twice to cover code paths before and after a protocol upgrade. + requestAndVerify(client, request, requestBodyBytes); + requestAndVerify(client, request, requestBodyBytes); + + } + + } + + } + + private HttpClient createClient(Version version, SSLContext sslContext) { + var clientBuilder = newClientBuilderForH3() + .proxy(NO_PROXY) + .version(version); + if (sslContext != null) { + clientBuilder.sslContext(sslContext); + } + return clientBuilder.build(); + } + + private static HttpRequest createRequest( + SSLContext sslContext, + HttpTestServer server, + String serverHandlerPath, + Version version, + byte[] requestBodyBytes) { + var requestUri = URI.create(String.format( + "%s://%s%s/x", + sslContext == null ? "http" : "https", + server.serverAuthority(), + serverHandlerPath)); + var requestBuilder = HttpRequest + .newBuilder(requestUri) + .version(version) + .POST(HttpRequest.BodyPublishers.ofByteArray(requestBodyBytes)); + if (HTTP_3.equals(version)) { + requestBuilder.setOption(H3_DISCOVERY, HTTP_3_URI_ONLY); + } + return requestBuilder.build(); + } + + private static void requestAndVerify(HttpClient client, HttpRequest request, byte[] requestBodyBytes) + throws IOException, InterruptedException { + var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); + if (response.statusCode() != 200) { + throw new AssertionError("Was expecting status code 200, found: " + response.statusCode()); + } + byte[] responseBodyBytes = response.body(); + int mismatchIndex = Arrays.mismatch(requestBodyBytes, responseBodyBytes); + assertTrue( + mismatchIndex < 0, + String.format( + "Response body (%s bytes) mismatches the request body (%s bytes) at index %s!", + responseBodyBytes.length, requestBodyBytes.length, mismatchIndex)); + } + +} diff --git a/test/jdk/java/net/httpclient/BufferSizePropertyClampTest.java b/test/jdk/java/net/httpclient/BufferSizePropertyClampTest.java new file mode 100644 index 0000000000000..caef0a58a6dc7 --- /dev/null +++ b/test/jdk/java/net/httpclient/BufferSizePropertyClampTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.internal.net.http.common.Utils; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/* + * @test + * @bug 8367976 + * @summary Verifies that the `jdk.httpclient.bufsize` system property is + * clamped correctly + * + * @library /test/lib + * + * @comment `-Djdk.httpclient.HttpClient.log=errors` is needed to enable + * logging and verify that invalid input gets logged + * @run junit/othervm + * -Djdk.httpclient.HttpClient.log=errors + * -Djdk.httpclient.bufsize=-1 + * BufferSizePropertyClampTest + * @run junit/othervm + * -Djdk.httpclient.HttpClient.log=errors + * -Djdk.httpclient.bufsize=0 + * BufferSizePropertyClampTest + * @run junit/othervm + * -Djdk.httpclient.HttpClient.log=errors + * -Djdk.httpclient.bufsize=16385 + * BufferSizePropertyClampTest + */ + +class BufferSizePropertyClampTest { + + /** Anchor to avoid the {@code Logger} instance get GC'ed */ + private static final Logger CLIENT_LOGGER = + Logger.getLogger("jdk.httpclient.HttpClient"); + + private static final List CLIENT_LOGGER_MESSAGES = + Collections.synchronizedList(new ArrayList<>()); + + @BeforeAll + static void registerLoggerHandler() { + CLIENT_LOGGER.addHandler(new Handler() { + + @Override + public void publish(LogRecord record) { + var message = MessageFormat.format(record.getMessage(), record.getParameters()); + CLIENT_LOGGER_MESSAGES.add(message); + } + + @Override + public void flush() { + // Do nothing + } + + @Override + public void close() { + // Do nothing + } + + }); + } + + @Test + void test() { + assertEquals(16384, Utils.BUFSIZE); + assertEquals( + 1, CLIENT_LOGGER_MESSAGES.size(), + "Unexpected number of logger messages: " + CLIENT_LOGGER_MESSAGES); + var expectedMessage = "ERROR: Property value for jdk.httpclient.bufsize=" + + System.getProperty("jdk.httpclient.bufsize") + + " not in [1..16384]: using default=16384"; + assertEquals(expectedMessage, CLIENT_LOGGER_MESSAGES.getFirst().replaceAll(",", "")); + } + +} diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java index 9973272b43513..19f7369125dee 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java @@ -43,8 +43,6 @@ * @run junit OfByteArrayTest * * @comment Using `main/othervm` to initiate tests that depend on a custom-configured JVM - * @run main/othervm -Djdk.httpclient.bufsize=-1 OfByteArrayTest testInvalidBufferSize - * @run main/othervm -Djdk.httpclient.bufsize=0 OfByteArrayTest testInvalidBufferSize * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking "" 0 0 "" * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking a 0 0 "" * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking a 1 0 "" @@ -88,7 +86,6 @@ void testInvalidOffsetOrLength(String contentText, int offset, int length) { */ public static void main(String[] args) throws InterruptedException { switch (args[0]) { - case "testInvalidBufferSize" -> testInvalidBufferSize(); case "testChunking" -> testChunking( parseStringArg(args[1]), Integer.parseInt(args[2]), @@ -102,10 +99,6 @@ private static String parseStringArg(String arg) { return arg == null || arg.trim().equals("\"\"") ? "" : arg; } - private static void testInvalidBufferSize() { - assertThrows(IllegalArgumentException.class, () -> HttpRequest.BodyPublishers.ofByteArray(new byte[1])); - } - private static void testChunking( String contentText, int offset, int length, String expectedBuffersText) throws InterruptedException { diff --git a/test/jdk/java/security/cert/CertPathBuilder/NoExtensions.java b/test/jdk/java/security/cert/CertPathBuilder/NoExtensions.java index 7109d310d51a6..e38d18dc9439f 100644 --- a/test/jdk/java/security/cert/CertPathBuilder/NoExtensions.java +++ b/test/jdk/java/security/cert/CertPathBuilder/NoExtensions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,10 @@ * @test * @bug 4519462 * @summary Verify Sun CertPathBuilder implementation handles certificates with no extensions + * @enablePreview */ +import java.security.PEMDecoder; import java.security.cert.X509Certificate; import java.security.cert.TrustAnchor; import java.security.cert.CollectionCertStoreParameters; @@ -35,16 +37,15 @@ import java.security.cert.CertPathBuilder; import java.security.cert.PKIXBuilderParameters; import java.security.cert.CertPathBuilderResult; -import java.security.cert.CertificateFactory; -import java.security.cert.CRL; import java.security.cert.CertPath; import java.util.HashSet; import java.util.ArrayList; -import java.io.ByteArrayInputStream; // Test based on user code submitted with bug by daniel.boggs@compass.net public class NoExtensions { + private static final PEMDecoder pemDecoder = PEMDecoder.of(); + public static void main(String[] args) { try { NoExtensions certs = new NoExtensions(); @@ -92,7 +93,7 @@ private void doBuild(X509Certificate userCert) throws Exception { // System.out.println(certPath.toString()); } - private static X509Certificate getTrustedCertificate() throws Exception { + private static X509Certificate getTrustedCertificate() { String sCert = "-----BEGIN CERTIFICATE-----\n" + "MIIBezCCASWgAwIBAgIQyWD8dLUoqpJFyDxrfRlrsTANBgkqhkiG9w0BAQQFADAW\n" @@ -104,12 +105,10 @@ private static X509Certificate getTrustedCertificate() throws Exception { + "AKoAZIoRz7jUqlw19DANBgkqhkiG9w0BAQQFAANBACJxAfP57yqaT9N+nRgAOugM\n" + "JG0aN3/peCIvL3p29epRL2xoWFvxpUUlsH2I39OZ6b8+twWCebhkv1I62segXAk=\n" + "-----END CERTIFICATE-----"; - CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream bytes = new ByteArrayInputStream(sCert.getBytes()); - return (X509Certificate)certFactory.generateCertificate(bytes); + return pemDecoder.decode(sCert, X509Certificate.class); } - private static X509Certificate getUserCertificate1() throws Exception { + private static X509Certificate getUserCertificate1() { // this certificate includes an extension String sCert = "-----BEGIN CERTIFICATE-----\n" @@ -123,12 +122,10 @@ private static X509Certificate getUserCertificate1() throws Exception { + "CxeUaYlXmvbxVNkxM65Pplsj3h4ntfZaynmlhahH3YsnnA8wk6xPt04LjSId12RB\n" + "PeuO\n" + "-----END CERTIFICATE-----"; - CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream bytes = new ByteArrayInputStream(sCert.getBytes()); - return (X509Certificate)certFactory.generateCertificate(bytes); + return pemDecoder.decode(sCert, X509Certificate.class); } - private static X509Certificate getUserCertificate2() throws Exception { + private static X509Certificate getUserCertificate2() { // this certificate does not include any extensions String sCert = "-----BEGIN CERTIFICATE-----\n" @@ -140,8 +137,6 @@ private static X509Certificate getUserCertificate2() throws Exception { + "BAUAA0EAQmj9SFHEx66JyAps3ew4pcSS3QvfVZ/6qsNUYCG75rFGcTUPHcXKql9y\n" + "qBT83iNLJ//krjw5Ju0WRPg/buHSww==\n" + "-----END CERTIFICATE-----"; - CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream bytes = new ByteArrayInputStream(sCert.getBytes()); - return (X509Certificate)certFactory.generateCertificate(bytes); + return pemDecoder.decode(sCert, X509Certificate.class); } } diff --git a/test/jdk/java/security/cert/CertPathBuilder/selfIssued/StatusLoopDependency.java b/test/jdk/java/security/cert/CertPathBuilder/selfIssued/StatusLoopDependency.java index 6524239416203..2a1514fae8abf 100644 --- a/test/jdk/java/security/cert/CertPathBuilder/selfIssued/StatusLoopDependency.java +++ b/test/jdk/java/security/cert/CertPathBuilder/selfIssued/StatusLoopDependency.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -33,18 +33,31 @@ * @summary PIT b61: PKI test suite fails because self signed certificates * are being rejected * @modules java.base/sun.security.util + * @enablePreview * @run main/othervm StatusLoopDependency subca * @run main/othervm StatusLoopDependency subci * @run main/othervm StatusLoopDependency alice - * @author Xuelei Fan */ -import java.io.*; -import java.net.SocketException; -import java.util.*; +import java.security.DEREncodable; +import java.security.PEMDecoder; import java.security.Security; -import java.security.cert.*; -import java.security.cert.CertPathValidatorException.BasicReason; +import java.security.cert.CertPathBuilder; +import java.security.cert.CertStore; +import java.security.cert.Certificate; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.PKIXCertPathBuilderResult; +import java.security.cert.TrustAnchor; +import java.security.cert.X509CRL; +import java.security.cert.X509CertSelector; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + import sun.security.util.DerInputStream; /** @@ -183,61 +196,46 @@ public final class StatusLoopDependency { "N9AvUXxGxU4DruoJuFPcrCI=\n" + "-----END X509 CRL-----"; - private static Set generateTrustAnchors() - throws CertificateException { - // generate certificate from cert string - CertificateFactory cf = CertificateFactory.getInstance("X.509"); + private static final PEMDecoder pemDecoder = PEMDecoder.of(); - ByteArrayInputStream is = - new ByteArrayInputStream(selfSignedCertStr.getBytes()); - Certificate selfSignedCert = cf.generateCertificate(is); + private static Set generateTrustAnchors() { + X509Certificate selfSignedCert = pemDecoder.decode(selfSignedCertStr, X509Certificate.class); // generate a trust anchor TrustAnchor anchor = - new TrustAnchor((X509Certificate)selfSignedCert, null); + new TrustAnchor(selfSignedCert, null); return Collections.singleton(anchor); } private static CertStore generateCertificateStore() throws Exception { - Collection entries = new HashSet(); - - // generate certificate from certificate string - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream is; + Collection entries = new HashSet<>(); - is = new ByteArrayInputStream(targetCertStr.getBytes()); - Certificate cert = cf.generateCertificate(is); + DEREncodable cert = pemDecoder.decode(targetCertStr, X509Certificate.class); entries.add(cert); - is = new ByteArrayInputStream(subCaCertStr.getBytes()); - cert = cf.generateCertificate(is); + cert = pemDecoder.decode(subCaCertStr, X509Certificate.class); entries.add(cert); - is = new ByteArrayInputStream(selfSignedCertStr.getBytes()); - cert = cf.generateCertificate(is); + cert = pemDecoder.decode(selfSignedCertStr, X509Certificate.class); entries.add(cert); - is = new ByteArrayInputStream(topCrlIssuerCertStr.getBytes()); - cert = cf.generateCertificate(is); + cert = pemDecoder.decode(topCrlIssuerCertStr, X509Certificate.class); entries.add(cert); - is = new ByteArrayInputStream(subCrlIssuerCertStr.getBytes()); - cert = cf.generateCertificate(is); + cert = pemDecoder.decode(subCrlIssuerCertStr, X509Certificate.class); entries.add(cert); // generate CRL from CRL string - is = new ByteArrayInputStream(topCrlStr.getBytes()); - Collection mixes = cf.generateCRLs(is); - entries.addAll(mixes); + DEREncodable mixes = pemDecoder.decode(topCrlStr, X509CRL.class); + entries.add(mixes); - is = new ByteArrayInputStream(subCrlStr.getBytes()); - mixes = cf.generateCRLs(is); - entries.addAll(mixes); + mixes = pemDecoder.decode(subCrlStr, X509CRL.class); + entries.add(mixes); return CertStore.getInstance("Collection", - new CollectionCertStoreParameters(entries)); + new CollectionCertStoreParameters(entries)); } private static X509CertSelector generateSelector(String name) @@ -245,17 +243,16 @@ private static X509CertSelector generateSelector(String name) X509CertSelector selector = new X509CertSelector(); // generate certificate from certificate string - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream is = null; + String cert; if (name.equals("subca")) { - is = new ByteArrayInputStream(subCaCertStr.getBytes()); + cert = subCaCertStr; } else if (name.equals("subci")) { - is = new ByteArrayInputStream(subCrlIssuerCertStr.getBytes()); + cert = subCrlIssuerCertStr; } else { - is = new ByteArrayInputStream(targetCertStr.getBytes()); + cert = targetCertStr; } - X509Certificate target = (X509Certificate)cf.generateCertificate(is); + X509Certificate target = pemDecoder.decode(cert, X509Certificate.class); byte[] extVal = target.getExtensionValue("2.5.29.14"); if (extVal != null) { DerInputStream in = new DerInputStream(extVal); @@ -269,21 +266,18 @@ private static X509CertSelector generateSelector(String name) return selector; } - private static boolean match(String name, Certificate cert) - throws Exception { - X509CertSelector selector = new X509CertSelector(); + private static boolean match(String name, Certificate cert) { // generate certificate from certificate string - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream is = null; + String newCert; if (name.equals("subca")) { - is = new ByteArrayInputStream(subCaCertStr.getBytes()); + newCert = subCaCertStr; } else if (name.equals("subci")) { - is = new ByteArrayInputStream(subCrlIssuerCertStr.getBytes()); + newCert = subCrlIssuerCertStr; } else { - is = new ByteArrayInputStream(targetCertStr.getBytes()); + newCert = targetCertStr; } - X509Certificate target = (X509Certificate)cf.generateCertificate(is); + X509Certificate target = pemDecoder.decode(newCert, X509Certificate.class); return target.equals(cert); } diff --git a/test/jdk/java/security/cert/CertPathValidator/indirectCRL/CircularCRLTwoLevel.java b/test/jdk/java/security/cert/CertPathValidator/indirectCRL/CircularCRLTwoLevel.java index d67db44e396df..c83f1bc1f5d3a 100644 --- a/test/jdk/java/security/cert/CertPathValidator/indirectCRL/CircularCRLTwoLevel.java +++ b/test/jdk/java/security/cert/CertPathValidator/indirectCRL/CircularCRLTwoLevel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -32,16 +32,34 @@ * * @bug 6720721 * @summary CRL check with circular depency support needed + * @enablePreview * @run main/othervm CircularCRLTwoLevel * @author Xuelei Fan */ -import java.io.*; -import java.net.SocketException; -import java.util.*; +import java.security.DEREncodable; +import java.security.PEMDecoder; import java.security.Security; -import java.security.cert.*; +import java.security.cert.CertPath; +import java.security.cert.CertPathValidator; +import java.security.cert.CertPathValidatorException; import java.security.cert.CertPathValidatorException.BasicReason; +import java.security.cert.CertStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.PKIXParameters; +import java.security.cert.TrustAnchor; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class CircularCRLTwoLevel { @@ -149,25 +167,19 @@ public class CircularCRLTwoLevel { "ARGr6Qu68MYGtLMC6ZqP3u0=\n" + "-----END X509 CRL-----"; + private static final PEMDecoder pemDecoder = PEMDecoder.of(); + private static CertPath generateCertificatePath() throws CertificateException { // generate certificate from cert strings CertificateFactory cf = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream is; - - is = new ByteArrayInputStream(targetCertStr.getBytes()); - Certificate targetCert = cf.generateCertificate(is); - - is = new ByteArrayInputStream(subCaCertStr.getBytes()); - Certificate subCaCert = cf.generateCertificate(is); - - is = new ByteArrayInputStream(selfSignedCertStr.getBytes()); - Certificate selfSignedCert = cf.generateCertificate(is); + Certificate targetCert = pemDecoder.decode(targetCertStr, X509Certificate.class); + Certificate subCaCert = pemDecoder.decode(subCaCertStr, X509Certificate.class); + Certificate selfSignedCert = pemDecoder.decode(selfSignedCertStr, X509Certificate.class); // generate certification path - List list = Arrays.asList(new Certificate[] { - targetCert, subCaCert, selfSignedCert}); + List list = Arrays.asList(targetCert, subCaCert, selfSignedCert); return cf.generateCertPath(list); } @@ -175,42 +187,33 @@ private static CertPath generateCertificatePath() private static Set generateTrustAnchors() throws CertificateException { // generate certificate from cert string - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream is = - new ByteArrayInputStream(selfSignedCertStr.getBytes()); - Certificate selfSignedCert = cf.generateCertificate(is); + final X509Certificate selfSignedCert = pemDecoder.decode(selfSignedCertStr, X509Certificate.class); // generate a trust anchor TrustAnchor anchor = - new TrustAnchor((X509Certificate)selfSignedCert, null); + new TrustAnchor(selfSignedCert, null); return Collections.singleton(anchor); } private static CertStore generateCertificateStore() throws Exception { - Collection entries = new HashSet(); + Collection entries = new HashSet<>(); // generate CRL from CRL string - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream is = - new ByteArrayInputStream(topCrlStr.getBytes()); - Collection mixes = cf.generateCRLs(is); - entries.addAll(mixes); + DEREncodable mixes = pemDecoder.decode(topCrlStr, X509CRL.class); + entries.add(mixes); - is = new ByteArrayInputStream(subCrlStr.getBytes()); - mixes = cf.generateCRLs(is); - entries.addAll(mixes); + mixes = pemDecoder.decode(subCrlStr, X509CRL.class); + entries.add(mixes); // intermediate certs - is = new ByteArrayInputStream(topCrlIssuerCertStr.getBytes()); - mixes = cf.generateCertificates(is); - entries.addAll(mixes); + mixes = pemDecoder.decode(topCrlIssuerCertStr, X509Certificate.class); + entries.add(mixes); - is = new ByteArrayInputStream(subCrlIssuerCertStr.getBytes()); - mixes = cf.generateCertificates(is); - entries.addAll(mixes); + mixes = pemDecoder.decode(subCrlIssuerCertStr, X509Certificate.class); + entries.add(mixes); return CertStore.getInstance("Collection", new CollectionCertStoreParameters(entries)); diff --git a/test/jdk/java/security/cert/CertPathValidator/indirectCRL/CircularCRLTwoLevelRevoked.java b/test/jdk/java/security/cert/CertPathValidator/indirectCRL/CircularCRLTwoLevelRevoked.java index e67934b8a19bb..7ac63072737e9 100644 --- a/test/jdk/java/security/cert/CertPathValidator/indirectCRL/CircularCRLTwoLevelRevoked.java +++ b/test/jdk/java/security/cert/CertPathValidator/indirectCRL/CircularCRLTwoLevelRevoked.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -32,16 +32,34 @@ * * @bug 6720721 * @summary CRL check with circular depency support needed + * @enablePreview * @run main/othervm CircularCRLTwoLevelRevoked * @author Xuelei Fan */ -import java.io.*; -import java.net.SocketException; -import java.util.*; +import java.security.DEREncodable; +import java.security.PEMDecoder; import java.security.Security; -import java.security.cert.*; +import java.security.cert.CertPath; +import java.security.cert.CertPathValidator; +import java.security.cert.CertPathValidatorException; import java.security.cert.CertPathValidatorException.BasicReason; +import java.security.cert.CertStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.PKIXParameters; +import java.security.cert.TrustAnchor; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class CircularCRLTwoLevelRevoked { @@ -150,25 +168,19 @@ public class CircularCRLTwoLevelRevoked { "ARGr6Qu68MYGtLMC6ZqP3u0=\n" + "-----END X509 CRL-----"; + private static final PEMDecoder pemDecoder = PEMDecoder.of(); + private static CertPath generateCertificatePath() throws CertificateException { // generate certificate from cert strings CertificateFactory cf = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream is; - - is = new ByteArrayInputStream(targetCertStr.getBytes()); - Certificate targetCert = cf.generateCertificate(is); - - is = new ByteArrayInputStream(subCaCertStr.getBytes()); - Certificate subCaCert = cf.generateCertificate(is); - - is = new ByteArrayInputStream(selfSignedCertStr.getBytes()); - Certificate selfSignedCert = cf.generateCertificate(is); + Certificate targetCert = pemDecoder.decode(targetCertStr, X509Certificate.class); + Certificate subCaCert = pemDecoder.decode(subCaCertStr, X509Certificate.class); + Certificate selfSignedCert = pemDecoder.decode(selfSignedCertStr, X509Certificate.class); // generate certification path - List list = Arrays.asList(new Certificate[] { - targetCert, subCaCert, selfSignedCert}); + List list = Arrays.asList(targetCert, subCaCert, selfSignedCert); return cf.generateCertPath(list); } @@ -176,45 +188,36 @@ private static CertPath generateCertificatePath() private static Set generateTrustAnchors() throws CertificateException { // generate certificate from cert string - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - - ByteArrayInputStream is = - new ByteArrayInputStream(selfSignedCertStr.getBytes()); - Certificate selfSignedCert = cf.generateCertificate(is); + final X509Certificate selfSignedCert = pemDecoder.decode(selfSignedCertStr, X509Certificate.class); // generate a trust anchor TrustAnchor anchor = - new TrustAnchor((X509Certificate)selfSignedCert, null); + new TrustAnchor(selfSignedCert, null); return Collections.singleton(anchor); } private static CertStore generateCertificateStore() throws Exception { - Collection entries = new HashSet(); + Collection entries = new HashSet<>(); // generate CRL from CRL string CertificateFactory cf = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream is = - new ByteArrayInputStream(topCrlStr.getBytes()); - Collection mixes = cf.generateCRLs(is); - entries.addAll(mixes); + DEREncodable mixes = pemDecoder.decode(topCrlStr, X509CRL.class); + entries.add(mixes); - is = new ByteArrayInputStream(subCrlStr.getBytes()); - mixes = cf.generateCRLs(is); - entries.addAll(mixes); + mixes = pemDecoder.decode(subCrlStr, X509CRL.class); + entries.add(mixes); // intermediate certs - is = new ByteArrayInputStream(topCrlIssuerCertStr.getBytes()); - mixes = cf.generateCertificates(is); - entries.addAll(mixes); + mixes = pemDecoder.decode(topCrlIssuerCertStr, X509Certificate.class); + entries.add(mixes); - is = new ByteArrayInputStream(subCrlIssuerCertStr.getBytes()); - mixes = cf.generateCertificates(is); - entries.addAll(mixes); + mixes = pemDecoder.decode(subCrlIssuerCertStr, X509Certificate.class); + entries.add(mixes); return CertStore.getInstance("Collection", - new CollectionCertStoreParameters(entries)); + new CollectionCertStoreParameters(entries)); } public static void main(String args[]) throws Exception { diff --git a/test/jdk/java/time/tck/java/time/TCKDuration.java b/test/jdk/java/time/tck/java/time/TCKDuration.java index ee3950dec06df..2057e8e8939af 100644 --- a/test/jdk/java/time/tck/java/time/TCKDuration.java +++ b/test/jdk/java/time/tck/java/time/TCKDuration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -71,6 +71,8 @@ import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertThrows; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; @@ -115,6 +117,44 @@ public void test_zero() { assertEquals(Duration.ZERO.getNano(), 0); } + @Test + public void test_min() { + assertEquals(Duration.MIN.getSeconds(), Long.MIN_VALUE); + assertEquals(Duration.MIN.getNano(), 0); + // no duration minimally less than MIN + assertThrows(ArithmeticException.class, () -> Duration.MIN.minusNanos(1)); + } + + @Test + public void test_max() { + assertEquals(Duration.MAX.getSeconds(), Long.MAX_VALUE); + assertEquals(Duration.MAX.getNano(), 999_999_999); + // no duration minimally greater than MAX + assertThrows(ArithmeticException.class, () -> Duration.MAX.plusNanos(1)); + } + + @Test + public void test_constant_properties() { + assertTrue(Duration.MIN.compareTo(Duration.MIN) == 0); + assertEquals(Duration.MIN, Duration.MIN); + assertTrue(Duration.ZERO.compareTo(Duration.ZERO) == 0); + assertEquals(Duration.ZERO, Duration.ZERO); + assertTrue(Duration.MAX.compareTo(Duration.MAX) == 0); + assertEquals(Duration.MAX, Duration.MAX); + + assertTrue(Duration.MIN.compareTo(Duration.ZERO) < 0); + assertTrue(Duration.ZERO.compareTo(Duration.MIN) > 0); + assertNotEquals(Duration.ZERO, Duration.MIN); + + assertTrue(Duration.ZERO.compareTo(Duration.MAX) < 0); + assertTrue(Duration.MAX.compareTo(Duration.ZERO) > 0); + assertNotEquals(Duration.ZERO, Duration.MAX); + + assertTrue(Duration.MIN.compareTo(Duration.MAX) < 0); + assertTrue(Duration.MAX.compareTo(Duration.MIN) > 0); + assertNotEquals(Duration.MIN, Duration.MAX); + } + //----------------------------------------------------------------------- // ofSeconds(long) //----------------------------------------------------------------------- diff --git a/test/jdk/javax/net/ssl/ServerName/SSLSocketSNISensitive.java b/test/jdk/javax/net/ssl/ServerName/SSLSocketSNISensitive.java index 45593b9129e7c..fd1569b4eeafd 100644 --- a/test/jdk/javax/net/ssl/ServerName/SSLSocketSNISensitive.java +++ b/test/jdk/javax/net/ssl/ServerName/SSLSocketSNISensitive.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -30,6 +30,7 @@ * @test * @bug 7068321 * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server + * @enablePreview * @run main/othervm SSLSocketSNISensitive PKIX www.example.com * @run main/othervm SSLSocketSNISensitive SunX509 www.example.com * @run main/othervm SSLSocketSNISensitive PKIX www.example.net @@ -38,19 +39,31 @@ * @run main/othervm SSLSocketSNISensitive SunX509 www.invalid.com */ -import java.net.*; -import java.util.*; -import java.io.*; -import javax.net.ssl.*; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.PEMDecoder; +import java.security.PEMEncoder; +import java.security.interfaces.RSAPrivateKey; +import java.security.spec.PKCS8EncodedKeySpec; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SNIHostName; +import javax.net.ssl.SNIServerName; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; import java.security.Security; import java.security.KeyStore; import java.security.KeyFactory; import java.security.cert.Certificate; import java.security.cert.X509Certificate; -import java.security.cert.CertificateFactory; -import java.security.spec.*; -import java.security.interfaces.*; +import java.util.ArrayList; import java.util.Base64; +import java.util.List; // Note: this test case works only on TLS 1.2 and prior versions because of // the use of MD5withRSA signed certificate. @@ -74,159 +87,167 @@ public class SSLSocketSNISensitive { */ // Certificates and key used in the test. static String trustedCertStr = - "-----BEGIN CERTIFICATE-----\n" + - "MIICkjCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" + - "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" + - "MTIwNDE3MTIwNjA3WhcNMzMwMzI4MTIwNjA3WjA7MQswCQYDVQQGEwJVUzENMAsG\n" + - "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwgZ8wDQYJ\n" + - "KoZIhvcNAQEBBQADgY0AMIGJAoGBANY+7Enp+1S566kLcKk+qe4Ki6BxaHGZ+v7r\n" + - "vLksx9IQZCbAEf4YLbrZhKzKD3SPIJXyxPFwknAknIh3Knk8mViOZks7T8L3GnJr\n" + - "TBaVvDyTzDJum/QYiahfO2qpfN/Oya2UILmqsBAeLyWpzbQsAyWBXfoUtkOUgnzK\n" + - "fk6QAKYrAgMBAAGjgaUwgaIwHQYDVR0OBBYEFEtmQi7jT1ijXOafPsfkrLwSVu9e\n" + - "MGMGA1UdIwRcMFqAFEtmQi7jT1ijXOafPsfkrLwSVu9eoT+kPTA7MQswCQYDVQQG\n" + - "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" + - "Y2WCAQAwDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQEE\n" + - "BQADgYEAkKWxMc4+ODk5WwLXXweB8/IKfVfrizNn0KLEgsZ6xNXFIXDpiPGAFcgl\n" + - "MzFO424JgyvUulsUc/X16Cnuwwntkk6KUG7vEV7h4o9sAV7Cax3gfQE/EZFb4ybn\n" + - "aBm1UsujMKd/ovqbbbxJbmOWzCeo0QfIGleDEyh3NBBZ0i11Kiw=\n" + - "-----END CERTIFICATE-----"; + "-----BEGIN CERTIFICATE-----\n" + + "MIICkjCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" + + "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" + + "MTIwNDE3MTIwNjA3WhcNMzMwMzI4MTIwNjA3WjA7MQswCQYDVQQGEwJVUzENMAsG\n" + + "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwgZ8wDQYJ\n" + + "KoZIhvcNAQEBBQADgY0AMIGJAoGBANY+7Enp+1S566kLcKk+qe4Ki6BxaHGZ+v7r\n" + + "vLksx9IQZCbAEf4YLbrZhKzKD3SPIJXyxPFwknAknIh3Knk8mViOZks7T8L3GnJr\n" + + "TBaVvDyTzDJum/QYiahfO2qpfN/Oya2UILmqsBAeLyWpzbQsAyWBXfoUtkOUgnzK\n" + + "fk6QAKYrAgMBAAGjgaUwgaIwHQYDVR0OBBYEFEtmQi7jT1ijXOafPsfkrLwSVu9e\n" + + "MGMGA1UdIwRcMFqAFEtmQi7jT1ijXOafPsfkrLwSVu9eoT+kPTA7MQswCQYDVQQG\n" + + "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" + + "Y2WCAQAwDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQEE\n" + + "BQADgYEAkKWxMc4+ODk5WwLXXweB8/IKfVfrizNn0KLEgsZ6xNXFIXDpiPGAFcgl\n" + + "MzFO424JgyvUulsUc/X16Cnuwwntkk6KUG7vEV7h4o9sAV7Cax3gfQE/EZFb4ybn\n" + + "aBm1UsujMKd/ovqbbbxJbmOWzCeo0QfIGleDEyh3NBBZ0i11Kiw=\n" + + "-----END CERTIFICATE-----"; // web server certificate, www.example.com static String targetCertStr_A = - "-----BEGIN CERTIFICATE-----\n" + - "MIICVTCCAb6gAwIBAgIBAjANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" + - "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" + - "MTIwNDE3MTIwNjA4WhcNMzIwMTAzMTIwNjA4WjBVMQswCQYDVQQGEwJVUzENMAsG\n" + - "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxGDAWBgNV\n" + - "BAMTD3d3dy5leGFtcGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" + - "4zFp3PZNzsd3ZwG6FNNWO9eSN+UBymlf8oCwpKJM2tIinmMWvWIXnlx/2UXIfSAq\n" + - "QEG3aXkAFyEiGGpQlBbqcfrESsHsiz2pnnm5dG2v/eS0Bwz1jmcuNmwnh3UQw2Vl\n" + - "+BLk8ukdrLjiCT8jARiHExYf1Xg+wUqQ9y8NV26hdaUCAwEAAaNPME0wCwYDVR0P\n" + - "BAQDAgPoMB0GA1UdDgQWBBQwtx+gqzn2w4y82brXlp7tqBYEZDAfBgNVHSMEGDAW\n" + - "gBRLZkIu409Yo1zmnz7H5Ky8ElbvXjANBgkqhkiG9w0BAQQFAAOBgQAJWo8B6Ud+\n" + - "/OU+UcZLihlfMX02OSlK2ZB7mfqpj2G3JT9yb0A+VbY3uuajmaYYIIxl3kXGz/n8\n" + - "M2Q/Ux/MDxG+IFKHC26Kuj4dAQgzjq2pILVPTE2QnaQTNCsgVZtTaC47SG9FRSoC\n" + - "qvnIvn/oTpKSqus76I1cR4joDtiV2OEuVw==\n" + - "-----END CERTIFICATE-----"; + "-----BEGIN CERTIFICATE-----\n" + + "MIICVTCCAb6gAwIBAgIBAjANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" + + "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" + + "MTIwNDE3MTIwNjA4WhcNMzIwMTAzMTIwNjA4WjBVMQswCQYDVQQGEwJVUzENMAsG\n" + + "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxGDAWBgNV\n" + + "BAMTD3d3dy5leGFtcGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" + + "4zFp3PZNzsd3ZwG6FNNWO9eSN+UBymlf8oCwpKJM2tIinmMWvWIXnlx/2UXIfSAq\n" + + "QEG3aXkAFyEiGGpQlBbqcfrESsHsiz2pnnm5dG2v/eS0Bwz1jmcuNmwnh3UQw2Vl\n" + + "+BLk8ukdrLjiCT8jARiHExYf1Xg+wUqQ9y8NV26hdaUCAwEAAaNPME0wCwYDVR0P\n" + + "BAQDAgPoMB0GA1UdDgQWBBQwtx+gqzn2w4y82brXlp7tqBYEZDAfBgNVHSMEGDAW\n" + + "gBRLZkIu409Yo1zmnz7H5Ky8ElbvXjANBgkqhkiG9w0BAQQFAAOBgQAJWo8B6Ud+\n" + + "/OU+UcZLihlfMX02OSlK2ZB7mfqpj2G3JT9yb0A+VbY3uuajmaYYIIxl3kXGz/n8\n" + + "M2Q/Ux/MDxG+IFKHC26Kuj4dAQgzjq2pILVPTE2QnaQTNCsgVZtTaC47SG9FRSoC\n" + + "qvnIvn/oTpKSqus76I1cR4joDtiV2OEuVw==\n" + + "-----END CERTIFICATE-----"; // Private key in the format of PKCS#8 static String targetPrivateKey_A = - "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOMxadz2Tc7Hd2cB\n" + - "uhTTVjvXkjflAcppX/KAsKSiTNrSIp5jFr1iF55cf9lFyH0gKkBBt2l5ABchIhhq\n" + - "UJQW6nH6xErB7Is9qZ55uXRtr/3ktAcM9Y5nLjZsJ4d1EMNlZfgS5PLpHay44gk/\n" + - "IwEYhxMWH9V4PsFKkPcvDVduoXWlAgMBAAECgYAqX2nuIyXp3fvgA0twXOYlbRRB\n" + - "Rn3qAXM6qFPJsNeCrFR2k+aG1cev6nKR1FkLNTeMGnWZv06MAcr5IML8i7WXyG4C\n" + - "LY/C0gedn94FDKFlln+bTENwQTGjn4lKysDA+IuNpasTeMCajbic+dPByhIdTOjZ\n" + - "iMCyxbLfpk40zQopVQJBAPyfGmkeHB3GjdbdgujWCGKb2UxBa4O8dy3O4l2yizTn\n" + - "uUqMGcwGY4ciNSVvZQ7jKo4vDmkSuYib4/woPChaNfMCQQDmO0BQuSWYGNtSwV35\n" + - "lafZfX1dNCLKm1iNA6A12evXgvQiE9WT4mqionig0VZW16HtiY4/BkHOcos/K9Um\n" + - "ARQHAkA8mkaRtSF1my5nv1gqVz5Hua+VdZQ/VDUbDiiL5cszc+ulkJqXsWirAG/T\n" + - "fTe3LJQG7A7+8fkEZrF4yoY0AAA1AkEAotokezULj5N9iAL5SzL9wIzQYV4ggfny\n" + - "YATBjXXxKccakwQ+ndWZIiMUeoS4ssLialhTgucVI0fIkU2a/r/ifwJAc6e+5Pvh\n" + - "MghQj/U788Od/v6rgqz/NGsduZ7uilCMcWiwA73OR2MHMH/OIuoofuEPrfuV9isV\n" + - "xVXhgpKfP/pdOA=="; + "-----BEGIN PRIVATE KEY-----\n" + + "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOMxadz2Tc7Hd2cB\n" + + "uhTTVjvXkjflAcppX/KAsKSiTNrSIp5jFr1iF55cf9lFyH0gKkBBt2l5ABchIhhq\n" + + "UJQW6nH6xErB7Is9qZ55uXRtr/3ktAcM9Y5nLjZsJ4d1EMNlZfgS5PLpHay44gk/\n" + + "IwEYhxMWH9V4PsFKkPcvDVduoXWlAgMBAAECgYAqX2nuIyXp3fvgA0twXOYlbRRB\n" + + "Rn3qAXM6qFPJsNeCrFR2k+aG1cev6nKR1FkLNTeMGnWZv06MAcr5IML8i7WXyG4C\n" + + "LY/C0gedn94FDKFlln+bTENwQTGjn4lKysDA+IuNpasTeMCajbic+dPByhIdTOjZ\n" + + "iMCyxbLfpk40zQopVQJBAPyfGmkeHB3GjdbdgujWCGKb2UxBa4O8dy3O4l2yizTn\n" + + "uUqMGcwGY4ciNSVvZQ7jKo4vDmkSuYib4/woPChaNfMCQQDmO0BQuSWYGNtSwV35\n" + + "lafZfX1dNCLKm1iNA6A12evXgvQiE9WT4mqionig0VZW16HtiY4/BkHOcos/K9Um\n" + + "ARQHAkA8mkaRtSF1my5nv1gqVz5Hua+VdZQ/VDUbDiiL5cszc+ulkJqXsWirAG/T\n" + + "fTe3LJQG7A7+8fkEZrF4yoY0AAA1AkEAotokezULj5N9iAL5SzL9wIzQYV4ggfny\n" + + "YATBjXXxKccakwQ+ndWZIiMUeoS4ssLialhTgucVI0fIkU2a/r/ifwJAc6e+5Pvh\n" + + "MghQj/U788Od/v6rgqz/NGsduZ7uilCMcWiwA73OR2MHMH/OIuoofuEPrfuV9isV\n" + + "xVXhgpKfP/pdOA==\n" + + "-----END PRIVATE KEY-----"; // web server certificate, www.example.net static String targetCertStr_B = - "-----BEGIN CERTIFICATE-----\n" + - "MIICVTCCAb6gAwIBAgIBBDANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" + - "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" + - "MTIwNDE3MTIwNjA5WhcNMzIwMTAzMTIwNjA5WjBVMQswCQYDVQQGEwJVUzENMAsG\n" + - "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxGDAWBgNV\n" + - "BAMTD3d3dy5leGFtcGxlLm5ldDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" + - "2VlzF1fvWYczDChrUeJiLJ1M/dIShCaOTfYGiXfQGEZCAWTacUclwr+rVMnZ75/c\n" + - "wwg5pNdXRijxMil8DBTS1gFcIFQhosLHvzIAe6ULlg/xB+/L6KBz+NTWfo/2KF6t\n" + - "xatmcToNrCcwi7eUOfbzQje65Tizs56jJYem2m7Rk0ECAwEAAaNPME0wCwYDVR0P\n" + - "BAQDAgPoMB0GA1UdDgQWBBQT/FR0cAWcZQ7h0X79KGki34OSQjAfBgNVHSMEGDAW\n" + - "gBRLZkIu409Yo1zmnz7H5Ky8ElbvXjANBgkqhkiG9w0BAQQFAAOBgQB67cPIT6fz\n" + - "6Ws8fBpYgW2ad4ci66i1WduBD9CpGFE+jRK2feRj6hvYBXocKj0AMWUFIEB2E3hA\n" + - "oIjxcf1GxIpHVl9DjlhxqXbA0Ktl7/NGNRlDSLTizOTl3FB1mMTlOGvXDVmpcFhl\n" + - "HuoP1hYvhTsBwPx5igGNchuPtDIUzL2mXw==\n" + - "-----END CERTIFICATE-----"; + "-----BEGIN CERTIFICATE-----\n" + + "MIICVTCCAb6gAwIBAgIBBDANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" + + "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" + + "MTIwNDE3MTIwNjA5WhcNMzIwMTAzMTIwNjA5WjBVMQswCQYDVQQGEwJVUzENMAsG\n" + + "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxGDAWBgNV\n" + + "BAMTD3d3dy5leGFtcGxlLm5ldDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" + + "2VlzF1fvWYczDChrUeJiLJ1M/dIShCaOTfYGiXfQGEZCAWTacUclwr+rVMnZ75/c\n" + + "wwg5pNdXRijxMil8DBTS1gFcIFQhosLHvzIAe6ULlg/xB+/L6KBz+NTWfo/2KF6t\n" + + "xatmcToNrCcwi7eUOfbzQje65Tizs56jJYem2m7Rk0ECAwEAAaNPME0wCwYDVR0P\n" + + "BAQDAgPoMB0GA1UdDgQWBBQT/FR0cAWcZQ7h0X79KGki34OSQjAfBgNVHSMEGDAW\n" + + "gBRLZkIu409Yo1zmnz7H5Ky8ElbvXjANBgkqhkiG9w0BAQQFAAOBgQB67cPIT6fz\n" + + "6Ws8fBpYgW2ad4ci66i1WduBD9CpGFE+jRK2feRj6hvYBXocKj0AMWUFIEB2E3hA\n" + + "oIjxcf1GxIpHVl9DjlhxqXbA0Ktl7/NGNRlDSLTizOTl3FB1mMTlOGvXDVmpcFhl\n" + + "HuoP1hYvhTsBwPx5igGNchuPtDIUzL2mXw==\n" + + "-----END CERTIFICATE-----"; static String targetPrivateKey_B = - "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANlZcxdX71mHMwwo\n" + - "a1HiYiydTP3SEoQmjk32Bol30BhGQgFk2nFHJcK/q1TJ2e+f3MMIOaTXV0Yo8TIp\n" + - "fAwU0tYBXCBUIaLCx78yAHulC5YP8Qfvy+igc/jU1n6P9ihercWrZnE6DawnMIu3\n" + - "lDn280I3uuU4s7OeoyWHptpu0ZNBAgMBAAECgYEAl19H26sfhD+32rDPxZCgBShs\n" + - "dZ33zVe45i0Bcn4iTLWpxKTDyf7eGps4rO2DvfKdYqt40ggzvSZIjUH9JcDe8GmG\n" + - "d3m0ILB7pg4jsFlpyeHpTO8grPLxA1G9s3o0DoFpz/rooqgFfe/DrRDmRoOSkgfV\n" + - "/gseIbgJHRO/Ctyvdh0CQQD6uFd0HxhH1jl/JzvPzIH4LSnPcdEh9zsMEb6uzh75\n" + - "9qL+IHD5N2I/pYZTKqDFIwhJf701+LKag55AX/zrDt7rAkEA3e00AbnwanDMa6Wj\n" + - "+gFekUQveSVra38LiihzCkyVvQpFjbiF1rUhSNQ0dpU5/hmrYF0C6H9VXAesfkUY\n" + - "WhpDgwJAYjgZOop77piDycZK7isFt32p5XSHIzFBVocVFlH1XKM8UyXOXDNQL/Le\n" + - "XnJSrSf+NRzvuNcG0PVC56Ey6brXpQJAY4M4vcltt5zq3R5CQBmbGRJ1IyKXX3Vx\n" + - "bDslEqoyvri7ZYgnY5aG3UxiVgYmIf3KrgQnCLAIS6MZQumiuMxsFwJAK5pEG063\n" + - "9ngUof4fDMvZphqZjZR1zMKz/V/9ge0DWBINaqFgsgebNu+MyImsC8C6WKjGmV/2\n" + - "f1MY0D7sC2vU/Q=="; + "-----BEGIN PRIVATE KEY-----\n" + + "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANlZcxdX71mHMwwo\n" + + "a1HiYiydTP3SEoQmjk32Bol30BhGQgFk2nFHJcK/q1TJ2e+f3MMIOaTXV0Yo8TIp\n" + + "fAwU0tYBXCBUIaLCx78yAHulC5YP8Qfvy+igc/jU1n6P9ihercWrZnE6DawnMIu3\n" + + "lDn280I3uuU4s7OeoyWHptpu0ZNBAgMBAAECgYEAl19H26sfhD+32rDPxZCgBShs\n" + + "dZ33zVe45i0Bcn4iTLWpxKTDyf7eGps4rO2DvfKdYqt40ggzvSZIjUH9JcDe8GmG\n" + + "d3m0ILB7pg4jsFlpyeHpTO8grPLxA1G9s3o0DoFpz/rooqgFfe/DrRDmRoOSkgfV\n" + + "/gseIbgJHRO/Ctyvdh0CQQD6uFd0HxhH1jl/JzvPzIH4LSnPcdEh9zsMEb6uzh75\n" + + "9qL+IHD5N2I/pYZTKqDFIwhJf701+LKag55AX/zrDt7rAkEA3e00AbnwanDMa6Wj\n" + + "+gFekUQveSVra38LiihzCkyVvQpFjbiF1rUhSNQ0dpU5/hmrYF0C6H9VXAesfkUY\n" + + "WhpDgwJAYjgZOop77piDycZK7isFt32p5XSHIzFBVocVFlH1XKM8UyXOXDNQL/Le\n" + + "XnJSrSf+NRzvuNcG0PVC56Ey6brXpQJAY4M4vcltt5zq3R5CQBmbGRJ1IyKXX3Vx\n" + + "bDslEqoyvri7ZYgnY5aG3UxiVgYmIf3KrgQnCLAIS6MZQumiuMxsFwJAK5pEG063\n" + + "9ngUof4fDMvZphqZjZR1zMKz/V/9ge0DWBINaqFgsgebNu+MyImsC8C6WKjGmV/2\n" + + "f1MY0D7sC2vU/Q==\n" + + "-----END PRIVATE KEY-----"; // web server certificate, www.invalid.com static String targetCertStr_C = - "-----BEGIN CERTIFICATE-----\n" + - "MIICVTCCAb6gAwIBAgIBAzANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" + - "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" + - "MTIwNDE3MTIwNjA5WhcNMzIwMTAzMTIwNjA5WjBVMQswCQYDVQQGEwJVUzENMAsG\n" + - "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxGDAWBgNV\n" + - "BAMTD3d3dy5pbnZhbGlkLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" + - "q6MyQwzCr2nJ41l0frmHL0qULSyW51MhevBC+1W28i0LE/efrmpwV3LdnlQEGFak\n" + - "DLDwtnff3iru8dSMcA7KdWVkivsE7ZTP+qFDaWBAy7XXiSsv6yZ2Nh4jJb0YcD28\n" + - "45zk2nAl5Az1/PuoTi1vpQxzFZKuBm1HGgz3MEZvBvMCAwEAAaNPME0wCwYDVR0P\n" + - "BAQDAgPoMB0GA1UdDgQWBBRRMifrND015Nm8N6gV5X7cg1YjjjAfBgNVHSMEGDAW\n" + - "gBRLZkIu409Yo1zmnz7H5Ky8ElbvXjANBgkqhkiG9w0BAQQFAAOBgQBjkUO6Ri/B\n" + - "uDC2gDMIyL5+NTe/1dPPQYM4HhCNa/KQYvU5lzCKO9Vpa+i+nyrUNNXUu8Tkyq4Y\n" + - "A+aGSm6+FT/i9rFwkYUdorBtD3KfQiwTIWrVERXBkWI5iZNaVZhx0TFy4vUpf65d\n" + - "QtwkbHpC66fdKc2EdLXkuY9KkmtZZJJ7YA==\n" + - "-----END CERTIFICATE-----"; + "-----BEGIN CERTIFICATE-----\n" + + "MIICVTCCAb6gAwIBAgIBAzANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" + + "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" + + "MTIwNDE3MTIwNjA5WhcNMzIwMTAzMTIwNjA5WjBVMQswCQYDVQQGEwJVUzENMAsG\n" + + "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxGDAWBgNV\n" + + "BAMTD3d3dy5pbnZhbGlkLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" + + "q6MyQwzCr2nJ41l0frmHL0qULSyW51MhevBC+1W28i0LE/efrmpwV3LdnlQEGFak\n" + + "DLDwtnff3iru8dSMcA7KdWVkivsE7ZTP+qFDaWBAy7XXiSsv6yZ2Nh4jJb0YcD28\n" + + "45zk2nAl5Az1/PuoTi1vpQxzFZKuBm1HGgz3MEZvBvMCAwEAAaNPME0wCwYDVR0P\n" + + "BAQDAgPoMB0GA1UdDgQWBBRRMifrND015Nm8N6gV5X7cg1YjjjAfBgNVHSMEGDAW\n" + + "gBRLZkIu409Yo1zmnz7H5Ky8ElbvXjANBgkqhkiG9w0BAQQFAAOBgQBjkUO6Ri/B\n" + + "uDC2gDMIyL5+NTe/1dPPQYM4HhCNa/KQYvU5lzCKO9Vpa+i+nyrUNNXUu8Tkyq4Y\n" + + "A+aGSm6+FT/i9rFwkYUdorBtD3KfQiwTIWrVERXBkWI5iZNaVZhx0TFy4vUpf65d\n" + + "QtwkbHpC66fdKc2EdLXkuY9KkmtZZJJ7YA==\n" + + "-----END CERTIFICATE-----"; static String targetPrivateKey_C = - "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKujMkMMwq9pyeNZ\n" + - "dH65hy9KlC0sludTIXrwQvtVtvItCxP3n65qcFdy3Z5UBBhWpAyw8LZ3394q7vHU\n" + - "jHAOynVlZIr7BO2Uz/qhQ2lgQMu114krL+smdjYeIyW9GHA9vOOc5NpwJeQM9fz7\n" + - "qE4tb6UMcxWSrgZtRxoM9zBGbwbzAgMBAAECgYASJDK40Y12Wvki1Z6xkkyOnBRj\n" + - "XfYpRykfxGtgA2RN3qLwHlk7Zzaul46DIKA6LlYynTUkJDF+Ww1cdDnP0lBlwcmM\n" + - "iD0ck3zYyYBLhQHuVbkK3SYE+ANRhM0icvvqANP2at/U4awQcPNEae/KCiecLNu3\n" + - "CJGqyhPDdrEAqPuJGQJBAN46pQC6l3yrcSYE2s53jSmsm2HVVOFlFXjU6k/RMTxG\n" + - "FfDJtGUAOQ37rPQ06ugr/gjLAmmPp+FXozaBdA32D80CQQDFuGRgv3WYqbglIcRL\n" + - "JRs6xlj9w1F97s/aiUenuwhIPNiUoRbV7mnNuZ/sGF0svOVE7SazRjuFX6UqL9Y9\n" + - "HzG/AkEA170pCI8cl4w8eUNHRB9trGKEKjMXhwVCFh7lJf2ZBcGodSzr8w2HVhrZ\n" + - "Ke7hiemDYffrbJ1oxmv05+o+x3r0lQJBAL6adVm2+FyFMFnLZXmzeb59O4jWY5bt\n" + - "Qz6/HG6bpO5OidMuP99YCHMkQQDOs/PO3Y5GuAoW6IY4n/Y9S2B80+0CQBl1/H9/\n" + - "0n/vrb6vW6Azds49tuS82RFAnOhtwTyBEajs08WF8rZQ3WD2RHJnH0+jjfL0anIp\n" + - "dQBSeNN7s7b6rRk="; + "-----BEGIN PRIVATE KEY-----\n" + + "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKujMkMMwq9pyeNZ\n" + + "dH65hy9KlC0sludTIXrwQvtVtvItCxP3n65qcFdy3Z5UBBhWpAyw8LZ3394q7vHU\n" + + "jHAOynVlZIr7BO2Uz/qhQ2lgQMu114krL+smdjYeIyW9GHA9vOOc5NpwJeQM9fz7\n" + + "qE4tb6UMcxWSrgZtRxoM9zBGbwbzAgMBAAECgYASJDK40Y12Wvki1Z6xkkyOnBRj\n" + + "XfYpRykfxGtgA2RN3qLwHlk7Zzaul46DIKA6LlYynTUkJDF+Ww1cdDnP0lBlwcmM\n" + + "iD0ck3zYyYBLhQHuVbkK3SYE+ANRhM0icvvqANP2at/U4awQcPNEae/KCiecLNu3\n" + + "CJGqyhPDdrEAqPuJGQJBAN46pQC6l3yrcSYE2s53jSmsm2HVVOFlFXjU6k/RMTxG\n" + + "FfDJtGUAOQ37rPQ06ugr/gjLAmmPp+FXozaBdA32D80CQQDFuGRgv3WYqbglIcRL\n" + + "JRs6xlj9w1F97s/aiUenuwhIPNiUoRbV7mnNuZ/sGF0svOVE7SazRjuFX6UqL9Y9\n" + + "HzG/AkEA170pCI8cl4w8eUNHRB9trGKEKjMXhwVCFh7lJf2ZBcGodSzr8w2HVhrZ\n" + + "Ke7hiemDYffrbJ1oxmv05+o+x3r0lQJBAL6adVm2+FyFMFnLZXmzeb59O4jWY5bt\n" + + "Qz6/HG6bpO5OidMuP99YCHMkQQDOs/PO3Y5GuAoW6IY4n/Y9S2B80+0CQBl1/H9/\n" + + "0n/vrb6vW6Azds49tuS82RFAnOhtwTyBEajs08WF8rZQ3WD2RHJnH0+jjfL0anIp\n" + + "dQBSeNN7s7b6rRk=\n" + + "-----END PRIVATE KEY-----"; // This is a certificate for client - static String targetCertStr_D= - "-----BEGIN CERTIFICATE-----\n" + - "MIICVDCCAb2gAwIBAgIBBTANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" + - "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" + - "MTIwNDE3MTIwNjEwWhcNMzIwMTAzMTIwNjEwWjBUMQswCQYDVQQGEwJVUzENMAsG\n" + - "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxFzAVBgNV\n" + - "BAMTDkludGVyT3AgVGVzdGVyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo\n" + - "Q/KoAIAC2ljFfW2KwjnxTzi4NQJeUuk2seqKpsAY8x4O5dvixzUl6142zmljapqi\n" + - "bJloQVpfB+CEc5/l4h5gzGRVzkuqP1oPzDrpZ5GsvmvuHenV/TzCIgX1cLETzQVt\n" + - "6Rk06okoBPnw3hDJEJiEc1Rv7HCE8p/p+SaiHrskwwIDAQABo08wTTALBgNVHQ8E\n" + - "BAMCA+gwHQYDVR0OBBYEFPr91O33RIGfFSqza2AwQIgE4QswMB8GA1UdIwQYMBaA\n" + - "FEtmQi7jT1ijXOafPsfkrLwSVu9eMA0GCSqGSIb3DQEBBAUAA4GBANIDFYgAhoj3\n" + - "B8u1YpqeoEp2Lt9TwrYBshaIrbmBPCwCGio0JIsoov3n8BCSg5F+8MnOtPl+TjeO\n" + - "0Ug+7guPdCk/wg8YNxLHgSsQlpcNJDjWiErqmUPVrg5BPPQb65qMund6KTmMN0y6\n" + - "4EbSmxRpZO/N0/5oK4umTk0EeXKNekBj\n" + - "-----END CERTIFICATE-----"; + static String targetCertStr_D = + "-----BEGIN CERTIFICATE-----\n" + + "MIICVDCCAb2gAwIBAgIBBTANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" + + "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" + + "MTIwNDE3MTIwNjEwWhcNMzIwMTAzMTIwNjEwWjBUMQswCQYDVQQGEwJVUzENMAsG\n" + + "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxFzAVBgNV\n" + + "BAMTDkludGVyT3AgVGVzdGVyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo\n" + + "Q/KoAIAC2ljFfW2KwjnxTzi4NQJeUuk2seqKpsAY8x4O5dvixzUl6142zmljapqi\n" + + "bJloQVpfB+CEc5/l4h5gzGRVzkuqP1oPzDrpZ5GsvmvuHenV/TzCIgX1cLETzQVt\n" + + "6Rk06okoBPnw3hDJEJiEc1Rv7HCE8p/p+SaiHrskwwIDAQABo08wTTALBgNVHQ8E\n" + + "BAMCA+gwHQYDVR0OBBYEFPr91O33RIGfFSqza2AwQIgE4QswMB8GA1UdIwQYMBaA\n" + + "FEtmQi7jT1ijXOafPsfkrLwSVu9eMA0GCSqGSIb3DQEBBAUAA4GBANIDFYgAhoj3\n" + + "B8u1YpqeoEp2Lt9TwrYBshaIrbmBPCwCGio0JIsoov3n8BCSg5F+8MnOtPl+TjeO\n" + + "0Ug+7guPdCk/wg8YNxLHgSsQlpcNJDjWiErqmUPVrg5BPPQb65qMund6KTmMN0y6\n" + + "4EbSmxRpZO/N0/5oK4umTk0EeXKNekBj\n" + + "-----END CERTIFICATE-----"; static String targetPrivateKey_D = - "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOhD8qgAgALaWMV9\n" + - "bYrCOfFPOLg1Al5S6Tax6oqmwBjzHg7l2+LHNSXrXjbOaWNqmqJsmWhBWl8H4IRz\n" + - "n+XiHmDMZFXOS6o/Wg/MOulnkay+a+4d6dX9PMIiBfVwsRPNBW3pGTTqiSgE+fDe\n" + - "EMkQmIRzVG/scITyn+n5JqIeuyTDAgMBAAECgYBw37yIKp4LRONJLnhSq6sO+0n8\n" + - "Mz6waiiN/Q6XTQwj09pysQAYCGlqwSRrDAqpVsBJWO+Ae+oYLrLMi4hUZnwN75v3\n" + - "pe1nXlrD11RmPLXwBxqFxNSvAs2FgLHZEtwHI7Bn8KybT/8bGkQ8csLceInYtMDD\n" + - "MuTyy2KRk/pj60zIKQJBAPgebQiAH6viFQ88AwHaNvQhlUfwmSC1i6f8LVoeqaHC\n" + - "lnP0LJBwlyDeeEInhHrCR2ibnCB6I/Pig+49XQgabK8CQQDvpJwuGEbsOO+3rkJJ\n" + - "OpOw4toG0QJZdRnT6l8I6BlboQRZSfFh+lGGahvFXkxc4KdUpJ7QPtXU7HHk6Huk\n" + - "8RYtAkA9CW8VGj+wTuuTVdX/jKjcIa7RhbSFwWNbrcOSWdys+Gt+luCnn6rt4QyA\n" + - "aaxDbquWZkFgE+voQR7nap0KM0XtAkAznd0WAJymHM1lXt9gLoHJQ9N6TGKZKiPa\n" + - "BU1a+cMcfV4WbVrUo7oTnZ9Fr73681iXXq3mZOJh7lvJ1llreZIxAkBEnbiTgEf4\n" + - "tvku68jHcRbRPmdS7CBSWNEBaHLOm4pUSTcxVTKKMHw7vmM5/UYUxJ8QNKCYxn6O\n" + - "+vtiBwBawwzN"; + "-----BEGIN PRIVATE KEY-----\n" + + "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOhD8qgAgALaWMV9\n" + + "bYrCOfFPOLg1Al5S6Tax6oqmwBjzHg7l2+LHNSXrXjbOaWNqmqJsmWhBWl8H4IRz\n" + + "n+XiHmDMZFXOS6o/Wg/MOulnkay+a+4d6dX9PMIiBfVwsRPNBW3pGTTqiSgE+fDe\n" + + "EMkQmIRzVG/scITyn+n5JqIeuyTDAgMBAAECgYBw37yIKp4LRONJLnhSq6sO+0n8\n" + + "Mz6waiiN/Q6XTQwj09pysQAYCGlqwSRrDAqpVsBJWO+Ae+oYLrLMi4hUZnwN75v3\n" + + "pe1nXlrD11RmPLXwBxqFxNSvAs2FgLHZEtwHI7Bn8KybT/8bGkQ8csLceInYtMDD\n" + + "MuTyy2KRk/pj60zIKQJBAPgebQiAH6viFQ88AwHaNvQhlUfwmSC1i6f8LVoeqaHC\n" + + "lnP0LJBwlyDeeEInhHrCR2ibnCB6I/Pig+49XQgabK8CQQDvpJwuGEbsOO+3rkJJ\n" + + "OpOw4toG0QJZdRnT6l8I6BlboQRZSfFh+lGGahvFXkxc4KdUpJ7QPtXU7HHk6Huk\n" + + "8RYtAkA9CW8VGj+wTuuTVdX/jKjcIa7RhbSFwWNbrcOSWdys+Gt+luCnn6rt4QyA\n" + + "aaxDbquWZkFgE+voQR7nap0KM0XtAkAznd0WAJymHM1lXt9gLoHJQ9N6TGKZKiPa\n" + + "BU1a+cMcfV4WbVrUo7oTnZ9Fr73681iXXq3mZOJh7lvJ1llreZIxAkBEnbiTgEf4\n" + + "tvku68jHcRbRPmdS7CBSWNEBaHLOm4pUSTcxVTKKMHw7vmM5/UYUxJ8QNKCYxn6O\n" + + "+vtiBwBawwzN\n" + + "-----END PRIVATE KEY-----"; static String[] serverCerts = {targetCertStr_A, targetCertStr_B, targetCertStr_C}; @@ -235,7 +256,7 @@ public class SSLSocketSNISensitive { static String[] clientCerts = {targetCertStr_D}; static String[] clientKeys = {targetPrivateKey_D}; - static char passphrase[] = "passphrase".toCharArray(); + static char[] passphrase = "passphrase".toCharArray(); /* * Is the server ready to serve? @@ -245,7 +266,7 @@ public class SSLSocketSNISensitive { /* * Turn on SSL debugging? */ - static boolean debug = false; + static boolean debug = Boolean.getBoolean("test.debug"); /* * Define the server side of the test. @@ -362,19 +383,16 @@ private static void parseArguments(String[] args) { private static SSLContext generateSSLContext(boolean isClient) throws Exception { - // generate certificate from cert string - CertificateFactory cf = CertificateFactory.getInstance("X.509"); + final PEMDecoder pemDecoder = PEMDecoder.of(); // create a key store KeyStore ks = KeyStore.getInstance("JKS"); ks.load(null, null); - // import the trused cert - ByteArrayInputStream is = - new ByteArrayInputStream(trustedCertStr.getBytes()); - Certificate trusedCert = cf.generateCertificate(is); - is.close(); + // generate certificate from cert string + Certificate trusedCert = pemDecoder.decode(trustedCertStr, X509Certificate.class); + // import the trused cert ks.setCertificateEntry("RSA Export Signer", trusedCert); String[] certStrs = null; @@ -390,17 +408,14 @@ private static SSLContext generateSSLContext(boolean isClient) for (int i = 0; i < certStrs.length; i++) { // generate the private key. String keySpecStr = keyStrs[i]; - PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec( - Base64.getMimeDecoder().decode(keySpecStr)); + PKCS8EncodedKeySpec priKeySpec = pemDecoder.decode(keySpecStr, PKCS8EncodedKeySpec.class); KeyFactory kf = KeyFactory.getInstance("RSA"); RSAPrivateKey priKey = (RSAPrivateKey)kf.generatePrivate(priKeySpec); // generate certificate chain String keyCertStr = certStrs[i]; - is = new ByteArrayInputStream(keyCertStr.getBytes()); - Certificate keyCert = cf.generateCertificate(is); - is.close(); + Certificate keyCert = pemDecoder.decode(keyCertStr, X509Certificate.class); Certificate[] chain = new Certificate[2]; chain[0] = keyCert; @@ -521,22 +536,20 @@ public static void main(String[] args) throws Exception { void startServer(boolean newThread) throws Exception { if (newThread) { - serverThread = new Thread() { - public void run() { - try { - doServerSide(); - } catch (Exception e) { - /* - * Our server thread just died. - * - * Release the client, if not active already... - */ - System.err.println("Server died, because of " + e); - serverReady = true; - serverException = e; - } + serverThread = new Thread(() -> { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + System.err.println("Server died, because of " + e); + serverReady = true; + serverException = e; } - }; + }); serverThread.start(); } else { try { @@ -551,19 +564,17 @@ public void run() { void startClient(boolean newThread) throws Exception { if (newThread) { - clientThread = new Thread() { - public void run() { - try { - doClientSide(); - } catch (Exception e) { - /* - * Our client thread just died. - */ - System.err.println("Client died, because of " + e); - clientException = e; - } + clientThread = new Thread(() -> { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + System.err.println("Client died, because of " + e); + clientException = e; } - }; + }); clientThread.start(); } else { try { diff --git a/test/jdk/javax/net/ssl/interop/ClientHelloBufferUnderflowException.java b/test/jdk/javax/net/ssl/interop/ClientHelloBufferUnderflowException.java index ca5742f37b2cd..0b030c7145989 100644 --- a/test/jdk/javax/net/ssl/interop/ClientHelloBufferUnderflowException.java +++ b/test/jdk/javax/net/ssl/interop/ClientHelloBufferUnderflowException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ * @test * @bug 8215790 8219389 * @summary Verify exception + * @enablePreview * @library /test/lib * @modules java.base/sun.security.util * @run main/othervm ClientHelloBufferUnderflowException diff --git a/test/jdk/javax/net/ssl/interop/ClientHelloChromeInterOp.java b/test/jdk/javax/net/ssl/interop/ClientHelloChromeInterOp.java index f426cce33e311..bcdfa27039424 100644 --- a/test/jdk/javax/net/ssl/interop/ClientHelloChromeInterOp.java +++ b/test/jdk/javax/net/ssl/interop/ClientHelloChromeInterOp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -30,6 +30,7 @@ * @test * @bug 8169362 * @summary Interop automated testing with Chrome + * @enablePreview * @library /test/lib * @modules jdk.crypto.ec * java.base/sun.security.util diff --git a/test/jdk/javax/net/ssl/interop/ClientHelloInterOp.java b/test/jdk/javax/net/ssl/interop/ClientHelloInterOp.java index c6bf74bd53325..808d137223e96 100644 --- a/test/jdk/javax/net/ssl/interop/ClientHelloInterOp.java +++ b/test/jdk/javax/net/ssl/interop/ClientHelloInterOp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -21,17 +21,22 @@ * questions. */ -import javax.net.ssl.*; -import javax.net.ssl.SSLEngineResult.*; -import java.io.*; -import java.nio.*; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManagerFactory; +import java.nio.ByteBuffer; import java.security.KeyStore; +import java.security.PEMDecoder; +import java.security.PEMRecord; import java.security.PrivateKey; -import java.security.KeyFactory; import java.security.cert.Certificate; -import java.security.cert.CertificateFactory; -import java.security.spec.*; -import java.util.Base64; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.RSAPrivateKey; public abstract class ClientHelloInterOp { @@ -138,13 +143,16 @@ public abstract class ClientHelloInterOp { // // EC private key related to cert endEntityCertStrs[0]. // + "-----BEGIN PRIVATE KEY-----\n" + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgA3pmS+OrIjGyUv2F\n" + "K/PkyayJIePM2RTFYxNoQqmJGnihRANCAASHi9c1QnNQurh7t8A68XRaJZTpyWU4\n" + - "Ay6zUapMW9ydE84KGXyy5my+Sw7QKlmoveGNeZVf12nUVX+tQEYujVob", + "Ay6zUapMW9ydE84KGXyy5my+Sw7QKlmoveGNeZVf12nUVX+tQEYujVob\n" + + "-----END PRIVATE KEY-----", // // RSA private key related to cert endEntityCertStrs[1]. // + "-----BEGIN PRIVATE KEY-----\n" + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDfq0lpd8nYH8AW\n" + "8RL62e57JA9I0AFW72d8x1T40Q9qYn4UftwQXxnVKmvW+VCA3MKkNRWt+eZPvmsJ\n" + "qmDPmV0D37L7eF19TIeNkHPN/H7oYdcsHi7p5TY0BNru+pIs1twtx9nv9CaQWqDg\n" + @@ -170,8 +178,9 @@ public abstract class ClientHelloInterOp { "sZ2JRtyK3OV9RtL/MYmYzPLqm1Ah02+GXLVNnvKWmwKBgE8Ble8CzrXYuuPdGxXz\n" + "BZU6HnXQrmTUcgeze0tj8SDHzCfsGsaG6pHrVNkT7CKsRuCHTZLM0kXmUijLFKuP\n" + "5xyE257z4IbbEbs+tcbB3p28n4/47MzZkSR3kt8+FrsEMZq5oOHbFTGzgp9dhZCC\n" + - "dKUqlw5BPHdbxoWB/JpSHGCV" - }; + "dKUqlw5BPHdbxoWB/JpSHGCV\n" + + "-----END PRIVATE KEY-----" + }; // Private key names of endEntityPrivateKeys. private final static String[] endEntityPrivateKeyNames = { @@ -179,6 +188,8 @@ public abstract class ClientHelloInterOp { "RSA" }; + private static final PEMDecoder pemDecoder = PEMDecoder.of(); + /* * Run the test case. */ @@ -251,13 +262,9 @@ protected SSLContext createSSLContext( KeyStore ts = null; // trust store KeyStore ks = null; // key store - char passphrase[] = "passphrase".toCharArray(); - - // Generate certificate from cert string. - CertificateFactory cf = CertificateFactory.getInstance("X.509"); + char[] passphrase = "passphrase".toCharArray(); // Import the trused certs. - ByteArrayInputStream is; if (trustedMaterials != null && trustedMaterials.length != 0) { ts = KeyStore.getInstance("JKS"); ts.load(null, null); @@ -266,13 +273,8 @@ protected SSLContext createSSLContext( new Certificate[trustedMaterials.length]; for (int i = 0; i < trustedMaterials.length; i++) { String trustedCertStr = trustedMaterials[i]; - - is = new ByteArrayInputStream(trustedCertStr.getBytes()); - try { - trustedCert[i] = cf.generateCertificate(is); - } finally { - is.close(); - } + // Generate certificate from cert string. + trustedCert[i] = pemDecoder.decode(trustedCertStr, X509Certificate.class); ts.setCertificateEntry("trusted-cert-" + i, trustedCert[i]); } @@ -295,21 +297,14 @@ protected SSLContext createSSLContext( String keyCertStr = keyMaterialCerts[i]; // generate the private key. - PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec( - Base64.getMimeDecoder().decode(keyMaterialKeys[i])); - KeyFactory kf = - KeyFactory.getInstance(keyMaterialKeyAlgs[i]); - PrivateKey priKey = kf.generatePrivate(priKeySpec); + PrivateKey priKey = switch (keyMaterialKeyAlgs[i]) { + case "RSA" -> pemDecoder.decode(keyMaterialKeys[i], RSAPrivateKey.class); + case "EC" -> pemDecoder.decode(keyMaterialKeys[i], ECPrivateKey.class); + default -> pemDecoder.decode(keyMaterialKeys[i], PrivateKey.class); + }; // generate certificate chain - is = new ByteArrayInputStream(keyCertStr.getBytes()); - Certificate keyCert = null; - try { - keyCert = cf.generateCertificate(is); - } finally { - is.close(); - } - + Certificate keyCert = pemDecoder.decode(keyCertStr, X509Certificate.class); Certificate[] chain = new Certificate[] { keyCert }; // import the key entry. diff --git a/test/jdk/javax/swing/JTabbedPane/4624207/bug4624207.java b/test/jdk/javax/swing/JTabbedPane/4624207/bug4624207.java index 10de2ab221ad4..4d2fdcf030cd8 100644 --- a/test/jdk/javax/swing/JTabbedPane/4624207/bug4624207.java +++ b/test/jdk/javax/swing/JTabbedPane/4624207/bug4624207.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -25,25 +25,26 @@ * @test * @key headful * @bug 4624207 + * @requires (os.family != "mac") * @summary JTabbedPane mnemonics don't work from outside the tabbed pane - * @author Oleg Mokhovikov - * @library /test/lib - * @library ../../regtesthelpers - * @build Util jdk.test.lib.Platform * @run main bug4624207 */ -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.*; + +import java.awt.BorderLayout; +import java.awt.Robot; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; -import jdk.test.lib.Platform; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; public class bug4624207 implements ChangeListener, FocusListener { - private static volatile boolean stateChanged = false; private static volatile boolean focusGained = false; private static JTextField txtField; @@ -71,51 +72,39 @@ public static void main(String[] args) throws Exception { Robot robot = new Robot(); robot.setAutoDelay(50); - SwingUtilities.invokeAndWait(new Runnable() { - - public void run() { - createAndShowGUI(); - } - }); - + SwingUtilities.invokeAndWait(() -> createAndShowGUI()); robot.waitForIdle(); - - SwingUtilities.invokeAndWait(new Runnable() { - - public void run() { - txtField.requestFocus(); - } - }); - + SwingUtilities.invokeAndWait(() -> txtField.requestFocus()); robot.waitForIdle(); if (!focusGained) { throw new RuntimeException("Couldn't gain focus for text field"); } - SwingUtilities.invokeAndWait(new Runnable() { - - public void run() { - tab.addChangeListener((ChangeListener) listener); - txtField.removeFocusListener((FocusListener) listener); - } + SwingUtilities.invokeAndWait(() -> { + tab.addChangeListener((ChangeListener) listener); + txtField.removeFocusListener((FocusListener) listener); }); robot.waitForIdle(); - if (Platform.isOSX()) { - Util.hitKeys(robot, KeyEvent.VK_CONTROL, KeyEvent.VK_ALT, KeyEvent.VK_B); - } else { - Util.hitKeys(robot, KeyEvent.VK_ALT, KeyEvent.VK_B); - } + robot.keyPress(KeyEvent.VK_ALT); + robot.keyPress(KeyEvent.VK_B); + robot.keyRelease(KeyEvent.VK_B); + robot.keyRelease(KeyEvent.VK_ALT); robot.waitForIdle(); if (!stateChanged || tab.getSelectedIndex() != 1) { - throw new RuntimeException("JTabbedPane mnemonics don't work from outside the tabbed pane"); + throw new RuntimeException("JTabbedPane mnemonics don't " + + "work from outside the tabbed pane"); } } finally { - if (frame != null) SwingUtilities.invokeAndWait(() -> frame.dispose()); + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); } } diff --git a/test/jdk/sun/security/provider/certpath/DisabledAlgorithms/CPValidatorTrustAnchor.java b/test/jdk/sun/security/provider/certpath/DisabledAlgorithms/CPValidatorTrustAnchor.java index 63eaa12b00c0e..d74d712d8d749 100644 --- a/test/jdk/sun/security/provider/certpath/DisabledAlgorithms/CPValidatorTrustAnchor.java +++ b/test/jdk/sun/security/provider/certpath/DisabledAlgorithms/CPValidatorTrustAnchor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -31,19 +31,35 @@ * @summary Disable MD2 support * new CertPathValidatorException.BasicReason enum constant for * constrained algorithm + * @enablePreview * @run main/othervm CPValidatorTrustAnchor * @author Xuelei Fan */ -import java.io.*; -import java.net.SocketException; -import java.util.*; +import java.io.ByteArrayInputStream; +import java.security.PEMDecoder; import java.security.Security; -import java.security.cert.*; -import java.security.cert.CertPathValidatorException.*; +import java.security.cert.CertPath; +import java.security.cert.CertPathValidator; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertPathValidatorException.BasicReason; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.PKIXParameters; +import java.security.cert.TrustAnchor; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Set; public class CPValidatorTrustAnchor { + private static final PEMDecoder pemDecoder = java.security.PEMDecoder.of(); + static String selfSignedCertStr = null; // SHA1withRSA 1024 @@ -104,33 +120,26 @@ public class CPValidatorTrustAnchor { private static CertPath generateCertificatePath() throws CertificateException { - // generate certificate from cert strings - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream is; + CertificateFactory cf = CertificateFactory.getInstance("X.509"); - is = new ByteArrayInputStream(selfSignedCertStr.getBytes()); - Certificate selfSignedCert = cf.generateCertificate(is); + // generate certificate from cert strings + Certificate selfSignedCert = pemDecoder.decode(selfSignedCertStr, X509Certificate.class); // generate certification path - List list = Arrays.asList(new Certificate[] { - selfSignedCert}); + List list = Collections.singletonList(selfSignedCert); return cf.generateCertPath(list); } - private static Set generateTrustAnchors() - throws CertificateException { - // generate certificate from cert string - CertificateFactory cf = CertificateFactory.getInstance("X.509"); + private static Set generateTrustAnchors() { - ByteArrayInputStream is = - new ByteArrayInputStream(selfSignedCertStr.getBytes()); - Certificate selfSignedCert = cf.generateCertificate(is); + // generate certificate from cert string + X509Certificate selfSignedCert = pemDecoder.decode(selfSignedCertStr, X509Certificate.class); // generate a trust anchor TrustAnchor anchor = - new TrustAnchor((X509Certificate)selfSignedCert, null); + new TrustAnchor(selfSignedCert, null); return Collections.singleton(anchor); } @@ -164,7 +173,7 @@ public static void main(String args[]) throws Exception { } private static void validate(String trustAnchor) - throws CertPathValidatorException, Exception { + throws Exception { selfSignedCertStr = trustAnchor; CertPath path = generateCertificatePath(); @@ -176,7 +185,11 @@ private static void validate(String trustAnchor) params.setRevocationEnabled(false); // set the validation time - params.setDate(new Date(109, 9, 1)); // 2009-09-01 + final Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.YEAR, 2009); + calendar.set(Calendar.MONTH, 9); + calendar.set(Calendar.DATE, 1); + params.setDate(calendar.getTime()); // 2009-09-01 CertPathValidator validator = CertPathValidator.getInstance("PKIX"); diff --git a/test/jdk/sun/security/rsa/InvalidBitString.java b/test/jdk/sun/security/rsa/InvalidBitString.java index be9e42ca5444f..7f8408f35f0bf 100644 --- a/test/jdk/sun/security/rsa/InvalidBitString.java +++ b/test/jdk/sun/security/rsa/InvalidBitString.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -23,15 +23,15 @@ /* @test * @summary Validation of signatures succeed when it should fail + * @enablePreview * @bug 6896700 */ -import java.io.InputStream; -import java.io.ByteArrayInputStream; +import java.security.PEMDecoder; import java.security.cert.Certificate; -import java.security.cert.CertificateFactory; import java.security.PublicKey; import java.security.SignatureException; +import java.security.cert.X509Certificate; public class InvalidBitString { @@ -87,16 +87,16 @@ public class InvalidBitString { "ZAM6mgkuSY7/vdnsiJtU\n" + "-----END CERTIFICATE-----\n"; - public static void main(String args[]) throws Exception { - - Certificate signer = generate(signerCertStr); + public static void main(String[] args) throws Exception { + final PEMDecoder pemDecoder = PEMDecoder.of(); + Certificate signer = pemDecoder.decode(signerCertStr, X509Certificate.class); // the valid certificate - Certificate normal = generate(normalCertStr); + Certificate normal = pemDecoder.decode(normalCertStr, X509Certificate.class); // the invalid certificate with extra signature bits - Certificate longer = generate(longerCertStr); + Certificate longer = pemDecoder.decode(longerCertStr, X509Certificate.class); // the invalid certificate without enough signature bits - Certificate shorter = generate(shorterCertStr); + Certificate shorter = pemDecoder.decode(shorterCertStr, X509Certificate.class); if (!test(normal, signer, " normal", true) || !test(longer, signer, " longer", false) || @@ -105,19 +105,6 @@ public static void main(String args[]) throws Exception { } } - private static Certificate generate(String certStr) throws Exception { - InputStream is = null; - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - is = new ByteArrayInputStream(certStr.getBytes()); - return cf.generateCertificate(is); - } finally { - if (is != null) { - is.close(); - } - } - } - private static boolean test(Certificate target, Certificate signer, String title, boolean expected) throws Exception { System.out.print("Checking " + title + ": expected: " + diff --git a/test/jdk/sun/security/ssl/ClientHandshaker/RSAExport.java b/test/jdk/sun/security/ssl/ClientHandshaker/RSAExport.java index 698bbdf88b1a0..26d5c69e21986 100644 --- a/test/jdk/sun/security/ssl/ClientHandshaker/RSAExport.java +++ b/test/jdk/sun/security/ssl/ClientHandshaker/RSAExport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ /* * @test * @bug 6690018 + * @enablePreview * @summary RSAClientKeyExchange NullPointerException * @run main/othervm RSAExport */ @@ -197,17 +198,24 @@ * */ -import java.io.*; -import java.net.*; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.PEMDecoder; import java.security.Security; import java.security.KeyStore; import java.security.KeyFactory; import java.security.cert.Certificate; -import java.security.cert.CertificateFactory; -import java.security.spec.*; -import java.security.interfaces.*; -import javax.net.ssl.*; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; import java.math.BigInteger; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPrivateKey; +import java.security.spec.RSAPrivateKeySpec; public class RSAExport { @@ -312,7 +320,7 @@ public class RSAExport { /* * Turn on SSL debugging? */ - static boolean debug = false; + static boolean debug = Boolean.getBoolean("test.debug"); /* * If the client or server is doing some kind of object creation @@ -386,7 +394,7 @@ void doClientSide() throws Exception { // Enable RSA_EXPORT cipher suites only. try { - String enabledSuites[] = { + String[] enabledSuites = { "SSL_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA"}; sslSocket.setEnabledCipherSuites(enabledSuites); @@ -471,22 +479,20 @@ public static void main(String[] args) throws Exception { void startServer(boolean newThread) throws Exception { if (newThread) { - serverThread = new Thread() { - public void run() { - try { - doServerSide(); - } catch (Exception e) { - /* - * Our server thread just died. - * - * Release the client, if not active already... - */ - System.err.println("Server died..." + e); - serverReady = true; - serverException = e; - } + serverThread = new Thread(() -> { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + System.err.println("Server died..." + e); + serverReady = true; + serverException = e; } - }; + }); serverThread.start(); } else { doServerSide(); @@ -495,19 +501,17 @@ public void run() { void startClient(boolean newThread) throws Exception { if (newThread) { - clientThread = new Thread() { - public void run() { - try { - doClientSide(); - } catch (Exception e) { - /* - * Our client thread just died. - */ - System.err.println("Client died..."); - clientException = e; - } + clientThread = new Thread(() -> { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + System.err.println("Client died..."); + clientException = e; } - }; + }); clientThread.start(); } else { doClientSide(); @@ -517,11 +521,10 @@ public void run() { // Get the SSL context private SSLContext getSSLContext(boolean authnRequired) throws Exception { // generate certificate from cert string - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream is = - new ByteArrayInputStream(trusedCertStr.getBytes()); - Certificate trustedCert = cf.generateCertificate(is); + final PEMDecoder pemDecoder = PEMDecoder.of(); + + Certificate trustedCert = pemDecoder.decode(trusedCertStr, X509Certificate.class); // create a key store KeyStore ks = KeyStore.getInstance("JKS"); @@ -540,8 +543,7 @@ private SSLContext getSSLContext(boolean authnRequired) throws Exception { (RSAPrivateKey)kf.generatePrivate(priKeySpec); // generate certificate chain - is = new ByteArrayInputStream(serverCertStr.getBytes()); - Certificate serverCert = cf.generateCertificate(is); + Certificate serverCert = pemDecoder.decode(serverCertStr, X509Certificate.class); Certificate[] chain = new Certificate[2]; chain[0] = serverCert; diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/BasicConstraints.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/BasicConstraints.java index 7f9573a8eebe1..051c940b3b080 100644 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/BasicConstraints.java +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/BasicConstraints.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -31,20 +31,33 @@ * @bug 7166570 * @summary JSSE certificate validation has started to fail for * certificate chains + * @enablePreview * @run main/othervm BasicConstraints PKIX * @run main/othervm BasicConstraints SunX509 */ -import java.util.*; -import java.io.*; -import javax.net.ssl.*; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.PEMDecoder; +import java.security.cert.CertPath; +import java.security.cert.CertPathValidator; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.PKIXParameters; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPrivateKey; +import java.security.spec.PKCS8EncodedKeySpec; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; import java.security.KeyStore; import java.security.KeyFactory; -import java.security.cert.*; -import java.security.spec.*; -import java.security.interfaces.*; -import java.util.Base64; +import java.util.Arrays; public class BasicConstraints { @@ -96,33 +109,6 @@ public class BasicConstraints { "cwIDUWqQda62xV7ChkTh7ia3uvBXob2iiB0aI3gVTTqDfK9F5XXtW4BXfqx0hvwB\n" + "6JzgmNyDQos=\n" + "-----END CERTIFICATE-----"; - static String trustedPrivateKey = // Private key in the format of PKCS#8 - "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDUJ3hT/9jY/i8i\n" + - "70EEaL6mbrhhdg/Ys1E0r97n+dZaY0olqkIBhh1r8UkKWtvOkj8WBFQ0sz0HhSjT\n" + - "rkVEisGLW+7zPJiDBPtQrRawvCDpnzUofnQ98zQKUTHji1OqhxgNzsKCy9vIh5Mh\n" + - "tX0CdGUScEDXlYUkAkxMKCVo2V5dRn34D+1rNGEeWxGnQ5vyPi0IwlpEOkYxhPLV\n" + - "dsb5aoLzBc/rdrrdzCM+svm7O38LhbVuA0F9NHAgdJRKE2F91ztkk1KvY0U9zCh1\n" + - "3u5WV7kl481qDujKGM4UURoEarbV2Xr+jNVGSpJZYCLU/sxFrL15iPeYtmJlovo2\n" + - "VbFed/NXAgMBAAECggEAUZvlQ5q1VbNhenTCc+m+/NK2hncd3WQNJtFIU7/dXuO2\n" + - "0ApQXbmzc6RbTmppB2tmbRe5NJSGM3BbpiHxb05Y6TyyDEsQ98Vgz0Xl5pJXrsaZ\n" + - "cjxChtoY+KcHI9qikoRpElaoqBu3LcpJJLxlnB4eCxu3NbbEgneH1fvTeCO1kvcp\n" + - "i3DDdyfY7WB9RW1yWAveiuqvtnbsPfJJLKEhFvZL2ArYCRTm/oIw64yukNe/QLR5\n" + - "bGzEJMT2ZNQMld1f+CW9tOrUKrnnPCGfMa351T5we+8B6sujWfftPutgEVx5TmHs\n" + - "AOW1SntMapbgg46K9EC/C5YQa5D1aNOH9ZTEMkgUMQKBgQDrpPQIHFozeeyZ0iiq\n" + - "HtReLPcqpkwr/9ELc3SjgUypSvpu0l/m++um0yLinlXMn25km/BP6Mv3t/+1uzAc\n" + - "qpopkcyek8X1hzNRhDkWuMv4KDOKk5c6qLx8FGSm6q8PYm5KbsiyeCM7CJoeoqJ5\n" + - "74IZjOIw7UrYLckCb6W8xGQLIwKBgQDmew3vGRR3JmCCSumtJQOqhF6bBYrNb6Qc\n" + - "r4vrng+QhNIquwGqHKPorAI1J8J1jOS+dkDWTxSz2xQKQ83nsOspzVPskpDh5mWL\n" + - "gGk5QCkX87jFsXfhvZFLksZMbIdpWze997Zs2fe/PWfPaH6o3erqo2zAhQV0eA9q\n" + - "C7tfImREPQKBgQDi2Xq/8CN52M9IScQx+dnyC5Gqckt0NCKXxn8sBIa7l129oDMI\n" + - "187FXA8CYPEyOu14V5KiKvdos66s0daAUlB04lI8+v+g3ZYuzH50/FQHwxPTPUBi\n" + - "DRzeyncXJWiAA/8vErWM8hDgfOh5w5Fsl4EEfdcmyNm7gWA4Qyknr1ysRwKBgQDC\n" + - "JSPepUy09VHUTxA59nT5HRmoEeoTFRizxTfi2LkZrphuwCotxoRXiRUu+3f1lyJU\n" + - "Qb5qCCFTQ5bE8squgTwGcVxhajC66V3ePePlAuPatkWN2ek28X1DoLaDR+Rk3h69\n" + - "Wb2EQbNMl4grkUUoMA8jaVhBb4vhyQSK+qjyAUFerQKBgQDXZPuflfsjH/d/O2yw\n" + - "qZbssKe9AKORjv795teblAc3vmsSlNwwVnPdS2aq1LHyoNbetc/OaZV151hTQ/9z\n" + - "bsA48oOojgrDD07Ovg3uDcNEIufxR0aGeSSvqhElp1r7wAYj8bAr6W/RH6MS16WW\n" + - "dRd+PH6hsap8BD2RlVCnrT3vIQ=="; // Certificate information: // Issuer: C=US, O=Java, OU=SunJSSE Test Serivce @@ -156,33 +142,6 @@ public class BasicConstraints { "P0QqaqP+xJIY+sRrzdckxSfS9AOOrJk2VXY8qEoxCN4wCvHJWuHEAF/Lm65d/hq3\n" + "2Uh8P+QHLeuEwF8RoTpjiGM9dXvaqcQz7w5G\n" + "-----END CERTIFICATE-----"; - static String caSignerPrivateKey = // Private key in the format of PKCS#8 - "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDAvGeLKlW1ljae\n" + - "eu8NvDCjfW5BNK2c0C4ry7Is+1mM4PC7FA4bRpMaQHKIjLsZ5D1hoA9183cv3p1a\n" + - "P75/ZYMOyx1id/hXmbd3jp8BR0wbvrKxa53+4lO0S5AL5dOpU2AVhcdeQ7+DwoL6\n" + - "iAuHqNcABg3CijrIcFeZHcPMwaZMd9YxJG6YrnNHMWjbXTGKpma02NMB1UnRxsdN\n" + - "phqfRt2gkUs18l6697sSJ7eblvSWEWw1Bmtrg9No28UUsiF8q0m9i/G0QzYOrS6v\n" + - "ghum5bpHAixxfA9Z/ozHrN8gf8gNDTRnG6phDwVb1Uj9nO2f9yTArx7Kz5EtRNmD\n" + - "x9SNMS9rAgMBAAECggEAZk6cF/8s5+sIqy9OXdgbaW1XbT1tOuQ23gCOX9o8Os/c\n" + - "eTG4GzpnM3QqV9l8J85D1uKD0nSeO8bLd/CGSlG0M9IVkwNjy/xIqyoFtUQHXmLn\n" + - "r84UXAv/qqDBoc8pf6RGSKZuodcMfgBuTlaQ6D3zgou0GiQN9//KP/jQyouwnr3A\n" + - "LyXQekxriwPuSYAPak8s5XLfugOebbSRm2UdGEgX3yrT9FVu9rtgeMKdRaCOU8T4\n" + - "G2UdpGaiDfm5yrR+2XEIv4oaH3WFxmmfQCxVcOFJ1iRvfKBbLb1UCgtJuCBD067y\n" + - "dq5PrwUTeAvd7hwZd0lxCSnWY7VvYFNr7iJfyElowQKBgQD8eosot+Th03hpkYDs\n" + - "BIVsw7oqhJmcrPV1bSZ+aQwqqrOGypNmb7nLGTC8Cj1sT+EzfGs7GqxiLOEn4NXr\n" + - "TYV//RUPBSEXVp2y+2dot1a9oq0BJ8FwGTYL0qSwJrIXJfkQFrYhVVz3JLIWJbwV\n" + - "cy4YCQr094BhXTS7joJOUDRsYwKBgQDDbI3Lv+bBK8lLfIBll1RY1k5Gqy/H+qxp\n" + - "sMN8FmadmIGzHhe9xml6b5EfAZphAUF4vZJhQXloT5Wm+NNIAf6X6dRjvzyw7N9B\n" + - "d48EFJF4ChqNGBocsQRNr2wPRzQ+k2caw9YyYMIjbhktDzO1U/FJGYW6/Vgr2v4K\n" + - "siROnXfLWQKBgBOVAZQP5z2opC8z7NbhZuPPrnG7xRpEw+jupUyqoxnwEWqD7bjF\n" + - "M5jQBFqhRLBQ5buTi9GSuQoIRxJLuuu8IH2TyH1YvX9M5YBLRXL2vVCJ/HcZeURT\n" + - "gECcfs92wNtQw6d+y3N8ZnB4tSNIm/Th8RJGKUZkp91lWECvxeWDDP3XAoGASfNq\n" + - "NRAJYlAPfGFAtTDu2i8+r79X9XUGiXg6gVp4umpbqkxY75eFkq9lWzZgFRVEkUwr\n" + - "eGIubyquluDSEw2uKg5yMMzNSqZYVY3IsOKXqbUpFvtn5jOWTU90tNNdEdD100sI\n" + - "Y0f6Ly4amNKH3rZFOERQNtJn6zCTsbh3xMgR7QECgYBhQTqxLU5eIu38MKobzRue\n" + - "RoUkMcoY3DePkKPSYjilFhkUDozIXf/xUGnB8kERZKO+44wUkuPGljiFL1/P/RO9\n" + - "zhHAV94Kw2ddtfxy05GVtUZ99miBmsMb2m8vumGJqfR8h2xpfc1Ra0zfrsPgLNru\n" + - "xDTDW+bNbM7XyPvg9mOf7Q=="; // Certificate information: // Issuer: C=US, O=Java, OU=SunJSSE Test Serivce, CN=casigner @@ -216,33 +175,6 @@ public class BasicConstraints { "zr4da2aIg9CKrH2QWoMkDfRKkJvrU3/VhVfVWpNbXFE2xZXftQl3hpFCJ3FkpciA\n" + "l3hKeq4byY3LXxhAClHpk1KkXJkMnQdOfA5aGekj/Cjuaz1/iKYAG2vRq7YcuM/o\n" + "-----END CERTIFICATE-----"; - static String certIssuerPrivateKey = // Private key in the format of PKCS#8 - "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC1lDVpzmzwbKOL\n" + - "yFWkjPjqtX9xLMq7SVqobvhBv+VChMGGjQbNQPbtczOcXNOcuMFyXxY++eXY7c37\n" + - "MzhbdZHv4Y4aWEn+A3EiX2/fTAbxx165qxKiHbD2EmlKk/Q6yIvi9M9EXXr/viEC\n" + - "Y4/Sdtd4KYtfETa0FpfF5/ZpZMYQo8I9RqBQOmhfvXL1l/Lodla5elZtvIUyp5k2\n" + - "nRQe58AxeP5hrilbIgfmEySf9mOkaTalRf2epBE/wRNA7Qi5Sr2O4pY2x3PPdmMy\n" + - "NL4cZaOJTgdyeDYbEMSW6vpiJW26ma/qeFgPIXZ8COFJZLSOEu310M4QOdSR1Y2c\n" + - "l3/V2E0VAgMBAAECggEBAJjfVrjl2kHwtSCSYchQB6FTfSBDnctgTrtP8iMo9FO0\n" + - "gVpOkVNtRndTbjhOzro7smIgPBJ5QlIIpErBLMmTinJza7gybNk2/KD7yKwuzgnw\n" + - "2IdoyB9E8B+8EHmBZzW2ck953KaqLUvzPsdMG2IOPAomr/gx/eRQwScVzBefiEGo\n" + - "sN+rGfUt/RNAHwWje1KuNDj21S84agQhN6hdYUnIMsvJLu/9mOwUb9ff+AzTUfFr\n" + - "zyx2MJL4Cx59DkUUMESCfinlHUc21llQjFWmX/zOoGY0X0qV/YM/GRsv1ZDFHw9o\n" + - "hQ6m8Ov7D9wB3TKZBI97sCyggjBfSeuYQlNbs99KWQECgYEA7IKNL0ME7FuIrKYu\n" + - "FCQ/Duz1N3oQXLzrTGKUSU1qSbrU2Jwk4SfJ8ZYCW1TP6vZkaQsTXmXun3yyCAqZ\n" + - "hcOtDBhI+b7Wpmmyf6nb83oYJtzHMRQZ5qS+9vOBfV9Uf1za8XI4p90EqkFHByCF\n" + - "tHfjVbjK39zN4CvaO3tqpOaYtL0CgYEAxIrTAhGWy9nBsxf8QeqDou0rV5Cw50Kl\n" + - "kQsE7KLmjvrMaFFpUc5lgWoC+pm/69VpNBUuN/38YozwxVjVi/nMJuuK150mhdWI\n" + - "B28FI7ORnFmVeSvTrP4mBX1ct2Tny9zpchXn3rpHR5NZUs7oBhjudHSfRMrHxeBs\n" + - "Kv2pr2s6uzkCgYAtrEh3iAm7WzHZpX3ghd9nknsIa5odTp5h8eeRAFI2Ss4vxneY\n" + - "w4ZMERwDZy1/wnVBk9H5uNWMFxiKVQGww0j3vPjawe/R0zeVT8gaDMn9N0WARNF7\n" + - "qPT3265196LptZTSa6xlPllYR6LfzXgEkeJk+3qyIIHheJZ8RikiDyYOQQKBgQC/\n" + - "rxlegiMNC4KDldf7vanGxAKqcz5lPbXWQOX7mGC+f9HNx+Cs3VxYHDltiXgJnOju\n" + - "191s1HRK9WR5REt5KhY2uzB9WxJQItJ5VYiwqhhQYXqLY/gdVv1kC0DayDndtMWk\n" + - "88JhklGkeAv83DikgbpGr9sJr6+oyFkWkLDmmfD82QKBgQCMgkZJzrdSNNlB0n5x\n" + - "xC3MzlsQ5aBJuUctnMfuyDi+11yLAuP1oLzGEJ7qEfFoGRO0V8zJWmHAfNhmVYEX\n" + - "ow5g0WbPT16GoRCiOAzq+ewH+TEELMF6HWqnDuTnCg28Jg0dw2kdVTqeyzKOQlLG\n" + - "ua9c2DY3PUTXQPNqLVhz+XxZKA=="; // Certificate information: // Issuer: C=US, O=Java, OU=SunJSSE Test Serivce, CN=certissuer @@ -277,6 +209,7 @@ public class BasicConstraints { "u/inkyf8NcG7zLBJJyuKfUXO/OzGPD5QMviVc+PCGTY=\n" + "-----END CERTIFICATE-----"; static String serverPrivateKey = // Private key in the format of PKCS#8 + "-----BEGIN PRIVATE KEY-----\n" + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCaDgoxN2UQQero\n" + "oBQ4JlQP1BFaZEtIkdIU2VJs4whz85J0LSB/68iEOS5e8wCz9wiQWr4isor7sl3e\n" + "B2dnLGY28BthOTw2j/CYw/dRqyDbPZniooB233uLGarKjqQWXpRFQi6bgEQmNqWe\n" + @@ -302,7 +235,8 @@ public class BasicConstraints { "/RiupLD4/awmf21ytpfHcmOWCcdQoE4WC69a6VyVAoGAboeogM5/TRKj80rXfUH2\n" + "lFZzgX246XGwNyOVVgOuv/Oxa61b5FeeCpnFQcjpZmC5vd63X3w7oYSDe2wUt+Wh\n" + "LhYunmcCEj+yb3of33loQb/FM2OLW9UoQakB7ewio9vtw+BAnWxnHFkEaqdxMXpy\n" + - "TiSXLpQ1Q9GvDpzngDzJzzY="; + "TiSXLpQ1Q9GvDpzngDzJzzY=\n" + + "-----END PRIVATE KEY-----"; // Certificate information: // Issuer: C=US, O=Java, OU=SunJSSE Test Serivce, CN=certissuer @@ -337,6 +271,7 @@ public class BasicConstraints { "tL85OZz8ov7d2jVet/w7FD4M5XfcogsNtpX4kaMsctyvQbDYRA==\n" + "-----END CERTIFICATE-----"; static String clientPrivateKey = // Private key in the format of PKCS#8 + "-----BEGIN PRIVATE KEY-----\n" + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFwNzVfqQ58J0I\n" + "FxUO1ng7XE3uKg0FfbQ4/XEWRakF6PeAt9JZLl83R++tW2QfOAxEldKiyJOv5/g/\n" + "UjrIO0j3u7noxtuK6Yf1aTwDaz16PI8cIfylvvMtKWDYoBVGQ4vphAwDhoMqmgG2\n" + @@ -362,9 +297,10 @@ public class BasicConstraints { "cWJdYS5BrwEUen8vaQt1LhgS6lOqYsjysCxkYm078QKBgEJuq4RzecgiGx8srWDb\n" + "pQKpxrdEt82Y7OXLVj+W9vixcW/xUYhDYGsfdUigZoOjo4nV8KVmMbuI48PIYwnw\n" + "haLwWrBWlki4x9MRwuZUdewOYoo7hDZToZmIDescdiwv8CA/Dg9kOX3YYLPW+cWl\n" + - "i1pnyMPaloBOhz3Y07sWXxCz"; + "i1pnyMPaloBOhz3Y07sWXxCz\n" + + "-----END PRIVATE KEY-----"; - static char passphrase[] = "passphrase".toCharArray(); + static char[] passphrase = "passphrase".toCharArray(); /* * Is the server ready to serve? @@ -374,7 +310,7 @@ public class BasicConstraints { /* * Turn on SSL debugging? */ - static boolean debug = false; + static boolean debug = Boolean.getBoolean("test.debug"); /* * Define the server side of the test. @@ -447,48 +383,39 @@ void doClientSide() throws Exception { // get the ssl context private static SSLContext getSSLContext(boolean isServer) throws Exception { - // generate certificate from cert string - CertificateFactory cf = CertificateFactory.getInstance("X.509"); + final PEMDecoder pemDecoder = PEMDecoder.of(); // create a key store KeyStore ks = KeyStore.getInstance("JKS"); ks.load(null, null); - // import the trused cert - ByteArrayInputStream is = - new ByteArrayInputStream(trusedCertStr.getBytes()); - Certificate trusedCert = cf.generateCertificate(is); - is.close(); + // generate certificate from cert string + Certificate trusedCert = pemDecoder.decode(trusedCertStr, X509Certificate.class); + + // import the trused cert ks.setCertificateEntry("SunJSSE Test Serivce", trusedCert); // import the certificate chain and key Certificate[] chain = new Certificate[3]; - is = new ByteArrayInputStream(caSignerStr.getBytes()); - Certificate caSignerCert = cf.generateCertificate(is); - is.close(); + Certificate caSignerCert =pemDecoder.decode(caSignerStr, X509Certificate.class); chain[2] = caSignerCert; - is = new ByteArrayInputStream(certIssuerStr.getBytes()); - Certificate certIssuerCert = cf.generateCertificate(is); - is.close(); + Certificate certIssuerCert =pemDecoder.decode(certIssuerStr, X509Certificate.class); chain[1] = certIssuerCert; - PKCS8EncodedKeySpec priKeySpec = null; + PKCS8EncodedKeySpec priKeySpec; + Certificate keyCert; if (isServer) { - priKeySpec = new PKCS8EncodedKeySpec( - Base64.getMimeDecoder().decode(serverPrivateKey)); - is = new ByteArrayInputStream(serverCertStr.getBytes()); + priKeySpec =pemDecoder.decode(serverPrivateKey, PKCS8EncodedKeySpec.class); + keyCert = pemDecoder.decode(serverCertStr, X509Certificate.class); } else { - priKeySpec = new PKCS8EncodedKeySpec( - Base64.getMimeDecoder().decode(clientPrivateKey)); - is = new ByteArrayInputStream(clientCertStr.getBytes()); + priKeySpec = pemDecoder.decode(clientPrivateKey, PKCS8EncodedKeySpec.class); + keyCert = pemDecoder.decode(clientCertStr, X509Certificate.class); } KeyFactory kf = KeyFactory.getInstance("RSA"); RSAPrivateKey priKey = (RSAPrivateKey)kf.generatePrivate(priKeySpec); - Certificate keyCert = cf.generateCertificate(is); - is.close(); chain[0] = keyCert; ks.setKeyEntry("End Entity", priKey, passphrase, chain); @@ -496,7 +423,8 @@ private static SSLContext getSSLContext(boolean isServer) throws Exception { // check the certification path PKIXParameters paras = new PKIXParameters(ks); paras.setRevocationEnabled(false); - CertPath path = cf.generateCertPath(Arrays.asList(chain)); + CertPath path = CertificateFactory.getInstance("X.509") + .generateCertPath(Arrays.asList(chain)); CertPathValidator cv = CertPathValidator.getInstance("PKIX"); cv.validate(path, paras); @@ -531,7 +459,7 @@ private static void parseArguments(String[] args) { volatile Exception serverException = null; volatile Exception clientException = null; - public static void main(String args[]) throws Exception { + public static void main(String[] args) throws Exception { if (debug) System.setProperty("javax.net.debug", "all"); @@ -586,22 +514,20 @@ public static void main(String args[]) throws Exception { void startServer(boolean newThread) throws Exception { if (newThread) { - serverThread = new Thread() { - public void run() { - try { - doServerSide(); - } catch (Exception e) { - /* - * Our server thread just died. - * - * Release the client, if not active already... - */ - System.err.println("Server died..."); - serverReady = true; - serverException = e; - } + serverThread = new Thread(() -> { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + System.err.println("Server died..."); + serverReady = true; + serverException = e; } - }; + }); serverThread.start(); } else { doServerSide(); @@ -610,19 +536,17 @@ public void run() { void startClient(boolean newThread) throws Exception { if (newThread) { - clientThread = new Thread() { - public void run() { - try { - doClientSide(); - } catch (Exception e) { - /* - * Our client thread just died. - */ - System.err.println("Client died..."); - clientException = e; - } + clientThread = new Thread(() -> { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + System.err.println("Client died..."); + clientException = e; } - }; + }); clientThread.start(); } else { doClientSide(); diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/ComodoHacker.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/ComodoHacker.java index 6a67364360f4f..f1e5415e2c3bb 100644 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/ComodoHacker.java +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/ComodoHacker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -25,21 +25,18 @@ * @test * @bug 7123519 * @summary Problem with java/classes_security + * @enablePreview * @run main/othervm ComodoHacker PKIX * @run main/othervm ComodoHacker SunX509 */ -import java.net.*; -import java.util.*; -import java.io.*; -import javax.net.ssl.*; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; import java.security.KeyStore; +import java.security.PEMDecoder; import java.security.cert.Certificate; -import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; -import java.security.spec.*; -import java.security.interfaces.*; public class ComodoHacker { // DigiNotar Root CA, untrusted root certificate @@ -213,6 +210,8 @@ public class ComodoHacker { "baB2sVGcVNBkK55bT8gPqnx8JypubyUvayzZGg==\n" + "-----END CERTIFICATE-----"; + private static final PEMDecoder pemDecoder = PEMDecoder.of(); + private static String tmAlgorithm; // trust manager public static void main(String args[]) throws Exception { @@ -253,19 +252,15 @@ private static void parseArguments(String[] args) { } private static X509TrustManager getTrustManager() throws Exception { - // generate certificate from cert string - CertificateFactory cf = CertificateFactory.getInstance("X.509"); // create a key store KeyStore ks = KeyStore.getInstance("JKS"); ks.load(null, null); + // generate certificate from cert string + Certificate trustedCert = pemDecoder.decode(trustedCertStr, X509Certificate.class); // import the trusted cert - try (ByteArrayInputStream is = - new ByteArrayInputStream(trustedCertStr.getBytes())) { - Certificate trustedCert = cf.generateCertificate(is); - ks.setCertificateEntry("RSA Export Signer", trustedCert); - } + ks.setCertificateEntry("RSA Export Signer", trustedCert); // create the trust manager TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmAlgorithm); @@ -276,28 +271,11 @@ private static X509TrustManager getTrustManager() throws Exception { private static X509Certificate[] getFraudulentChain() throws Exception { // generate certificate from cert string - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - X509Certificate[] chain = new X509Certificate[4]; - try (ByteArrayInputStream is = - new ByteArrayInputStream(targetCertStr.getBytes())) { - chain[0] = (X509Certificate)cf.generateCertificate(is); - } - - try (ByteArrayInputStream is = - new ByteArrayInputStream(intermediateCertStr.getBytes())) { - chain[1] = (X509Certificate)cf.generateCertificate(is); - } - - try (ByteArrayInputStream is = - new ByteArrayInputStream(compromisedCertStr.getBytes())) { - chain[2] = (X509Certificate)cf.generateCertificate(is); - } - - try (ByteArrayInputStream is = - new ByteArrayInputStream(untrustedCrossCertStr.getBytes())) { - chain[3] = (X509Certificate)cf.generateCertificate(is); - } + chain[0] = pemDecoder.decode(targetCertStr, X509Certificate.class); + chain[1] = pemDecoder.decode(intermediateCertStr, X509Certificate.class); + chain[2] = pemDecoder.decode(compromisedCertStr, X509Certificate.class); + chain[3] = pemDecoder.decode(untrustedCrossCertStr, X509Certificate.class); return chain; } diff --git a/test/jdk/sun/security/x509/AlgorithmId/AlgorithmIdEqualsHashCode.java b/test/jdk/sun/security/x509/AlgorithmId/AlgorithmIdEqualsHashCode.java index ff91e3dff81fe..be3da70f85108 100644 --- a/test/jdk/sun/security/x509/AlgorithmId/AlgorithmIdEqualsHashCode.java +++ b/test/jdk/sun/security/x509/AlgorithmId/AlgorithmIdEqualsHashCode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -24,16 +24,19 @@ /* * @test * @author Gary Ellison - * @bug 4170635 8258247 + * @bug 4170635 8258247 8367008 + * @library /test/lib * @summary Verify equals()/hashCode() contract honored * @modules java.base/sun.security.x509 java.base/sun.security.util */ -import java.io.*; +import java.io.IOException; import java.security.AlgorithmParameters; import java.security.spec.MGF1ParameterSpec; import java.security.spec.PSSParameterSpec; +import jdk.test.lib.Asserts; + import sun.security.util.DerValue; import sun.security.x509.*; @@ -97,5 +100,20 @@ public static void main(String[] args) throws Exception { } else { System.out.println("PASSED equals() test"); } + + // Construct an AlgorithmId with explicit DER NULL parameters + DerValue explicitNullParams = new DerValue(DerValue.tag_Null, new byte[0]); + AlgorithmId aiNullParams = new AlgorithmId(AlgorithmId.SHA256_oid, + explicitNullParams); + // The constructor should canonicalize this to "no parameters" + Asserts.assertTrue(aiNullParams.getEncodedParams() == null); + AlgorithmId aiNormal = AlgorithmId.get("SHA-256"); + Asserts.assertEquals(aiNullParams, aiNormal); + Asserts.assertEquals(aiNullParams.hashCode(), aiNormal.hashCode()); + + // Test invalid ASN.1 NULL (non-zero length) + DerValue invalidNull = new DerValue(DerValue.tag_Null, new byte[]{0x00}); + Asserts.assertThrows(IOException.class, + () -> new AlgorithmId(AlgorithmId.SHA256_oid, invalidNull)); } } diff --git a/test/jdk/sun/security/x509/AlgorithmId/NullParams.java b/test/jdk/sun/security/x509/AlgorithmId/NullParams.java index 733ab9fa52231..0b542997a1991 100644 --- a/test/jdk/sun/security/x509/AlgorithmId/NullParams.java +++ b/test/jdk/sun/security/x509/AlgorithmId/NullParams.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -67,6 +67,13 @@ public static void main(String[] args) throws Exception { test("SHA3-256withRSA", true); test("SHA3-384withRSA", true); test("SHA3-512withRSA", true); + test("HmacSHA1", true); + test("HmacSHA224", true); + test("HmacSHA256", true); + test("HmacSHA384", true); + test("HmacSHA512", true); + test("HmacSHA512/224", true); + test("HmacSHA512/256", true); // Full old list: must be absent test("SHA1withECDSA", false); @@ -83,7 +90,6 @@ public static void main(String[] args) throws Exception { // Others test("DSA", false); test("SHA1withDSA", false); - test("HmacSHA1", false); if (failed) { throw new RuntimeException("At least one failed"); diff --git a/test/jdk/sun/security/x509/X509CRLImpl/Verify.java b/test/jdk/sun/security/x509/X509CRLImpl/Verify.java index 911f53f512075..a10a18971d20d 100644 --- a/test/jdk/sun/security/x509/X509CRLImpl/Verify.java +++ b/test/jdk/sun/security/x509/X509CRLImpl/Verify.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -25,11 +25,19 @@ * @test * @bug 7026347 * @summary X509CRL should have verify(PublicKey key, Provider sigProvider) + * @enablePreview */ -import java.io.ByteArrayInputStream; -import java.security.*; -import java.security.cert.*; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PEMDecoder; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Security; +import java.security.SignatureException; +import java.security.cert.CRLException; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; public class Verify { @@ -144,23 +152,21 @@ public static void main(String[] args) throws Exception { } } - private static void setup() throws CertificateException, CRLException { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); + private static void setup() { + final PEMDecoder pemDecoder = PEMDecoder.of(); /* Create CRL */ - ByteArrayInputStream inputStream = - new ByteArrayInputStream(crlStr.getBytes()); - crl = (X509CRL)cf.generateCRL(inputStream); + crl = pemDecoder.decode(crlStr, X509CRL.class); /* Get public key of the CRL issuer cert */ - inputStream = new ByteArrayInputStream(crlIssuerCertStr.getBytes()); - X509Certificate cert - = (X509Certificate)cf.generateCertificate(inputStream); - crlIssuerCertPubKey = cert.getPublicKey(); + crlIssuerCertPubKey = pemDecoder.decode(crlIssuerCertStr, X509Certificate.class) + .getPublicKey(); + /* Get public key of the self-signed Cert */ - inputStream = new ByteArrayInputStream(selfSignedCertStr.getBytes()); - selfSignedCertPubKey = cf.generateCertificate(inputStream).getPublicKey(); + selfSignedCertPubKey = pemDecoder.decode(selfSignedCertStr, X509Certificate.class) + .getPublicKey(); + } private static void verifyCRL(PublicKey key, String providerName) diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/AnnotationsTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/AnnotationsTest.java index e16b175ab8a0c..f88d1f81a3467 100644 --- a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/AnnotationsTest.java +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/AnnotationsTest.java @@ -95,6 +95,18 @@ public void testVarArg2(int a, String b, String ... other) { recordTestCase(a, b, other); } + enum Tack { + STARBOARD, + PORTSIDE; + } + + @Test + @Parameter({"STARBOARD"}) + @Parameter({"PORTSIDE", "STARBOARD"}) + public void testEnumVarArg(Tack ... cource) { + recordTestCase((Object[]) cource); + } + @Test @ParameterSupplier("dateSupplier") @ParameterSupplier("jdk.jpackage.test.AnnotationsTest.dateSupplier") @@ -118,6 +130,8 @@ public static Set getExpectedTestDescs() { "().testVarArg2(-89, bar, [more, moore](length=2))", "().testVarArg2(-89, bar, [more](length=1))", "().testVarArg2(12, foo, [](length=0))", + "().testEnumVarArg(STARBOARD)", + "().testEnumVarArg(PORTSIDE, STARBOARD)", "().testDates(2018-05-05)", "().testDates(2018-07-11)", "().testDates(2034-05-05)", diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JUnitUtilsTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JUnitUtilsTest.java new file mode 100644 index 0000000000000..28b55f98fe203 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JUnitUtilsTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class JUnitUtilsTest { + + @Test + public void test_assertArrayEquals() { + JUnitUtils.assertArrayEquals(new int[] {1, 2, 3}, new int[] {1, 2, 3}); + JUnitUtils.assertArrayEquals(new long[] {1, 2, 3}, new long[] {1, 2, 3}); + JUnitUtils.assertArrayEquals(new boolean[] {true, true}, new boolean[] {true, true}); + } + + @Test + public void test_assertArrayEquals_negative() { + assertThrows(AssertionError.class, () -> { + JUnitUtils.assertArrayEquals(new int[] {1, 2, 3}, new int[] {2, 3}); + }); + } + + @Test + public void test_exceptionAsPropertyMapWithMessageWithoutCause() { + + var ex = new Exception("foo"); + + var map = JUnitUtils.exceptionAsPropertyMap(ex); + + assertEquals(Map.of("getClass", Exception.class.getName(), "getMessage", "foo"), map); + } +} diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/ObjectMapperTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/ObjectMapperTest.java new file mode 100644 index 0000000000000..0310d276e218b --- /dev/null +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/ObjectMapperTest.java @@ -0,0 +1,731 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.math.BigInteger; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import org.junit.jupiter.api.Test; + +public class ObjectMapperTest { + + @Test + public void test_String() { + var om = ObjectMapper.blank().create(); + + var map = om.map("foo"); + + assertEquals("foo", map); + } + + @Test + public void test_int() { + var om = ObjectMapper.blank().create(); + + var map = om.map(100); + + assertEquals(100, map); + } + + @Test + public void test_null() { + var om = ObjectMapper.blank().create(); + + var map = om.map(null); + + assertNull(map); + } + + @Test + public void test_Object() { + var obj = new Object(); + assertSame(obj, ObjectMapper.blank().create().map(obj)); + assertSame(obj, ObjectMapper.standard().create().map(obj)); + } + + @Test + public void test_Path() { + var obj = Path.of("foo/bar"); + + assertSame(obj, ObjectMapper.standard().create().map(obj)); + } + + @Test + public void test_UUID() { + var obj = UUID.randomUUID(); + + assertSame(obj, ObjectMapper.standard().create().map(obj)); + } + + @Test + public void test_BigInteger() { + var obj = BigInteger.TEN; + + assertSame(obj, ObjectMapper.standard().create().map(obj)); + } + + @Test + public void test_Enum() { + + var expected = Map.of( + "name", TestEnum.BAR.name(), + "ordinal", TestEnum.BAR.ordinal(), + "a", "A", + "b", 123, + "num", 100 + ); + + assertEquals(expected, ObjectMapper.standard().create().map(TestEnum.BAR)); + } + + @Test + public void test_array_int() { + + var obj = new int[] { 1, 4, 5 }; + + assertSame(obj, ObjectMapper.standard().create().map(obj)); + } + + @Test + public void test_array_String() { + + var obj = new String[] { "Hello", "Bye" }; + + assertSame(obj, ObjectMapper.standard().create().map(obj)); + } + + @Test + public void test_array_empty() { + + var obj = new Thread[0]; + + assertSame(obj, ObjectMapper.standard().create().map(obj)); + } + + @Test + public void test_array_nulls() { + + var obj = new Thread[10]; + + assertSame(obj, ObjectMapper.standard().create().map(obj)); + } + + @Test + public void test_array_Path() { + + var obj = new Path[] { Path.of("foo/bar"), null, Path.of("").toAbsolutePath() }; + + assertSame(obj, ObjectMapper.standard().create().map(obj)); + } + + @Test + public void test_array_Object() { + + var obj = new Object[] { Path.of("foo/bar"), null, 145, new Simple.Stub("Hello", 738), "foo" }; + + var expected = new Object[] { Path.of("foo/bar"), null, 145, Map.of("a", "Hello", "b", 738), "foo" }; + + assertArrayEquals(expected, (Object[])ObjectMapper.standard().create().map(obj)); + } + + @Test + public void test_functional() { + assertWrappedIdentity(new Function() { + + @Override + public Integer apply(Object o) { + throw new AssertionError(); + } + + }); + + assertWrappedIdentity(new BiFunction() { + + @Override + public Integer apply(Object a, String b) { + throw new AssertionError(); + } + + }); + + assertWrappedIdentity(new Consumer<>() { + + @Override + public void accept(Object o) { + throw new AssertionError(); + } + + }); + + assertWrappedIdentity(new BiConsumer<>() { + + @Override + public void accept(Object a, Object b) { + throw new AssertionError(); + } + + }); + + assertWrappedIdentity(new Predicate<>() { + + @Override + public boolean test(Object o) { + throw new AssertionError(); + } + + }); + + assertWrappedIdentity(new Supplier<>() { + + @Override + public Object get() { + throw new AssertionError(); + } + + }); + + assertWrappedIdentity(new Runnable() { + + @Override + public void run() { + throw new AssertionError(); + } + + }); + } + + @Test + public void testIdentityWrapper() { + var om = ObjectMapper.standard().create(); + + var a = new Object() {}; + var b = new Object() {}; + + var amap = om.map(a); + var amap2 = om.map(a); + + assertEquals(amap, amap2); + assertEquals(ObjectMapper.wrapIdentity(a), amap); + + var bmap = om.map(b); + + assertNotEquals(amap, bmap); + assertEquals(ObjectMapper.wrapIdentity(b), bmap); + } + + @Test + public void test_wrapIdentity() { + + assertThrowsExactly(NullPointerException.class, () -> ObjectMapper.wrapIdentity(null)); + + var iw = ObjectMapper.wrapIdentity(new Object()); + + assertSame(iw, ObjectMapper.wrapIdentity(iw)); + + var simpleStubA = new Simple.Stub("Hello", 77); + var simpleStubB = new Simple.Stub("Hello", 77); + + assertEquals(simpleStubA, simpleStubB); + assertNotEquals(ObjectMapper.wrapIdentity(simpleStubA), ObjectMapper.wrapIdentity(simpleStubB)); + assertEquals(ObjectMapper.wrapIdentity(simpleStubA), ObjectMapper.wrapIdentity(simpleStubA)); + } + + @Test + public void test_empty_List() { + var om = ObjectMapper.blank().create(); + + var map = om.map(List.of()); + + assertEquals(List.of(), map); + } + + @Test + public void test_List() { + var om = ObjectMapper.blank().create(); + + var map = om.map(List.of(100, "foo")); + + assertEquals(List.of(100, "foo"), map); + } + + @Test + public void test_empty_Map() { + var om = ObjectMapper.blank().create(); + + var map = om.map(Map.of()); + + assertEquals(Map.of(), map); + } + + @Test + public void test_Map() { + var om = ObjectMapper.blank().create(); + + var map = om.map(Map.of(100, "foo")); + + assertEquals(Map.of(100, "foo"), map); + } + + @Test + public void test_MapSimple() { + var om = ObjectMapper.standard().create(); + + var map = om.map(Map.of(123, "foo", 321, new Simple.Stub("Hello", 567))); + + assertEquals(Map.of(123, "foo", 321, Map.of("a", "Hello", "b", 567)), map); + } + + @Test + public void test_ListSimple() { + var om = ObjectMapper.standard().create(); + + var map = om.map(List.of(100, new Simple.Stub("Hello", 567), "bar", new Simple() {})); + + assertEquals(List.of(100, Map.of("a", "Hello", "b", 567), "bar", Map.of("a", "foo", "b", 123)), map); + } + + @Test + public void test_Simple() { + var om = ObjectMapper.standard().create(); + + var map = om.map(new Simple() {}); + + assertEquals(Map.of("a", "foo", "b", 123), map); + } + + @Test + public void test_Proxy() { + var om = ObjectMapper.standard().create(); + + var map = om.map(Proxy.newProxyInstance(Simple.class.getClassLoader(), new Class[] { Simple.class }, new InvocationHandler() { + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + switch (method.getName()) { + case "a" -> { + return "Bye"; + } + case "b" -> { + return 335; + } + default -> { + throw new UnsupportedOperationException(); + } + } + } + + })); + + assertEquals(Map.of("a", "Bye", "b", 335), map); + } + + @Test + public void test_Simple_null_property() { + var om = ObjectMapper.standard().create(); + + var map = om.map(new Simple.Stub(null, 123)); + + assertEquals(Map.of("b", 123, "a", ObjectMapper.NULL), map); + } + + @Test + public void test_Optional_String() { + var om = ObjectMapper.standard().create(); + + var map = om.map(Optional.of("foo")); + + assertEquals(Map.of("get", "foo"), map); + } + + @Test + public void test_Optional_empty() { + var om = ObjectMapper.standard().create(); + + var map = om.map(Optional.empty()); + + assertEquals(Map.of("get", ObjectMapper.NULL), map); + } + + @Test + public void test_toMap() { + var om = ObjectMapper.standard().create(); + + assertNull(om.toMap(null)); + assertEquals(Map.of("value", "Hello"), om.toMap("Hello")); + assertEquals(Map.of("a", "foo", "b", 123), om.toMap(new Simple() {})); + } + + @Test + public void test_getter_throws() { + var om = ObjectMapper.blank() + .mutate(ObjectMapper.configureObject()) + .mutate(ObjectMapper.configureLeafClasses()) + .mutate(ObjectMapper.configureException()) + .create(); + + var expected = Map.of("get", om.toMap(new UnsupportedOperationException("Not for you!"))); + + var actual = om.toMap(new Supplier<>() { + @Override + public Object get() { + throw new UnsupportedOperationException("Not for you!"); + } + }); + + assertEquals(expected, actual); + } + + @Test + public void test_exception_with_message_with_cause() { + + var ex = new Exception("foo", new IllegalArgumentException("Cause", new RuntimeException("Ops!"))); + + var om = ObjectMapper.standard().create(); + + var map = om.toMap(ex); + + assertEquals(Map.of( + "getClass", Exception.class.getName(), + "getMessage", "foo", + "getCause", Map.of( + "getClass", IllegalArgumentException.class.getName(), + "getMessage", "Cause", + "getCause", Map.of( + "getClass", RuntimeException.class.getName(), + "getMessage", "Ops!" + ) + ) + ), map); + } + + @Test + public void test_exception_without_message_with_cause() { + + var ex = new RuntimeException(null, new UnknownError("Ops!")); + + var om = ObjectMapper.standard().create(); + + var map = om.toMap(ex); + + assertEquals(Map.of( + "getClass", RuntimeException.class.getName(), + "getCause", Map.of( + "getMessage", "Ops!", + "getCause", ObjectMapper.NULL + ) + ), map); + } + + @Test + public void test_exception_without_message_without_cause() { + + var ex = new UnsupportedOperationException(); + + var om = ObjectMapper.standard().create(); + + var map = om.toMap(ex); + + assertEquals(Map.of("getClass", UnsupportedOperationException.class.getName()), map); + } + + @Test + public void test_exception_CustomException() { + + var ex = new CustomException("Hello", Path.of(""), Optional.empty(), null); + + var om = ObjectMapper.standard().create(); + + var map = om.toMap(ex); + + assertEquals(Map.of( + "getClass", CustomException.class.getName(), + "getMessage", "Hello", + "op", Map.of("get", ObjectMapper.NULL), + "path2", Path.of("") + ), map); + } + + @Test + public void test_Builder_accessPackageMethods() { + + var obj = new TestType().foo("Hello").bar(81); + + var map = ObjectMapper.standard().create().toMap(obj); + + assertEquals(Map.of("foo", "Hello"), map); + + map = ObjectMapper.standard().accessPackageMethods(TestType.class.getPackage()).create().toMap(obj); + + assertEquals(Map.of("foo", "Hello", "bar", 81), map); + } + + @Test + public void test_Builder_methods_Simple() { + + var om = ObjectMapper.standard().exceptSomeMethods(Simple.class).add("a").apply().create(); + + assertEquals(Map.of("b", 123), om.toMap(new Simple() {})); + assertEquals(Map.of("b", 345), om.toMap(new Simple.Stub("Hello", 345))); + assertEquals(Map.of("b", 123), om.toMap(new Simple.Default("Hello"))); + assertEquals(Map.of("b", 345 + 10), om.toMap(new Simple.DefaultExt("Hello", 345))); + + om = ObjectMapper.standard().exceptSomeMethods(Simple.class).add("b").apply().create(); + + assertEquals(Map.of("a", "foo"), om.toMap(new Simple() {})); + assertEquals(Map.of("a", "Hello"), om.toMap(new Simple.Stub("Hello", 345))); + assertEquals(Map.of("a", "Hello"), om.toMap(new Simple.Default("Hello"))); + assertEquals(Map.of("a", "[Hello]"), om.toMap(new Simple.DefaultExt("Hello", 345))); + } + + @Test + public void test_Builder_methods_SimpleStub() { + + var om = ObjectMapper.standard().exceptSomeMethods(Simple.Stub.class).add("a").apply().create(); + + assertEquals(Map.of("a", "foo", "b", 123), om.toMap(new Simple() {})); + assertEquals(Map.of("b", 345), om.toMap(new Simple.Stub("Hello", 345))); + assertEquals(Map.of("a", "Hello", "b", 123), om.toMap(new Simple.Default("Hello"))); + assertEquals(Map.of("a", "[Hello]", "b", 345 + 10), om.toMap(new Simple.DefaultExt("Hello", 345))); + + om = ObjectMapper.standard().exceptSomeMethods(Simple.Stub.class).add("b").apply().create(); + + assertEquals(Map.of("a", "foo", "b", 123), om.toMap(new Simple() {})); + assertEquals(Map.of("a", "Hello"), om.toMap(new Simple.Stub("Hello", 345))); + assertEquals(Map.of("a", "Hello", "b", 123), om.toMap(new Simple.Default("Hello"))); + assertEquals(Map.of("a", "[Hello]", "b", 345 + 10), om.toMap(new Simple.DefaultExt("Hello", 345))); + } + + @Test + public void test_Builder_methods_SimpleDefault() { + + var om = ObjectMapper.standard().exceptSomeMethods(Simple.Default.class).add("a").apply().create(); + + assertEquals(Map.of("a", "foo", "b", 123), om.toMap(new Simple() {})); + assertEquals(Map.of("a", "Hello", "b", 345), om.toMap(new Simple.Stub("Hello", 345))); + assertEquals(Map.of("b", 123), om.toMap(new Simple.Default("Hello"))); + assertEquals(Map.of("b", 345 + 10), om.toMap(new Simple.DefaultExt("Hello", 345))); + + om = ObjectMapper.standard().exceptSomeMethods(Simple.Default.class).add("b").apply().create(); + + assertEquals(Map.of("a", "foo"), om.toMap(new Simple() {})); + assertEquals(Map.of("a", "Hello"), om.toMap(new Simple.Stub("Hello", 345))); + assertEquals(Map.of("a", "Hello"), om.toMap(new Simple.Default("Hello"))); + assertEquals(Map.of("a", "[Hello]"), om.toMap(new Simple.DefaultExt("Hello", 345))); + } + + @Test + public void test_Builder_methods_SimpleDefaultExt() { + + var om = ObjectMapper.standard().exceptSomeMethods(Simple.DefaultExt.class).add("a").apply().create(); + + assertEquals(Map.of("a", "foo", "b", 123), om.toMap(new Simple() {})); + assertEquals(Map.of("a", "Hello", "b", 345), om.toMap(new Simple.Stub("Hello", 345))); + assertEquals(Map.of("a", "Hello", "b", 123), om.toMap(new Simple.Default("Hello"))); + assertEquals(Map.of("b", 345 + 10), om.toMap(new Simple.DefaultExt("Hello", 345))); + + om = ObjectMapper.standard().exceptSomeMethods(Simple.DefaultExt.class).add("b").apply().create(); + + assertEquals(Map.of("a", "foo", "b", 123), om.toMap(new Simple() {})); + assertEquals(Map.of("a", "Hello", "b", 345), om.toMap(new Simple.Stub("Hello", 345))); + assertEquals(Map.of("a", "Hello", "b", 123), om.toMap(new Simple.Default("Hello"))); + assertEquals(Map.of("a", "[Hello]"), om.toMap(new Simple.DefaultExt("Hello", 345))); + } + + @Test + public void test_Builder_methods_SimpleStub_and_SimpleDefault() { + + var om = ObjectMapper.standard() + .exceptSomeMethods(Simple.Stub.class).add("a").apply() + .exceptSomeMethods(Simple.Default.class).add("a").apply() + .create(); + + assertEquals(Map.of("a", "foo", "b", 123), om.toMap(new Simple() {})); + assertEquals(Map.of("b", 345), om.toMap(new Simple.Stub("Hello", 345))); + assertEquals(Map.of("b", 123), om.toMap(new Simple.Default("Hello"))); + assertEquals(Map.of("b", 345 + 10), om.toMap(new Simple.DefaultExt("Hello", 345))); + + om = ObjectMapper.standard() + .exceptSomeMethods(Simple.Stub.class).add("b").apply() + .exceptSomeMethods(Simple.Default.class).add("b").apply() + .create(); + + assertEquals(Map.of("a", "foo"), om.toMap(new Simple() {})); + assertEquals(Map.of("a", "Hello"), om.toMap(new Simple.Stub("Hello", 345))); + assertEquals(Map.of("a", "Hello"), om.toMap(new Simple.Default("Hello"))); + assertEquals(Map.of("a", "[Hello]"), om.toMap(new Simple.DefaultExt("Hello", 345))); + } + + @Test + public void test_Builder_methods_all_excluded() { + + var om = ObjectMapper.standard() + .exceptSomeMethods(Simple.class).add("a").apply() + .exceptSomeMethods(Simple.Stub.class).add("b").apply() + .create(); + + var obj = new Simple.Stub("Hello", 345); + + assertEquals(ObjectMapper.wrapIdentity(obj), om.map(obj)); + } + + interface Simple { + default String a() { + return "foo"; + } + + default int b() { + return 123; + } + + record Stub(String a, int b) implements Simple {} + + static class Default implements Simple { + Default(String a) { + this.a = a; + } + + @Override + public String a() { + return a; + } + + private final String a; + } + + static class DefaultExt extends Default { + DefaultExt(String a, int b) { + super(a); + this.b = b; + } + + @Override + public String a() { + return "[" + super.a() + "]"; + } + + @Override + public int b() { + return 10 + b; + } + + private final int b; + } + } + + final class TestType { + + public String foo() { + return foo; + } + + public TestType foo(String v) { + foo = v; + return this; + } + + int bar() { + return bar; + } + + TestType bar(int v) { + bar = v; + return this; + } + + private String foo; + private int bar; + } + + enum TestEnum implements Simple { + FOO, + BAR; + + public int num() { + return 100; + } + + public int num(int v) { + return v; + } + + @Override + public String a() { + return "A"; + } + } + + static final class CustomException extends Exception { + + CustomException(String message, Path path, Optional optional, Throwable cause) { + super(message, cause); + this.path = path; + this.optional = optional; + } + + Path path() { + return path; + } + + public Path path2() { + return path; + } + + public Optional op() { + return optional; + } + + private final Path path; + private final Optional optional; + + private static final long serialVersionUID = 1L; + + } + + private static void assertWrappedIdentity(ObjectMapper om, Object obj) { + var map = om.toMap(obj); + assertEquals(Map.of("value", ObjectMapper.wrapIdentity(obj)), map); + } + + private static void assertWrappedIdentity(Object obj) { + assertWrappedIdentity(ObjectMapper.standard().create(), obj); + } +} diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.java index da94db30925da..4cf89fca3cc8a 100644 --- a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.java +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.java @@ -341,7 +341,7 @@ public PackageType packageType() { } @Override - JPackageCommand assertAppLayout() { + JPackageCommand runStandardAsserts() { return this; } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java index 50222d89cebdc..66da89fc3f98a 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java @@ -198,7 +198,7 @@ static void forEachAdditionalLauncher(JPackageCommand cmd, } } - static PropertyFile getAdditionalLauncherProperties( + public static PropertyFile getAdditionalLauncherProperties( JPackageCommand cmd, String launcherName) { PropertyFile shell[] = new PropertyFile[1]; forEachAdditionalLauncher(cmd, (name, propertiesFilePath) -> { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ApplicationLayout.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ApplicationLayout.java index 7ab3b824aa44b..0701421e999f8 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ApplicationLayout.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ApplicationLayout.java @@ -98,12 +98,18 @@ public static ApplicationLayout platformAppImage() { throw new IllegalArgumentException("Unknown platform"); } - public static ApplicationLayout javaRuntime() { + public static ApplicationLayout platformJavaRuntime() { + Path runtime = Path.of(""); + Path runtimeHome = runtime; + if (TKit.isOSX()) { + runtimeHome = Path.of("Contents/Home"); + } + return new ApplicationLayout( null, null, - Path.of(""), - null, + runtime, + runtimeHome, null, null, null, diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigFilesStasher.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigFilesStasher.java index e630659bdb17d..2321e4e852e3e 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigFilesStasher.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigFilesStasher.java @@ -374,8 +374,14 @@ private boolean isPathEmpty() { private static Path setupDirectory(JPackageCommand cmd, String argName) { if (!cmd.hasArgument(argName)) { - // Use absolute path as jpackage can be executed in another directory - cmd.setArgumentValue(argName, TKit.createTempDirectory("stash-script-resource-dir").toAbsolutePath()); + // Use absolute path as jpackage can be executed in another directory. + // Some tests expect a specific last argument, don't interfere with them + // and insert the argument at the beginning of the command line. + List args = new ArrayList<>(); + args.add(argName); + args.add(TKit.createTempDirectory("stash-script-resource-dir").toAbsolutePath().toString()); + args.addAll(cmd.getAllArguments()); + cmd.clearArguments().addArguments(args); } return Path.of(cmd.getArgumentValue(argName)); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigurationTarget.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigurationTarget.java new file mode 100644 index 0000000000000..ba3131a76807d --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigurationTarget.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; + +/** + * Provides uniform way to configure {@code JPackageCommand} and + * {@code PackageTest} instances. + */ +public record ConfigurationTarget(Optional cmd, Optional test) { + + public ConfigurationTarget { + Objects.requireNonNull(cmd); + Objects.requireNonNull(test); + if (cmd.isEmpty() == test.isEmpty()) { + throw new IllegalArgumentException(); + } + } + + public ConfigurationTarget(JPackageCommand target) { + this(Optional.of(target), Optional.empty()); + } + + public ConfigurationTarget(PackageTest target) { + this(Optional.empty(), Optional.of(target)); + } + + public ConfigurationTarget apply(Consumer a, Consumer b) { + cmd.ifPresent(Objects.requireNonNull(a)); + test.ifPresent(Objects.requireNonNull(b)); + return this; + } + + public ConfigurationTarget addInitializer(Consumer initializer) { + cmd.ifPresent(Objects.requireNonNull(initializer)); + test.ifPresent(v -> { + v.addInitializer(initializer::accept); + }); + return this; + } + + public ConfigurationTarget add(AdditionalLauncher addLauncher) { + return apply(addLauncher::applyTo, addLauncher::applyTo); + } +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java index 69ea4ecfaa099..6c7b6a2525570 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java @@ -125,6 +125,8 @@ private JarBuilder createJarBuilder() { if (appDesc.isWithMainClass()) { builder.setMainClass(appDesc.className()); } + // Use an old release number to make test app classes runnable on older runtimes. + builder.setRelease(11); return builder; } 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 8984450f54be2..b3729093ad200 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -39,7 +39,6 @@ import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.ListIterator; @@ -68,9 +67,11 @@ */ public class JPackageCommand extends CommandArguments { + @SuppressWarnings("this-escape") public JPackageCommand() { prerequisiteActions = new Actions(); verifyActions = new Actions(); + excludeStandardAsserts(StandardAssert.MAIN_LAUNCHER_DESCRIPTION); } private JPackageCommand(JPackageCommand cmd, boolean immutable) { @@ -86,7 +87,7 @@ private JPackageCommand(JPackageCommand cmd, boolean immutable) { dmgInstallDir = cmd.dmgInstallDir; prerequisiteActions = new Actions(cmd.prerequisiteActions); verifyActions = new Actions(cmd.verifyActions); - appLayoutAsserts = cmd.appLayoutAsserts; + standardAsserts = cmd.standardAsserts; readOnlyPathAsserts = cmd.readOnlyPathAsserts; outputValidators = cmd.outputValidators; executeInDirectory = cmd.executeInDirectory; @@ -202,6 +203,17 @@ public JPackageCommand addArguments(String name, Path value) { return addArguments(name, value.toString()); } + public JPackageCommand mutate(Consumer mutator) { + return mutate(List.of(mutator)); + } + + public JPackageCommand mutate(Iterable> mutators) { + for (var mutator : mutators) { + mutator.accept(this); + } + return this; + } + public boolean isImagePackageType() { return PackageType.IMAGE == getArgumentValue("--type", () -> null, PACKAGE_TYPES::get); @@ -460,7 +472,7 @@ public ApplicationLayout appLayout() { if (layout != null) { } else if (isRuntime()) { - layout = ApplicationLayout.javaRuntime(); + layout = ApplicationLayout.platformJavaRuntime(); } else { layout = ApplicationLayout.platformAppImage(); } @@ -691,7 +703,7 @@ public boolean isPackageUnpacked() { } public static void useToolProviderByDefault(ToolProvider jpackageToolProvider) { - defaultToolProvider = Optional.of(jpackageToolProvider); + defaultToolProvider.set(Optional.of(jpackageToolProvider)); } public static void useToolProviderByDefault() { @@ -699,7 +711,7 @@ public static void useToolProviderByDefault() { } public static void useExecutableByDefault() { - defaultToolProvider = Optional.empty(); + defaultToolProvider.set(Optional.empty()); } public JPackageCommand useToolProvider(boolean v) { @@ -808,7 +820,9 @@ public JPackageCommand validateOutput(CannedFormattedString... str) { } public boolean isWithToolProvider() { - return Optional.ofNullable(withToolProvider).orElseGet(defaultToolProvider::isPresent); + return Optional.ofNullable(withToolProvider).orElseGet(() -> { + return defaultToolProvider.get().isPresent(); + }); } public JPackageCommand executePrerequisiteActions() { @@ -824,7 +838,7 @@ private Executor createExecutor() { .addArguments(args); if (isWithToolProvider()) { - exec.setToolProvider(defaultToolProvider.orElseGet(JavaTool.JPACKAGE::asToolProvider)); + exec.setToolProvider(defaultToolProvider.get().orElseGet(JavaTool.JPACKAGE::asToolProvider)); } else { exec.setExecutable(JavaTool.JPACKAGE); if (TKit.isWindows()) { @@ -932,7 +946,7 @@ public Executor.Result executeAndAssertImageCreated() { public JPackageCommand assertImageCreated() { verifyIsOfType(PackageType.IMAGE); - assertAppLayout(); + runStandardAsserts(); return this; } @@ -975,10 +989,10 @@ private static final class ReadOnlyPathsAssert { void updateAndAssert() { final var newSnapshots = createSnapshots(); for (final var a : asserts.keySet().stream().sorted().toList()) { - final var snapshopGroup = snapshots.get(a); - final var newSnapshopGroup = newSnapshots.get(a); - for (int i = 0; i < snapshopGroup.size(); i++) { - TKit.PathSnapshot.assertEquals(snapshopGroup.get(i), newSnapshopGroup.get(i), + final var snapshotGroup = snapshots.get(a); + final var newSnapshotGroup = newSnapshots.get(a); + for (int i = 0; i < snapshotGroup.size(); i++) { + snapshotGroup.get(i).assertEquals(newSnapshotGroup.get(i), String.format("Check jpackage didn't modify ${%s}=[%s]", a, asserts.get(a).get(i))); } } @@ -1093,7 +1107,7 @@ public JPackageCommand excludeReadOnlyPathAssert(ReadOnlyPathAssert... asserts) asSet::contains)).toArray(ReadOnlyPathAssert[]::new)); } - public static enum AppLayoutAssert { + public static enum StandardAssert { APP_IMAGE_FILE(JPackageCommand::assertAppImageFile), PACKAGE_FILE(JPackageCommand::assertPackageFile), NO_MAIN_LAUNCHER_IN_RUNTIME(cmd -> { @@ -1113,6 +1127,11 @@ public static enum AppLayoutAssert { LauncherVerifier.Action.VERIFY_MAC_ENTITLEMENTS); } }), + MAIN_LAUNCHER_DESCRIPTION(cmd -> { + if (!cmd.isRuntime()) { + new LauncherVerifier(cmd).verify(cmd, LauncherVerifier.Action.VERIFY_DESCRIPTION); + } + }), MAIN_JAR_FILE(cmd -> { Optional.ofNullable(cmd.getArgumentValue("--main-jar", () -> null)).ifPresent(mainJar -> { TKit.assertFileExists(cmd.appLayout().appDirectory().resolve(mainJar)); @@ -1130,9 +1149,14 @@ public static enum AppLayoutAssert { MacHelper.verifyBundleStructure(cmd); } }), + MAC_BUNDLE_UNSIGNED_SIGNATURE(cmd -> { + if (TKit.isOSX() && !MacHelper.appImageSigned(cmd)) { + MacHelper.verifyUnsignedBundleSignature(cmd); + } + }), ; - AppLayoutAssert(Consumer action) { + StandardAssert(Consumer action) { this.action = action; } @@ -1150,21 +1174,21 @@ private static JPackageCommand convertFromRuntime(JPackageCommand cmd) { private final Consumer action; } - public JPackageCommand setAppLayoutAsserts(AppLayoutAssert ... asserts) { + public JPackageCommand setStandardAsserts(StandardAssert ... asserts) { verifyMutable(); - appLayoutAsserts = Set.of(asserts); + standardAsserts = Set.of(asserts); return this; } - public JPackageCommand excludeAppLayoutAsserts(AppLayoutAssert... asserts) { + public JPackageCommand excludeStandardAsserts(StandardAssert... asserts) { var asSet = Set.of(asserts); - return setAppLayoutAsserts(appLayoutAsserts.stream().filter(Predicate.not( - asSet::contains)).toArray(AppLayoutAssert[]::new)); + return setStandardAsserts(standardAsserts.stream().filter(Predicate.not( + asSet::contains)).toArray(StandardAssert[]::new)); } - JPackageCommand assertAppLayout() { - for (var appLayoutAssert : appLayoutAsserts.stream().sorted().toList()) { - appLayoutAssert.action.accept(this); + JPackageCommand runStandardAsserts() { + for (var standardAssert : standardAsserts.stream().sorted().toList()) { + standardAssert.action.accept(this); } return this; } @@ -1256,10 +1280,7 @@ public void assertFileNotInAppImage(Path filename) { private void assertFileInAppImage(Path filename, Path expectedPath) { if (expectedPath != null) { - if (expectedPath.isAbsolute()) { - throw new IllegalArgumentException(); - } - if (!expectedPath.getFileName().equals(filename.getFileName())) { + if (expectedPath.isAbsolute() || !expectedPath.getFileName().equals(filename.getFileName())) { throw new IllegalArgumentException(); } } @@ -1345,7 +1366,7 @@ private JPackageCommand adjustArgumentsBeforeExecution() { addArguments("--runtime-image", DEFAULT_RUNTIME_IMAGE); } - if (!hasArgument("--verbose") && TKit.VERBOSE_JPACKAGE && !ignoreDefaultVerbose) { + if (!hasArgument("--verbose") && TKit.verboseJPackage() && !ignoreDefaultVerbose) { addArgument("--verbose"); } @@ -1369,11 +1390,7 @@ public void verifyIsOfType(PackageType ... types) { final var typesSet = Stream.of(types).collect(Collectors.toSet()); if (!hasArgument("--type")) { if (!isImagePackageType()) { - if (TKit.isLinux() && typesSet.equals(PackageType.LINUX)) { - return; - } - - if (TKit.isWindows() && typesSet.equals(PackageType.WINDOWS)) { + if ((TKit.isLinux() && typesSet.equals(PackageType.LINUX)) || (TKit.isWindows() && typesSet.equals(PackageType.WINDOWS))) { return; } @@ -1521,31 +1538,23 @@ public void run() { private Path winMsiLogFile; private Path unpackedPackageDirectory; private Set readOnlyPathAsserts = Set.of(ReadOnlyPathAssert.values()); - private Set appLayoutAsserts = Set.of(AppLayoutAssert.values()); + private Set standardAsserts = Set.of(StandardAssert.values()); private List>> outputValidators = new ArrayList<>(); - private static Optional defaultToolProvider = Optional.empty(); - - private static final Map PACKAGE_TYPES = Functional.identity( - () -> { - Map reply = new HashMap<>(); - for (PackageType type : PackageType.values()) { - reply.put(type.getType(), type); - } - return reply; - }).get(); - - public static final Path DEFAULT_RUNTIME_IMAGE = Functional.identity(() -> { - // Set the property to the path of run-time image to speed up - // building app images and platform bundles by avoiding running jlink - // The value of the property will be automativcally appended to - // jpackage command line if the command line doesn't have - // `--runtime-image` parameter set. - String val = TKit.getConfigProperty("runtime-image"); - if (val != null) { - return Path.of(val); + private static InheritableThreadLocal> defaultToolProvider = new InheritableThreadLocal<>() { + @Override + protected Optional initialValue() { + return Optional.empty(); } - return null; - }).get(); + }; + + private static final Map PACKAGE_TYPES = Stream.of(PackageType.values()).collect(toMap(PackageType::getType, x -> x)); + + // Set the property to the path of run-time image to speed up + // building app images and platform bundles by avoiding running jlink. + // The value of the property will be automatically appended to + // jpackage command line if the command line doesn't have + // `--runtime-image` parameter set. + public static final Path DEFAULT_RUNTIME_IMAGE = Optional.ofNullable(TKit.getConfigProperty("runtime-image")).map(Path::of).orElse(null); // [HH:mm:ss.SSS] private static final Pattern TIMESTAMP_REGEXP = Pattern.compile( diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JarBuilder.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JarBuilder.java index d62575d2fefcf..c69c29af53a04 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JarBuilder.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JarBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Optional; /** @@ -48,6 +49,11 @@ public JarBuilder setMainClass(String v) { return this; } + public JarBuilder setRelease(int v) { + release = v; + return this; + } + public JarBuilder addSourceFile(Path v) { sourceFiles.add(v); return this; @@ -61,11 +67,15 @@ public JarBuilder setModuleVersion(String v) { public void create() { TKit.withTempDirectory("jar-workdir", workDir -> { if (!sourceFiles.isEmpty()) { - new Executor() + var exec = new Executor() .setToolProvider(JavaTool.JAVAC) - .addArguments("-d", workDir.toString()) - .addPathArguments(sourceFiles) - .execute(); + .addArguments("-d", workDir.toString()); + + Optional.ofNullable(release).ifPresent(r -> { + exec.addArguments("--release", r.toString()); + }); + + exec.addPathArguments(sourceFiles).execute(); } Files.createDirectories(outputJar.getParent()); @@ -92,4 +102,5 @@ public void create() { private Path outputJar; private String mainClass; private String moduleVersion; + private Integer release; } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java index 278cd569baca7..79652a9828e00 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.Optional; public final class LauncherIconVerifier { public LauncherIconVerifier() { @@ -37,19 +38,33 @@ public LauncherIconVerifier setLauncherName(String v) { public LauncherIconVerifier setExpectedIcon(Path v) { expectedIcon = v; + expectedDefault = false; return this; } public LauncherIconVerifier setExpectedDefaultIcon() { + expectedIcon = null; expectedDefault = true; return this; } + public LauncherIconVerifier setExpectedNoIcon() { + return setExpectedIcon(null); + } + public LauncherIconVerifier verifyFileInAppImageOnly(boolean v) { verifyFileInAppImageOnly = true; return this; } + public boolean expectDefaultIcon() { + return expectedDefault; + } + + public Optional expectIcon() { + return Optional.ofNullable(expectedIcon); + } + public void applyTo(JPackageCommand cmd) throws IOException { final String curLauncherName; final String label; @@ -70,7 +85,7 @@ public void applyTo(JPackageCommand cmd) throws IOException { WinExecutableIconVerifier.verifyLauncherIcon(cmd, launcherName, expectedIcon, expectedDefault); } } else if (expectedDefault) { - TKit.assertPathExists(iconPath, true); + TKit.assertFileExists(iconPath); } else if (expectedIcon == null) { TKit.assertPathExists(iconPath, false); } else { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java index 87dc203daa148..55cb38f21cfc9 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java @@ -339,9 +339,20 @@ private void verifyMacEntitlements(JPackageCommand cmd) throws ParserConfigurati TKit.assertTrue(entitlements.isPresent(), String.format("Check [%s] launcher is signed with entitlements", name)); + var customFile = Optional.ofNullable(cmd.getArgumentValue("--mac-entitlements")).map(Path::of); + if (customFile.isEmpty()) { + // Try from the resource dir. + var resourceDirFile = Optional.ofNullable(cmd.getArgumentValue("--resource-dir")).map(Path::of).map(resourceDir -> { + return resourceDir.resolve(cmd.name() + ".entitlements"); + }).filter(Files::exists); + if (resourceDirFile.isPresent()) { + customFile = resourceDirFile; + } + } + Map expected; - if (cmd.hasArgument("--mac-entitlements")) { - expected = new PListReader(Files.readAllBytes(Path.of(cmd.getArgumentValue("--mac-entitlements")))).toMap(true); + if (customFile.isPresent()) { + expected = new PListReader(Files.readAllBytes(customFile.orElseThrow())).toMap(true); } else if (cmd.hasArgument("--mac-app-store")) { expected = DefaultEntitlements.APP_STORE; } else { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java index 3a27ae32f437a..9776ab5c4c838 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java @@ -22,7 +22,11 @@ */ package jdk.jpackage.test; +import static jdk.jpackage.test.AdditionalLauncher.getAdditionalLauncherProperties; import static java.util.Collections.unmodifiableSortedSet; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.toSet; import java.io.IOException; import java.io.UncheckedIOException; @@ -45,7 +49,6 @@ import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.util.function.ThrowingConsumer; @@ -164,8 +167,7 @@ public static List getPrerequisitePackages(JPackageCommand cmd) { switch (packageType) { case LINUX_DEB: return Stream.of(getDebBundleProperty(cmd.outputBundle(), - "Depends").split(",")).map(String::strip).collect( - Collectors.toList()); + "Depends").split(",")).map(String::strip).toList(); case LINUX_RPM: return Executor.of("rpm", "-qp", "-R") @@ -326,10 +328,9 @@ static void verifyPackageBundleEssential(JPackageCommand cmd) { if (cmd.isRuntime()) { Path runtimeDir = cmd.appRuntimeDirectory(); Set expectedCriticalRuntimePaths = CRITICAL_RUNTIME_FILES.stream().map( - runtimeDir::resolve).collect(Collectors.toSet()); + runtimeDir::resolve).collect(toSet()); Set actualCriticalRuntimePaths = getPackageFiles(cmd).filter( - expectedCriticalRuntimePaths::contains).collect( - Collectors.toSet()); + expectedCriticalRuntimePaths::contains).collect(toSet()); checkPrerequisites = expectedCriticalRuntimePaths.equals( actualCriticalRuntimePaths); } else { @@ -375,8 +376,7 @@ static void addBundleDesktopIntegrationVerifier(PackageTest test, boolean integr Function, String> verifier = (lines) -> { // Lookup for xdg commands return lines.stream().filter(line -> { - Set words = Stream.of(line.split("\\s+")).collect( - Collectors.toSet()); + Set words = Stream.of(line.split("\\s+")).collect(toSet()); return words.contains("xdg-desktop-menu") || words.contains( "xdg-mime") || words.contains("xdg-icon-resource"); }).findFirst().orElse(null); @@ -454,11 +454,29 @@ static void verifyDesktopFiles(JPackageCommand cmd, boolean installed) { } private static Collection getDesktopFiles(JPackageCommand cmd) { + var unpackedDir = cmd.appLayout().desktopIntegrationDirectory(); + + return relativePackageFilesInSubdirectory(cmd, ApplicationLayout::desktopIntegrationDirectory) + .filter(path -> { + return path.getNameCount() == 1; + }) + .filter(path -> { + return ".desktop".equals(PathUtils.getSuffix(path)); + }) + .map(unpackedDir::resolve) + .toList(); + } + + private static Stream relativePackageFilesInSubdirectory( + JPackageCommand cmd, Function subdirFunc) { + + var unpackedDir = subdirFunc.apply(cmd.appLayout()); var packageDir = cmd.pathToPackageFile(unpackedDir); + return getPackageFiles(cmd).filter(path -> { - return packageDir.equals(path.getParent()) && path.getFileName().toString().endsWith(".desktop"); - }).map(Path::getFileName).map(unpackedDir::resolve).toList(); + return path.startsWith(packageDir); + }).map(packageDir::relativize); } private static String launcherNameFromDesktopFile(JPackageCommand cmd, Optional predefinedAppImage, Path desktopFile) { @@ -496,7 +514,20 @@ private static void verifyDesktopFile(JPackageCommand cmd, Optional { + return Optional.ofNullable(cmd.getArgumentValue("--description")); + }).orElseGet(cmd::name); + } + + for (var e : List.of( + Map.entry("Type", "Application"), + Map.entry("Terminal", "false"), + Map.entry("Comment", launcherDescription) + )) { String key = e.getKey(); TKit.assertEquals(e.getValue(), data.find(key).orElseThrow(), String.format( "Check value of [%s] key", key)); @@ -768,10 +799,10 @@ private static enum Scriptlet { static final Pattern RPM_HEADER_PATTERN = Pattern.compile(String.format( "(%s) scriptlet \\(using /bin/sh\\):", Stream.of(values()).map( - v -> v.rpm).collect(Collectors.joining("|")))); + v -> v.rpm).collect(joining("|")))); static final Map RPM_MAP = Stream.of(values()).collect( - Collectors.toMap(v -> v.rpm, v -> v)); + toMap(v -> v.rpm, v -> v)); } public static String getDefaultPackageArch(PackageType type) { @@ -848,7 +879,7 @@ private static final class DesktopFile { } else { return Map.entry(components[0], components[1]); } - }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + }).collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); } catch (IOException ex) { throw new UncheckedIOException(ex); } 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 d01536e327db5..3900851f81009 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java @@ -52,7 +52,9 @@ import java.util.Optional; import java.util.Properties; import java.util.Set; +import java.util.function.BiConsumer; import java.util.function.BiFunction; +import java.util.function.Consumer; import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -66,6 +68,7 @@ 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.CertificateRequest; import jdk.jpackage.test.PackageTest.PackageHandlers; public final class MacHelper { @@ -229,25 +232,61 @@ private static void collectPListProperty(Map accumulator, String } } + /** + * Returns {@code true} if the given jpackage command line is configured to sign + * predefined app image in place. + *

    + * jpackage will not create a new app image or a native bundle. + * + * @param cmd the jpackage command to examine + * @return {@code true} if the given jpackage command line is configured to sign + * predefined app image in place and {@code false} otherwise. + */ public static boolean signPredefinedAppImage(JPackageCommand cmd) { Objects.requireNonNull(cmd); if (!TKit.isOSX()) { throw new UnsupportedOperationException(); } - return cmd.hasArgument("--mac-sign") && cmd.hasArgument("--app-image"); - } - + return cmd.hasArgument("--mac-sign") && cmd.hasArgument("--app-image") && cmd.isImagePackageType(); + } + + /** + * Returns {@code true} if the given jpackage command line is configured such + * that the app image it will produce will be signed. + *

    + * If the jpackage command line is bundling a native package, the function + * returns {@code true} if the bundled app image will be signed. + * + * @param cmd the jpackage command to examine + * @return {@code true} if the given jpackage command line is configured such + * that the app image it will produce will be signed and {@code false} + * otherwise. + */ public static boolean appImageSigned(JPackageCommand cmd) { Objects.requireNonNull(cmd); if (!TKit.isOSX()) { throw new UnsupportedOperationException(); } - if (Optional.ofNullable(cmd.getArgumentValue("--app-image")).map(Path::of).map(AppImageFile::load).map(AppImageFile::macSigned).orElse(false)) { + var runtimeImage = Optional.ofNullable(cmd.getArgumentValue("--runtime-image")).map(Path::of); + var appImage = Optional.ofNullable(cmd.getArgumentValue("--app-image")).map(Path::of); + + if (cmd.isRuntime() && Files.isDirectory(runtimeImage.orElseThrow().resolve("Contents/_CodeSignature"))) { + // If the predefined runtime is a signed bundle, bundled image should be signed too. + return true; + } else if (appImage.map(AppImageFile::load).map(AppImageFile::macSigned).orElse(false)) { // The external 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; } @@ -332,6 +371,110 @@ public static void writeFaPListFragment(JPackageCommand cmd, XMLStreamWriter xml }).run(); } + public static Consumer useKeychain(MacSign.ResolvedKeychain keychain) { + return useKeychain(keychain.spec().keychain()); + } + + public static Consumer useKeychain(MacSign.Keychain keychain) { + return cmd -> { + useKeychain(cmd, keychain); + }; + } + + public static JPackageCommand useKeychain(JPackageCommand cmd, MacSign.ResolvedKeychain keychain) { + return useKeychain(cmd, keychain.spec().keychain()); + } + + public static JPackageCommand useKeychain(JPackageCommand cmd, MacSign.Keychain keychain) { + return sign(cmd).addArguments("--mac-signing-keychain", keychain.name()); + } + + public static JPackageCommand sign(JPackageCommand cmd) { + if (!cmd.hasArgument("--mac-sign")) { + cmd.addArgument("--mac-sign"); + } + return cmd; + } + + public record SignKeyOption(Type type, CertificateRequest certRequest) { + + public SignKeyOption { + Objects.requireNonNull(type); + Objects.requireNonNull(certRequest); + } + + public enum Type { + SIGN_KEY_USER_NAME, + SIGN_KEY_IDENTITY, + ; + } + + @Override + public String toString() { + var sb = new StringBuffer(); + applyTo((optionName, _) -> { + sb.append(String.format("{%s: %s}", optionName, certRequest)); + }); + return sb.toString(); + } + + public JPackageCommand addTo(JPackageCommand cmd) { + applyTo(cmd::addArguments); + return sign(cmd); + } + + public JPackageCommand setTo(JPackageCommand cmd) { + applyTo(cmd::setArgumentValue); + return sign(cmd); + } + + private void applyTo(BiConsumer sink) { + switch (certRequest.type()) { + case INSTALLER -> { + switch (type) { + case SIGN_KEY_IDENTITY -> { + sink.accept("--mac-installer-sign-identity", certRequest.name()); + return; + } + case SIGN_KEY_USER_NAME -> { + sink.accept("--mac-signing-key-user-name", certRequest.shortName()); + return; + } + } + } + case CODE_SIGN -> { + switch (type) { + case SIGN_KEY_IDENTITY -> { + sink.accept("--mac-app-image-sign-identity", certRequest.name()); + return; + } + case SIGN_KEY_USER_NAME -> { + sink.accept("--mac-signing-key-user-name", certRequest.shortName()); + return; + } + } + } + } + + throw new AssertionError(); + } + } + + static void verifyUnsignedBundleSignature(JPackageCommand cmd) { + if (!cmd.isImagePackageType()) { + MacSignVerify.assertUnsigned(cmd.outputBundle()); + } + + final Path bundleRoot; + if (cmd.isImagePackageType()) { + bundleRoot = cmd.outputBundle(); + } else { + bundleRoot = cmd.pathToUnpackedPackageFile(cmd.appInstallationDirectory()); + } + + MacSignVerify.assertAdhocSigned(bundleRoot); + } + static PackageHandlers createDmgPackageHandlers() { return new PackageHandlers(MacHelper::installDmg, MacHelper::uninstallDmg, MacHelper::unpackDmg); } 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 af9f57c4f7f47..7d2bb908edbac 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java @@ -59,6 +59,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; import javax.naming.ldap.LdapName; @@ -351,6 +352,52 @@ private String validatedPassword() { private String password; } + public static final class UsageBuilder { + + UsageBuilder(Collection keychains) { + this.keychains = List.copyOf(keychains); + } + + public void run(Runnable runnable) { + Objects.requireNonNull(runnable); + + final Optional> oldKeychains; + if (addToSearchList) { + oldKeychains = Optional.ofNullable(activeKeychainFiles()); + Keychain.addToSearchList(keychains); + } else { + oldKeychains = Optional.empty(); + } + + try { + // Ensure keychains to be used for signing are unlocked. + // When the codesign command operates on a locked keychain in a ssh session + // it emits cryptic "errSecInternalComponent" error without other details. + keychains.forEach(Keychain::unlock); + runnable.run(); + } finally { + oldKeychains.ifPresent(restoreKeychains -> { + security("list-keychains", "-d", "user", "-s") + .addArguments(restoreKeychains.stream().map(Path::toString).toList()) + .execute(); + }); + } + } + + public UsageBuilder addToSearchList(boolean v) { + addToSearchList = v; + return this; + } + + public UsageBuilder addToSearchList() { + return addToSearchList(true); + } + + private final Collection keychains; + private boolean addToSearchList; + } + + Keychain create() { final var exec = createExecutor("create-keychain"); final var result = exec.saveOutput().executeWithoutExitCodeCheck(); @@ -415,24 +462,12 @@ List findCertificates() { return certs; } - public static void addToSearchList(Collection keychains) { + static void addToSearchList(Collection keychains) { security("list-keychains", "-d", "user", "-s", "login.keychain") .addArguments(keychains.stream().map(Keychain::name).toList()) .execute(); } - public static void withAddedKeychains(Collection keychains, Runnable runnable) { - final var curKeychains = activeKeychainFiles(); - addToSearchList(keychains); - try { - runnable.run(); - } finally { - security("list-keychains", "-d", "user", "-s") - .addArguments(curKeychains.stream().map(Path::toString).toList()) - .execute(); - } - } - private static List activeKeychainFiles() { // $ security list-keychains // "/Users/alexeysemenyuk/Library/Keychains/login.keychain-db" @@ -1037,6 +1072,47 @@ public static boolean isDeployed(List specs) { return !missingKeychain && !missingCertificates && !invalidCertificates; } + public static Keychain.UsageBuilder withKeychains(KeychainWithCertsSpec... keychains) { + return withKeychains(Stream.of(keychains).map(KeychainWithCertsSpec::keychain).toArray(Keychain[]::new)); + } + + public static Keychain.UsageBuilder withKeychains(Keychain... keychains) { + return new Keychain.UsageBuilder(List.of(keychains)); + } + + public static void withKeychains(Runnable runnable, Consumer mutator, Keychain... keychains) { + Objects.requireNonNull(runnable); + var builder = withKeychains(keychains); + mutator.accept(builder); + builder.run(runnable); + } + + public static void withKeychains(Runnable runnable, Keychain... keychains) { + withKeychains(runnable, _ -> {}, keychains); + } + + public static void withKeychain(Consumer consumer, Consumer mutator, Keychain keychain) { + Objects.requireNonNull(consumer); + withKeychains(() -> { + consumer.accept(keychain); + }, mutator, keychain); + } + + public static void withKeychain(Consumer consumer, Keychain keychain) { + withKeychain(consumer, _ -> {}, keychain); + } + + public static void withKeychain(Consumer consumer, Consumer mutator, ResolvedKeychain keychain) { + Objects.requireNonNull(consumer); + withKeychains(() -> { + consumer.accept(keychain); + }, mutator, keychain.spec().keychain()); + } + + public static void withKeychain(Consumer consumer, ResolvedKeychain keychain) { + withKeychain(consumer, _ -> {}, keychain); + } + public static final class ResolvedKeychain { public ResolvedKeychain(KeychainWithCertsSpec spec) { this.spec = Objects.requireNonNull(spec); @@ -1046,6 +1122,10 @@ public KeychainWithCertsSpec spec() { return spec; } + public String name() { + return spec.keychain().name(); + } + public Map mapCertificateRequests() { if (certMap == null) { synchronized (this) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java index ae27e292bf676..81d31ed726789 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java @@ -44,6 +44,43 @@ */ public final class MacSignVerify { + public static void verifyAppImageSigned( + JPackageCommand cmd, CertificateRequest certRequest, MacSign.ResolvedKeychain keychain) { + + cmd.verifyIsOfType(PackageType.MAC); + Objects.requireNonNull(certRequest); + Objects.requireNonNull(keychain); + + final Path bundleRoot; + if (cmd.isImagePackageType()) { + bundleRoot = cmd.outputBundle(); + } else { + bundleRoot = cmd.pathToUnpackedPackageFile( + cmd.appInstallationDirectory()); + } + + assertSigned(bundleRoot, certRequest); + + if (!cmd.isRuntime()) { + cmd.addLauncherNames().stream().map(cmd::appLauncherPath).forEach(launcherPath -> { + assertSigned(launcherPath, certRequest); + }); + } + + // Set to "null" if the sign origin is not found, instead of bailing out with an exception. + // Let is fail in the following TKit.assertEquals() call with a proper log message. + var signOrigin = findSpctlSignOrigin(SpctlType.EXEC, bundleRoot).orElse(null); + + TKit.assertEquals(certRequest.name(), signOrigin, + String.format("Check [%s] has sign origin as expected", bundleRoot)); + } + + public static void verifyPkgSigned(JPackageCommand cmd, CertificateRequest certRequest, MacSign.ResolvedKeychain keychain) { + cmd.verifyIsOfType(PackageType.MAC_PKG); + assertPkgSigned(cmd.outputBundle(), certRequest, + Objects.requireNonNull(keychain.mapCertificateRequests().get(certRequest))); + } + public static void assertSigned(Path path, CertificateRequest certRequest) { assertSigned(path); TKit.assertEquals(certRequest.name(), findCodesignSignOrigin(path).orElse(null), @@ -114,8 +151,8 @@ public static Optional findSpctlSignOrigin(SpctlType type, Path path) { } public static Optional findCodesignSignOrigin(Path path) { - final var exec = Executor.of("/usr/bin/codesign", "--display", "--verbose=4", path.toString()).saveOutput(); - final var result = exec.executeWithoutExitCodeCheck(); + final var exec = Executor.of("/usr/bin/codesign", "--display", "--verbose=4", path.toString()); + final var result = exec.saveOutput().executeWithoutExitCodeCheck(); if (result.getExitCode() == 0) { return Optional.of(result.getOutput().stream().map(line -> { if (line.equals("Signature=adhoc")) { @@ -144,12 +181,34 @@ public static Optional findCodesignSignOrigin(Path path) { } public static void assertSigned(Path path) { - final var verifier = TKit.TextStreamVerifier.group() - .add(TKit.assertTextStream(": valid on disk").predicate(String::endsWith)) - .add(TKit.assertTextStream(": satisfies its Designated Requirement").predicate(String::endsWith)) - .create(); - verifier.accept(Executor.of("/usr/bin/codesign", "--verify", "--deep", - "--strict", "--verbose=2", path.toString()).executeAndGetOutput().iterator()); + assertSigned(path, false); + } + + private static void assertSigned(Path path, boolean sudo) { + final Executor exec; + if (sudo) { + exec = Executor.of("sudo", "/usr/bin/codesign"); + } else { + exec = Executor.of("/usr/bin/codesign"); + } + exec.addArguments("--verify", "--deep", "--strict", "--verbose=2", path.toString()); + final var result = exec.saveOutput().executeWithoutExitCodeCheck(); + if (result.getExitCode() == 0) { + TKit.TextStreamVerifier.group() + .add(TKit.assertTextStream(": valid on disk").predicate(String::endsWith)) + .add(TKit.assertTextStream(": satisfies its Designated Requirement").predicate(String::endsWith)) + .create().accept(result.getOutput().iterator()); + } else if (!sudo && result.getOutput().stream().findFirst().filter(str -> { + // By some reason /usr/bin/codesign command fails for some installed bundles. + // It is known to fail for some AppContentTest test cases and all FileAssociationsTest test cases. + // Rerunning the command with "sudo" works, though. + return str.equals(String.format("%s: Permission denied", path)); + }).isPresent()) { + TKit.trace("Try /usr/bin/codesign again with `sudo`"); + assertSigned(path, true); + } else { + reportUnexpectedCommandOutcome(exec.getPrintableCommandLine(), result); + } } public static List getPkgCertificateChain(Path path) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java index e7f06b0d60852..fa8fe166f5a33 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java @@ -41,11 +41,11 @@ public final class Main { - public static void main(String args[]) throws Throwable { + public static void main(String... args) throws Throwable { main(TestBuilder.build(), args); } - public static void main(TestBuilder.Builder builder, String args[]) throws Throwable { + public static void main(TestBuilder.Builder builder, String... args) throws Throwable { boolean listTests = false; List tests = new ArrayList<>(); try (TestBuilder testBuilder = builder.testConsumer(tests::add).create()) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ObjectMapper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ObjectMapper.java new file mode 100644 index 0000000000000..f35e255951eeb --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ObjectMapper.java @@ -0,0 +1,780 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.toSet; +import static jdk.jpackage.internal.util.function.ExceptionBox.rethrowUnchecked; +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; +import static jdk.jpackage.internal.util.function.ThrowingRunnable.toRunnable; +import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; + +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.math.BigInteger; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntPredicate; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.xml.stream.XMLStreamWriter; +import jdk.jpackage.internal.util.IdentityWrapper; + +public final class ObjectMapper { + + private ObjectMapper( + Predicate classFilter, + Predicate> methodFilter, + Predicate leafClassFilter, + Map> substitutes, + Map, BiConsumer>> mutators, + Set accessPackageMethods) { + + this.classFilter = Objects.requireNonNull(classFilter); + this.methodFilter = Objects.requireNonNull(methodFilter); + this.leafClassFilter = Objects.requireNonNull(leafClassFilter); + this.substitutes = Objects.requireNonNull(substitutes); + this.mutators = Objects.requireNonNull(mutators); + this.accessPackageMethods = accessPackageMethods; + } + + public static Builder blank() { + return new Builder().allowAllLeafClasses(false).exceptLeafClasses().add(Stream.of( + Object.class, + String.class, String[].class, + boolean.class, Boolean.class, boolean[].class, Boolean[].class, + byte.class, Byte.class, byte[].class, Byte[].class, + char.class, Character.class, char[].class, Character[].class, + short.class, Short.class, short[].class, Short[].class, + int.class, Integer.class, int[].class, Integer[].class, + long.class, Long.class, long[].class, Long[].class, + float.class, Float.class, float[].class, Float[].class, + double.class, Double.class, double[].class, Double[].class, + void.class, Void.class, Void[].class + ).map(Class::getName).toList()).apply(); + } + + public static Builder standard() { + return blank() + .mutate(configureObject()) + .mutate(configureLeafClasses()) + .mutate(configureOptional()) + .mutate(configureFunctionalTypes()) + .mutate(configureEnum()) + .mutate(configureException()); + } + + public static Consumer configureObject() { + // Exclude all method of Object class. + return builder -> { + builder.exceptMethods().add(OBJECT_METHODS).apply(); + }; + } + + public static Consumer configureLeafClasses() { + return builder -> { + builder.exceptLeafClasses().add(Stream.of( + IdentityWrapper.class, + Class.class, + Path.class, + Path.of("").getClass(), + UUID.class, + BigInteger.class + ).map(Class::getName).toList()).apply(); + }; + } + + public static Consumer configureOptional() { + return builder -> { + // Filter out all but "get()" methods of "Optional" class. + builder.exceptAllMethods(Optional.class).remove("get").apply(); + // Substitute "Optional.get()" with the function that will return "null" if the value is "null". + builder.subst(Optional.class, "get", opt -> { + if (opt.isPresent()) { + return opt.get(); + } else { + return null; + } + }); + }; + } + + public static Consumer configureFunctionalTypes() { + // Remove all getters from the standard functional types. + return builder -> { + builder.exceptAllMethods(Predicate.class).apply(); + builder.exceptAllMethods(Supplier.class).apply(); + }; + } + + public static Consumer configureEnum() { + return builder -> { + // Filter out "getDeclaringClass()" and "describeConstable()" methods of "Enum" class. + builder.exceptSomeMethods(Enum.class).add("getDeclaringClass", "describeConstable").apply(); + }; + } + + public static Consumer configureException() { + return builder -> { + // Include only "getMessage()" and "getCause()" methods of "Exception" class. + builder.exceptAllMethods(Exception.class).remove("getMessage", "getCause").apply(); + builder.mutator(Exception.class, (ex, map) -> { + var eit = map.entrySet().iterator(); + while (eit.hasNext()) { + var e = eit.next(); + if (e.getValue() == NULL) { + // Remove property with the "null" value. + eit.remove(); + } + } + map.put("getClass", ex.getClass().getName()); + }); + }; + } + + public static String lookupFullMethodName(Method m) { + return lookupFullMethodName(m.getDeclaringClass(), m.getName()); + } + + public static String lookupFullMethodName(Class c, String m) { + return Objects.requireNonNull(c).getName() + lookupMethodName(m); + } + + public static String lookupMethodName(Method m) { + return lookupMethodName(m.getName()); + } + + public static String lookupMethodName(String m) { + return "#" + Objects.requireNonNull(m); + } + + public static Object wrapIdentity(Object v) { + if (v instanceof IdentityWrapper wrapper) { + return wrapper; + } else { + return new IdentityWrapper(v); + } + } + + public static void store(Map map, XMLStreamWriter xml) { + XmlWriter.writePropertyMap(map, xml); + } + + @SuppressWarnings("unchecked") + public static Optional findNonNullProperty(Map map, String propertyName) { + Objects.requireNonNull(propertyName); + Objects.requireNonNull(map); + + return Optional.ofNullable(map.get(propertyName)).filter(Predicate.not(NULL::equals)).map(v -> { + return (T)v; + }); + } + + public Object map(Object obj) { + if (obj != null) { + return mapObject(obj).orElseGet(Map::of); + } else { + return null; + } + } + + @SuppressWarnings("unchecked") + public Map toMap(Object obj) { + if (obj == null) { + return null; + } else { + var mappedObj = map(obj); + if (mappedObj instanceof Map m) { + return (Map)m; + } else { + return Map.of("value", mappedObj); + } + } + } + + public Optional mapObject(Object obj) { + if (obj == null) { + return Optional.empty(); + } + + if (leafClassFilter.test(obj.getClass().getName())) { + return Optional.of(obj); + } + + if (!filter(obj.getClass())) { + return Optional.empty(); + } + + if (obj instanceof Iterable col) { + return Optional.of(mapIterable(col)); + } + + if (obj instanceof Map map) { + return Optional.of(mapMap(map)); + } + + if (obj.getClass().isArray()) { + return Optional.of(mapArray(obj)); + } + + var theMap = getMethods(obj).map(m -> { + final Object propertyValue; + final var subst = substitutes.get(m); + if (subst != null) { + propertyValue = applyGetter(obj, subst); + } else { + propertyValue = invoke(m, obj); + } + return Map.entry(m.getName(), mapObject(propertyValue).orElse(NULL)); + }).collect(toMutableMap(Map.Entry::getKey, Map.Entry::getValue)); + + mutators.entrySet().stream().filter(m -> { + return m.getKey().isInstance(obj); + }).findFirst().ifPresent(m -> { + m.getValue().accept(obj, theMap); + }); + + if (theMap.isEmpty()) { + return Optional.of(wrapIdentity(obj)); + } + + return Optional.of(theMap); + } + + private Object invoke(Method m, Object obj) { + try { + return m.invoke(obj); + } catch (IllegalAccessException ex) { + throw rethrowUnchecked(ex); + } catch (InvocationTargetException ex) { + return map(ex.getTargetException()); + } + } + + private Collection mapIterable(Iterable col) { + final List list = new ArrayList<>(); + for (var obj : col) { + list.add(mapObject(obj).orElse(NULL)); + } + return list; + } + + private Map mapMap(Map map) { + return map.entrySet().stream().collect(toMutableMap(e -> { + return mapObject(e.getKey()).orElse(NULL); + }, e -> { + return mapObject(e.getValue()).orElse(NULL); + })); + } + + private Object mapArray(Object arr) { + final var len = Array.getLength(arr); + + if (len == 0) { + return arr; + } + + Object[] buf = null; + + for (int i = 0; i != len; i++) { + var from = Array.get(arr, i); + if (from != null) { + var to = mapObject(from).orElseThrow(); + if (from != to || buf != null) { + if (buf == null) { + buf = (Object[])Array.newInstance(Object.class, len); + System.arraycopy(arr, 0, buf, 0, i); + } + buf[i] = to; + } + } + } + + return Optional.ofNullable((Object)buf).orElse(arr); + } + + @SuppressWarnings("unchecked") + private static Object applyGetter(Object obj, Function getter) { + return getter.apply((T)obj); + } + + private boolean filter(Class type) { + return classFilter.test(type.getName()); + } + + private boolean filter(Method m) { + return methodFilter.test(List.of(lookupMethodName(m), lookupFullMethodName(m))); + } + + private Stream getMethods(Object obj) { + return MethodGroups.create(obj.getClass(), accessPackageMethods).filter(this::filter).map(MethodGroup::callable); + } + + private static boolean defaultFilter(Method m) { + if (Modifier.isStatic(m.getModifiers()) || (m.getParameterCount() > 0) || void.class.equals(m.getReturnType())) { + return false; + } + return true; + } + + private static + Collector> toMutableMap(Function keyMapper, + Function valueMapper) { + return Collectors.toMap(keyMapper, valueMapper, (x , y) -> { + throw new UnsupportedOperationException( + String.format("Entries with the same key and different values [%s] and [%s]", x, y)); + }, HashMap::new); + } + + public static final class Builder { + + private Builder() { + allowAllClasses(); + allowAllLeafClasses(); + allowAllMethods(); + } + + public ObjectMapper create() { + return new ObjectMapper( + classFilter.createPredicate(), + methodFilter.createMultiPredicate(), + leafClassFilter.createPredicate(), + Map.copyOf(substitutes), + Map.copyOf(mutators), + accessPackageMethods); + } + + + public final class NamePredicateBuilder { + + NamePredicateBuilder(Filter sink) { + this.sink = Objects.requireNonNull(sink); + } + + public Builder apply() { + sink.addAll(items); + return Builder.this; + } + + public NamePredicateBuilder add(String... v) { + return add(List.of(v)); + } + + public NamePredicateBuilder add(Collection v) { + items.addAll(v); + return this; + } + + private final Filter sink; + private final Set items = new HashSet<>(); + } + + + public final class AllMethodPredicateBuilder { + + AllMethodPredicateBuilder(Class type) { + impl = new MethodPredicateBuilder(type, false); + } + + public AllMethodPredicateBuilder remove(String... v) { + return remove(List.of(v)); + } + + public AllMethodPredicateBuilder remove(Collection v) { + impl.add(v); + return this; + } + + public Builder apply() { + return impl.apply(); + } + + private final MethodPredicateBuilder impl; + } + + + public final class SomeMethodPredicateBuilder { + + SomeMethodPredicateBuilder(Class type) { + impl = new MethodPredicateBuilder(type, true); + } + + public SomeMethodPredicateBuilder add(String... v) { + return add(List.of(v)); + } + + public SomeMethodPredicateBuilder add(Collection v) { + impl.add(v); + return this; + } + + public Builder apply() { + return impl.apply(); + } + + private final MethodPredicateBuilder impl; + } + + + public Builder allowAllClasses(boolean v) { + classFilter.negate(v); + return this; + } + + public Builder allowAllClasses() { + return allowAllClasses(true); + } + + public Builder allowAllMethods(boolean v) { + methodFilter.negate(v); + return this; + } + + public Builder allowAllMethods() { + return allowAllMethods(true); + } + + public Builder allowAllLeafClasses(boolean v) { + leafClassFilter.negate(v); + return this; + } + + public Builder allowAllLeafClasses() { + return allowAllLeafClasses(true); + } + + public NamePredicateBuilder exceptClasses() { + return new NamePredicateBuilder(classFilter); + } + + public AllMethodPredicateBuilder exceptAllMethods(Class type) { + return new AllMethodPredicateBuilder(type); + } + + public SomeMethodPredicateBuilder exceptSomeMethods(Class type) { + return new SomeMethodPredicateBuilder(type); + } + + public NamePredicateBuilder exceptMethods() { + return new NamePredicateBuilder(methodFilter); + } + + public NamePredicateBuilder exceptLeafClasses() { + return new NamePredicateBuilder(leafClassFilter); + } + + public Builder subst(Method target, Function substitute) { + substitutes.put(Objects.requireNonNull(target), Objects.requireNonNull(substitute)); + return this; + } + + public Builder subst(Class targetClass, String targetMethodName, Function substitute) { + var method = toSupplier(() -> targetClass.getMethod(targetMethodName)).get(); + return subst(method, substitute); + } + + public Builder mutator(Class targetClass, BiConsumer> mutator) { + mutators.put(Objects.requireNonNull(targetClass), Objects.requireNonNull(mutator)); + return this; + } + + public Builder mutate(Consumer mutator) { + mutator.accept(this); + return this; + } + + public Builder accessPackageMethods(Package... packages) { + Stream.of(packages).map(Package::getName).forEach(accessPackageMethods::add); + return this; + } + + + private final class MethodPredicateBuilder { + + MethodPredicateBuilder(Class type, boolean negate) { + this.type = Objects.requireNonNull(type); + buffer.negate(negate); + } + + void add(Collection v) { + buffer.addAll(v); + } + + Builder apply() { + var pred = buffer.createPredicate(); + + var items = MethodGroups.create(type, accessPackageMethods).groups().stream().map(MethodGroup::primary).filter(m -> { + return !OBJECT_METHODS.contains(ObjectMapper.lookupMethodName(m)); + }).filter(m -> { + return !pred.test(m.getName()); + }).map(ObjectMapper::lookupFullMethodName).toList(); + + return exceptMethods().add(items).apply(); + } + + private final Class type; + private final Filter buffer = new Filter(); + } + + + private static final class Filter { + Predicate> createMultiPredicate() { + if (items.isEmpty()) { + var match = negate; + return v -> match; + } else if (negate) { + return v -> { + return v.stream().noneMatch(Set.copyOf(items)::contains); + }; + } else { + return v -> { + return v.stream().anyMatch(Set.copyOf(items)::contains); + }; + } + } + + Predicate createPredicate() { + if (items.isEmpty()) { + var match = negate; + return v -> match; + } else if (negate) { + return Predicate.not(Set.copyOf(items)::contains); + } else { + return Set.copyOf(items)::contains; + } + } + + void addAll(Collection v) { + items.addAll(v); + } + + void negate(boolean v) { + negate = v; + } + + private boolean negate; + private final Set items = new HashSet<>(); + } + + + private final Filter classFilter = new Filter(); + private final Filter methodFilter = new Filter(); + private final Filter leafClassFilter = new Filter(); + private final Map> substitutes = new HashMap<>(); + private final Map, BiConsumer>> mutators = new HashMap<>(); + private final Set accessPackageMethods = new HashSet<>(); + } + + + private record MethodGroup(List methods) { + + MethodGroup { + Objects.requireNonNull(methods); + + if (methods.isEmpty()) { + throw new IllegalArgumentException(); + } + + methods.stream().map(Method::getName).reduce((a, b) -> { + if (!a.equals(b)) { + throw new IllegalArgumentException(); + } else { + return a; + } + }); + } + + Method callable() { + var primary = primary(); + if (!primary.getDeclaringClass().isInterface()) { + primary = methods.stream().filter(m -> { + return m.getDeclaringClass().isInterface(); + }).findFirst().orElse(primary); + } + return primary; + } + + Method primary() { + return methods.getFirst(); + } + + boolean match(Predicate predicate) { + Objects.requireNonNull(predicate); + return methods.stream().allMatch(predicate); + } + } + + + private record MethodGroups(Collection groups) { + + MethodGroups { + Objects.requireNonNull(groups); + } + + Stream filter(Predicate predicate) { + Objects.requireNonNull(predicate); + + return groups.stream().filter(g -> { + return g.match(predicate); + }); + } + + static MethodGroups create(Class type, Set accessPackageMethods) { + List> types = new ArrayList<>(); + + collectSuperclassAndInterfaces(type, types::add); + + final var methodGroups = types.stream() + .map(c -> { + if (accessPackageMethods.contains(c.getPackageName())) { + return PUBLIC_AND_PACKAGE_METHODS_GETTER.apply(c); + } else { + return PUBLIC_METHODS_GETTER.apply(c); + } + }) + .flatMap(x -> x) + .filter(ObjectMapper::defaultFilter) + .collect(groupingBy(Method::getName)); + + return new MethodGroups(methodGroups.values().stream().distinct().map(MethodGroup::new).toList()); + } + + private static void collectSuperclassAndInterfaces(Class type, Consumer> sink) { + Objects.requireNonNull(type); + Objects.requireNonNull(sink); + + for (; type != null; type = type.getSuperclass()) { + sink.accept(type); + for (var i : type.getInterfaces()) { + collectSuperclassAndInterfaces(i, sink); + } + } + } + } + + + private static final class XmlWriter { + static void write(Object obj, XMLStreamWriter xml) { + if (obj instanceof Map map) { + writePropertyMap(map, xml); + } else if (obj instanceof Collection col) { + writeCollection(col, xml); + } else if (obj.getClass().isArray()) { + writeArray(obj, xml); + } else { + toRunnable(() -> xml.writeCharacters(obj.toString())).run(); + } + } + + private static void writePropertyMap(Map map, XMLStreamWriter xml) { + map.entrySet().stream().sorted(Comparator.comparing(e -> e.getKey().toString())).forEach(toConsumer(e -> { + xml.writeStartElement("property"); + xml.writeAttribute("name", e.getKey().toString()); + write(e.getValue(), xml); + xml.writeEndElement(); + })); + } + + private static void writeCollection(Collection col, XMLStreamWriter xml) { + try { + xml.writeStartElement("collection"); + xml.writeAttribute("size", Integer.toString(col.size())); + for (var item : col) { + xml.writeStartElement("item"); + write(item, xml); + xml.writeEndElement(); + } + xml.writeEndElement(); + } catch (Exception ex) { + rethrowUnchecked(ex); + } + } + + private static void writeArray(Object arr, XMLStreamWriter xml) { + var len = Array.getLength(arr); + try { + xml.writeStartElement("array"); + xml.writeAttribute("size", Integer.toString(len)); + for (int i = 0; i != len; i++) { + xml.writeStartElement("item"); + write(Array.get(arr, i), xml); + xml.writeEndElement(); + } + xml.writeEndElement(); + } catch (Exception ex) { + rethrowUnchecked(ex); + } + } + } + + + private final Predicate classFilter; + private final Predicate> methodFilter; + private final Predicate leafClassFilter; + private final Map> substitutes; + private final Map, BiConsumer>> mutators; + private final Set accessPackageMethods; + + static final Object NULL = new Object() { + @Override + public String toString() { + return ""; + } + }; + + private static final Set OBJECT_METHODS = + Stream.of(Object.class.getMethods()).map(ObjectMapper::lookupMethodName).collect(toSet()); + + private static final Function, Stream> PUBLIC_METHODS_GETTER = type -> { + return Stream.of(type.getMethods()); + }; + + private static final Function, Stream> PUBLIC_AND_PACKAGE_METHODS_GETTER = type -> { + return Stream.of(type.getDeclaredMethods()).filter(m -> { + return Stream.of(Modifier::isPrivate, Modifier::isProtected).map(p -> { + return p.test(m.getModifiers()); + }).allMatch(v -> !v); + }).map(m -> { + m.setAccessible(true); + return m; + }); + }; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java index 84453038cd2c8..3226811fe36e3 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java @@ -39,7 +39,6 @@ import java.time.Duration; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -318,6 +317,11 @@ PackageTest addHelloAppFileAssociationsVerifier(FileAssociations fa) { return this; } + public PackageTest mutate(Consumer mutator) { + mutator.accept(this); + return this; + } + public PackageTest forTypes(Collection types, Runnable action) { final var oldTypes = Set.of(currentTypes.toArray(PackageType[]::new)); try { @@ -334,7 +338,11 @@ public PackageTest forTypes(PackageType type, Runnable action) { } public PackageTest forTypes(PackageType type, Consumer action) { - return forTypes(List.of(type), () -> action.accept(this)); + return forTypes(List.of(type), action); + } + + public PackageTest forTypes(Collection types, Consumer action) { + return forTypes(types, () -> action.accept(this)); } public PackageTest notForTypes(Collection types, Runnable action) { @@ -348,7 +356,11 @@ public PackageTest notForTypes(PackageType type, Runnable action) { } public PackageTest notForTypes(PackageType type, Consumer action) { - return notForTypes(List.of(type), () -> action.accept(this)); + return notForTypes(List.of(type), action); + } + + public PackageTest notForTypes(Collection types, Consumer action) { + return notForTypes(types, () -> action.accept(this)); } public PackageTest configureHelloApp() { @@ -780,7 +792,7 @@ private void verifyPackageInstalled(JPackageCommand cmd) { LauncherAsServiceVerifier.verify(cmd); } - cmd.assertAppLayout(); + cmd.runStandardAsserts(); installVerifiers.forEach(v -> v.accept(cmd)); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java index ed9c727abccd9..a19b3697a8159 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java @@ -29,7 +29,6 @@ import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import java.io.Closeable; -import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.io.UncheckedIOException; @@ -39,6 +38,7 @@ import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; @@ -110,7 +110,7 @@ public final class TKit { }).get(); static void withExtraLogStream(ThrowingRunnable action) { - if (extraLogStream != null) { + if (state().extraLogStream != null) { ThrowingRunnable.toRunnable(action).run(); } else { try (PrintStream logStream = openLogStream()) { @@ -120,12 +120,44 @@ static void withExtraLogStream(ThrowingRunnable action) { } static void withExtraLogStream(ThrowingRunnable action, PrintStream logStream) { - var oldExtraLogStream = extraLogStream; + withNewState(action, stateBuilder -> { + stateBuilder.extraLogStream(logStream); + }); + } + + public static void withMainLogStream(ThrowingRunnable action, PrintStream logStream) { + withNewState(action, stateBuilder -> { + stateBuilder.mainLogStream(logStream); + }); + } + + public static void withStackTraceStream(ThrowingRunnable action, PrintStream logStream) { + withNewState(action, stateBuilder -> { + stateBuilder.stackTraceStream(logStream); + }); + } + + public static State state() { + return STATE.get(); + } + + public static void state(State v) { + STATE.set(Objects.requireNonNull(v)); + } + + private static void withNewState(ThrowingRunnable action, Consumer stateBuilderMutator) { + Objects.requireNonNull(action); + Objects.requireNonNull(stateBuilderMutator); + + var oldState = state(); + var builder = oldState.buildCopy(); + stateBuilderMutator.accept(builder); + var newState = builder.create(); try { - extraLogStream = logStream; + state(newState); ThrowingRunnable.toRunnable(action).run(); } finally { - extraLogStream = oldExtraLogStream; + state(oldState); } } @@ -142,26 +174,25 @@ static void runTests(List tests) { static void runTests(List tests, Set modes) { Objects.requireNonNull(tests); Objects.requireNonNull(modes); - if (currentTest != null) { - throw new IllegalStateException( - "Unexpected nested or concurrent Test.run() call"); + if (currentTest() != null) { + throw new IllegalStateException("Unexpected nested Test.run() call"); } withExtraLogStream(() -> { tests.stream().forEach(test -> { - currentTest = test; - try { - if (modes.contains(RunTestMode.FAIL_FAST)) { - ThrowingRunnable.toRunnable(test::run).run(); - } else { - ignoreExceptions(test).run(); - } - } finally { - currentTest = null; - if (extraLogStream != null) { - extraLogStream.flush(); + withNewState(() -> { + try { + if (modes.contains(RunTestMode.FAIL_FAST)) { + test.run(); + } else { + ignoreExceptions(test).run(); + } + } finally { + Optional.ofNullable(state().extraLogStream).ifPresent(PrintStream::flush); } - } + }, stateBuilder -> { + stateBuilder.currentTest(test); + }); }); }); } @@ -218,18 +249,18 @@ static void unbox(Throwable throwable) throws Throwable { } public static Path workDir() { - return currentTest.workDir(); + return currentTest().workDir(); } static String getCurrentDefaultAppName() { // Construct app name from swapping and joining test base name // and test function name. // Say the test name is `FooTest.testBasic`. Then app name would be `BasicFooTest`. - String appNamePrefix = currentTest.functionName(); + String appNamePrefix = currentTest().functionName(); if (appNamePrefix != null && appNamePrefix.startsWith("test")) { appNamePrefix = appNamePrefix.substring("test".length()); } - return Stream.of(appNamePrefix, currentTest.baseName()).filter( + return Stream.of(appNamePrefix, currentTest().baseName()).filter( v -> v != null && !v.isEmpty()).collect(Collectors.joining()); } @@ -257,9 +288,10 @@ private static String addTimestamp(String msg) { static void log(String v) { v = addTimestamp(v); - System.out.println(v); - if (extraLogStream != null) { - extraLogStream.println(v); + var state = state(); + state.mainLogStream.println(v); + if (state.extraLogStream != null) { + state.extraLogStream.println(v); } } @@ -309,13 +341,13 @@ public static void createPropertiesFile(Path propsFilename, } public static void trace(String v) { - if (TRACE) { + if (state().trace) { log("TRACE: " + v); } } private static void traceAssert(String v) { - if (TRACE_ASSERTS) { + if (state().traceAsserts) { log("TRACE: " + v); } } @@ -576,10 +608,14 @@ public static RuntimeException throwSkippedException(String reason) { public static RuntimeException throwSkippedException(RuntimeException ex) { trace("Skip the test: " + ex.getMessage()); - currentTest.notifySkipped(ex); + currentTest().notifySkipped(ex); throw ex; } + public static boolean isSkippedException(Throwable t) { + return JtregSkippedExceptionClass.INSTANCE.isInstance(t); + } + public static Path createRelativePathCopy(final Path file) { Path fileCopy = ThrowingSupplier.toSupplier(() -> { Path localPath = createTempFile(file.getFileName()); @@ -654,10 +690,9 @@ private static void waitForFileCreated(Path fileToWaitFor, Duration timeout) thr } static void printStackTrace(Throwable throwable) { - if (extraLogStream != null) { - throwable.printStackTrace(extraLogStream); - } - throwable.printStackTrace(); + var state = state(); + Optional.ofNullable(state.extraLogStream).ifPresent(throwable::printStackTrace); + throwable.printStackTrace(state.stackTraceStream); } private static String concatMessages(String msg, String msg2) { @@ -668,7 +703,7 @@ private static String concatMessages(String msg, String msg2) { } public static void assertEquals(long expected, long actual, String msg) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); if (expected != actual) { error(concatMessages(String.format( "Expected [%d]. Actual [%d]", expected, actual), @@ -679,7 +714,7 @@ public static void assertEquals(long expected, long actual, String msg) { } public static void assertNotEquals(long expected, long actual, String msg) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); if (expected == actual) { error(concatMessages(String.format("Unexpected [%d] value", actual), msg)); @@ -690,7 +725,7 @@ public static void assertNotEquals(long expected, long actual, String msg) { } public static void assertEquals(boolean expected, boolean actual, String msg) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); if (expected != actual) { error(concatMessages(String.format( "Expected [%s]. Actual [%s]", expected, actual), @@ -701,7 +736,7 @@ public static void assertEquals(boolean expected, boolean actual, String msg) { } public static void assertNotEquals(boolean expected, boolean actual, String msg) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); if (expected == actual) { error(concatMessages(String.format("Unexpected [%s] value", actual), msg)); @@ -713,7 +748,7 @@ public static void assertNotEquals(boolean expected, boolean actual, String msg) public static void assertEquals(Object expected, Object actual, String msg) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); if ((actual != null && !actual.equals(expected)) || (expected != null && !expected.equals(actual))) { error(concatMessages(String.format( @@ -725,7 +760,7 @@ public static void assertEquals(Object expected, Object actual, String msg) { } public static void assertNotEquals(Object expected, Object actual, String msg) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); if ((actual != null && !actual.equals(expected)) || (expected != null && !expected.equals(actual))) { @@ -738,7 +773,7 @@ public static void assertNotEquals(Object expected, Object actual, String msg) { } public static void assertNull(Object value, String msg) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); if (value != null) { error(concatMessages(String.format("Unexpected not null value [%s]", value), msg)); @@ -748,7 +783,7 @@ public static void assertNull(Object value, String msg) { } public static void assertNotNull(Object value, String msg) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); if (value == null) { error(concatMessages("Unexpected null value", msg)); } @@ -765,7 +800,7 @@ public static void assertFalse(boolean actual, String msg) { } public static void assertTrue(boolean actual, String msg, Runnable onFail) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); if (!actual) { if (onFail != null) { onFail.run(); @@ -777,7 +812,7 @@ public static void assertTrue(boolean actual, String msg, Runnable onFail) { } public static void assertFalse(boolean actual, String msg, Runnable onFail) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); if (actual) { if (onFail != null) { onFail.run(); @@ -883,7 +918,7 @@ public static void assertReadableFileExists(Path path) { } public static void assertUnexpected(String msg) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); error(concatMessages("Unexpected", msg)); } @@ -909,7 +944,7 @@ public void match(Path ... expected) { } public void match(Set expected) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); var comm = Comm.compare(content, expected); if (!comm.unique1().isEmpty() && !comm.unique2().isEmpty()) { @@ -936,7 +971,7 @@ public void contains(Path ... expected) { } public void contains(Set expected) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); var comm = Comm.compare(content, expected); if (!comm.unique2().isEmpty()) { @@ -981,7 +1016,7 @@ private static String format(Set paths) { public static void assertStringListEquals(List expected, List actual, String msg) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); traceAssert(concatMessages("assertStringListEquals()", msg)); @@ -1207,12 +1242,13 @@ public static TextStreamVerifier assertTextStream(String what) { } private static PrintStream openLogStream() { - if (LOG_FILE == null) { - return null; - } - - return ThrowingSupplier.toSupplier(() -> new PrintStream( - new FileOutputStream(LOG_FILE.toFile(), true))).get(); + return state().logFile.map(logfile -> { + try { + return Files.newOutputStream(logfile, StandardOpenOption.CREATE, StandardOpenOption.APPEND); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }).map(PrintStream::new).orElse(null); } public record PathSnapshot(List contentHashes) { @@ -1224,8 +1260,8 @@ public PathSnapshot(Path path) { this(hashRecursive(path)); } - public static void assertEquals(PathSnapshot a, PathSnapshot b, String msg) { - assertStringListEquals(a.contentHashes(), b.contentHashes(), msg); + public void assertEquals(PathSnapshot other, String msg) { + assertStringListEquals(contentHashes(), other.contentHashes(), msg); } private static List hashRecursive(Path path) { @@ -1256,15 +1292,6 @@ private static String hashFile(Path path) { } } - private static TestInstance currentTest; - private static PrintStream extraLogStream; - - private static final boolean TRACE; - private static final boolean TRACE_ASSERTS; - - static final boolean VERBOSE_JPACKAGE; - static final boolean VERBOSE_TEST_SETUP; - static String getConfigProperty(String propertyName) { return System.getProperty(getConfigPropertyName(propertyName)); } @@ -1292,38 +1319,19 @@ static Set tokenizeConfigProperty(String propertyName) { return tokens.stream().collect(Collectors.toSet()); } - static final Path LOG_FILE = Functional.identity(() -> { - String val = getConfigProperty("logfile"); - if (val == null) { - return null; - } - return Path.of(val); - }).get(); + private static TestInstance currentTest() { + return state().currentTest; + } - static { - Set logOptions = tokenizeConfigProperty("suppress-logging"); - if (logOptions == null) { - TRACE = true; - TRACE_ASSERTS = true; - VERBOSE_JPACKAGE = true; - VERBOSE_TEST_SETUP = true; - } else if (logOptions.contains("all")) { - TRACE = false; - TRACE_ASSERTS = false; - VERBOSE_JPACKAGE = false; - VERBOSE_TEST_SETUP = false; - } else { - Predicate> isNonOf = options -> { - return Collections.disjoint(logOptions, options); - }; + static boolean verboseJPackage() { + return state().verboseJPackage; + } - TRACE = isNonOf.test(Set.of("trace", "t")); - TRACE_ASSERTS = isNonOf.test(Set.of("assert", "a")); - VERBOSE_JPACKAGE = isNonOf.test(Set.of("jpackage", "jp")); - VERBOSE_TEST_SETUP = isNonOf.test(Set.of("init", "i")); - } + static boolean verboseTestSetup() { + return state().verboseTestSetup; } + private static final class JtregSkippedExceptionClass extends ClassLoader { @SuppressWarnings("unchecked") JtregSkippedExceptionClass() { @@ -1349,4 +1357,159 @@ private static final class JtregSkippedExceptionClass extends ClassLoader { static final Class INSTANCE = new JtregSkippedExceptionClass().clazz; } + + + public static final class State { + + private State( + Optional logFile, + TestInstance currentTest, + PrintStream mainLogStream, + PrintStream stackTraceStream, + PrintStream extraLogStream, + boolean trace, + boolean traceAsserts, + boolean verboseJPackage, + boolean verboseTestSetup) { + + Objects.requireNonNull(logFile); + Objects.requireNonNull(mainLogStream); + Objects.requireNonNull(stackTraceStream); + + this.logFile = logFile; + this.currentTest = currentTest; + this.mainLogStream = mainLogStream; + this.stackTraceStream = stackTraceStream; + this.extraLogStream = extraLogStream; + + this.trace = trace; + this.traceAsserts = traceAsserts; + + this.verboseJPackage = verboseJPackage; + this.verboseTestSetup = verboseTestSetup; + } + + + Builder buildCopy() { + return build().initFrom(this); + } + + static Builder build() { + return new Builder(); + } + + + static final class Builder { + + Builder initDefaults() { + logFile = Optional.ofNullable(getConfigProperty("logfile")).map(Path::of); + currentTest = null; + mainLogStream = System.out; + stackTraceStream = System.err; + extraLogStream = null; + + var logOptions = tokenizeConfigProperty("suppress-logging"); + if (logOptions == null) { + trace = true; + traceAsserts = true; + verboseJPackage = true; + verboseTestSetup = true; + } else if (logOptions.contains("all")) { + trace = false; + traceAsserts = false; + verboseJPackage = false; + verboseTestSetup = false; + } else { + Predicate> isNonOf = options -> { + return Collections.disjoint(logOptions, options); + }; + + trace = isNonOf.test(Set.of("trace", "t")); + traceAsserts = isNonOf.test(Set.of("assert", "a")); + verboseJPackage = isNonOf.test(Set.of("jpackage", "jp")); + verboseTestSetup = isNonOf.test(Set.of("init", "i")); + } + + return this; + } + + Builder initFrom(State state) { + logFile = state.logFile; + currentTest = state.currentTest; + mainLogStream = state.mainLogStream; + stackTraceStream = state.stackTraceStream; + extraLogStream = state.extraLogStream; + + trace = state.trace; + traceAsserts = state.traceAsserts; + + verboseJPackage = state.verboseJPackage; + verboseTestSetup = state.verboseTestSetup; + + return this; + } + + Builder logFile(Optional v) { + logFile = v; + return this; + } + + Builder currentTest(TestInstance v) { + currentTest = v; + return this; + } + + Builder mainLogStream(PrintStream v) { + mainLogStream = v; + return this; + } + + Builder stackTraceStream(PrintStream v) { + stackTraceStream = v; + return this; + } + + Builder extraLogStream(PrintStream v) { + extraLogStream = v; + return this; + } + + State create() { + return new State(logFile, currentTest, mainLogStream, stackTraceStream, extraLogStream, trace, traceAsserts, verboseJPackage, verboseTestSetup); + } + + private Optional logFile; + private TestInstance currentTest; + private PrintStream mainLogStream; + private PrintStream stackTraceStream; + private PrintStream extraLogStream; + + private boolean trace; + private boolean traceAsserts; + + private boolean verboseJPackage; + private boolean verboseTestSetup; + } + + + private final Optional logFile; + private final TestInstance currentTest; + private final PrintStream mainLogStream; + private final PrintStream stackTraceStream; + private final PrintStream extraLogStream; + + private final boolean trace; + private final boolean traceAsserts; + + private final boolean verboseJPackage; + private final boolean verboseTestSetup; + } + + + private static final InheritableThreadLocal STATE = new InheritableThreadLocal<>() { + @Override + protected State initialValue() { + return State.build().initDefaults().create(); + } + }; } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestBuilder.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestBuilder.java index 227c73bc68e4d..4009fe2f68723 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestBuilder.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestBuilder.java @@ -369,7 +369,7 @@ public String getMessage() { } static void trace(String msg) { - if (TKit.VERBOSE_TEST_SETUP) { + if (TKit.verboseTestSetup()) { TKit.log(msg); } } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestMethodSupplier.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestMethodSupplier.java index 36ae81b7db4d6..80c8b1337905c 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestMethodSupplier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestMethodSupplier.java @@ -409,7 +409,7 @@ private static Object fromString(String value, Class toType) { } private static void trace(String msg) { - if (TKit.VERBOSE_TEST_SETUP) { + if (TKit.verboseTestSetup()) { TKit.log(msg); } } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/IdentityWrapperTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/IdentityWrapperTest.java new file mode 100644 index 0000000000000..471a7cb55a966 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/IdentityWrapperTest.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import org.junit.jupiter.api.Test; + + +public class IdentityWrapperTest { + + @Test + public void test_null() { + assertThrows(NullPointerException.class, () -> identityOf(null)); + } + + @Test + public void test_equals() { + var obj = new TestRecord(10); + assertEquals(identityOf(obj), identityOf(obj)); + } + + @Test + public void test_not_equals() { + var identity = identityOf(new TestRecord(10)); + var identity2 = identityOf(new TestRecord(10)); + assertNotEquals(identity, identity2); + assertEquals(identity.value(), identity2.value()); + } + + @Test + public void test_Foo() { + var foo = new Foo(10); + assertFalse(foo.accessed()); + + foo.hashCode(); + assertTrue(foo.accessed()); + assertTrue(foo.hashCodeCalled()); + assertFalse(foo.equalsCalled()); + + foo = new Foo(1); + foo.equals(null); + assertTrue(foo.accessed()); + assertFalse(foo.hashCodeCalled()); + assertTrue(foo.equalsCalled()); + } + + @Test + public void test_wrappedValue_not_accessed() { + var identity = identityOf(new Foo(10)); + var identity2 = identityOf(new Foo(10)); + assertNotEquals(identity, identity2); + + assertFalse(identity.value().accessed()); + assertFalse(identity2.value().accessed()); + + assertEquals(identity.value(), identity2.value()); + assertEquals(identity2.value(), identity.value()); + + assertTrue(identity.value().accessed()); + assertTrue(identity2.value().accessed()); + } + + @Test + public void test_wrappedValue_not_accessed_in_set() { + var identitySet = Set.of(identityOf(new Foo(10)), identityOf(new Foo(10)), identityOf(new Foo(10))); + assertEquals(3, identitySet.size()); + + var valueSet = identitySet.stream().peek(identity -> { + assertFalse(identity.value().accessed()); + }).map(IdentityWrapper::value).collect(Collectors.toSet()); + + assertEquals(1, valueSet.size()); + } + + private static IdentityWrapper identityOf(T obj) { + return new IdentityWrapper<>(obj); + } + + private record TestRecord(int v) {} + + private final static class Foo { + + Foo(int v) { + this.v = v; + } + + @Override + public int hashCode() { + try { + return Objects.hash(v); + } finally { + hashCodeCalled = true; + } + } + + @Override + public boolean equals(Object obj) { + try { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Foo other = (Foo) obj; + return v == other.v; + } finally { + equalsCalled = true; + } + } + + boolean equalsCalled() { + return equalsCalled; + } + + boolean hashCodeCalled() { + return hashCodeCalled; + } + + boolean accessed() { + return equalsCalled() || hashCodeCalled(); + } + + private final int v; + private boolean equalsCalled; + private boolean hashCodeCalled; + } +} diff --git a/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitUtils.java b/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitUtils.java new file mode 100644 index 0000000000000..c91b178cb108b --- /dev/null +++ b/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitUtils.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.util.Map; +import java.util.Objects; +import org.junit.jupiter.api.Assertions; + + +public final class JUnitUtils { + + /** + * Convenience adapter for {@link Assertions#assertArrayEquals(byte[], byte[])}, + * {@link Assertions#assertArrayEquals(int[], int[])}, + * {@link Assertions#assertArrayEquals(Object[], Object[])}, etc. methods. + * + * @param expected the expected array to test for equality + * @param actual the actual array to test for equality + */ + public static void assertArrayEquals(Object expected, Object actual) { + ARRAY_ASSERTERS.getOrDefault(expected.getClass().componentType(), OBJECT_ARRAY_ASSERTER).acceptUnchecked(expected, actual); + } + + /** + * Converts the given exception object to a property map. + *

    + * Values returned by public getters are added to the map. Names of getters are + * the keys in the returned map. The values are property map representations of + * the objects returned by the getters. Only {@link Throwable#getMessage()} and + * {@link Throwable#getCause()} getters are picked for the property map by + * default. If the exception class has additional getters, they will be added to + * the map. {@code null} is permitted. + * + * @param ex the exception to convert into a property map + * @return the property map view of the given exception object + */ + public static Map exceptionAsPropertyMap(Exception ex) { + return EXCEPTION_OM.toMap(ex); + } + + + public static final class ExceptionPattern { + + public ExceptionPattern() { + } + + public boolean match(Exception ex) { + Objects.requireNonNull(ex); + + if (expectedType != null && !expectedType.isInstance(ex)) { + return false; + } + + if (expectedMessage != null && !expectedMessage.equals(ex.getMessage())) { + return false; + } + + if (expectedCauseType != null && !expectedCauseType.isInstance(ex.getCause())) { + return false; + } + + return true; + } + + public ExceptionPattern hasMessage(String v) { + expectedMessage = v; + return this; + } + + public ExceptionPattern isInstanceOf(Class v) { + expectedType = v; + return this; + } + + public ExceptionPattern isCauseInstanceOf(Class v) { + expectedCauseType = v; + return this; + } + + public ExceptionPattern hasCause(boolean v) { + return isCauseInstanceOf(v ? Exception.class : null); + } + + public ExceptionPattern hasCause() { + return hasCause(true); + } + + private String expectedMessage; + private Class expectedType; + private Class expectedCauseType; + } + + + @FunctionalInterface + private interface ArrayEqualsAsserter { + void accept(T expected, T actual); + + @SuppressWarnings("unchecked") + default void acceptUnchecked(Object expected, Object actual) { + accept((T)expected, (T)actual); + } + } + + + private static final Map, ArrayEqualsAsserter> ARRAY_ASSERTERS = Map.of( + boolean.class, (ArrayEqualsAsserter)Assertions::assertArrayEquals, + byte.class, (ArrayEqualsAsserter)Assertions::assertArrayEquals, + char.class, (ArrayEqualsAsserter)Assertions::assertArrayEquals, + double.class, (ArrayEqualsAsserter)Assertions::assertArrayEquals, + float.class, (ArrayEqualsAsserter)Assertions::assertArrayEquals, + int.class, (ArrayEqualsAsserter)Assertions::assertArrayEquals, + long.class, (ArrayEqualsAsserter)Assertions::assertArrayEquals, + short.class, (ArrayEqualsAsserter)Assertions::assertArrayEquals + ); + + private static final ArrayEqualsAsserter OBJECT_ARRAY_ASSERTER = Assertions::assertArrayEquals; + + private static final ObjectMapper EXCEPTION_OM = ObjectMapper.standard().create(); +} diff --git a/test/jdk/tools/jpackage/linux/ShortcutHintTest.java b/test/jdk/tools/jpackage/linux/ShortcutHintTest.java index 4d3b33bcd6b58..8d373cb2b86dc 100644 --- a/test/jdk/tools/jpackage/linux/ShortcutHintTest.java +++ b/test/jdk/tools/jpackage/linux/ShortcutHintTest.java @@ -164,7 +164,7 @@ public static void testDesktopFileFromResourceDir() throws IOException { "Exec=APPLICATION_LAUNCHER", "Terminal=false", "Type=Application", - "Comment=", + "Comment=APPLICATION_DESCRIPTION", "Icon=APPLICATION_ICON", "Categories=DEPLOY_BUNDLE_CATEGORY", expectedVersionString diff --git a/test/jdk/tools/jpackage/macosx/EntitlementsTest.java b/test/jdk/tools/jpackage/macosx/EntitlementsTest.java new file mode 100644 index 0000000000000..aa5879e0c619e --- /dev/null +++ b/test/jdk/tools/jpackage/macosx/EntitlementsTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import static jdk.jpackage.internal.util.PListWriter.writeBoolean; +import static jdk.jpackage.internal.util.PListWriter.writeDict; +import static jdk.jpackage.internal.util.PListWriter.writePList; +import static jdk.jpackage.internal.util.XmlUtils.createXml; +import static jdk.jpackage.internal.util.XmlUtils.toXmlConsumer; +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.function.Consumer; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.function.ThrowingConsumer; +import jdk.jpackage.test.AdditionalLauncher; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.MacHelper.SignKeyOption; +import jdk.jpackage.test.MacSign; +import jdk.jpackage.test.TKit; + +/* + * Test generates signed app-image with custom entitlements file from the + * "--mac-entitlements" parameter and the resource directory. Following cases + * are covered: + * - Custom entitlements file in the resource directory. + * - Custom entitlements file specified with the "--mac-entitlements" parameter. + * - Custom entitlements file in the resource directory and specified with the + * "--mac-entitlements" parameter. + */ + +/* + * @test + * @summary jpackage with --type app-image "--mac-entitlements" parameter + * @library /test/jdk/tools/jpackage/helpers + * @library base + * @build SigningBase + * @build jdk.jpackage.test.* + * @build EntitlementsTest + * @requires (jpackage.test.MacSignTests == "run") + * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=EntitlementsTest + * --jpt-before-run=SigningBase.verifySignTestEnvReady + */ +public class EntitlementsTest { + + private static void createEntitlementsFile(Path file, boolean microphone) throws IOException { + createXml(file, xml -> { + writePList(xml, toXmlConsumer(() -> { + writeDict(xml, toXmlConsumer(() -> { + writeBoolean(xml, "com.apple.security.cs.allow-jit", true); + writeBoolean(xml, "com.apple.security.cs.allow-unsigned-executable-memory", true); + writeBoolean(xml, "com.apple.security.cs.disable-library-validation", true); + writeBoolean(xml, "com.apple.security.cs.allow-dyld-environment-variables", true); + writeBoolean(xml, "com.apple.security.cs.debugger", true); + writeBoolean(xml, "com.apple.security.device.audio-input", true); + writeBoolean(xml, "com.apple.security.device.microphone", microphone); + })); + })); + }); + } + + public enum EntitlementsSource implements Consumer { + CMDLINE(cmd -> { + var macEntitlementsFile = TKit.createTempFile("foo.plist"); + createEntitlementsFile(macEntitlementsFile, true); + cmd.addArguments("--mac-entitlements", macEntitlementsFile); + }), + RESOURCE_DIR(cmd -> { + if (!cmd.hasArgument("--resource-dir")) { + cmd.setArgumentValue("--resource-dir", TKit.createTempDirectory("resources")); + } + + var resourcesDir = Path.of(cmd.getArgumentValue("--resource-dir")); + createEntitlementsFile(resourcesDir.resolve(cmd.name() + ".entitlements"), false); + }), + ; + + EntitlementsSource(ThrowingConsumer initializer) { + this.initializer = toConsumer(initializer); + } + + @Override + public void accept(JPackageCommand cmd) { + initializer.accept(cmd); + } + + private final Consumer initializer; + } + + @Test + @Parameter({"CMDLINE"}) + @Parameter({"RESOURCE_DIR"}) + @Parameter({"CMDLINE", "RESOURCE_DIR"}) + public static void test(EntitlementsSource... entitlementsSources) { + MacSign.withKeychain(toConsumer(keychain -> { + test(keychain, Stream.of(entitlementsSources)); + }), SigningBase.StandardKeychain.MAIN.keychain()); + } + + @Test + @Parameter({"CMDLINE"}) + @Parameter({"RESOURCE_DIR"}) + @Parameter({"CMDLINE", "RESOURCE_DIR"}) + public static void testAppStore(EntitlementsSource... entitlementsSources) { + MacSign.withKeychain(toConsumer(keychain -> { + test(keychain, Stream.concat(Stream.of(cmd -> { + cmd.addArguments("--mac-app-store"); + // Ignore externally supplied runtime as it may have the "bin" + // directory that will cause jpackage to bail out. + cmd.ignoreDefaultRuntime(true); + }), Stream.of(entitlementsSources))); + }), SigningBase.StandardKeychain.MAIN.keychain()); + } + + private static void test(MacSign.ResolvedKeychain keychain, Stream> mutators) { + + var cmd = JPackageCommand.helloAppImage(); + + cmd.mutate(MacHelper.useKeychain(keychain)).mutate(new SignKeyOption( + SignKeyOption.Type.SIGN_KEY_IDENTITY, + SigningBase.StandardCertificateRequest.CODESIGN.spec() + )::addTo); + + cmd.mutate(new AdditionalLauncher("x")::applyTo); + + mutators.forEach(cmd::mutate); + + cmd.executeAndAssertHelloAppImageCreated(); + } +} diff --git a/test/jdk/tools/jpackage/macosx/MacSignTest.java b/test/jdk/tools/jpackage/macosx/MacSignTest.java index b6f3f91ff585e..f5f8b3825ccae 100644 --- a/test/jdk/tools/jpackage/macosx/MacSignTest.java +++ b/test/jdk/tools/jpackage/macosx/MacSignTest.java @@ -70,14 +70,14 @@ public static void testAppContentWarning() throws IOException { final List expectedStrings = new ArrayList<>(); expectedStrings.add(JPackageStringBundle.MAIN.cannedFormattedString("message.codesign.failed.reason.app.content")); + expectedStrings.add(JPackageStringBundle.MAIN.cannedFormattedString("error.tool.failed.with.output", "codesign")); + final var xcodeWarning = JPackageStringBundle.MAIN.cannedFormattedString("message.codesign.failed.reason.xcode.tools"); if (!MacHelper.isXcodeDevToolsInstalled()) { expectedStrings.add(xcodeWarning); } - final var keychain = SigningBase.StandardKeychain.EXPIRED.spec().keychain(); - - MacSign.Keychain.withAddedKeychains(List.of(keychain), () -> { + MacSign.withKeychain(keychain -> { // --app-content and --type app-image // Expect `message.codesign.failed.reason.app.content` message in the log. // This is not a fatal error, just a warning. @@ -86,8 +86,6 @@ public static void testAppContentWarning() throws IOException { .ignoreDefaultVerbose(true) .validateOutput(expectedStrings.toArray(CannedFormattedString[]::new)) .addArguments("--app-content", appContent) - .addArguments("--mac-sign") - .addArguments("--mac-signing-keychain", keychain.name()) .addArguments("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.CODESIGN.spec().name()); if (MacHelper.isXcodeDevToolsInstalled()) { @@ -95,8 +93,36 @@ public static void testAppContentWarning() throws IOException { cmd.validateOutput(TKit.assertTextStream(xcodeWarning.getValue()).negate()); } - cmd.execute(1); - }); + MacHelper.useKeychain(cmd, keychain).execute(1); + }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.MAIN.keychain()); + } + + @Test + public static void testCodesignUnspecifiedFailure() throws IOException { + + var appImageCmd = JPackageCommand.helloAppImage().setFakeRuntime(); + + appImageCmd.executeIgnoreExitCode().assertExitCodeIsZero(); + + // This test expects jpackage to respond in a specific way on a codesign failure. + // The simplest option to trigger codesign failure is to request the signing of an invalid bundle. + // Create app content directory with the name known to fail signing. + final var appContent = appImageCmd.appLayout().contentDirectory().resolve("foo.1"); + Files.createDirectory(appContent); + Files.createFile(appContent.resolve("file")); + + final List expectedStrings = new ArrayList<>(); + expectedStrings.add(JPackageStringBundle.MAIN.cannedFormattedString("error.tool.failed.with.output", "codesign")); + + MacSign.withKeychain(keychain -> { + final var cmd = new JPackageCommand().setPackageType(PackageType.IMAGE) + .ignoreDefaultVerbose(true) + .validateOutput(expectedStrings.toArray(CannedFormattedString[]::new)) + .addArguments("--app-image", appImageCmd.outputBundle()) + .addArguments("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.CODESIGN.spec().name()); + + MacHelper.useKeychain(cmd, keychain).execute(1); + }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.MAIN.keychain()); } @Test @@ -116,20 +142,16 @@ public static void testAppContentWarning() throws IOException { @Parameter({"MAC_PKG", "EXPIRED_CODESIGN_SIGN_IDENTITY", "GOOD_PKG_SIGN_IDENTITY"}) public static void testExpiredCertificate(PackageType type, SignOption... options) { - final var keychain = SigningBase.StandardKeychain.EXPIRED.spec().keychain(); - - MacSign.Keychain.withAddedKeychains(List.of(keychain), () -> { - final var cmd = JPackageCommand.helloAppImage() + MacSign.withKeychain(keychain -> { + final var cmd = MacHelper.useKeychain(JPackageCommand.helloAppImage(), keychain) .ignoreDefaultVerbose(true) - .addArguments("--mac-sign") - .addArguments("--mac-signing-keychain", keychain.name()) .addArguments(Stream.of(options).map(SignOption::args).flatMap(List::stream).toList()) .setPackageType(type); SignOption.configureOutputValidation(cmd, Stream.of(options).filter(SignOption::expired).toList(), opt -> { return JPackageStringBundle.MAIN.cannedFormattedString("error.certificate.expired", opt.identityName()); }).execute(1); - }); + }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.EXPIRED.keychain()); } @Test @@ -148,39 +170,31 @@ public static void testExpiredCertificate(PackageType type, SignOption... option @Parameter({"MAC_PKG", "1", "GOOD_PKG_SIGN_IDENTITY"}) public static void testMultipleCertificates(PackageType type, int jpackageExitCode, SignOption... options) { - final var keychain = SigningBase.StandardKeychain.DUPLICATE.spec().keychain(); - - MacSign.Keychain.withAddedKeychains(List.of(keychain), () -> { - final var cmd = JPackageCommand.helloAppImage() + MacSign.withKeychain(keychain -> { + final var cmd = MacHelper.useKeychain(JPackageCommand.helloAppImage(), keychain) .ignoreDefaultVerbose(true) - .addArguments("--mac-sign") - .addArguments("--mac-signing-keychain", keychain.name()) .addArguments(Stream.of(options).map(SignOption::args).flatMap(List::stream).toList()) .setPackageType(type); SignOption.configureOutputValidation(cmd, List.of(options), opt -> { return JPackageStringBundle.MAIN.cannedFormattedString("error.multiple.certs.found", opt.identityName(), keychain.name()); }).execute(jpackageExitCode); - }); + }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.DUPLICATE.keychain()); } @Test @ParameterSupplier public static void testSelectSigningIdentity(String signingKeyUserName, CertificateRequest certRequest) { - final var keychain = SigningBase.StandardKeychain.MAIN.spec().keychain(); - - MacSign.Keychain.withAddedKeychains(List.of(keychain), () -> { - final var cmd = JPackageCommand.helloAppImage() + MacSign.withKeychain(keychain -> { + final var cmd = MacHelper.useKeychain(JPackageCommand.helloAppImage(), keychain) .setFakeRuntime() - .addArguments("--mac-sign") - .addArguments("--mac-signing-keychain", keychain.name()) .addArguments("--mac-signing-key-user-name", signingKeyUserName); cmd.executeAndAssertHelloAppImageCreated(); MacSignVerify.assertSigned(cmd.outputBundle(), certRequest); - }); + }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.MAIN.keychain()); } public static Collection testSelectSigningIdentity() { diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java index e93e659408fef..37ba8a6c299ac 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java @@ -21,12 +21,14 @@ * questions. */ -import java.nio.file.Path; +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; -import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.Annotations.Parameter; +import java.nio.file.Path; import jdk.jpackage.test.AdditionalLauncher; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.MacSign; /** * Tests generation of app image with --mac-sign and related arguments. Test will @@ -68,13 +70,19 @@ public class SigningAppImageTest { // Unsigned @Parameter({"false", "true", "INVALID_INDEX"}) public void test(boolean doSign, boolean signingKey, SigningBase.CertIndex certEnum) throws Exception { + MacSign.withKeychain(toConsumer(keychain -> { + test(keychain, doSign, signingKey, certEnum); + }), SigningBase.StandardKeychain.MAIN.keychain()); + } + + private void test(MacSign.ResolvedKeychain keychain, boolean doSign, boolean signingKey, SigningBase.CertIndex certEnum) throws Exception { final var certIndex = certEnum.value(); JPackageCommand cmd = JPackageCommand.helloAppImage(); if (doSign) { cmd.addArguments("--mac-sign", "--mac-signing-keychain", - SigningBase.getKeyChain()); + keychain.name()); if (signingKey) { cmd.addArguments("--mac-signing-key-user-name", SigningBase.getDevName(certIndex)); diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java index 94199b31434f7..906734e6a9cb2 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -21,14 +21,16 @@ * questions. */ -import java.nio.file.Path; +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; +import java.nio.file.Path; +import jdk.jpackage.test.AdditionalLauncher; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.TKit; +import jdk.jpackage.test.MacSign; import jdk.jpackage.test.PackageType; -import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.Annotations.Parameter; -import jdk.jpackage.test.AdditionalLauncher; +import jdk.jpackage.test.TKit; /** * Tests generation of app image and then signs generated app image with --mac-sign @@ -67,6 +69,12 @@ public class SigningAppImageTwoStepsTest { // Unsigned @Parameter({"false", "true"}) public void test(boolean signAppImage, boolean signingKey) throws Exception { + MacSign.withKeychain(toConsumer(keychain -> { + test(keychain, signAppImage, signingKey); + }), SigningBase.StandardKeychain.MAIN.keychain()); + } + + private static void test(MacSign.ResolvedKeychain keychain, boolean signAppImage, boolean signingKey) throws Exception { Path appimageOutput = TKit.createTempDirectory("appimage"); @@ -78,7 +86,7 @@ public void test(boolean signAppImage, boolean signingKey) throws Exception { if (signAppImage) { appImageCmd.addArguments("--mac-sign", "--mac-signing-keychain", - SigningBase.getKeyChain()); + keychain.name()); if (signingKey) { appImageCmd.addArguments("--mac-signing-key-user-name", SigningBase.getDevName(SigningBase.DEFAULT_INDEX)); @@ -103,7 +111,7 @@ public void test(boolean signAppImage, boolean signingKey) throws Exception { cmd.setPackageType(PackageType.IMAGE) .addArguments("--app-image", appImageCmd.outputBundle().toAbsolutePath()) .addArguments("--mac-sign") - .addArguments("--mac-signing-keychain", SigningBase.getKeyChain()); + .addArguments("--mac-signing-keychain", keychain.name()); if (signingKey) { cmd.addArguments("--mac-signing-key-user-name", SigningBase.getDevName(SigningBase.DEFAULT_INDEX)); diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageFromTwoStepAppImageTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageFromTwoStepAppImageTest.java index d25d9a7fa8146..6db1cb2faabb4 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageFromTwoStepAppImageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageFromTwoStepAppImageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -21,15 +21,18 @@ * questions. */ +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; + import java.nio.file.Path; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.ApplicationLayout; import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.TKit; +import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.MacSign; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; -import jdk.jpackage.test.MacHelper; -import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.TKit; /** * Tests generation of dmg and pkg from signed predefined app image which was @@ -102,6 +105,12 @@ private static void verifyAppImageInDMG(JPackageCommand cmd) { // Unsigned @Parameter({"false", "true"}) public void test(boolean signAppImage, boolean signingKey) throws Exception { + MacSign.withKeychain(toConsumer(keychain -> { + test(keychain, signAppImage, signingKey); + }), SigningBase.StandardKeychain.MAIN.keychain()); + } + + private void test(MacSign.ResolvedKeychain keychain, boolean signAppImage, boolean signingKey) throws Exception { Path appimageOutput = TKit.createTempDirectory("appimage"); @@ -112,7 +121,7 @@ public void test(boolean signAppImage, boolean signingKey) throws Exception { .setArgumentValue("--dest", appimageOutput); if (signAppImage) { appImageCmd.addArguments("--mac-sign", - "--mac-signing-keychain", SigningBase.getKeyChain()); + "--mac-signing-keychain", keychain.name()); if (signingKey) { appImageCmd.addArguments("--mac-signing-key-user-name", SigningBase.getDevName(SigningBase.DEFAULT_INDEX)); @@ -133,7 +142,7 @@ public void test(boolean signAppImage, boolean signingKey) throws Exception { appImageSignedCmd.setPackageType(PackageType.IMAGE) .addArguments("--app-image", appImageCmd.outputBundle().toAbsolutePath()) .addArguments("--mac-sign") - .addArguments("--mac-signing-keychain", SigningBase.getKeyChain()); + .addArguments("--mac-signing-keychain", keychain.name()); if (signingKey) { appImageSignedCmd.addArguments("--mac-signing-key-user-name", SigningBase.getDevName(SigningBase.DEFAULT_INDEX)); @@ -154,7 +163,7 @@ public void test(boolean signAppImage, boolean signingKey) throws Exception { if (signAppImage) { cmd.addArguments("--mac-sign", "--mac-signing-keychain", - SigningBase.getKeyChain()); + keychain.name()); if (signingKey) { cmd.addArguments("--mac-signing-key-user-name", SigningBase.getDevName(SigningBase.DEFAULT_INDEX)); diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java index 2c2f5c3bb0f0c..b1e9155dacbcf 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java @@ -21,14 +21,17 @@ * questions. */ +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; + import java.nio.file.Path; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.ApplicationLayout; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.MacSign; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; -import jdk.jpackage.test.MacHelper; -import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.Annotations.Parameter; /** * Tests generation of dmg and pkg with --mac-sign and related arguments. @@ -144,6 +147,12 @@ private static int getCertIndex(JPackageCommand cmd) { // Signing-indentity, but sign pkg only and UNICODE certificate @Parameter({"false", "false", "true", "UNICODE_INDEX"}) public static void test(boolean signingKey, boolean signAppImage, boolean signPKG, SigningBase.CertIndex certEnum) throws Exception { + MacSign.withKeychain(toConsumer(keychain -> { + test(keychain, signingKey, signAppImage, signPKG, certEnum); + }), SigningBase.StandardKeychain.MAIN.keychain()); + } + + private static void test(MacSign.ResolvedKeychain keychain, boolean signingKey, boolean signAppImage, boolean signPKG, SigningBase.CertIndex certEnum) throws Exception { final var certIndex = certEnum.value(); new PackageTest() @@ -151,7 +160,7 @@ public static void test(boolean signingKey, boolean signAppImage, boolean signPK .forTypes(PackageType.MAC) .addInitializer(cmd -> { cmd.addArguments("--mac-sign", - "--mac-signing-keychain", SigningBase.getKeyChain()); + "--mac-signing-keychain", keychain.name()); if (signingKey) { cmd.addArguments("--mac-signing-key-user-name", SigningBase.getDevName(certIndex)); diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java index 3522d8d43e527..16cf616cfd377 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -21,31 +21,39 @@ * questions. */ +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; + import java.nio.file.Path; -import jdk.jpackage.test.ApplicationLayout; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.stream.Stream; +import jdk.jpackage.test.Annotations.ParameterSupplier; +import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.TKit; +import jdk.jpackage.test.JPackageStringBundle; +import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.MacHelper.SignKeyOption; +import jdk.jpackage.test.MacSign; +import jdk.jpackage.test.MacSignVerify; +import jdk.jpackage.test.PackageFile; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; -import jdk.jpackage.test.MacHelper; -import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.TKit; /** - * Note: Testing unsgined app image is done to verify support for per-user - * configuration by checking for PackageFile. - * Tests generation of dmg and pkg from signed or unsigned predefined app image. - * Test will generate pkg and verifies its signature. It verifies that dmg - * is not signed, but app image inside dmg is signed or unsigned. This test - * requires that the machine is configured with test certificate for - * "Developer ID Installer: jpackage.openjdk.java.net" in - * jpackagerTest keychain with - * always allowed access to this keychain for user which runs test. - * note: - * "jpackage.openjdk.java.net" can be over-ridden by system property - * "jpackage.mac.signing.key.user.name", and - * "jpackagerTest" can be over-ridden by system property - * "jpackage.mac.signing.keychain" + * Tests packaging of a signed/unsigned predefined app image into a + * signed/unsigned .pkg or .dmg package. + * + *

    + * Prerequisites: A keychain with self-signed certificates as specified in + * {@link SigningBase.StandardKeychain#MAIN}. */ /* @@ -64,100 +72,180 @@ */ public class SigningPackageTwoStepTest { - private static void verifyPKG(JPackageCommand cmd) { - if (!cmd.hasArgument("--mac-sign")) { - return; // Nothing to check if not signed + @Test + @ParameterSupplier + public static void test(TestSpec spec) { + MacSign.withKeychain(toConsumer(keychain -> { + spec.test(keychain); + }), SigningBase.StandardKeychain.MAIN.keychain()); + } + + public record TestSpec(Optional signAppImage, Map signPackage) { + + public TestSpec { + Objects.requireNonNull(signAppImage); + Objects.requireNonNull(signPackage); + + if ((signAppImage.isEmpty() && signPackage.isEmpty()) || !PackageType.MAC.containsAll(signPackage.keySet())) { + // Unexpected package types. + throw new IllegalArgumentException(); + } + + // Ensure stable result of toString() call. + if (!SortedMap.class.isInstance(signPackage)) { + signPackage = new TreeMap<>(signPackage); + } } - Path outputBundle = cmd.outputBundle(); - SigningBase.verifyPkgutil(outputBundle, true, SigningBase.DEFAULT_INDEX); - SigningBase.verifySpctl(outputBundle, "install", SigningBase.DEFAULT_INDEX); - } + @Override + public String toString() { + var sb = new StringBuilder(); - private static void verifyDMG(JPackageCommand cmd) { - // DMG always unsigned, so we will check it - Path outputBundle = cmd.outputBundle(); - SigningBase.verifyDMG(outputBundle); - } + signAppImage.ifPresent(signOption -> { + sb.append(String.format("app-image=%s", signOption)); + }); - private static void verifyAppImageInDMG(JPackageCommand cmd) { - MacHelper.withExplodedDmg(cmd, dmgImage -> { - // We will be called with all folders in DMG since JDK-8263155, but - // we only need to verify app. - if (dmgImage.endsWith(cmd.name() + ".app")) { - boolean isSigned = cmd.hasArgument("--mac-sign"); - Path launcherPath = ApplicationLayout.platformAppImage() - .resolveAt(dmgImage).launchersDirectory().resolve(cmd.name()); - SigningBase.verifyCodesign(launcherPath, isSigned, SigningBase.DEFAULT_INDEX); - SigningBase.verifyCodesign(dmgImage, isSigned, SigningBase.DEFAULT_INDEX); - if (isSigned) { - SigningBase.verifySpctl(dmgImage, "exec", SigningBase.DEFAULT_INDEX); - } + if (!sb.isEmpty() && !signPackage.isEmpty()) { + sb.append("; "); } - }); - } - @Test - // (Signed, "signing-key or sign-identity"}) - // Signed and signing-key - @Parameter({"true", "true"}) - // Signed and signing-identity - @Parameter({"true", "false"}) - // Unsigned - @Parameter({"false", "true"}) - public static void test(boolean signAppImage, boolean signingKey) throws Exception { - Path appimageOutput = TKit.createTempDirectory("appimage"); - - JPackageCommand appImageCmd = JPackageCommand.helloAppImage() - .setArgumentValue("--dest", appimageOutput); - if (signAppImage) { - appImageCmd.addArguments("--mac-sign") - .addArguments("--mac-signing-keychain", - SigningBase.getKeyChain()); - if (signingKey) { - appImageCmd.addArguments("--mac-signing-key-user-name", - SigningBase.getDevName(SigningBase.DEFAULT_INDEX)); - } else { - appImageCmd.addArguments("--mac-app-image-sign-identity", - SigningBase.getAppCert(SigningBase.DEFAULT_INDEX)); + if (!signPackage.isEmpty()) { + sb.append(signPackage); } + + return sb.toString(); + } + + boolean signNativeBundle() { + return signPackage.isEmpty(); } - new PackageTest() - .addRunOnceInitializer(() -> appImageCmd.execute()) - .forTypes(PackageType.MAC) - .addInitializer(cmd -> { - cmd.addArguments("--app-image", appImageCmd.outputBundle()); - cmd.removeArgumentWithValue("--input"); - if (signAppImage) { - cmd.addArguments("--mac-sign", - "--mac-signing-keychain", - SigningBase.getKeyChain()); - if (signingKey) { - cmd.addArguments("--mac-signing-key-user-name", - SigningBase.getDevName(SigningBase.DEFAULT_INDEX)); - } else { - cmd.addArguments("--mac-installer-sign-identity", - SigningBase.getInstallerCert(SigningBase.DEFAULT_INDEX)); - } - } - }) - .forTypes(PackageType.MAC_PKG) - .addBundleVerifier(SigningPackageTwoStepTest::verifyPKG) - .forTypes(PackageType.MAC_DMG) - .addInitializer(cmd -> { - if (signAppImage && !signingKey) { - // jpackage throws expected error with - // --mac-installer-sign-identity and DMG type - cmd.removeArgumentWithValue("--mac-installer-sign-identity"); - // It will do nothing, but it signals test that app - // image itself is signed for verification. - cmd.addArguments("--mac-app-image-sign-identity", - SigningBase.getAppCert(SigningBase.DEFAULT_INDEX)); - } - }) - .addBundleVerifier(SigningPackageTwoStepTest::verifyDMG) - .addBundleVerifier(SigningPackageTwoStepTest::verifyAppImageInDMG) - .run(); + static Builder build() { + return new Builder(); + } + + static class Builder { + + TestSpec create() { + return new TestSpec(Optional.ofNullable(signAppImage), signPackage); + } + + Builder certRequest(SigningBase.StandardCertificateRequest v) { + return certRequest(v.spec()); + } + + Builder certRequest(MacSign.CertificateRequest v) { + certRequest = Objects.requireNonNull(v); + return this; + } + + Builder signIdentityType(SignKeyOption.Type v) { + signIdentityType = Objects.requireNonNull(v); + return this; + } + + Builder signAppImage() { + signAppImage = createSignKeyOption(); + return this; + } + + Builder signPackage(PackageType type) { + Objects.requireNonNull(type); + signPackage.put(type, createSignKeyOption()); + return this; + } + + Builder signPackage() { + PackageType.MAC.forEach(this::signPackage); + return this; + } + + private SignKeyOption createSignKeyOption() { + return new SignKeyOption(signIdentityType, certRequest); + } + + private MacSign.CertificateRequest certRequest = SigningBase.StandardCertificateRequest.CODESIGN.spec(); + private SignKeyOption.Type signIdentityType = SignKeyOption.Type.SIGN_KEY_IDENTITY; + + private SignKeyOption signAppImage; + private Map signPackage = new HashMap<>(); + } + + void test(MacSign.ResolvedKeychain keychain) { + + var appImageCmd = JPackageCommand.helloAppImage().setFakeRuntime(); + MacHelper.useKeychain(appImageCmd, keychain); + signAppImage.ifPresent(signOption -> { + signOption.setTo(appImageCmd); + }); + + var test = new PackageTest(); + + signAppImage.map(SignKeyOption::certRequest).ifPresent(certRequest -> { + // The predefined app image is signed, verify bundled app image is signed too. + test.addInstallVerifier(cmd -> { + MacSignVerify.verifyAppImageSigned(cmd, certRequest, keychain); + }); + }); + + Optional.ofNullable(signPackage.get(PackageType.MAC_PKG)).map(SignKeyOption::certRequest).ifPresent(certRequest -> { + test.forTypes(PackageType.MAC_PKG, () -> { + test.addBundleVerifier(cmd -> { + MacSignVerify.verifyPkgSigned(cmd, certRequest, keychain); + }); + }); + }); + + test.forTypes(signPackage.keySet()).addRunOnceInitializer(() -> { + appImageCmd.setArgumentValue("--dest", TKit.createTempDirectory("appimage")).execute(0); + }).addInitializer(cmd -> { + MacHelper.useKeychain(cmd, keychain); + cmd.addArguments("--app-image", appImageCmd.outputBundle()); + cmd.removeArgumentWithValue("--input"); + Optional.ofNullable(signPackage.get(cmd.packageType())).ifPresent(signOption -> { + signOption.setTo(cmd); + }); + + if (signAppImage.isPresent()) { + // Predefined app image is signed. Expect a warning. + cmd.validateOutput(JPackageStringBundle.MAIN.cannedFormattedString( + "warning.per.user.app.image.signed", + PackageFile.getPathInAppImage(Path.of("")))); + } else if (cmd.packageType() == PackageType.MAC_PKG && signPackage.containsKey(cmd.packageType())) { + // Create signed ".pkg" bundle from the unsigned predefined app image. Expect a warning. + cmd.validateOutput(JPackageStringBundle.MAIN.cannedFormattedString("warning.unsigned.app.image", "pkg")); + } + }) + .run(); + } + } + + public static Collection test() { + + List data = new ArrayList<>(); + + Stream.of(SignKeyOption.Type.values()).flatMap(signIdentityType -> { + return Stream.of( + // Sign both predefined app image and native package. + TestSpec.build().signIdentityType(signIdentityType) + .signAppImage() + .signPackage() + .certRequest(SigningBase.StandardCertificateRequest.PKG) + .signPackage(PackageType.MAC_PKG), + + // Don't sign predefined app image, sign native package. + TestSpec.build().signIdentityType(signIdentityType) + .signPackage() + .certRequest(SigningBase.StandardCertificateRequest.PKG) + .signPackage(PackageType.MAC_PKG), + + // Sign predefined app image, don't sign native package. + TestSpec.build().signIdentityType(signIdentityType).signAppImage() + ); + }).forEach(data::add); + + return data.stream().map(TestSpec.Builder::create).map(v -> { + return new Object[] {v}; + }).toList(); } } diff --git a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java index b137824a910f5..efcaadc3fa82f 100644 --- a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java @@ -21,51 +21,53 @@ * questions. */ +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; + import java.io.IOException; import java.nio.file.Path; import java.util.function.Predicate; import java.util.stream.Stream; - import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.Executor; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.MacSign; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; import jdk.jpackage.test.TKit; /** - * Tests generation of dmg and pkg with --mac-sign and related arguments. - * Test will generate pkg and verifies its signature. It verifies that dmg - * is not signed, but runtime image inside dmg is signed. + * Tests generation of dmg and pkg with --mac-sign and related arguments. Test + * will generate pkg and verifies its signature. It verifies that dmg is not + * signed, but runtime image inside dmg is signed. * - * Note: Specific UNICODE signing is not tested, since it is shared code - * with app image signing and it will be covered by SigningPackageTest. + *

    + * Note: Specific UNICODE signing is not tested, since it is shared code with + * app image signing and it will be covered by SigningPackageTest. * + *

    * Following combinations are tested: - * 1) "--runtime-image" points to unsigned JDK bundle and --mac-sign is not + *

      + *
    1. "--runtime-image" points to unsigned JDK bundle and --mac-sign is not * provided. Expected result: runtime image ad-hoc signed. - * 2) "--runtime-image" points to unsigned JDK bundle and --mac-sign is + *
    2. "--runtime-image" points to unsigned JDK bundle and --mac-sign is * provided. Expected result: Everything is signed with provided certificate. - * 3) "--runtime-image" points to signed JDK bundle and --mac-sign is not + *
    3. "--runtime-image" points to signed JDK bundle and --mac-sign is not * provided. Expected result: runtime image is signed with original certificate. - * 4) "--runtime-image" points to signed JDK bundle and --mac-sign is provided. + *
    4. "--runtime-image" points to signed JDK bundle and --mac-sign is provided. * Expected result: runtime image is signed with provided certificate. - * 5) "--runtime-image" points to JDK image and --mac-sign is not provided. + *
    5. "--runtime-image" points to JDK image and --mac-sign is not provided. * Expected result: runtime image ad-hoc signed. - * 6) "--runtime-image" points to JDK image and --mac-sign is provided. + *
    6. "--runtime-image" points to JDK image and --mac-sign is provided. * Expected result: Everything is signed with provided certificate. + *
    * * This test requires that the machine is configured with test certificate for - * "Developer ID Installer: jpackage.openjdk.java.net" in - * jpackagerTest keychain with - * always allowed access to this keychain for user which runs test. - * note: + * "Developer ID Installer: jpackage.openjdk.java.net" in jpackagerTest keychain + * with always allowed access to this keychain for user which runs test. note: * "jpackage.openjdk.java.net" can be over-ridden by system property - * "jpackage.mac.signing.key.user.name", and - * "jpackagerTest" can be over-ridden by system property - * "jpackage.mac.signing.keychain" + * "jpackage.mac.signing.key.user.name" */ /* @@ -84,17 +86,17 @@ */ public class SigningRuntimeImagePackageTest { - private static JPackageCommand addSignOptions(JPackageCommand cmd, int certIndex) { + private static JPackageCommand addSignOptions(JPackageCommand cmd, MacSign.ResolvedKeychain keychain, int certIndex) { if (certIndex != SigningBase.CertIndex.INVALID_INDEX.value()) { cmd.addArguments( "--mac-sign", - "--mac-signing-keychain", SigningBase.getKeyChain(), + "--mac-signing-keychain", keychain.name(), "--mac-signing-key-user-name", SigningBase.getDevName(certIndex)); } return cmd; } - private static Path createInputRuntimeBundle(int certIndex) throws IOException { + private static Path createInputRuntimeBundle(MacSign.ResolvedKeychain keychain, int certIndex) throws IOException { final var runtimeImage = JPackageCommand.createInputRuntimeImage(); @@ -111,7 +113,7 @@ private static Path createInputRuntimeBundle(int certIndex) throws IOException { .addArguments("--runtime-image", runtimeImage) .addArguments("--dest", runtimeBundleWorkDir); - addSignOptions(cmd, certIndex); + addSignOptions(cmd, keychain, certIndex); cmd.execute(); @@ -147,13 +149,21 @@ private static Path createInputRuntimeBundle(int certIndex) throws IOException { public static void test(boolean useJDKBundle, SigningBase.CertIndex jdkBundleCert, SigningBase.CertIndex signCert) throws Exception { + MacSign.withKeychain(toConsumer(keychain -> { + test(keychain, useJDKBundle, jdkBundleCert, signCert); + }), SigningBase.StandardKeychain.MAIN.keychain()); + } + + private static void test(MacSign.ResolvedKeychain keychain, boolean useJDKBundle, + SigningBase.CertIndex jdkBundleCert, + SigningBase.CertIndex signCert) throws Exception { final Path inputRuntime[] = new Path[1]; new PackageTest() .addRunOnceInitializer(() -> { if (useJDKBundle) { - inputRuntime[0] = createInputRuntimeBundle(jdkBundleCert.value()); + inputRuntime[0] = createInputRuntimeBundle(keychain, jdkBundleCert.value()); } else { inputRuntime[0] = JPackageCommand.createInputRuntimeImage(); } @@ -164,7 +174,7 @@ public static void test(boolean useJDKBundle, // create input directory in the test and jpackage fails // if --input references non existent directory. cmd.removeArgumentWithValue("--input"); - addSignOptions(cmd, signCert.value()); + addSignOptions(cmd, keychain, signCert.value()); }) .addInstallVerifier(cmd -> { final var certIndex = Stream.of(signCert, jdkBundleCert) diff --git a/test/jdk/tools/jpackage/macosx/base/SigningBase.java b/test/jdk/tools/jpackage/macosx/base/SigningBase.java index 5484245f111dd..1e38f9b0c29a9 100644 --- a/test/jdk/tools/jpackage/macosx/base/SigningBase.java +++ b/test/jdk/tools/jpackage/macosx/base/SigningBase.java @@ -90,17 +90,29 @@ private static CertificateRequest.Builder cert() { private final CertificateRequest spec; } + /** + * Standard keychains used in signing tests. + */ public enum StandardKeychain { - MAIN(DEFAULT_KEYCHAIN, + /** + * The primary keychain with good certificates. + */ + MAIN("jpackagerTest.keychain", StandardCertificateRequest.CODESIGN, StandardCertificateRequest.PKG, StandardCertificateRequest.CODESIGN_UNICODE, StandardCertificateRequest.PKG_UNICODE), + /** + * A keychain with some good and some expired certificates. + */ EXPIRED("jpackagerTest-expired.keychain", StandardCertificateRequest.CODESIGN, StandardCertificateRequest.PKG, StandardCertificateRequest.CODESIGN_EXPIRED, StandardCertificateRequest.PKG_EXPIRED), + /** + * A keychain with duplicated certificates. + */ DUPLICATE("jpackagerTest-duplicate.keychain", StandardCertificateRequest.CODESIGN, StandardCertificateRequest.PKG, @@ -114,30 +126,26 @@ public enum StandardKeychain { StandardKeychain(String keychainName, CertificateRequest cert, CertificateRequest... otherCerts) { final var builder = keychain(keychainName).addCert(cert); List.of(otherCerts).forEach(builder::addCert); - this.spec = new ResolvedKeychain(builder.create()); + this.keychain = new ResolvedKeychain(builder.create()); } - public KeychainWithCertsSpec spec() { - return spec.spec(); + public ResolvedKeychain keychain() { + return keychain; } public X509Certificate mapCertificateRequest(CertificateRequest certRequest) { - return Objects.requireNonNull(spec.mapCertificateRequests().get(certRequest)); + return Objects.requireNonNull(keychain.mapCertificateRequests().get(certRequest)); } private static KeychainWithCertsSpec.Builder keychain(String name) { return new KeychainWithCertsSpec.Builder().name(name); } - private static CertificateRequest.Builder cert() { - return new CertificateRequest.Builder(); - } - private static List signingEnv() { - return Stream.of(values()).map(StandardKeychain::spec).toList(); + return Stream.of(values()).map(StandardKeychain::keychain).map(ResolvedKeychain::spec).toList(); } - private final ResolvedKeychain spec; + private final ResolvedKeychain keychain; } public static void setUp() { @@ -179,7 +187,6 @@ int value() { "jpackage.openjdk.java.net", "jpackage.openjdk.java.net (รถ)", }; - private static String DEFAULT_KEYCHAIN = "jpackagerTest.keychain"; public static String getDevName(int certIndex) { // Always use values from system properties if set @@ -195,16 +202,6 @@ public static int getDevNameIndex(String devName) { return Arrays.binarySearch(DEV_NAMES, devName); } - // Returns 'true' if dev name from DEV_NAMES - public static boolean isDevNameDefault() { - String value = System.getProperty("jpackage.mac.signing.key.user.name"); - if (value != null) { - return false; - } - - return true; - } - public static String getAppCert(int certIndex) { return "Developer ID Application: " + getDevName(certIndex); } @@ -213,16 +210,6 @@ public static String getInstallerCert(int certIndex) { return "Developer ID Installer: " + getDevName(certIndex); } - public static String getKeyChain() { - // Always use values from system properties if set - String value = System.getProperty("jpackage.mac.signing.keychain"); - if (value != null) { - return value; - } - - return DEFAULT_KEYCHAIN; - } - public static void verifyCodesign(Path target, boolean signed, int certIndex) { if (signed) { final var certRequest = getCertRequest(certIndex); diff --git a/test/jdk/tools/jpackage/share/AddLShortcutTest.java b/test/jdk/tools/jpackage/share/AddLShortcutTest.java index 9c50c6ffc98ca..f000e79227e76 100644 --- a/test/jdk/tools/jpackage/share/AddLShortcutTest.java +++ b/test/jdk/tools/jpackage/share/AddLShortcutTest.java @@ -118,6 +118,12 @@ public void test() { HelloApp.createBundle(JavaAppDesc.parse(addLauncherApp + "*another.jar:Welcome"), cmd.inputDir()); }); + if (RunnablePackageTest.hasAction(RunnablePackageTest.Action.INSTALL)) { + // Ensure launchers are executable because the output bundle will be installed + // and launchers will be attempted to be executed through their shortcuts. + packageTest.addInitializer(JPackageCommand::ignoreFakeRuntime); + } + new FileAssociations(packageName).applyTo(packageTest); new AdditionalLauncher("Foo") diff --git a/test/jdk/tools/jpackage/share/AddLauncherTest.java b/test/jdk/tools/jpackage/share/AddLauncherTest.java index a7bfbf376edb8..21f475cbd7810 100644 --- a/test/jdk/tools/jpackage/share/AddLauncherTest.java +++ b/test/jdk/tools/jpackage/share/AddLauncherTest.java @@ -21,18 +21,22 @@ * questions. */ -import java.nio.file.Path; -import java.util.Map; import java.lang.invoke.MethodHandles; -import jdk.jpackage.test.PackageTest; -import jdk.jpackage.test.FileAssociations; +import java.nio.file.Path; +import java.util.function.Consumer; +import jdk.internal.util.OperatingSystem; import jdk.jpackage.test.AdditionalLauncher; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.CfgFile; +import jdk.jpackage.test.ConfigurationTarget; +import jdk.jpackage.test.FileAssociations; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JavaAppDesc; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.RunnablePackageTest.Action; import jdk.jpackage.test.TKit; -import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.Annotations.Parameter; -import jdk.jpackage.test.CfgFile; /** * Test --add-launcher parameter. Output of the test should be @@ -233,6 +237,61 @@ public void testMainLauncherIsModular(boolean mainLauncherIsModular) { "Check app.classpath value in ModularAppLauncher cfg file"); } + /** + * Test --description option + */ + @Test(ifNotOS = OperatingSystem.MACOS) // Don't run on macOS as launcher description is ignored on this platform + @Parameter("true") + @Parameter("fase") + public void testDescription(boolean withPredefinedAppImage) { + + ConfigurationTarget target; + if (TKit.isWindows() || withPredefinedAppImage) { + target = new ConfigurationTarget(JPackageCommand.helloAppImage()); + } else { + target = new ConfigurationTarget(new PackageTest().configureHelloApp()); + } + + target.addInitializer(cmd -> { + cmd.setArgumentValue("--name", "Foo").setArgumentValue("--description", "Hello"); + cmd.setFakeRuntime(); + cmd.setStandardAsserts(JPackageCommand.StandardAssert.MAIN_LAUNCHER_DESCRIPTION); + }); + + target.add(new AdditionalLauncher("x")); + target.add(new AdditionalLauncher("bye").setProperty("description", "Bye")); + + target.test().ifPresent(test -> { + // Make all launchers have shortcuts and thus .desktop files. + // Launcher description is recorded in a desktop file and verified automatically. + test.mutate(addLinuxShortcuts()); + }); + + target.cmd().ifPresent(withPredefinedAppImage ? JPackageCommand::execute : JPackageCommand::executeAndAssertImageCreated); + target.test().ifPresent(test -> { + test.run(Action.CREATE_AND_UNPACK); + }); + + if (withPredefinedAppImage) { + new PackageTest().addInitializer(cmd -> { + cmd.setArgumentValue("--name", "Bar"); + // Should not have impact of launcher descriptions, but it does. + cmd.setArgumentValue("--description", "Installer"); + cmd.removeArgumentWithValue("--input").setArgumentValue("--app-image", target.cmd().orElseThrow().outputBundle()); + }).mutate(addLinuxShortcuts()).run(Action.CREATE_AND_UNPACK); + } + } + + private static Consumer addLinuxShortcuts() { + return test -> { + test.forTypes(PackageType.LINUX, () -> { + test.addInitializer(cmd -> { + cmd.addArgument("--linux-shortcut"); + }); + }); + }; + } + private static final Path GOLDEN_ICON = TKit.TEST_SRC_ROOT.resolve(Path.of( "resources", "icon" + TKit.ICON_SUFFIX)); } diff --git a/test/jdk/tools/jpackage/share/AppImagePackageTest.java b/test/jdk/tools/jpackage/share/AppImagePackageTest.java index 34a418c6f9ec9..aacb76b122b29 100644 --- a/test/jdk/tools/jpackage/share/AppImagePackageTest.java +++ b/test/jdk/tools/jpackage/share/AppImagePackageTest.java @@ -21,20 +21,21 @@ * questions. */ -import java.nio.file.Path; -import java.nio.file.Files; import java.io.IOException; -import java.util.List; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.function.Predicate; import jdk.jpackage.internal.util.XmlUtils; -import jdk.jpackage.test.AppImageFile; import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.AppImageFile; import jdk.jpackage.test.CannedFormattedString; -import jdk.jpackage.test.TKit; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JPackageCommand.StandardAssert; import jdk.jpackage.test.JPackageStringBundle; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.RunnablePackageTest.Action; -import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.TKit; /** * Test --app-image parameter. The output installer should provide the same @@ -55,56 +56,86 @@ */ public class AppImagePackageTest { + /** + * Create a native bundle from a valid predefined app image produced by jpackage. + */ @Test public static void test() { - Path appimageOutput = TKit.workDir().resolve("appimage"); - JPackageCommand appImageCmd = JPackageCommand.helloAppImage() - .setArgumentValue("--dest", appimageOutput); + var appImageCmd = JPackageCommand.helloAppImage() + .setArgumentValue("--dest", TKit.createTempDirectory("appimage")); new PackageTest() - .addRunOnceInitializer(() -> appImageCmd.execute()) + .addRunOnceInitializer(appImageCmd::execute) .addInitializer(cmd -> { cmd.addArguments("--app-image", appImageCmd.outputBundle()); cmd.removeArgumentWithValue("--input"); }).addBundleDesktopIntegrationVerifier(false).run(); } + /** + * Create a native bundle from a predefined app image not produced by jpackage + * but having a valid ".jpackage.xml" file. + * + * @param withIcon {@code true} if jpackage command line should have "--icon" + * option + */ @Test @Parameter("true") @Parameter("false") public static void testEmpty(boolean withIcon) throws IOException { - final String name = "EmptyAppImagePackageTest"; - final String imageName = name + (TKit.isOSX() ? ".app" : ""); - Path appImageDir = TKit.createTempDirectory("appimage").resolve(imageName); - Files.createDirectories(appImageDir.resolve("bin")); - Path libDir = Files.createDirectories(appImageDir.resolve("lib")); - TKit.createTextFile(libDir.resolve("README"), - List.of("This is some arbitrary text for the README file\n")); + var appImageCmd = JPackageCommand.helloAppImage() + .setFakeRuntime() + .setArgumentValue("--name", "EmptyAppImagePackageTest") + .setArgumentValue("--dest", TKit.createTempDirectory("appimage")); new PackageTest() + .addRunOnceInitializer(appImageCmd::execute) + .addRunOnceInitializer(() -> { + var layout = appImageCmd.appLayout(); + if (!TKit.isOSX()) { + // Delete the launcher if not on macOS. + // On macOS, deleting the launcher will render the app bundle invalid. + TKit.deleteIfExists(appImageCmd.appLauncherPath()); + } + // Delete the runtime. + TKit.deleteDirectoryRecursive(layout.runtimeDirectory()); + // Delete the "app" dir. + TKit.deleteDirectoryRecursive(layout.appDirectory()); + + new AppImageFile(appImageCmd.name(), "PhonyMainClass").save(appImageCmd.outputBundle()); + var appImageDir = appImageCmd.outputBundle(); + + TKit.trace(String.format("Files in [%s] app image:", appImageDir)); + try (var files = Files.walk(appImageDir)) { + files.sequential() + .filter(Predicate.isEqual(appImageDir).negate()) + .map(path -> String.format("[%s]", appImageDir.relativize(path))) + .forEachOrdered(TKit::trace); + TKit.trace("Done"); + } + }) .addInitializer(cmd -> { - cmd.addArguments("--app-image", appImageDir); + cmd.addArguments("--app-image", appImageCmd.outputBundle()); if (withIcon) { cmd.addArguments("--icon", iconPath("icon")); } cmd.removeArgumentWithValue("--input"); - new AppImageFile("EmptyAppImagePackageTest", "Hello").save(appImageDir); - // on mac, with --app-image and without --mac-package-identifier, - // will try to infer it from the image, so foreign image needs it. - if (TKit.isOSX()) { - cmd.addArguments("--mac-package-identifier", name); - } + cmd.excludeStandardAsserts( + StandardAssert.MAIN_JAR_FILE, + StandardAssert.MAIN_LAUNCHER_FILES, + StandardAssert.MAC_BUNDLE_STRUCTURE, + StandardAssert.RUNTIME_DIRECTORY); }) - // On macOS we always signing app image and signing will fail, since - // test produces invalid app bundle. - .setExpectedExitCode(TKit.isOSX() ? 1 : 0) - .run(Action.CREATE, Action.UNPACK); - // default: {CREATE, UNPACK, VERIFY}, but we can't verify foreign image + .run(Action.CREATE_AND_UNPACK); } + /** + * Bad predefined app image - not an output of jpackage. + * jpackage command using the bad predefined app image doesn't have "--name" option. + */ @Test public static void testBadAppImage() throws IOException { Path appImageDir = TKit.createTempDirectory("appimage"); @@ -114,6 +145,9 @@ public static void testBadAppImage() throws IOException { }).run(Action.CREATE); } + /** + * Bad predefined app image - not an output of jpackage. + */ @Test public static void testBadAppImage2() throws IOException { Path appImageDir = TKit.createTempDirectory("appimage"); @@ -121,8 +155,11 @@ public static void testBadAppImage2() throws IOException { configureBadAppImage(appImageDir).run(Action.CREATE); } + /** + * Bad predefined app image - valid app image missing ".jpackage.xml" file. + */ @Test - public static void testBadAppImage3() throws IOException { + public static void testBadAppImage3() { Path appImageDir = TKit.createTempDirectory("appimage"); JPackageCommand appImageCmd = JPackageCommand.helloAppImage(). @@ -134,8 +171,11 @@ public static void testBadAppImage3() throws IOException { }).run(Action.CREATE); } + /** + * Bad predefined app image - valid app image with invalid ".jpackage.xml" file. + */ @Test - public static void testBadAppImageFile() throws IOException { + public static void testBadAppImageFile() { final var appImageRoot = TKit.createTempDirectory("appimage"); final var appImageCmd = JPackageCommand.helloAppImage(). diff --git a/test/jdk/tools/jpackage/share/IconTest.java b/test/jdk/tools/jpackage/share/IconTest.java index 051cad84a13b4..03726e524dc36 100644 --- a/test/jdk/tools/jpackage/share/IconTest.java +++ b/test/jdk/tools/jpackage/share/IconTest.java @@ -21,7 +21,10 @@ * questions. */ +import static jdk.jpackage.test.AdditionalLauncher.getAdditionalLauncherProperties; + import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; @@ -29,10 +32,10 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.TreeMap; -import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.jpackage.internal.util.function.ThrowingBiConsumer; @@ -40,8 +43,11 @@ import jdk.jpackage.test.AdditionalLauncher; import jdk.jpackage.test.Annotations.Parameters; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.CannedFormattedString; +import jdk.jpackage.test.ConfigurationTarget; import jdk.jpackage.test.Executor; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JPackageStringBundle; import jdk.jpackage.test.LauncherIconVerifier; import jdk.jpackage.test.LinuxHelper; import jdk.jpackage.test.PackageTest; @@ -159,27 +165,37 @@ public static Collection data() { @Test public void test() throws IOException { + + final ConfigurationTarget target; if (appImage) { - JPackageCommand cmd = initAppImageTest(); - var result = cmd.executeAndAssertImageCreated(); - ThrowingConsumer.toConsumer(createInstallVerifier()).accept(cmd); - ThrowingBiConsumer.toBiConsumer(createBundleVerifier()).accept(cmd, result); + target = new ConfigurationTarget(JPackageCommand.helloAppImage()); } else { - PackageTest test = initPackageTest(); - test.addInstallVerifier(createInstallVerifier()); - test.addBundleVerifier(createBundleVerifier()); + target = new ConfigurationTarget(new PackageTest().configureHelloApp()); + } + + initTest(target); + var installVerifier = createInstallVerifier(); + var bundleVerifier = createBundleVerifier(); + + var cmdResult = target.cmd().map(JPackageCommand::executeAndAssertImageCreated); + + target.apply(ThrowingConsumer.toConsumer(installVerifier), test -> { + test.addInstallVerifier(installVerifier); + }).apply(cmd -> { + ThrowingBiConsumer.toBiConsumer(bundleVerifier).accept(cmd, cmdResult.orElseThrow()); + }, test -> { + test.addBundleVerifier(bundleVerifier); test.addBundleDesktopIntegrationVerifier(config.values().stream() .anyMatch(this::isWithDesktopIntegration)); + }); - test.run(PackageTest.Action.CREATE_AND_UNPACK); - } + target.test().ifPresent(v -> { + v.run(PackageTest.Action.CREATE_AND_UNPACK); + }); } boolean isWithDesktopIntegration(IconType iconType) { - if (appImage) { - return false; - } boolean withDesktopFile = !Set.of( IconType.NoIcon, IconType.DefaultIcon).contains(iconType); @@ -189,89 +205,110 @@ boolean isWithDesktopIntegration(IconType iconType) { private ThrowingBiConsumer createBundleVerifier() { return (cmd, result) -> { - var verifier = createConsoleOutputVerifier(cmd.name(), config.get( - Launcher.Main), null); - if (verifier != null) { - verifier.apply(result.getOutput()); - } - - if (config.containsKey(Launcher.Additional)) { - verifier = createConsoleOutputVerifier( - Launcher.Additional.launcherName, config.get( - Launcher.Additional), config.get(Launcher.Main)); - if (verifier != null) { + Stream.of(Launcher.Main, Launcher.Additional).filter(config::containsKey).forEach(launcher -> { + createConsoleOutputVerifier(cmd, launcher).ifPresent(verifier -> { verifier.apply(result.getOutput()); - } - } + }); + }); }; } - private TKit.TextStreamVerifier createConsoleOutputVerifier( - String launcherName, IconType iconType, IconType mainIconType) { - if (iconType == IconType.DefaultIcon && mainIconType != null) { - iconType = mainIconType; + private Optional createConsoleOutputVerifier( + JPackageCommand cmd, Launcher launcher) { + + var launcherName = Optional.ofNullable(launcher.launcherName).orElseGet(cmd::name); + var resourceName = launcherName; + Optional customIcon; + + if (launcherName.equals(cmd.name())) { + customIcon = Optional.ofNullable(cmd.getArgumentValue("--icon")).map(Path::of); + } else if (config.get(launcher) == IconType.DefaultIcon) { + resourceName = cmd.name(); + customIcon = Optional.ofNullable(cmd.getArgumentValue("--icon")).map(Path::of); + } else { + customIcon = getAdditionalLauncherProperties(cmd, launcherName).findProperty("icon").map(Path::of); } - return createConsoleOutputVerifier(launcherName, iconType); + + return createConsoleOutputVerifier( + getBundleIconType(cmd, launcher), + launcherName, + resourceName, + customIcon); } - private static TKit.TextStreamVerifier createConsoleOutputVerifier( - String launcherName, IconType iconType) { - String lookupString = null; + private static Optional createConsoleOutputVerifier( + IconType iconType, String launcherName, String resourceName, Optional customIcon) { + + Objects.requireNonNull(launcherName); + Objects.requireNonNull(resourceName); + Objects.requireNonNull(customIcon); + + CannedFormattedString lookupString; + switch (iconType) { case DefaultIcon: - lookupString = String.format( - "Using default package resource %s [icon] (add %s%s to the resource-dir to customize)", + lookupString = JPackageStringBundle.MAIN.cannedFormattedString( + "message.using-default-resource", "JavaApp" + TKit.ICON_SUFFIX, - launcherName, TKit.ICON_SUFFIX); + "[icon]", + launcherName + TKit.ICON_SUFFIX); break; case ResourceDirIcon: - lookupString = String.format( - "Using custom package resource [icon] (loaded from %s%s)", - launcherName, TKit.ICON_SUFFIX); + lookupString = JPackageStringBundle.MAIN.cannedFormattedString( + "message.using-custom-resource", + "[icon]", + resourceName + TKit.ICON_SUFFIX); break; case CustomIcon: case CustomWithResourceDirIcon: - lookupString = "Using custom package resource [icon] (loaded from file"; + lookupString = JPackageStringBundle.MAIN.cannedFormattedString( + "message.using-custom-resource-from-file", + "[icon]", + customIcon.orElseThrow()); break; default: - return null; + return Optional.empty(); } - return TKit.assertTextStream(lookupString); + return Optional.of(TKit.assertTextStream(lookupString.getValue())); } private ThrowingConsumer createInstallVerifier() { - LauncherIconVerifier verifier = new LauncherIconVerifier(); - switch (config.get(Launcher.Main)) { - case NoIcon: - verifier.setExpectedIcon(null); - break; + return cmd -> { + var verifier = new LauncherIconVerifier(); - case DefaultIcon: - verifier.setExpectedDefaultIcon(); - break; + var bundleIconType = getBundleIconType(cmd, Launcher.Main); - case CustomIcon: - verifier.setExpectedIcon(Launcher.Main.cmdlineIcon); - break; + switch (bundleIconType) { + case NoIcon: + verifier.setExpectedNoIcon(); + break; - case ResourceDirIcon: - verifier.setExpectedIcon(Launcher.Main.resourceDirIcon); - break; + case DefaultIcon: + verifier.setExpectedDefaultIcon(); + break; - case CustomWithResourceDirIcon: - verifier.setExpectedIcon(Launcher.Main2.cmdlineIcon); - break; - } + case CustomIcon: + verifier.setExpectedIcon(Launcher.Main.cmdlineIcon); + break; + + case ResourceDirIcon: + verifier.setExpectedIcon(Launcher.Main.resourceDirIcon); + break; + + case CustomWithResourceDirIcon: + verifier.setExpectedIcon(Launcher.Main2.cmdlineIcon); + break; + } - return cmd -> { verifier.applyTo(cmd); + if (TKit.isLinux() && !cmd.isImagePackageType()) { Path desktopFile = LinuxHelper.getDesktopFile(cmd); - if (isWithDesktopIntegration(config.get(Launcher.Main))) { + if (isWithDesktopIntegration(bundleIconType)) { TKit.assertFileExists(desktopFile); } else { TKit.assertPathExists(desktopFile, false); @@ -280,80 +317,61 @@ private ThrowingConsumer createInstallVerifier() { }; } - private void initTest(JPackageCommand cmd, PackageTest test) { + private void initTest(ConfigurationTarget target) { config.entrySet().forEach(ThrowingConsumer.toConsumer(entry -> { - initTest(entry.getKey(), entry.getValue(), cmd, test); + initTest(entry.getKey(), entry.getValue(), target); })); - ThrowingConsumer initializer = testCmd -> { - testCmd.saveConsoleOutput(true); - testCmd.setFakeRuntime(); - testCmd.addArguments(extraJPackageArgs); - }; - - if (test != null) { - test.addInitializer(initializer); - } else { - ThrowingConsumer.toConsumer(initializer).accept(cmd); - } + target.addInitializer(cmd -> { + cmd.saveConsoleOutput(true); + cmd.setFakeRuntime(); + cmd.addArguments(extraJPackageArgs); + }); } private static void initTest(Launcher cfg, IconType iconType, - JPackageCommand cmd, PackageTest test) throws IOException { - Consumer addLauncher = v -> { - if (test != null) { - v.applyTo(test); - } else { - v.applyTo(cmd); - } - }; + ConfigurationTarget target) throws IOException { switch (iconType) { case DefaultIcon: - if (cfg.launcherName != null) { - addLauncher.accept(new AdditionalLauncher(cfg.launcherName)); - } + Optional.ofNullable(cfg.launcherName).map(AdditionalLauncher::new) + .ifPresent(target::add); break; case NoIcon: - if (cfg.launcherName != null) { - addLauncher.accept( - new AdditionalLauncher(cfg.launcherName).setNoIcon()); - } + Optional.ofNullable(cfg.launcherName).map(AdditionalLauncher::new) + .map(AdditionalLauncher::setNoIcon) + .ifPresent(target::add); break; case CustomIcon: - if (test != null) { - addCustomIcon(null, test, cfg.launcherName, cfg.cmdlineIcon); - } else { - addCustomIcon(cmd, null, cfg.launcherName, cfg.cmdlineIcon); - } + addCustomIcon(target, cfg.launcherName, cfg.cmdlineIcon); break; case ResourceDirIcon: - if (Launcher.PRIMARY.contains(cfg) && cfg.launcherName != null) { - addLauncher.accept(new AdditionalLauncher(cfg.launcherName)); - } - if (test != null) { - test.addInitializer(testCmd -> { - addResourceDirIcon(testCmd, cfg.launcherName, - cfg.resourceDirIcon); - }); - } else { - addResourceDirIcon(cmd, cfg.launcherName, cfg.resourceDirIcon); + if (Launcher.PRIMARY.contains(cfg)) { + Optional.ofNullable(cfg.launcherName).map(AdditionalLauncher::new) + .ifPresent(target::add); } + target.addInitializer(cmd -> { + try { + addResourceDirIcon(cmd, cfg.launcherName, cfg.resourceDirIcon); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); break; case CustomWithResourceDirIcon: switch (cfg) { case Main: - initTest(Launcher.Main2, IconType.CustomIcon, cmd, test); - initTest(Launcher.Main2, IconType.ResourceDirIcon, cmd, test); + initTest(Launcher.Main2, IconType.CustomIcon, target); + initTest(Launcher.Main2, IconType.ResourceDirIcon, target); break; case Additional: - initTest(Launcher.Additional2, IconType.CustomIcon, cmd, test); - initTest(Launcher.Additional2, IconType.ResourceDirIcon, cmd, test); + initTest(Launcher.Additional2, IconType.CustomIcon, target); + initTest(Launcher.Additional2, IconType.ResourceDirIcon, target); break; default: @@ -363,29 +381,46 @@ private static void initTest(Launcher cfg, IconType iconType, } } - private JPackageCommand initAppImageTest() { - JPackageCommand cmd = JPackageCommand.helloAppImage(); - initTest(cmd, null); - return cmd; + private IconType getBundleIconType(JPackageCommand cmd, Launcher launcher) { + return getBundleIconType(cmd, config.get(Launcher.Main), launcher, config.get(launcher)); } - private PackageTest initPackageTest() { - PackageTest test = new PackageTest().configureHelloApp(); - initTest(null, test); - return test; + /** + * Returns the expected icon type of the given launcher in the output bundle + * that the given jpackage command line will output based on the icon type + * configured for the launcher. + * + * @param cmd jpackage command line + * @param mainLauncherIconType the icon type configured for the main launcher + * @param launcher the launcher + * @param iconType the icon type configured for the specified + * launcher + * @return the type of of an icon of the given launcher in the output bundle + */ + private static IconType getBundleIconType(JPackageCommand cmd, + IconType mainLauncherIconType, Launcher launcher, IconType iconType) { + + Objects.requireNonNull(cmd); + Objects.requireNonNull(mainLauncherIconType); + Objects.requireNonNull(launcher); + Objects.requireNonNull(iconType); + + if (iconType == IconType.DefaultIcon) { + iconType = mainLauncherIconType; + } + + return iconType; } private static void addResourceDirIcon(JPackageCommand cmd, String launcherName, Path iconPath) throws IOException { - Path resourceDir = cmd.getArgumentValue("--resource-dir", () -> null, - Path::of); - if (resourceDir == null) { - resourceDir = TKit.createTempDirectory("resources"); - cmd.addArguments("--resource-dir", resourceDir); - } + var resourceDir = Optional.ofNullable(cmd.getArgumentValue("--resource-dir")).map(Path::of).orElseGet(() -> { + return TKit.createTempDirectory("resources"); + }); - String dstIconFileName = Optional.ofNullable(launcherName).orElseGet( - () -> cmd.name()) + TKit.ICON_SUFFIX; + cmd.addArguments("--resource-dir", resourceDir); + + String dstIconFileName = Optional.ofNullable(launcherName).orElseGet(cmd::name) + TKit.ICON_SUFFIX; TKit.trace(String.format("Resource file: [%s] <- [%s]", resourceDir.resolve(dstIconFileName), iconPath)); @@ -393,23 +428,16 @@ private static void addResourceDirIcon(JPackageCommand cmd, StandardCopyOption.REPLACE_EXISTING); } - private static void addCustomIcon(JPackageCommand cmd, PackageTest test, - String launcherName, Path iconPath) throws IOException { + private static void addCustomIcon(ConfigurationTarget target, + String launcherName, Path iconPath) { if (launcherName != null) { - AdditionalLauncher al = new AdditionalLauncher(launcherName).setIcon( - iconPath); - if (test != null) { - al.applyTo(test); - } else { - al.applyTo(cmd); - } - } else if (test != null) { - test.addInitializer(testCmd -> { - testCmd.addArguments("--icon", iconPath); - }); + var al = new AdditionalLauncher(launcherName).setIcon(iconPath); + target.apply(al::applyTo, al::applyTo); } else { - cmd.addArguments("--icon", iconPath); + target.addInitializer(cmd -> { + cmd.addArguments("--icon", iconPath); + }); } } diff --git a/test/jdk/tools/jpackage/share/InOutPathTest.java b/test/jdk/tools/jpackage/share/InOutPathTest.java index f7c597d2ed346..d36731c2960e5 100644 --- a/test/jdk/tools/jpackage/share/InOutPathTest.java +++ b/test/jdk/tools/jpackage/share/InOutPathTest.java @@ -38,7 +38,7 @@ import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.internal.util.function.ThrowingConsumer; import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.JPackageCommand.AppLayoutAssert; +import jdk.jpackage.test.JPackageCommand.StandardAssert; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; import static jdk.jpackage.test.RunnablePackageTest.Action.CREATE_AND_UNPACK; @@ -177,7 +177,7 @@ private static void runTest(Set packageTypes, if (!isAppImageValid(cmd)) { // Standard asserts for .jpackage.xml fail in messed up app image. Disable them. // Other standard asserts for app image contents should pass. - cmd.excludeAppLayoutAsserts(AppLayoutAssert.APP_IMAGE_FILE); + cmd.excludeStandardAsserts(StandardAssert.APP_IMAGE_FILE); } }; diff --git a/test/jdk/tools/jpackage/share/LicenseTest.java b/test/jdk/tools/jpackage/share/LicenseTest.java index c9e3c8508aa61..1c6bfd51b62d6 100644 --- a/test/jdk/tools/jpackage/share/LicenseTest.java +++ b/test/jdk/tools/jpackage/share/LicenseTest.java @@ -208,7 +208,7 @@ private static Path linuxLicenseFile(JPackageCommand cmd) { private static void verifyLicenseFileInLinuxPackage(JPackageCommand cmd, Path expectedLicensePath) { TKit.assertTrue(LinuxHelper.getPackageFiles(cmd).filter(path -> path.equals( - expectedLicensePath)).findFirst().orElse(null) != null, + expectedLicensePath)).findFirst().isPresent(), String.format("Check license file [%s] is in %s package", expectedLicensePath, LinuxHelper.getPackageName(cmd))); } diff --git a/test/jdk/tools/jpackage/share/RuntimePackageTest.java b/test/jdk/tools/jpackage/share/RuntimePackageTest.java index f66f774b227ac..caa129713b48b 100644 --- a/test/jdk/tools/jpackage/share/RuntimePackageTest.java +++ b/test/jdk/tools/jpackage/share/RuntimePackageTest.java @@ -135,11 +135,7 @@ private static PackageTest init(ThrowingSupplier createRuntime) { }) .addInstallVerifier(cmd -> { var src = TKit.assertDirectoryContentRecursive(inputRuntimeDir(cmd)).items(); - Path dest = cmd.appRuntimeDirectory(); - if (TKit.isOSX()) { - dest = dest.resolve("Contents/Home"); - } - + var dest = cmd.appLayout().runtimeHomeDirectory(); TKit.assertDirectoryContentRecursive(dest).match(src); }) .forTypes(PackageType.LINUX_DEB, test -> { diff --git a/test/jdk/tools/jpackage/windows/WinNoRestartTest.java b/test/jdk/tools/jpackage/windows/WinNoRestartTest.java index 7f04ee2bd2ef3..909ee06b01a12 100644 --- a/test/jdk/tools/jpackage/windows/WinNoRestartTest.java +++ b/test/jdk/tools/jpackage/windows/WinNoRestartTest.java @@ -21,16 +21,15 @@ * questions. */ +import static jdk.jpackage.test.WindowsHelper.killAppLauncherProcess; + import java.io.IOException; import java.time.Duration; import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.CfgFile; import jdk.jpackage.test.HelloApp; -import static jdk.jpackage.test.WindowsHelper.killAppLauncherProcess; +import jdk.jpackage.test.JPackageCommand; /* @test * @bug 8340311 @@ -93,18 +92,16 @@ void apply(JPackageCommand cmd, CfgFile origCfgFile) throws InterruptedException // Save updated main launcher .cfg file cfgFile.save(cmd.appLauncherCfgPath(null)); - try ( // Launch the app in a separate thread - ExecutorService exec = Executors.newSingleThreadExecutor()) { - exec.execute(() -> { - HelloApp.executeLauncher(cmd); - }); + // Launch the app in a separate thread + new Thread(() -> { + HelloApp.executeLauncher(cmd); + }).start(); - // Wait a bit to let the app start - Thread.sleep(Duration.ofSeconds(10)); + // Wait a bit to let the app start + Thread.sleep(Duration.ofSeconds(10)); - // Find the main app launcher process and kill it - killAppLauncherProcess(cmd, null, expectedNoRestarted ? 1 : 2); - } + // Find the main app launcher process and kill it + killAppLauncherProcess(cmd, null, expectedNoRestarted ? 1 : 2); } } diff --git a/test/lib/jdk/test/lib/security/CertificateBuilder.java b/test/lib/jdk/test/lib/security/CertificateBuilder.java index e5044d46b0f9b..d35a21e7ab5ee 100644 --- a/test/lib/jdk/test/lib/security/CertificateBuilder.java +++ b/test/lib/jdk/test/lib/security/CertificateBuilder.java @@ -53,6 +53,7 @@ import sun.security.x509.SubjectAlternativeNameExtension; import sun.security.x509.URIName; import sun.security.x509.KeyIdentifier; +import sun.security.x509.X500Name; /** @@ -90,7 +91,7 @@ public class CertificateBuilder { private final CertificateFactory factory; - private X500Principal subjectName = null; + private X500Name subjectName = null; private BigInteger serialNumber = null; private PublicKey publicKey = null; private Date notBefore = null; @@ -199,7 +200,7 @@ public CertificateBuilder() throws CertificateException { * on this certificate. */ public CertificateBuilder setSubjectName(X500Principal name) { - subjectName = name; + subjectName = X500Name.asX500Name(name); return this; } @@ -209,7 +210,23 @@ public CertificateBuilder setSubjectName(X500Principal name) { * @param name The subject name in RFC 2253 format */ public CertificateBuilder setSubjectName(String name) { - subjectName = new X500Principal(name); + try { + subjectName = new X500Name(name); + } catch (IOException ioe) { + throw new IllegalArgumentException(ioe); + } + return this; + } + + /** + * Set the subject name for the certificate. This method is useful when + * you need more control over the contents of the subject name. + * + * @param name an {@code X500Name} to be used as the subject name + * on this certificate + */ + public CertificateBuilder setSubjectName(X500Name name) { + subjectName = name; return this; } diff --git a/test/micro/org/openjdk/bench/java/lang/runtime/RecordMethodsBenchmark.java b/test/micro/org/openjdk/bench/java/lang/runtime/RecordMethodsBenchmark.java new file mode 100644 index 0000000000000..91d26601383a0 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/runtime/RecordMethodsBenchmark.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.java.lang.runtime; + +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +/// Tests the generated equals and hashCode for records. +/// There are 4 types of methods: +/// - distinct: distinct sites for type profiling +/// - polluted: megamorphic site that blocks type profiling +/// - generated: actual body generated by ObjectMethods::bootstrap +/// - specialized: generated body for non-extensible types +/// The result of generated compared to the other distinct/polluted shows +/// whether the generated code could perform type profiling. +/// Specialized is the result of distinct without trap, should be even faster. +@Fork(3) +@Warmup(iterations = 10, time = 1) +@Measurement(iterations = 5, time = 2) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@BenchmarkMode(Mode.Throughput) +public class RecordMethodsBenchmark { + + record One(int a) {} + + @State(Scope.Thread) + public static class BenchmarkState { + Key k1 = new Key(new One(1), "a"); + Key k2 = new Key(new One(1), new String("a")); + SpecializedKey sk1 = new SpecializedKey(new One(1), "a"); + SpecializedKey sk2 = new SpecializedKey(new One(1), new String("a")); + } + + @Benchmark + public int hashCodeDistinct(BenchmarkState state) { + return state.k1.hashCodeDistinct(); + } + + @Benchmark + public int hashCodePolluted(BenchmarkState state) { + return state.k1.hashCodePolluted(); + } + + @Benchmark + public int hashCodeGenerated(BenchmarkState state) { + return state.k1.hashCode(); + } + + @Benchmark + public int hashCodeSpecial(BenchmarkState state) { + return state.sk1.hashCode(); + } + + @Benchmark + public boolean equalsDistinct(BenchmarkState state) { + return state.k1.equalsDistinct(state.k2); + } + + @Benchmark + public boolean equalsPolluted(BenchmarkState state) { + return state.k1.equalsPolluted(state.k2); + } + + @Benchmark + public boolean equalsGenerated(BenchmarkState state) { + return state.k1.equals(state.k2); + } + + @Benchmark + public boolean equalsSpecial(BenchmarkState state) { + return state.sk1.equals(state.sk2); + } + + /// A key object. + /// + /// Having both field as Object pollutes Object.equals for record object + /// method MH tree. We must verify the leaf Object.equals calls don't + /// share the same profile in generated code. + record Key(Object key1, Object key2) { + /// A hashCode method which has distinct hashCode invocations + /// in bytecode for each field for type profiling. + public int hashCodeDistinct() { + final int prime = 31; + int result = 1; + result = prime * result + ((key1 == null) ? 0 : key1.hashCode()); + result = prime * result + ((key2 == null) ? 0 : key2.hashCode()); + return result; + } + + /// A hashCode method which uses a megamorphic polluted + /// Object.hashCode virtual invocation in Objects.hashCode. + public int hashCodePolluted() { + final int prime = 31; + int result = 1; + result = prime * result + Objects.hashCode(key1); + result = prime * result + Objects.hashCode(key2); + return result; + } + + /// An equals method which has distinct equals invocations + /// in bytecode for each field for type profiling. + public boolean equalsDistinct(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Key other = (Key) obj; + if (key1 == null) { + if (other.key1 != null) + return false; + } + else if (!key1.equals(other.key1)) + return false; + if (key2 == null) { + if (other.key2 != null) + return false; + } + else if (!key2.equals(other.key2)) + return false; + return true; + } + + /// An equals method which uses a megamorphic polluted + /// Object.equals virtual invocation in Objects.equals. + public boolean equalsPolluted(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Key other = (Key) obj; + return Objects.equals(key1, other.key1) && Objects.equals(key2, other.key2); + } + } + + record SpecializedKey(One key1, String key2) {} +}