diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf index 74a830cbcc2..bd73e909062 100644 --- a/make/conf/github-actions.conf +++ b/make/conf/github-actions.conf @@ -29,21 +29,21 @@ GTEST_VERSION=1.14.0 JTREG_VERSION=8.1+1 LINUX_X64_BOOT_JDK_EXT=tar.gz -LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk24/1f9ff9062db4449d8ca828c504ffae90/36/GPL/openjdk-24_linux-x64_bin.tar.gz -LINUX_X64_BOOT_JDK_SHA256=88b090fa80c6c1d084ec9a755233967458788e2c0777ae2e172230c5c692d7ef +LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk25/bd75d5f9689641da8e1daabeccb5528b/36/GPL/openjdk-25_linux-x64_bin.tar.gz +LINUX_X64_BOOT_JDK_SHA256=59cdcaf255add4721de38eb411d4ecfe779356b61fb671aee63c7dec78054c2b ALPINE_LINUX_X64_BOOT_JDK_EXT=tar.gz -ALPINE_LINUX_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin24-binaries/releases/download/jdk-24%2B36/OpenJDK24U-jdk_x64_alpine-linux_hotspot_24_36.tar.gz -ALPINE_LINUX_X64_BOOT_JDK_SHA256=a642608f0da78344ee6812fb1490b8bc1d7ad5a18064c70994d6f330568c51cb +ALPINE_LINUX_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25%2B36/OpenJDK25U-jdk_x64_alpine-linux_hotspot_25_36.tar.gz +ALPINE_LINUX_X64_BOOT_JDK_SHA256=637e47474d411ed86134f413af7d5fef4180ddb0bf556347b7e74a88cf8904c8 MACOS_AARCH64_BOOT_JDK_EXT=tar.gz -MACOS_AARCH64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk24/1f9ff9062db4449d8ca828c504ffae90/36/GPL/openjdk-24_macos-aarch64_bin.tar.gz -MACOS_AARCH64_BOOT_JDK_SHA256=f7133238a12714a62c5ad2bd4da6741130be1a82512065da9ca23dee26b2d3d3 +MACOS_AARCH64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk25/bd75d5f9689641da8e1daabeccb5528b/36/GPL/openjdk-25_macos-aarch64_bin.tar.gz +MACOS_AARCH64_BOOT_JDK_SHA256=2006337bf326fdfdf6117081751ba38c1c8706d63419ecac7ff102ff7c776876 MACOS_X64_BOOT_JDK_EXT=tar.gz -MACOS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk24/1f9ff9062db4449d8ca828c504ffae90/36/GPL/openjdk-24_macos-x64_bin.tar.gz -MACOS_X64_BOOT_JDK_SHA256=6bbfb1d01741cbe55ab90299cb91464b695de9a3ace85c15131aa2f50292f321 +MACOS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk25/bd75d5f9689641da8e1daabeccb5528b/36/GPL/openjdk-25_macos-x64_bin.tar.gz +MACOS_X64_BOOT_JDK_SHA256=47482ad9888991ecac9b2bcc131e2b53ff78aff275104cef85f66252308e8a09 WINDOWS_X64_BOOT_JDK_EXT=zip -WINDOWS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk24/1f9ff9062db4449d8ca828c504ffae90/36/GPL/openjdk-24_windows-x64_bin.zip -WINDOWS_X64_BOOT_JDK_SHA256=11d1d9f6ac272d5361c8a0bef01894364081c7fb1a6914c2ad2fc312ae83d63b +WINDOWS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk25/bd75d5f9689641da8e1daabeccb5528b/36/GPL/openjdk-25_windows-x64_bin.zip +WINDOWS_X64_BOOT_JDK_SHA256=85bcc178461e2cb3c549ab9ca9dfa73afd54c09a175d6510d0884071867137d3 diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index c200084bfe2..e14ce322860 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -387,8 +387,8 @@ var getJibProfilesCommon = function (input, data) { }; }; - common.boot_jdk_version = "24"; - common.boot_jdk_build_number = "36"; + common.boot_jdk_version = "25"; + common.boot_jdk_build_number = "37"; common.boot_jdk_home = input.get("boot_jdk", "install_path") + "/jdk-" + common.boot_jdk_version + (input.build_os == "macosx" ? ".jdk/Contents/Home" : ""); diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf index 013ca8327fb..90989a86da0 100644 --- a/make/conf/version-numbers.conf +++ b/make/conf/version-numbers.conf @@ -37,6 +37,6 @@ DEFAULT_VERSION_DATE=2026-03-17 DEFAULT_VERSION_CLASSFILE_MAJOR=70 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 -DEFAULT_ACCEPTABLE_BOOT_VERSIONS="24 25 26" +DEFAULT_ACCEPTABLE_BOOT_VERSIONS="25 26" DEFAULT_JDK_SOURCE_TARGET_VERSION=26 DEFAULT_PROMOTED_VERSION_PRE=jep401ea2 diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 57268fd33f2..3b4d11685d3 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1266,39 +1266,39 @@ source %{ // adlc register classes to make AArch64 rheapbase (r27) and rfp (r29) // registers conditionally reserved. - _ANY_REG32_mask = _ALL_REG32_mask; - _ANY_REG32_mask.Remove(OptoReg::as_OptoReg(r31_sp->as_VMReg())); + _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.SUBTRACT(_NON_ALLOCATABLE_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.SUBTRACT(_NON_ALLOCATABLE_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.SUBTRACT(_NON_ALLOCATABLE_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 // zero, compressed klass pointers doesn't use r27 after JDK-8234794 if (UseCompressedOops && (CompressedOops::base() != nullptr)) { - _NO_SPECIAL_REG32_mask.Remove(OptoReg::as_OptoReg(r27->as_VMReg())); - _NO_SPECIAL_REG_mask.Remove(OptoReg::as_OptoReg(r27->as_VMReg())); - _NO_SPECIAL_PTR_REG_mask.Remove(OptoReg::as_OptoReg(r27->as_VMReg())); + _NO_SPECIAL_REG32_mask.remove(OptoReg::as_OptoReg(r27->as_VMReg())); + _NO_SPECIAL_REG_mask.remove(OptoReg::as_OptoReg(r27->as_VMReg())); + _NO_SPECIAL_PTR_REG_mask.remove(OptoReg::as_OptoReg(r27->as_VMReg())); } // r29 is not allocatable when PreserveFramePointer is on if (PreserveFramePointer) { - _NO_SPECIAL_REG32_mask.Remove(OptoReg::as_OptoReg(r29->as_VMReg())); - _NO_SPECIAL_REG_mask.Remove(OptoReg::as_OptoReg(r29->as_VMReg())); - _NO_SPECIAL_PTR_REG_mask.Remove(OptoReg::as_OptoReg(r29->as_VMReg())); + _NO_SPECIAL_REG32_mask.remove(OptoReg::as_OptoReg(r29->as_VMReg())); + _NO_SPECIAL_REG_mask.remove(OptoReg::as_OptoReg(r29->as_VMReg())); + _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.Remove(OptoReg::as_OptoReg(r29->as_VMReg())); + _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())); } // Optimizaton of volatile gets and puts @@ -1737,7 +1737,7 @@ uint MachBreakpointNode::size(PhaseRegAlloc *ra_) const { } //============================================================================= -const RegMask& MachConstantBaseNode::_out_RegMask = RegMask::Empty; +const RegMask& MachConstantBaseNode::_out_RegMask = RegMask::EMPTY; int ConstantTable::calculate_table_base_offset() const { return 0; // absolute addressing, no offset @@ -2511,10 +2511,10 @@ uint Matcher::int_pressure_limit() // as a spilled LRG. Spilling heuristics(Spill-USE) explicitly skip // derived pointers and lastly fail to spill after reaching maximum // number of iterations. Lowering the default pressure threshold to - // (_NO_SPECIAL_REG32_mask.Size() minus 1) forces CallNode to become + // (_NO_SPECIAL_REG32_mask.size() minus 1) forces CallNode to become // a high register pressure area of the code so that split_DEF can // generate DefinitionSpillCopy for the derived pointer. - uint default_int_pressure_threshold = _NO_SPECIAL_REG32_mask.Size() - 1; + uint default_int_pressure_threshold = _NO_SPECIAL_REG32_mask.size() - 1; if (!PreserveFramePointer) { // When PreserveFramePointer is off, frame pointer is allocatable, // but different from other SOC registers, it is excluded from @@ -2529,34 +2529,34 @@ uint Matcher::int_pressure_limit() uint Matcher::float_pressure_limit() { // _FLOAT_REG_mask is generated by adlc from the float_reg register class. - return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.Size() : FLOATPRESSURE; + return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.size() : FLOATPRESSURE; } 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 ef35b66003d..3379041b2cc 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 012de7e46d8..6d296cbdb3a 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 4c4251fbe9f..a8f378e524f 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 6cfad1a47b8..8bb25169eed 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -2224,114 +2224,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 6cdd24b6b65..ea2778d3c56 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp @@ -175,13 +175,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/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index fdb4467a667..b380a5fef26 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -1954,3 +1954,14 @@ void InterpreterMacroAssembler::load_method_entry(Register cache, Register index add(cache, cache, Array::base_offset_in_bytes()); lea(cache, Address(cache, index)); } + +#ifdef ASSERT +void InterpreterMacroAssembler::verify_field_offset(Register reg) { + // Verify the field offset is not in the header, implicitly checks for 0 + Label L; + subs(zr, reg, oopDesc::base_offset_in_bytes()); + br(Assembler::GE, L); + stop("bad field offset"); + bind(L); +} +#endif diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp index 05d5e516dc6..47ad383d00b 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp @@ -349,6 +349,8 @@ class InterpreterMacroAssembler: public MacroAssembler { 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 verify_field_offset(Register reg) NOT_DEBUG_RETURN; }; #endif // CPU_AARCH64_INTERP_MASM_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index dd559171bc0..ae834b3b1cc 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -153,56 +153,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; +static uint32_t insn_at(address insn_addr, int n) { + return ((uint32_t*)insn_addr)[n]; +} - const address _insn_addr; - const uint32_t _insn; - - 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: @@ -214,9 +192,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 @@ -229,27 +207,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; } @@ -257,7 +235,7 @@ class RelocActions { case 0b011001: case 0b101001: case 0b111001: { - instructions = immediate(insn_addr, target); + instructions = T::immediate(insn_addr, target); break; } default: { @@ -265,42 +243,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; @@ -310,17 +282,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; @@ -330,7 +303,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); @@ -339,21 +312,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"); @@ -363,7 +336,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)) { @@ -397,56 +370,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); @@ -455,14 +426,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) | @@ -481,35 +452,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) { @@ -551,11 +520,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) { @@ -7904,10 +7873,9 @@ void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Registe // Try to lock. Transition lock bits 0b01 => 0b00 assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea"); orr(mark, mark, markWord::unlocked_value); - if (EnableValhalla) { - // Mask inline_type bit such that we go to the slow path if object is an inline type - andr(mark, mark, ~((int) markWord::inline_type_bit_in_place)); - } + // Mask inline_type bit such that we go to the slow path if object is an inline type + andr(mark, mark, ~((int) markWord::inline_type_bit_in_place)); + eor(t, mark, markWord::unlocked_value); cmpxchg(/*addr*/ obj, /*expected*/ mark, /*new*/ t, Assembler::xword, /*acquire*/ true, /*release*/ false, /*weak*/ false, noreg); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 84f7fbf36f1..cd1e6eeee8f 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -703,16 +703,8 @@ class MacroAssembler: public Assembler { void test_flat_array_layout(Register lh, Label& is_flat_array); void test_non_flat_array_layout(Register lh, Label& is_non_flat_array); - 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/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index a985ced5fd2..a559b23ca40 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -168,6 +168,7 @@ void TemplateTable::patch_bytecode(Bytecodes::Code bc, Register bc_reg, Register temp_reg, bool load_bc_into_bc_reg/*=true*/, int byte_no) { + assert_different_registers(bc_reg, temp_reg); if (!RewriteBytecodes) return; Label L_patch_done; @@ -232,9 +233,12 @@ void TemplateTable::patch_bytecode(Bytecodes::Code bc, Register bc_reg, __ stop("patching the wrong bytecode"); __ bind(L_okay); #endif - - // patch bytecode - __ strb(bc_reg, at_bcp(0)); + // Patch bytecode with release store to coordinate with ResolvedFieldEntry loads + // in fast bytecode codelets. load_field_entry has a memory barrier that gains + // the needed ordering, together with control dependency on entering the fast codelet + // itself. + __ lea(temp_reg, at_bcp(0)); + __ stlrb(bc_reg, temp_reg); __ bind(L_patch_done); } @@ -3275,6 +3279,7 @@ void TemplateTable::fast_storefield(TosState state) // R1: field offset, R2: field holder, R5: flags load_resolved_field_entry(r2, r2, noreg, r1, r5); + __ verify_field_offset(r1); { Label notVolatile; @@ -3380,6 +3385,8 @@ void TemplateTable::fast_accessfield(TosState state) __ load_field_entry(r2, r1); __ load_sized_value(r1, Address(r2, in_bytes(ResolvedFieldEntry::field_offset_offset())), sizeof(int), true /*is_signed*/); + __ verify_field_offset(r1); + __ load_unsigned_byte(r3, Address(r2, in_bytes(ResolvedFieldEntry::flags_offset()))); // r0: object @@ -3455,7 +3462,9 @@ void TemplateTable::fast_xaccess(TosState state) __ ldr(r0, aaddress(0)); // access constant pool cache __ load_field_entry(r2, r3, 2); + __ load_sized_value(r1, Address(r2, in_bytes(ResolvedFieldEntry::field_offset_offset())), sizeof(int), true /*is_signed*/); + __ verify_field_offset(r1); // 8179954: We need to make sure that the code generated for // volatile accesses forms a sequentially-consistent set of diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 68fece5263d..31a442be624 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 d3969427db3..9140dd7ca4e 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 1f6e8451154..dcd5f0e8cca 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 2c83b2d5765..03dbd0e780b 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 41fbe66647e..09acd1c067d 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/ppc/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp index e2dfd4ecec9..8b1de754650 100644 --- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp +++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp @@ -99,6 +99,10 @@ void VM_Version::initialize() { FLAG_SET_ERGO(TrapBasedRangeChecks, false); } + if (FLAG_IS_DEFAULT(UsePopCountInstruction)) { + FLAG_SET_ERGO(UsePopCountInstruction, true); + } + if (PowerArchitecturePPC64 >= 9) { // Performance is good since Power9. if (FLAG_IS_DEFAULT(SuperwordUseVSX)) { diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp index 7c4b8444407..549c9cda7b6 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp @@ -1841,6 +1841,15 @@ void InterpreterMacroAssembler::load_method_entry(Register cache, Register index } #ifdef ASSERT +void InterpreterMacroAssembler::verify_field_offset(Register reg) { + // Verify the field offset is not in the header, implicitly checks for 0 + Label L; + mv(t0, oopDesc::base_offset_in_bytes()); + bge(reg, t0, L); + stop("bad field offset"); + bind(L); +} + void InterpreterMacroAssembler::verify_access_flags(Register access_flags, uint32_t flag, const char* msg, bool stop_by_hit) { Label L; diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp index 0732191ea83..295f1b22191 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp @@ -300,11 +300,10 @@ class InterpreterMacroAssembler: public MacroAssembler { void load_field_entry(Register cache, Register index, int bcp_offset = 1); void load_method_entry(Register cache, Register index, int bcp_offset = 1); -#ifdef ASSERT + void verify_field_offset(Register reg) NOT_DEBUG_RETURN; 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 009acd628a0..83c59af9113 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1092,40 +1092,40 @@ RegMask _NO_SPECIAL_NO_FP_PTR_REG_mask; void reg_mask_init() { - _ANY_REG32_mask = _ALL_REG32_mask; - _ANY_REG32_mask.Remove(OptoReg::as_OptoReg(x0->as_VMReg())); + _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.SUBTRACT(_ZR_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.SUBTRACT(_ZR_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.SUBTRACT(_NON_ALLOCATABLE_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.SUBTRACT(_NON_ALLOCATABLE_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.SUBTRACT(_NON_ALLOCATABLE_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 if (UseCompressedOops) { - _NO_SPECIAL_REG32_mask.Remove(OptoReg::as_OptoReg(x27->as_VMReg())); - _NO_SPECIAL_REG_mask.Remove(OptoReg::as_OptoReg(x27->as_VMReg())); - _NO_SPECIAL_PTR_REG_mask.Remove(OptoReg::as_OptoReg(x27->as_VMReg())); + _NO_SPECIAL_REG32_mask.remove(OptoReg::as_OptoReg(x27->as_VMReg())); + _NO_SPECIAL_REG_mask.remove(OptoReg::as_OptoReg(x27->as_VMReg())); + _NO_SPECIAL_PTR_REG_mask.remove(OptoReg::as_OptoReg(x27->as_VMReg())); } // x8 is not allocatable when PreserveFramePointer is on if (PreserveFramePointer) { - _NO_SPECIAL_REG32_mask.Remove(OptoReg::as_OptoReg(x8->as_VMReg())); - _NO_SPECIAL_REG_mask.Remove(OptoReg::as_OptoReg(x8->as_VMReg())); - _NO_SPECIAL_PTR_REG_mask.Remove(OptoReg::as_OptoReg(x8->as_VMReg())); + _NO_SPECIAL_REG32_mask.remove(OptoReg::as_OptoReg(x8->as_VMReg())); + _NO_SPECIAL_REG_mask.remove(OptoReg::as_OptoReg(x8->as_VMReg())); + _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.Remove(OptoReg::as_OptoReg(x8->as_VMReg())); + _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())); } void PhaseOutput::pd_perform_mach_node_analysis() { @@ -1326,7 +1326,7 @@ uint MachBreakpointNode::size(PhaseRegAlloc *ra_) const { } //============================================================================= -const RegMask& MachConstantBaseNode::_out_RegMask = RegMask::Empty; +const RegMask& MachConstantBaseNode::_out_RegMask = RegMask::EMPTY; int ConstantTable::calculate_table_base_offset() const { return 0; // absolute addressing, no offset @@ -2104,10 +2104,10 @@ uint Matcher::int_pressure_limit() // as a spilled LRG. Spilling heuristics(Spill-USE) explicitly skip // derived pointers and lastly fail to spill after reaching maximum // number of iterations. Lowering the default pressure threshold to - // (_NO_SPECIAL_REG32_mask.Size() minus 1) forces CallNode to become + // (_NO_SPECIAL_REG32_mask.size() minus 1) forces CallNode to become // a high register pressure area of the code so that split_DEF can // generate DefinitionSpillCopy for the derived pointer. - uint default_int_pressure_threshold = _NO_SPECIAL_REG32_mask.Size() - 1; + uint default_int_pressure_threshold = _NO_SPECIAL_REG32_mask.size() - 1; if (!PreserveFramePointer) { // When PreserveFramePointer is off, frame pointer is allocatable, // but different from other SOC registers, it is excluded from @@ -2122,34 +2122,34 @@ uint Matcher::int_pressure_limit() uint Matcher::float_pressure_limit() { // _FLOAT_REG_mask is generated by adlc from the float_reg register class. - return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.Size() : FLOATPRESSURE; + return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.size() : FLOATPRESSURE; } 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 14775a969d2..e4cb0ff65a6 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(); @@ -1547,9 +1545,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/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp index 31776418e9f..c8544e8bb72 100644 --- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp @@ -133,6 +133,7 @@ Address TemplateTable::at_bcp(int offset) { void TemplateTable::patch_bytecode(Bytecodes::Code bc, Register bc_reg, Register temp_reg, bool load_bc_into_bc_reg /*=true*/, int byte_no) { + assert_different_registers(bc_reg, temp_reg); if (!RewriteBytecodes) { return; } Label L_patch_done; @@ -196,7 +197,11 @@ void TemplateTable::patch_bytecode(Bytecodes::Code bc, Register bc_reg, __ bind(L_okay); #endif - // patch bytecode + // Patch bytecode with release store to coordinate with ResolvedFieldEntry loads + // in fast bytecode codelets. load_field_entry has a memory barrier that gains + // the needed ordering, together with control dependency on entering the fast codelet + // itself. + __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); __ sb(bc_reg, at_bcp(0)); __ bind(L_patch_done); } @@ -3029,6 +3034,7 @@ void TemplateTable::fast_storefield(TosState state) { // X11: field offset, X12: field holder, X13: flags load_resolved_field_entry(x12, x12, noreg, x11, x13); + __ verify_field_offset(x11); { Label notVolatile; @@ -3116,6 +3122,8 @@ void TemplateTable::fast_accessfield(TosState state) { __ load_field_entry(x12, x11); __ load_sized_value(x11, Address(x12, in_bytes(ResolvedFieldEntry::field_offset_offset())), sizeof(int), true /*is_signed*/); + __ verify_field_offset(x11); + __ load_unsigned_byte(x13, Address(x12, in_bytes(ResolvedFieldEntry::flags_offset()))); // x10: object @@ -3171,7 +3179,9 @@ void TemplateTable::fast_xaccess(TosState state) { __ ld(x10, aaddress(0)); // access constant pool cache __ load_field_entry(x12, x13, 2); + __ load_sized_value(x11, Address(x12, in_bytes(ResolvedFieldEntry::field_offset_offset())), sizeof(int), true /*is_signed*/); + __ verify_field_offset(x11); // make sure exception is reported in correct bcp range (getfield is // next instruction) diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp index 9d6146a8389..6f4babc872f 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp @@ -103,17 +103,6 @@ void VM_Version::common_initialize() { useRVA23U64Profile(); } - // Enable vendor specific features - - if (mvendorid.enabled()) { - // Rivos - if (mvendorid.value() == RIVOS) { - if (FLAG_IS_DEFAULT(UseConservativeFence)) { - FLAG_SET_DEFAULT(UseConservativeFence, false); - } - } - } - if (UseZic64b) { if (CacheLineSize != 64) { assert(!FLAG_IS_DEFAULT(CacheLineSize), "default cache line size should be 64 bytes"); @@ -199,7 +188,7 @@ void VM_Version::common_initialize() { FLAG_SET_DEFAULT(UsePopCountInstruction, false); } - if (UseZicboz && zicboz_block_size.enabled() && zicboz_block_size.value() > 0) { + if (UseZicboz && zicboz_block_size.value() > 0) { assert(is_power_of_2(zicboz_block_size.value()), "Sanity"); if (FLAG_IS_DEFAULT(UseBlockZeroing)) { FLAG_SET_DEFAULT(UseBlockZeroing, true); diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index 346ca35dc1e..f74992cbc37 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -52,53 +52,19 @@ class VM_Version : public Abstract_VM_Version { const char* const _pretty; const bool _feature_string; const uint64_t _linux_feature_bit; - int64_t _value; + public: RVFeatureValue(const char* pretty, int linux_bit_num, bool fstring) : - _pretty(pretty), _feature_string(fstring), _linux_feature_bit(nth_bit(linux_bit_num)), - _value(-1) { - } - virtual void enable_feature(int64_t value = 0) { - _value = value; - } - virtual void disable_feature() { - _value = -1; + _pretty(pretty), _feature_string(fstring), _linux_feature_bit(nth_bit(linux_bit_num)) { } + virtual void enable_feature(int64_t value = 0) = 0; + virtual void disable_feature() = 0; const char* pretty() { return _pretty; } uint64_t feature_bit() { return _linux_feature_bit; } bool feature_string() { return _feature_string; } - int64_t value() { return _value; } virtual bool enabled() = 0; virtual void update_flag() = 0; - - protected: - bool deps_all_enabled(RVFeatureValue* dep0, ...) { - assert(dep0 != nullptr, "must not"); - - va_list va; - va_start(va, dep0); - RVFeatureValue* next = dep0; - bool enabled = true; - while (next != nullptr && enabled) { - enabled = next->enabled(); - next = va_arg(va, RVFeatureValue*); - } - va_end(va); - return enabled; - } - - void deps_string(stringStream& ss, RVFeatureValue* dep0, ...) { - assert(dep0 != nullptr, "must not"); - ss.print("%s (%s)", dep0->pretty(), dep0->enabled() ? "enabled" : "disabled"); - - va_list va; - va_start(va, dep0); - RVFeatureValue* next = nullptr; - while ((next = va_arg(va, RVFeatureValue*)) != nullptr) { - ss.print(", %s (%s)", next->pretty(), next->enabled() ? "enabled" : "disabled"); - } - va_end(va); - } + virtual void log_enabled() = 0; }; #define UPDATE_DEFAULT(flag) \ @@ -117,8 +83,9 @@ class VM_Version : public Abstract_VM_Version { #define UPDATE_DEFAULT_DEP(flag, dep0, ...) \ void update_flag() { \ assert(enabled(), "Must be."); \ + DEBUG_ONLY(verify_deps(dep0, ##__VA_ARGS__)); \ if (FLAG_IS_DEFAULT(flag)) { \ - if (this->deps_all_enabled(dep0, ##__VA_ARGS__)) { \ + if (deps_all_enabled(dep0, ##__VA_ARGS__)) { \ FLAG_SET_DEFAULT(flag, true); \ } else { \ FLAG_SET_DEFAULT(flag, false); \ @@ -149,40 +116,96 @@ class VM_Version : public Abstract_VM_Version { class RVExtFeatureValue : public RVFeatureValue { const uint32_t _cpu_feature_index; + public: RVExtFeatureValue(const char* pretty, int linux_bit_num, uint32_t cpu_feature_index, bool fstring) : RVFeatureValue(pretty, linux_bit_num, fstring), _cpu_feature_index(cpu_feature_index) { } + int cpu_feature_index() { + // Can be used to check, for example, v is declared before Zvfh in RV_EXT_FEATURE_FLAGS. + return _cpu_feature_index; + } bool enabled() { return RVExtFeatures::current()->support_feature(_cpu_feature_index); } void enable_feature(int64_t value = 0) { - RVFeatureValue::enable_feature(value); RVExtFeatures::current()->set_feature(_cpu_feature_index); } void disable_feature() { - RVFeatureValue::disable_feature(); RVExtFeatures::current()->clear_feature(_cpu_feature_index); } + void log_enabled(); + + protected: + bool deps_all_enabled(RVExtFeatureValue* dep0, ...) { + assert(dep0 != nullptr, "must not"); + + va_list va; + va_start(va, dep0); + RVExtFeatureValue* next = dep0; + bool enabled = true; + while (next != nullptr && enabled) { + enabled = next->enabled(); + next = va_arg(va, RVExtFeatureValue*); + } + va_end(va); + return enabled; + } + + void deps_string(stringStream& ss, RVExtFeatureValue* dep0, ...) { + assert(dep0 != nullptr, "must not"); + ss.print("%s (%s)", dep0->pretty(), dep0->enabled() ? "enabled" : "disabled"); + + va_list va; + va_start(va, dep0); + RVExtFeatureValue* next = nullptr; + while ((next = va_arg(va, RVExtFeatureValue*)) != nullptr) { + ss.print(", %s (%s)", next->pretty(), next->enabled() ? "enabled" : "disabled"); + } + va_end(va); + } + +#ifdef ASSERT + void verify_deps(RVExtFeatureValue* dep0, ...) { + assert(dep0 != nullptr, "must not"); + assert(cpu_feature_index() >= 0, "must"); + + va_list va; + va_start(va, dep0); + RVExtFeatureValue* next = dep0; + while (next != nullptr) { + assert(next->cpu_feature_index() >= 0, "must"); + // We only need to check depenency relationship for extension flags. + // The dependant ones must be declared before this, for example, v must be declared + // before Zvfh in RV_EXT_FEATURE_FLAGS. The reason is in setup_cpu_available_features + // we need to make sure v is `update_flag`ed before Zvfh, so Zvfh is `update_flag`ed + // based on v. + assert(cpu_feature_index() > next->cpu_feature_index(), "Invalid"); + next = va_arg(va, RVExtFeatureValue*); + } + va_end(va); + } +#endif // ASSERT }; class RVNonExtFeatureValue : public RVFeatureValue { - bool _enabled; + static const int64_t DEFAULT_VALUE = -1; + int64_t _value; + public: RVNonExtFeatureValue(const char* pretty, int linux_bit_num, bool fstring) : RVFeatureValue(pretty, linux_bit_num, fstring), - _enabled(false) { + _value(DEFAULT_VALUE) { } - bool enabled() { return _enabled; } - void enable_feature(int64_t value = 0) { - RVFeatureValue::enable_feature(value); - _enabled = true; - } - void disable_feature() { - RVFeatureValue::disable_feature(); - _enabled = false; + bool enabled() { return _value != DEFAULT_VALUE; } + void enable_feature(int64_t value) { + assert(value != DEFAULT_VALUE, "Sanity"); + _value = value; } + void disable_feature() { _value = DEFAULT_VALUE; } + int64_t value() { return _value; } + void log_enabled(); }; public: @@ -282,14 +305,14 @@ class VM_Version : public Abstract_VM_Version { decl(marchid , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ /* A unique encoding of the version of the processor implementation. */ \ decl(mimpid , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ + /* Manufactory JEDEC id encoded, ISA vol 2 3.1.2.. */ \ + decl(mvendorid , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ /* SATP bits (number of virtual addr bits) mbare, sv39, sv48, sv57, sv64 */ \ decl(satp_mode , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ /* Performance of misaligned scalar accesses (unknown, emulated, slow, fast, unsupported) */ \ decl(unaligned_scalar , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ /* Performance of misaligned vector accesses (unknown, unspported, slow, fast) */ \ decl(unaligned_vector , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ - /* Manufactory JEDEC id encoded, ISA vol 2 3.1.2.. */ \ - decl(mvendorid , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ decl(zicboz_block_size , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ #define DECLARE_RV_NON_EXT_FEATURE(PRETTY, LINUX_BIT, FSTRING, FLAGF) \ diff --git a/src/hotspot/cpu/s390/gc/g1/g1_s390.ad b/src/hotspot/cpu/s390/gc/g1/g1_s390.ad index 7aed374fdae..000ac3bc5ba 100644 --- a/src/hotspot/cpu/s390/gc/g1/g1_s390.ad +++ b/src/hotspot/cpu/s390/gc/g1/g1_s390.ad @@ -356,7 +356,7 @@ instruct g1CompareAndExchangeP(iRegP mem_ptr, rarg5RegP oldval, iRegP_N2P newval __ z_lgr($res$$Register, $oldval$$Register); // previous content - __ z_csg($oldval$$Register, $newval$$Register, 0, $mem_ptr$$reg); + __ z_csg($res$$Register, $newval$$Register, 0, $mem_ptr$$reg); write_barrier_post(masm, this, $mem_ptr$$Register /* store_addr */, diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index cfc8b19534b..ab991896b53 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/gc/shared/barrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp index 8eafa486ac0..97dfccc0e7c 100644 --- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp @@ -486,33 +486,33 @@ void SaveLiveRegisters::initialize(BarrierStubC2* stub) { // Create mask of caller saved registers that need to // be saved/restored if live RegMask caller_saved; - caller_saved.Insert(OptoReg::as_OptoReg(rax->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(rcx->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(rdx->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(rsi->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(rdi->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r8->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r9->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r10->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r11->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(rax->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(rcx->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(rdx->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(rsi->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(rdi->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r8->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r9->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r10->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r11->as_VMReg())); if (UseAPX) { - caller_saved.Insert(OptoReg::as_OptoReg(r16->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r17->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r18->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r19->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r20->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r21->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r22->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r23->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r24->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r25->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r26->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r27->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r28->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r29->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r30->as_VMReg())); - caller_saved.Insert(OptoReg::as_OptoReg(r31->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r16->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r17->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r18->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r19->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r20->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r21->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r22->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r23->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r24->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r25->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r26->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r27->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r28->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r29->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r30->as_VMReg())); + caller_saved.insert(OptoReg::as_OptoReg(r31->as_VMReg())); } int gp_spill_size = 0; @@ -526,7 +526,7 @@ void SaveLiveRegisters::initialize(BarrierStubC2* stub) { const VMReg vm_reg = OptoReg::as_VMReg(opto_reg); if (vm_reg->is_Register()) { - if (caller_saved.Member(opto_reg)) { + if (caller_saved.member(opto_reg)) { _gp_registers.append(vm_reg->as_Register()); gp_spill_size += 8; } diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index a9af9109799..8700a8352d5 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -10439,10 +10439,9 @@ void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Registe movptr(tmp, reg_rax); andptr(tmp, ~(int32_t)markWord::unlocked_value); orptr(reg_rax, markWord::unlocked_value); - if (EnableValhalla) { - // Mask inline_type bit such that we go to the slow path if object is an inline type - andptr(reg_rax, ~((int) markWord::inline_type_bit_in_place)); - } + // Mask inline_type bit such that we go to the slow path if object is an inline type + andptr(reg_rax, ~((int) markWord::inline_type_bit_in_place)); + lock(); cmpxchgptr(tmp, Address(obj, oopDesc::mark_offset_in_bytes())); jcc(Assembler::notEqual, slow); diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index f3ac392e542..01401b3cbbb 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -497,96 +497,96 @@ 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())); - _ANY_REG_mask.Remove(OptoReg::as_OptoReg(rbp->as_VMReg()->next())); + _ANY_REG_mask.remove(OptoReg::as_OptoReg(rbp->as_VMReg())); + _ANY_REG_mask.remove(OptoReg::as_OptoReg(rbp->as_VMReg()->next())); } if (need_r12_heapbase()) { - _ANY_REG_mask.Remove(OptoReg::as_OptoReg(r12->as_VMReg())); - _ANY_REG_mask.Remove(OptoReg::as_OptoReg(r12->as_VMReg()->next())); + _ANY_REG_mask.remove(OptoReg::as_OptoReg(r12->as_VMReg())); + _ANY_REG_mask.remove(OptoReg::as_OptoReg(r12->as_VMReg()->next())); } - _PTR_REG_mask = _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())); - _PTR_REG_mask.Remove(OptoReg::as_OptoReg(r15->as_VMReg()->next())); + _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())); + _PTR_REG_mask.remove(OptoReg::as_OptoReg(r15->as_VMReg()->next())); if (!UseAPX) { for (uint i = 0; i < sizeof(egprs)/sizeof(Register); i++) { - _PTR_REG_mask.Remove(OptoReg::as_OptoReg(egprs[i]->as_VMReg())); - _PTR_REG_mask.Remove(OptoReg::as_OptoReg(egprs[i]->as_VMReg()->next())); + _PTR_REG_mask.remove(OptoReg::as_OptoReg(egprs[i]->as_VMReg())); + _PTR_REG_mask.remove(OptoReg::as_OptoReg(egprs[i]->as_VMReg()->next())); } } - _STACK_OR_PTR_REG_mask = _PTR_REG_mask; - _STACK_OR_PTR_REG_mask.OR(STACK_OR_STACK_SLOTS_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.Remove(OptoReg::as_OptoReg(rbp->as_VMReg())); - _PTR_REG_NO_RBP_mask.Remove(OptoReg::as_OptoReg(rbp->as_VMReg()->next())); + _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.Remove(OptoReg::as_OptoReg(rax->as_VMReg())); - _PTR_NO_RAX_REG_mask.Remove(OptoReg::as_OptoReg(rax->as_VMReg()->next())); + _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.Remove(OptoReg::as_OptoReg(rbx->as_VMReg())); - _PTR_NO_RAX_RBX_REG_mask.Remove(OptoReg::as_OptoReg(rbx->as_VMReg()->next())); + _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; - _STACK_OR_LONG_REG_mask.OR(STACK_OR_STACK_SLOTS_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.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_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.Remove(OptoReg::as_OptoReg(rcx->as_VMReg())); - _LONG_NO_RCX_REG_mask.Remove(OptoReg::as_OptoReg(rcx->as_VMReg()->next())); + _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.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())); + _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())); + _INT_REG_mask.remove(OptoReg::as_OptoReg(egprs[i]->as_VMReg())); } } if (PreserveFramePointer) { - _INT_REG_mask.Remove(OptoReg::as_OptoReg(rbp->as_VMReg())); + _INT_REG_mask.remove(OptoReg::as_OptoReg(rbp->as_VMReg())); } if (need_r12_heapbase()) { - _INT_REG_mask.Remove(OptoReg::as_OptoReg(r12->as_VMReg())); + _INT_REG_mask.remove(OptoReg::as_OptoReg(r12->as_VMReg())); } - _STACK_OR_INT_REG_mask = _INT_REG_mask; - _STACK_OR_INT_REG_mask.OR(STACK_OR_STACK_SLOTS_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.Remove(OptoReg::as_OptoReg(rax->as_VMReg())); - _INT_NO_RAX_RDX_REG_mask.Remove(OptoReg::as_OptoReg(rdx->as_VMReg())); + _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.Remove(OptoReg::as_OptoReg(rcx->as_VMReg())); + _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.Remove(OptoReg::as_OptoReg(rbp->as_VMReg())); - _INT_NO_RBP_R13_REG_mask.Remove(OptoReg::as_OptoReg(r13->as_VMReg())); + _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) { @@ -761,7 +761,7 @@ static void emit_fp_min_max(MacroAssembler* masm, XMMRegister dst, } //============================================================================= -const RegMask& MachConstantBaseNode::_out_RegMask = RegMask::Empty; +const RegMask& MachConstantBaseNode::_out_RegMask = RegMask::EMPTY; int ConstantTable::calculate_table_base_offset() const { return 0; // absolute addressing, no offset @@ -1667,7 +1667,7 @@ bool Matcher::is_spillable_arg(int reg) uint Matcher::int_pressure_limit() { - return (INTPRESSURE == -1) ? _INT_REG_mask.Size() : INTPRESSURE; + return (INTPRESSURE == -1) ? _INT_REG_mask.size() : INTPRESSURE; } uint Matcher::float_pressure_limit() @@ -1675,7 +1675,7 @@ uint Matcher::float_pressure_limit() // After experiment around with different values, the following default threshold // works best for LCM's register pressure scheduling on x64. uint dec_count = VM_Version::supports_evex() ? 4 : 2; - uint default_float_pressure_threshold = _FLOAT_REG_mask.Size() - dec_count; + uint default_float_pressure_threshold = _FLOAT_REG_mask.size() - dec_count; return (FLOATPRESSURE == -1) ? default_float_pressure_threshold : FLOATPRESSURE; } @@ -1687,22 +1687,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/bsd/gc/z/zNUMA_bsd.cpp b/src/hotspot/os/bsd/gc/z/zNUMA_bsd.cpp index d0c06e2ebf1..3acaa9ab8f9 100644 --- a/src/hotspot/os/bsd/gc/z/zNUMA_bsd.cpp +++ b/src/hotspot/os/bsd/gc/z/zNUMA_bsd.cpp @@ -46,3 +46,7 @@ uint32_t ZNUMA::memory_id(uintptr_t addr) { // NUMA support not enabled, assume everything belongs to node zero return 0; } + +int ZNUMA::numa_id_to_node(uint32_t numa_id) { + ShouldNotCallThis(); +} diff --git a/src/hotspot/os/linux/gc/z/zNUMA_linux.cpp b/src/hotspot/os/linux/gc/z/zNUMA_linux.cpp index 74e69655940..ab7498b313c 100644 --- a/src/hotspot/os/linux/gc/z/zNUMA_linux.cpp +++ b/src/hotspot/os/linux/gc/z/zNUMA_linux.cpp @@ -32,12 +32,35 @@ #include "runtime/os.hpp" #include "utilities/debug.hpp" +static uint* z_numa_id_to_node = nullptr; +static uint32_t* z_node_to_numa_id = nullptr; + void ZNUMA::pd_initialize() { _enabled = UseNUMA; + size_t configured_nodes = 0; + + if (UseNUMA) { + const size_t max_nodes = os::Linux::numa_num_configured_nodes(); + z_numa_id_to_node = NEW_C_HEAP_ARRAY(uint, max_nodes, mtGC); + configured_nodes = os::numa_get_leaf_groups(z_numa_id_to_node, 0); + + z_node_to_numa_id = NEW_C_HEAP_ARRAY(uint32_t, max_nodes, mtGC); + + // Fill the array with invalid NUMA ids + for (uint32_t i = 0; i < max_nodes; i++) { + z_node_to_numa_id[i] = (uint32_t)-1; + } + + // Fill the reverse mappings + for (uint32_t i = 0; i < configured_nodes; i++) { + z_node_to_numa_id[z_numa_id_to_node[i]] = i; + } + } + // UseNUMA and is_faked() are mutually excluded in zArguments.cpp. _count = UseNUMA - ? os::Linux::numa_max_node() + 1 + ? configured_nodes : !FLAG_IS_DEFAULT(ZFakeNUMA) ? ZFakeNUMA : 1; // No NUMA nodes @@ -54,7 +77,7 @@ uint32_t ZNUMA::id() { return 0; } - return os::Linux::get_node_by_cpu(ZCPU::id()); + return z_node_to_numa_id[os::Linux::get_node_by_cpu(ZCPU::id())]; } uint32_t ZNUMA::memory_id(uintptr_t addr) { @@ -63,14 +86,21 @@ uint32_t ZNUMA::memory_id(uintptr_t addr) { return 0; } - uint32_t id = (uint32_t)-1; + int node = -1; - if (ZSyscall::get_mempolicy((int*)&id, nullptr, 0, (void*)addr, MPOL_F_NODE | MPOL_F_ADDR) == -1) { + if (ZSyscall::get_mempolicy(&node, nullptr, 0, (void*)addr, MPOL_F_NODE | MPOL_F_ADDR) == -1) { ZErrno err; fatal("Failed to get NUMA id for memory at " PTR_FORMAT " (%s)", addr, err.to_string()); } - assert(id < _count, "Invalid NUMA id"); + DEBUG_ONLY(const int max_nodes = os::Linux::numa_num_configured_nodes();) + assert(node < max_nodes, "NUMA node is out of bounds node=%d, max=%d", node, max_nodes); + + return z_node_to_numa_id[node]; +} + +int ZNUMA::numa_id_to_node(uint32_t numa_id) { + assert(numa_id < _count, "NUMA id out of range 0 <= %ud <= %ud", numa_id, _count); - return id; + return (int)z_numa_id_to_node[numa_id]; } diff --git a/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp b/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp index 84dfcbd6614..25ffd0b8078 100644 --- a/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp +++ b/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp @@ -629,7 +629,7 @@ bool ZPhysicalMemoryBacking::commit_inner(zbacking_offset offset, size_t length) size_t ZPhysicalMemoryBacking::commit_numa_preferred(zbacking_offset offset, size_t length, uint32_t numa_id) const { // Setup NUMA policy to allocate memory from a preferred node - os::Linux::numa_set_preferred((int)numa_id); + os::Linux::numa_set_preferred(ZNUMA::numa_id_to_node(numa_id)); const size_t committed = commit_default(offset, length); diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp index ed83487265c..2cc0263d291 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 714eac12d22..5833e324070 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.cpp @@ -42,6 +42,7 @@ #include "signals_posix.hpp" #include "suspendResume_posix.hpp" #include "utilities/checkedCast.hpp" +#include "utilities/deferredStatic.hpp" #include "utilities/events.hpp" #include "utilities/ostream.hpp" #include "utilities/parseInteger.hpp" @@ -167,9 +168,9 @@ static get_signal_t get_signal_action = nullptr; // suspend/resume support #if defined(__APPLE__) - static OSXSemaphore sr_semaphore; +static DeferredStatic sr_semaphore; #else - static PosixSemaphore sr_semaphore; +static DeferredStatic sr_semaphore; #endif // Signal number used to suspend/resume a thread @@ -177,7 +178,7 @@ static get_signal_t get_signal_action = nullptr; int PosixSignals::SR_signum = SIGUSR2; // sun.misc.Signal support -static Semaphore* sig_semaphore = nullptr; +static DeferredStatic sig_semaphore; // a counter for each possible signal value static volatile jint pending_signals[NSIG+1] = { 0 }; @@ -351,17 +352,16 @@ static void jdk_misc_signal_init() { ::memset((void*)pending_signals, 0, sizeof(pending_signals)); // Initialize signal semaphore - sig_semaphore = new Semaphore(); + int sem_count = 0; + sig_semaphore.initialize(sem_count); } void os::signal_notify(int sig) { - if (sig_semaphore != nullptr) { + // 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(); - } else { - // Signal thread is not created with ReduceSignalUsage and jdk_misc_signal_init - // initialization isn't called. - assert(ReduceSignalUsage, "signal semaphore should be created"); } } @@ -1696,7 +1696,7 @@ static void SR_handler(int sig, siginfo_t* siginfo, void* context) { pthread_sigmask(SIG_BLOCK, nullptr, &suspend_set); sigdelset(&suspend_set, PosixSignals::SR_signum); - sr_semaphore.signal(); + sr_semaphore->signal(); // wait here until we are resumed while (1) { @@ -1705,7 +1705,7 @@ static void SR_handler(int sig, siginfo_t* siginfo, void* context) { SuspendResume::State result = osthread->sr.running(); if (result == SuspendResume::SR_RUNNING) { // double check AIX doesn't need this! - sr_semaphore.signal(); + sr_semaphore->signal(); break; } else if (result != SuspendResume::SR_SUSPENDED) { ShouldNotReachHere(); @@ -1731,6 +1731,9 @@ static void SR_handler(int sig, siginfo_t* siginfo, void* context) { } static int SR_initialize() { + int sem_count = 0; + sr_semaphore.initialize(sem_count); + struct sigaction act; char *s; // Get signal number to use for suspend/resume @@ -1778,7 +1781,7 @@ static int sr_notify(OSThread* osthread) { // but this seems the normal response to library errors bool PosixSignals::do_suspend(OSThread* osthread) { assert(osthread->sr.is_running(), "thread should be running"); - assert(!sr_semaphore.trywait(), "semaphore has invalid state"); + assert(!sr_semaphore->trywait(), "semaphore has invalid state"); // mark as suspended and send signal if (osthread->sr.request_suspend() != SuspendResume::SR_SUSPEND_REQUEST) { @@ -1793,7 +1796,7 @@ bool PosixSignals::do_suspend(OSThread* osthread) { // managed to send the signal and switch to SUSPEND_REQUEST, now wait for SUSPENDED while (true) { - if (sr_semaphore.timedwait(2)) { + if (sr_semaphore->timedwait(2)) { break; } else { // timeout @@ -1802,7 +1805,7 @@ bool PosixSignals::do_suspend(OSThread* osthread) { return false; } else if (cancelled == SuspendResume::SR_SUSPENDED) { // make sure that we consume the signal on the semaphore as well - sr_semaphore.wait(); + sr_semaphore->wait(); break; } else { ShouldNotReachHere(); @@ -1817,7 +1820,7 @@ bool PosixSignals::do_suspend(OSThread* osthread) { void PosixSignals::do_resume(OSThread* osthread) { assert(osthread->sr.is_suspended(), "thread should be suspended"); - assert(!sr_semaphore.trywait(), "invalid semaphore state"); + assert(!sr_semaphore->trywait(), "invalid semaphore state"); if (osthread->sr.request_wakeup() != SuspendResume::SR_WAKEUP_REQUEST) { // failed to switch to WAKEUP_REQUEST @@ -1827,7 +1830,7 @@ void PosixSignals::do_resume(OSThread* osthread) { while (true) { if (sr_notify(osthread) == 0) { - if (sr_semaphore.timedwait(2)) { + if (sr_semaphore->timedwait(2)) { if (osthread->sr.is_running()) { return; } diff --git a/src/hotspot/os/windows/gc/z/zNUMA_windows.cpp b/src/hotspot/os/windows/gc/z/zNUMA_windows.cpp index dc7521dde56..e2bd6803584 100644 --- a/src/hotspot/os/windows/gc/z/zNUMA_windows.cpp +++ b/src/hotspot/os/windows/gc/z/zNUMA_windows.cpp @@ -46,3 +46,7 @@ uint32_t ZNUMA::memory_id(uintptr_t addr) { // NUMA support not enabled, assume everything belongs to node zero return 0; } + +int ZNUMA::numa_id_to_node(uint32_t numa_id) { + ShouldNotCallThis(); +} diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index ba05d390c9f..7934c9d6ffb 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 a9b2eebb7be..f815f3bd104 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/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp index 017d8a43666..ec756c44fe6 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp @@ -167,27 +167,20 @@ static bool is_set(int64_t key, uint64_t value_mask) { void RiscvHwprobe::add_features_from_query_result() { assert(rw_hwprobe_completed, "hwprobe not init yet."); - if (is_valid(RISCV_HWPROBE_KEY_MVENDORID)) { - VM_Version::mvendorid.enable_feature(query[RISCV_HWPROBE_KEY_MVENDORID].value); - } - if (is_valid(RISCV_HWPROBE_KEY_MARCHID)) { - VM_Version::marchid.enable_feature(query[RISCV_HWPROBE_KEY_MARCHID].value); - } - if (is_valid(RISCV_HWPROBE_KEY_MIMPID)) { - VM_Version::mimpid.enable_feature(query[RISCV_HWPROBE_KEY_MIMPID].value); - } + // ====== extensions ====== + // if (is_set(RISCV_HWPROBE_KEY_BASE_BEHAVIOR, RISCV_HWPROBE_BASE_BEHAVIOR_IMA)) { + VM_Version::ext_a.enable_feature(); VM_Version::ext_i.enable_feature(); VM_Version::ext_m.enable_feature(); - VM_Version::ext_a.enable_feature(); - } - if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_IMA_FD)) { - VM_Version::ext_f.enable_feature(); - VM_Version::ext_d.enable_feature(); } if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_IMA_C)) { VM_Version::ext_c.enable_feature(); } + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_IMA_FD)) { + VM_Version::ext_d.enable_feature(); + VM_Version::ext_f.enable_feature(); + } if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_IMA_V)) { // Linux signal return bug when using vector with vlen > 128b in pre 6.8.5. long major, minor, patch; @@ -202,21 +195,29 @@ void RiscvHwprobe::add_features_from_query_result() { VM_Version::ext_v.enable_feature(); } } + +#ifndef PRODUCT + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZACAS)) { + VM_Version::ext_Zacas.enable_feature(); + } +#endif if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZBA)) { VM_Version::ext_Zba.enable_feature(); } if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZBB)) { VM_Version::ext_Zbb.enable_feature(); } +#ifndef PRODUCT + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZBKB)) { + VM_Version::ext_Zbkb.enable_feature(); + } +#endif if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZBS)) { VM_Version::ext_Zbs.enable_feature(); } #ifndef PRODUCT - if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZICBOZ)) { - VM_Version::ext_Zicboz.enable_feature(); - } - if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZBKB)) { - VM_Version::ext_Zbkb.enable_feature(); + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZFA)) { + VM_Version::ext_Zfa.enable_feature(); } #endif if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZFH)) { @@ -226,15 +227,28 @@ void RiscvHwprobe::add_features_from_query_result() { VM_Version::ext_Zfhmin.enable_feature(); } #ifndef PRODUCT + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZICBOZ)) { + VM_Version::ext_Zicboz.enable_feature(); + } + // Currently tests shows that cmove using Zicond instructions will bring + // performance regression, but to get a test coverage all the time, will + // still prefer to enabling it in debug version. + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZICOND)) { + VM_Version::ext_Zicond.enable_feature(); + } + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZTSO)) { + VM_Version::ext_Ztso.enable_feature(); + } if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVBB)) { VM_Version::ext_Zvbb.enable_feature(); } -#endif -#ifndef PRODUCT if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVBC)) { VM_Version::ext_Zvbc.enable_feature(); } #endif + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVFH)) { + VM_Version::ext_Zvfh.enable_feature(); + } #ifndef PRODUCT if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVKNED) && is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVKNHB) && @@ -243,30 +257,18 @@ void RiscvHwprobe::add_features_from_query_result() { VM_Version::ext_Zvkn.enable_feature(); } #endif - if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVFH)) { - VM_Version::ext_Zvfh.enable_feature(); - } -#ifndef PRODUCT - if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZFA)) { - VM_Version::ext_Zfa.enable_feature(); - } -#endif -#ifndef PRODUCT - if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZTSO)) { - VM_Version::ext_Ztso.enable_feature(); + + // ====== non-extensions ====== + // + if (is_valid(RISCV_HWPROBE_KEY_MARCHID)) { + VM_Version::marchid.enable_feature(query[RISCV_HWPROBE_KEY_MARCHID].value); } -#endif -#ifndef PRODUCT - if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZACAS)) { - VM_Version::ext_Zacas.enable_feature(); + if (is_valid(RISCV_HWPROBE_KEY_MIMPID)) { + VM_Version::mimpid.enable_feature(query[RISCV_HWPROBE_KEY_MIMPID].value); } - // Currently tests shows that cmove using Zicond instructions will bring - // performance regression, but to get a test coverage all the time, will - // still prefer to enabling it in debug version. - if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZICOND)) { - VM_Version::ext_Zicond.enable_feature(); + if (is_valid(RISCV_HWPROBE_KEY_MVENDORID)) { + VM_Version::mvendorid.enable_feature(query[RISCV_HWPROBE_KEY_MVENDORID].value); } -#endif // RISCV_HWPROBE_KEY_CPUPERF_0 is deprecated and returns similar values // to RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF. Keep it there for backward // compatibility with old kernels. @@ -277,7 +279,6 @@ void RiscvHwprobe::add_features_from_query_result() { VM_Version::unaligned_scalar.enable_feature( query[RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF].value); } - if (is_valid(RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF)) { VM_Version::unaligned_vector.enable_feature( query[RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF].value); diff --git a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp index e414a3889c2..0799de014a9 100644 --- a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp +++ b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp @@ -103,6 +103,14 @@ uint32_t VM_Version::cpu_vector_length() { return (uint32_t)read_csr(CSR_VLENB); } +void VM_Version::RVExtFeatureValue::log_enabled() { + log_debug(os, cpu)("Enabled RV64 feature \"%s\"", pretty()); +} + +void VM_Version::RVNonExtFeatureValue::log_enabled() { + log_debug(os, cpu)("Enabled RV64 feature \"%s\" (%ld)", pretty(), value()); +} + void VM_Version::setup_cpu_available_features() { assert(ext_i.feature_bit() == HWCAP_ISA_I, "Bit for I must follow Linux HWCAP"); @@ -144,9 +152,8 @@ void VM_Version::setup_cpu_available_features() { continue; } - log_debug(os, cpu)("Enabled RV64 feature \"%s\" (%ld)", - _feature_list[i]->pretty(), - _feature_list[i]->value()); + _feature_list[i]->log_enabled(); + // The feature string if (_feature_list[i]->feature_string()) { const char* tmp = _feature_list[i]->pretty(); 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 794aa12155b..568b6e3938e 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 c188919595c..53f96479832 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/adlc/archDesc.cpp b/src/hotspot/share/adlc/archDesc.cpp index 263752c521d..2461903ea26 100644 --- a/src/hotspot/share/adlc/archDesc.cpp +++ b/src/hotspot/share/adlc/archDesc.cpp @@ -899,10 +899,12 @@ int ArchDesc::emit_msg(int quiet, int flag, int line, const char *fmt, // Construct the name of the register mask. static const char *getRegMask(const char *reg_class_name) { - if( reg_class_name == nullptr ) return "RegMask::Empty"; + if (reg_class_name == nullptr) { + return "RegMask::EMPTY"; + } if (strcmp(reg_class_name,"Universe")==0) { - return "RegMask::Empty"; + return "RegMask::EMPTY"; } else if (strcmp(reg_class_name,"stack_slots")==0) { return "(Compile::current()->FIRST_STACK_mask())"; } else if (strcmp(reg_class_name, "dynamic")==0) { @@ -920,7 +922,7 @@ static const char *getRegMask(const char *reg_class_name) { // Convert a register class name to its register mask. const char *ArchDesc::reg_class_to_reg_mask(const char *rc_name) { - const char *reg_mask = "RegMask::Empty"; + const char* reg_mask = "RegMask::EMPTY"; if( _register ) { RegClass *reg_class = _register->getRegClass(rc_name); @@ -939,7 +941,7 @@ const char *ArchDesc::reg_class_to_reg_mask(const char *rc_name) { // Obtain the name of the RegMask for an OperandForm const char *ArchDesc::reg_mask(OperandForm &opForm) { - const char *regMask = "RegMask::Empty"; + const char* regMask = "RegMask::EMPTY"; // Check constraints on result's register class const char *result_class = opForm.constrained_reg_class(); @@ -968,9 +970,9 @@ const char *ArchDesc::reg_mask(InstructForm &inForm) { abort(); } - // Instructions producing 'Universe' use RegMask::Empty + // Instructions producing 'Universe' use RegMask::EMPTY if (strcmp(result,"Universe") == 0) { - return "RegMask::Empty"; + return "RegMask::EMPTY"; } // Lookup this result operand and get its register class diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index 1d06ca97eb2..ed1421a60f5 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -2424,7 +2424,7 @@ const char *OperandForm::constrained_reg_class() const { // Return the register class associated with 'leaf'. const char *OperandForm::in_reg_class(uint leaf, FormDict &globals) { - const char *reg_class = nullptr; // "RegMask::Empty"; + const char* reg_class = nullptr; // "RegMask::EMPTY"; if((_matrule == nullptr) || (_matrule->is_chain_rule(globals))) { reg_class = constrained_reg_class(); diff --git a/src/hotspot/share/adlc/output_c.cpp b/src/hotspot/share/adlc/output_c.cpp index caf2c9952a6..110db7f0e98 100644 --- a/src/hotspot/share/adlc/output_c.cpp +++ b/src/hotspot/share/adlc/output_c.cpp @@ -2837,7 +2837,7 @@ static void defineIn_RegMask(FILE *fp, FormDict &globals, OperandForm &oper) { if (strcmp(first_reg_class, "stack_slots") == 0) { fprintf(fp," return &(Compile::current()->FIRST_STACK_mask());\n"); } else if (strcmp(first_reg_class, "dynamic") == 0) { - fprintf(fp," return &RegMask::Empty;\n"); + fprintf(fp, " return &RegMask::EMPTY;\n"); } else { const char* first_reg_class_to_upper = toUpper(first_reg_class); fprintf(fp," return &%s_mask();\n", first_reg_class_to_upper); diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp index 6cc3a81c2ae..8b4e60dece2 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/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp index e7145b25457..3653f9d518c 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp @@ -42,6 +42,8 @@ #include "oops/trainingData.hpp" #include "runtime/handles.inline.hpp" #include "runtime/java.hpp" +#include "runtime/serviceThread.hpp" +#include "utilities/growableArray.hpp" void AOTLinkedClassBulkLoader::serialize(SerializeClosure* soc) { AOTLinkedClassTable::get()->serialize(soc); @@ -53,6 +55,8 @@ void AOTLinkedClassBulkLoader::serialize(SerializeClosure* soc) { // step in restoring the JVM's state from the snapshot recorded in the AOT cache: other AOT optimizations // such as AOT compiled methods can make direct references to the preloaded classes, knowing that // these classes are guaranteed to be in at least the "loaded" state. +// +// Note: we can't link the classes yet because SharedRuntime is not yet ready to generate adapters. void AOTLinkedClassBulkLoader::preload_classes(JavaThread* current) { preload_classes_impl(current); if (current->has_pending_exception()) { @@ -112,6 +116,44 @@ void AOTLinkedClassBulkLoader::preload_classes_in_table(Array* c } } +// Some cached heap objects may hold references to methods in aot-linked +// classes (via MemberName). We need to make sure all classes are +// linked before executing any bytecode. +void AOTLinkedClassBulkLoader::link_classes(JavaThread* current) { + link_classes_impl(current); + if (current->has_pending_exception()) { + exit_on_exception(current); + } +} + +void AOTLinkedClassBulkLoader::link_classes_impl(TRAPS) { + precond(CDSConfig::is_using_aot_linked_classes()); + + AOTLinkedClassTable* table = AOTLinkedClassTable::get(); + + link_classes_in_table(table->boot1(), CHECK); + link_classes_in_table(table->boot2(), CHECK); + link_classes_in_table(table->platform(), CHECK); + link_classes_in_table(table->app(), CHECK); +} + +void AOTLinkedClassBulkLoader::link_classes_in_table(Array* classes, TRAPS) { + if (classes != nullptr) { + for (int i = 0; i < classes->length(); i++) { + // NOTE: CDSConfig::is_preserving_verification_constraints() is required + // when storing ik in the AOT cache. This means we don't have to verify + // ik at all. + // + // Without is_preserving_verification_constraints(), ik->link_class() may cause + // class loading, which may result in invocation of ClassLoader::loadClass() calls, + // which CANNOT happen because we are not ready to execute any Java byecodes yet + // at this point. + InstanceKlass* ik = classes->at(i); + ik->link_class(CHECK); + } + } +} + #ifdef ASSERT void AOTLinkedClassBulkLoader::validate_module_of_preloaded_classes() { oop javabase_module_oop = ModuleEntryTable::javabase_moduleEntry()->module_oop(); @@ -173,25 +215,21 @@ void AOTLinkedClassBulkLoader::validate_module(Klass* k, const char* category_na } #endif -// Link all java.base classes in the AOTLinkedClassTable. Of those classes, -// move the ones that have been AOT-initialized to the "initialized" state. -void AOTLinkedClassBulkLoader::link_or_init_javabase_classes(JavaThread* current) { - link_or_init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot1(), current); +void AOTLinkedClassBulkLoader::init_javabase_classes(JavaThread* current) { + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot1(), current); if (current->has_pending_exception()) { exit_on_exception(current); } } -// Do the same thing as link_or_init_javabase_classes(), but for the classes that are not -// in the java.base module. -void AOTLinkedClassBulkLoader::link_or_init_non_javabase_classes(JavaThread* current) { - link_or_init_non_javabase_classes_impl(current); +void AOTLinkedClassBulkLoader::init_non_javabase_classes(JavaThread* current) { + init_non_javabase_classes_impl(current); if (current->has_pending_exception()) { exit_on_exception(current); } } -void AOTLinkedClassBulkLoader::link_or_init_non_javabase_classes_impl(TRAPS) { +void AOTLinkedClassBulkLoader::init_non_javabase_classes_impl(TRAPS) { assert(CDSConfig::is_using_aot_linked_classes(), "sanity"); DEBUG_ONLY(validate_module_of_preloaded_classes()); @@ -208,9 +246,9 @@ void AOTLinkedClassBulkLoader::link_or_init_non_javabase_classes_impl(TRAPS) { assert(h_system_loader() != nullptr, "must be"); AOTLinkedClassTable* table = AOTLinkedClassTable::get(); - link_or_init_classes_for_loader(Handle(), table->boot2(), CHECK); - link_or_init_classes_for_loader(h_platform_loader, table->platform(), CHECK); - link_or_init_classes_for_loader(h_system_loader, table->app(), CHECK); + init_classes_for_loader(Handle(), table->boot2(), CHECK); + init_classes_for_loader(h_platform_loader, table->platform(), CHECK); + init_classes_for_loader(h_system_loader, table->app(), CHECK); if (Universe::is_fully_initialized() && VerifyDuringStartup) { // Make sure we're still in a clean state. @@ -242,8 +280,9 @@ void AOTLinkedClassBulkLoader::exit_on_exception(JavaThread* current) { log_error(aot)("Out of memory. Please run with a larger Java heap, current MaxHeapSize = " "%zuM", MaxHeapSize/M); } else { + oop message = java_lang_Throwable::message(current->pending_exception()); log_error(aot)("%s: %s", current->pending_exception()->klass()->external_name(), - java_lang_String::as_utf8_string(java_lang_Throwable::message(current->pending_exception()))); + message == nullptr ? "(no message)" : java_lang_String::as_utf8_string(message)); } vm_exit_during_initialization("Unexpected exception when loading aot-linked classes."); } @@ -289,23 +328,13 @@ void AOTLinkedClassBulkLoader::initiate_loading(JavaThread* current, const char* // - classes that were AOT-initialized by AOTClassInitializer // - the classes of all objects that are reachable from the archived mirrors of // the AOT-linked classes for . -void AOTLinkedClassBulkLoader::link_or_init_classes_for_loader(Handle class_loader, Array* classes, TRAPS) { +void AOTLinkedClassBulkLoader::init_classes_for_loader(Handle class_loader, Array* classes, TRAPS) { if (classes != nullptr) { for (int i = 0; i < classes->length(); i++) { InstanceKlass* ik = classes->at(i); - if (ik->class_loader_data() == nullptr) { - // This class is not yet loaded. We will initialize it in a later phase. - // For example, we have loaded only AOTLinkedClassCategory::BOOT1 classes - // but k is part of AOTLinkedClassCategory::BOOT2. - continue; - } + assert(ik->class_loader_data() != nullptr, "must be"); if (ik->has_aot_initialized_mirror()) { ik->initialize_with_aot_initialized_mirror(CHECK); - } else { - // Some cached heap objects may hold references to methods in aot-linked - // classes (via MemberName). We need to make sure all classes are - // linked to allow such MemberNames to be invoked. - ik->link_class(CHECK); } } } diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp index 77400a86104..31fdac386fe 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp @@ -52,10 +52,11 @@ class AOTLinkedClassBulkLoader : AllStatic { static void preload_classes_impl(TRAPS); static void preload_classes_in_table(Array* classes, const char* category_name, Handle loader, TRAPS); - static void initiate_loading(JavaThread* current, const char* category, Handle initiating_loader, - Array* classes); - static void link_or_init_non_javabase_classes_impl(TRAPS); - static void link_or_init_classes_for_loader(Handle class_loader, Array* classes, TRAPS); + static void initiate_loading(JavaThread* current, const char* category, Handle initiating_loader, Array* classes); + static void link_classes_impl(TRAPS); + static void link_classes_in_table(Array* classes, TRAPS); + static void init_non_javabase_classes_impl(TRAPS); + static void init_classes_for_loader(Handle class_loader, Array* classes, TRAPS); static void replay_training_at_init(Array* classes, TRAPS) NOT_CDS_RETURN; #ifdef ASSERT @@ -67,9 +68,10 @@ class AOTLinkedClassBulkLoader : AllStatic { public: static void serialize(SerializeClosure* soc) NOT_CDS_RETURN; - static void preload_classes(JavaThread* current); - static void link_or_init_javabase_classes(JavaThread* current) NOT_CDS_RETURN; - static void link_or_init_non_javabase_classes(JavaThread* current) NOT_CDS_RETURN; + static void preload_classes(JavaThread* current) NOT_CDS_RETURN; + static void link_classes(JavaThread* current) NOT_CDS_RETURN; + static void init_javabase_classes(JavaThread* current) NOT_CDS_RETURN; + static void init_non_javabase_classes(JavaThread* current) NOT_CDS_RETURN; static void exit_on_exception(JavaThread* current); static void replay_training_at_init_for_preloaded_classes(TRAPS) NOT_CDS_RETURN; diff --git a/src/hotspot/share/cds/aotMapLogger.cpp b/src/hotspot/share/cds/aotMapLogger.cpp index b0e410b5cf1..151c15048c2 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/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 8d4045f4560..dcfc9947b84 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -964,8 +964,9 @@ bool CDSConfig::is_preserving_verification_constraints() { return AOTClassLinking; } else if (is_dumping_final_static_archive()) { // writing AOT cache return is_dumping_aot_linked_classes(); + } else if (is_dumping_classic_static_archive()) { + return is_dumping_aot_linked_classes(); } else { - // For simplicity, we don't support this optimization with the old CDS workflow. return false; } } diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index dfe74acd6c1..a9bbc398736 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/cds/runTimeClassInfo.cpp b/src/hotspot/share/cds/runTimeClassInfo.cpp index 832b0ce8932..fe940ca6c18 100644 --- a/src/hotspot/share/cds/runTimeClassInfo.cpp +++ b/src/hotspot/share/cds/runTimeClassInfo.cpp @@ -41,7 +41,7 @@ void RunTimeClassInfo::init(DumpTimeClassInfo& info) { _num_loader_constraints = info.num_loader_constraints(); int i; - if (CDSConfig::is_preserving_verification_constraints() && CDSConfig::is_dumping_final_static_archive()) { + if (CDSConfig::is_preserving_verification_constraints()) { // The production run doesn't need the verifier constraints, as we can guarantee that all classes checked by // the verifier during AOT training/assembly phases cannot be replaced in the production run. _num_verifier_constraints = 0; diff --git a/src/hotspot/share/classfile/stackMapTable.cpp b/src/hotspot/share/classfile/stackMapTable.cpp index b0da0e7b3ec..e4cf4083fa5 100644 --- a/src/hotspot/share/classfile/stackMapTable.cpp +++ b/src/hotspot/share/classfile/stackMapTable.cpp @@ -133,8 +133,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 e2dd9ad4a4d..8dd00315a6a 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/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 9d4d414fa7c..c5d76fc9066 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -211,9 +211,8 @@ ClassLoaderData* SystemDictionary::register_loader(Handle class_loader, bool cre if (class_loader() == nullptr) { return ClassLoaderData::the_null_class_loader_data(); } else { - bool Bug8370217_FIXED = false; ClassLoaderData* cld = ClassLoaderDataGraph::find_or_create(class_loader); - if (EnableValhalla && Bug8370217_FIXED) { + if (Arguments::enable_preview() && EnableValhalla) { add_migrated_value_classes(cld); } return cld; diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index b092e71f4e7..cb2ae96348e 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -855,6 +855,28 @@ class UnregisteredClassesDuplicationChecker : StackObj { } }; +void SystemDictionaryShared::link_all_exclusion_check_candidates(InstanceKlass* ik) { + bool need_to_link = false; + { + MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); + ExclusionCheckCandidates candidates(ik); + + candidates.iterate_all([&] (InstanceKlass* k, DumpTimeClassInfo* info) { + if (!k->is_linked()) { + need_to_link = true; + } + }); + } + if (need_to_link) { + JavaThread* THREAD = JavaThread::current(); + if (log_is_enabled(Info, aot, link)) { + ResourceMark rm(THREAD); + log_info(aot, link)("Link all loaded classes for %s", ik->external_name()); + } + AOTMetaspace::link_all_loaded_classes(THREAD); + } +} + // Returns true if the class should be excluded. This can be called by // AOTConstantPoolResolver before or after we enter the CDS safepoint. // When called before the safepoint, we need to link the class so that @@ -878,27 +900,19 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) { InstanceKlass* ik = InstanceKlass::cast(k); if (!SafepointSynchronize::is_at_safepoint()) { - if (!ik->is_linked()) { - // should_be_excluded_impl() below doesn't link unlinked classes. We come - // here only when we are trying to aot-link constant pool entries, so - // we'd better link the class. - JavaThread* THREAD = JavaThread::current(); - ik->link_class(THREAD); - if (HAS_PENDING_EXCEPTION) { - CLEAR_PENDING_EXCEPTION; - return true; // linking failed -- let's exclude it + { + // fast path + MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); + DumpTimeClassInfo* p = get_info_locked(ik); + if (p->has_checked_exclusion()) { + return p->is_excluded(); } - - // Also link any classes that were loaded for the verification of ik or its supertypes. - // Otherwise we might miss the verification constraints of those classes. - AOTMetaspace::link_all_loaded_classes(THREAD); } + link_all_exclusion_check_candidates(ik); + MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); DumpTimeClassInfo* p = get_info_locked(ik); - if (p->is_excluded()) { - return true; - } return should_be_excluded_impl(ik, p); } else { // When called within the CDS safepoint, the correctness of this function @@ -912,7 +926,7 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) { // No need to check for is_linked() as all eligible classes should have // already been linked in AOTMetaspace::link_class_for_cds(). - // Can't take the lock as we are in safepoint. + // Don't take DumpTimeTable_lock as we are in safepoint. DumpTimeClassInfo* p = _dumptime_table->get(ik); if (p->is_excluded()) { return true; @@ -1420,6 +1434,10 @@ void SystemDictionaryShared::get_all_archived_classes(bool is_static_archive, Gr get_archive(is_static_archive)->_builtin_dictionary.iterate([&] (const RunTimeClassInfo* record) { classes->append(record->klass()); }); + + get_archive(is_static_archive)->_unregistered_dictionary.iterate([&] (const RunTimeClassInfo* record) { + classes->append(record->klass()); + }); } class SharedDictionaryPrinter : StackObj { diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index 5ff57653dd0..2619a642fd1 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -175,6 +175,7 @@ class SystemDictionaryShared: public SystemDictionary { static void write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin); static bool is_jfr_event_class(InstanceKlass *k); + static void link_all_exclusion_check_candidates(InstanceKlass* ik); static bool should_be_excluded_impl(InstanceKlass* k, DumpTimeClassInfo* info); // exclusion checks diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 8c0c72b0170..2598ab52308 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -816,7 +816,6 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) { // Merge with the next instruction { - int target; VerificationType type, type2; VerificationType atype; @@ -1641,9 +1640,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 : @@ -1654,19 +1652,16 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) { case Bytecodes::_ifnonnull : current_frame.pop_stack( object_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::_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 : @@ -2312,15 +2307,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/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index ef2977d5090..6837041d94a 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -23,6 +23,7 @@ */ #include "asm/assembler.inline.hpp" +#include "cds/cdsConfig.hpp" #include "code/codeCache.hpp" #include "code/compiledIC.hpp" #include "code/dependencies.hpp" @@ -1158,7 +1159,7 @@ nmethod* nmethod::new_nmethod(const methodHandle& method, + align_up(speculations_len , oopSize) #endif + align_up(debug_info->data_size() , oopSize) - + align_up(ImmutableDataReferencesCounterSize, oopSize); + + ImmutableDataReferencesCounterSize; // First, allocate space for immutable data in C heap. address immutable_data = nullptr; @@ -1337,6 +1338,7 @@ nmethod::nmethod( #if INCLUDE_JVMCI _speculations_offset = 0; #endif + _immutable_data_reference_counter_offset = 0; code_buffer->copy_code_and_locs_to(this); code_buffer->copy_values_to(this); @@ -1435,15 +1437,6 @@ nmethod::nmethod(const nmethod &nm) : CodeBlob(nm._name, nm._kind, nm._size, nm. _method = nm._method; _osr_link = nullptr; - // Increment number of references to immutable data to share it between nmethods - _immutable_data_size = nm._immutable_data_size; - if (_immutable_data_size > 0) { - _immutable_data = nm._immutable_data; - set_immutable_data_references_counter(get_immutable_data_references_counter() + 1); - } else { - _immutable_data = blob_end(); - } - _exception_cache = nullptr; _gc_data = nullptr; _oops_do_mark_nmethods = nullptr; @@ -1459,6 +1452,7 @@ nmethod::nmethod(const nmethod &nm) : CodeBlob(nm._name, nm._kind, nm._size, nm. _entry_offset = nm._entry_offset; _verified_entry_offset = nm._verified_entry_offset; _entry_bci = nm._entry_bci; + _immutable_data_size = nm._immutable_data_size; _skipped_instructions_size = nm._skipped_instructions_size; _stub_offset = nm._stub_offset; @@ -1477,6 +1471,15 @@ nmethod::nmethod(const nmethod &nm) : CodeBlob(nm._name, nm._kind, nm._size, nm. #if INCLUDE_JVMCI _speculations_offset = nm._speculations_offset; #endif + _immutable_data_reference_counter_offset = nm._immutable_data_reference_counter_offset; + + // Increment number of references to immutable data to share it between nmethods + if (_immutable_data_size > 0) { + _immutable_data = nm._immutable_data; + set_immutable_data_references_counter(get_immutable_data_references_counter() + 1); + } else { + _immutable_data = blob_end(); + } _orig_pc_offset = nm._orig_pc_offset; _compile_id = nm._compile_id; @@ -1770,9 +1773,11 @@ nmethod::nmethod( #if INCLUDE_JVMCI _speculations_offset = _scopes_data_offset + align_up(debug_info->data_size(), oopSize); - DEBUG_ONLY( int immutable_data_end_offset = _speculations_offset + align_up(speculations_len, oopSize) + align_up(ImmutableDataReferencesCounterSize, oopSize); ) + _immutable_data_reference_counter_offset = _speculations_offset + align_up(speculations_len, oopSize); + DEBUG_ONLY( int immutable_data_end_offset = _immutable_data_reference_counter_offset + ImmutableDataReferencesCounterSize; ) #else - DEBUG_ONLY( int immutable_data_end_offset = _scopes_data_offset + align_up(debug_info->data_size(), oopSize) + align_up(ImmutableDataReferencesCounterSize, oopSize); ) + _immutable_data_reference_counter_offset = _scopes_data_offset + align_up(debug_info->data_size(), oopSize); + DEBUG_ONLY( int immutable_data_end_offset = _immutable_data_reference_counter_offset + ImmutableDataReferencesCounterSize; ) #endif assert(immutable_data_end_offset <= immutable_data_size, "wrong read-only data size: %d > %d", immutable_data_end_offset, immutable_data_size); @@ -2519,11 +2524,48 @@ void nmethod::post_compiled_method(CompileTask* task) { maybe_print_nmethod(directive); } +#if INCLUDE_CDS +static GrowableArrayCHeap* _delayed_compiled_method_load_events = nullptr; + +void nmethod::add_delayed_compiled_method_load_event(nmethod* nm) { + precond(CDSConfig::is_using_aot_linked_classes()); + precond(!ServiceThread::has_started()); + + // We are still in single threaded stage of VM bootstrap. No need to lock. + if (_delayed_compiled_method_load_events == nullptr) { + _delayed_compiled_method_load_events = new GrowableArrayCHeap(); + } + _delayed_compiled_method_load_events->append(nm); +} + +void nmethod::post_delayed_compiled_method_load_events() { + precond(ServiceThread::has_started()); + if (_delayed_compiled_method_load_events != nullptr) { + for (int i = 0; i < _delayed_compiled_method_load_events->length(); i++) { + nmethod* nm = _delayed_compiled_method_load_events->at(i); + nm->post_compiled_method_load_event(); + } + delete _delayed_compiled_method_load_events; + _delayed_compiled_method_load_events = nullptr; + } +} +#endif + // ------------------------------------------------------------------ // post_compiled_method_load_event // new method for install_code() path // Transfer information from compilation to jvmti void nmethod::post_compiled_method_load_event(JvmtiThreadState* state) { +#if INCLUDE_CDS + if (!ServiceThread::has_started()) { + // With AOT-linked classes, we could compile wrappers for native methods before the + // ServiceThread has been started, so we must delay the events to be posted later. + assert(state == nullptr, "must be"); + add_delayed_compiled_method_load_event(this); + return; + } +#endif + // This is a bad time for a safepoint. We don't want // this nmethod to get unloaded while we're queueing the event. NoSafepointVerifier nsv; diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index c2164913612..b26520a9471 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -255,6 +255,7 @@ class nmethod : public CodeBlob { #if INCLUDE_JVMCI int _speculations_offset; #endif + int _immutable_data_reference_counter_offset; // location in frame (offset for sp) that deopt can store the original // pc during a deopt. @@ -651,12 +652,11 @@ class nmethod : public CodeBlob { #if INCLUDE_JVMCI address scopes_data_end () const { return _immutable_data + _speculations_offset ; } address speculations_begin () const { return _immutable_data + _speculations_offset ; } - address speculations_end () const { return immutable_data_end() - ImmutableDataReferencesCounterSize ; } + address speculations_end () const { return _immutable_data + _immutable_data_reference_counter_offset ; } #else - address scopes_data_end () const { return immutable_data_end() - ImmutableDataReferencesCounterSize ; } + address scopes_data_end () const { return _immutable_data + _immutable_data_reference_counter_offset ; } #endif - - address immutable_data_references_counter_begin () const { return immutable_data_end() - ImmutableDataReferencesCounterSize ; } + address immutable_data_references_counter_begin () const { return _immutable_data + _immutable_data_reference_counter_offset ; } // Sizes int immutable_data_size() const { return _immutable_data_size; } @@ -983,6 +983,8 @@ class nmethod : public CodeBlob { inline int get_immutable_data_references_counter() { return *((int*)immutable_data_references_counter_begin()); } inline void set_immutable_data_references_counter(int count) { *((int*)immutable_data_references_counter_begin()) = count; } + static void add_delayed_compiled_method_load_event(nmethod* nm) NOT_CDS_RETURN; + public: // ScopeDesc retrieval operation PcDesc* pc_desc_at(address pc) { return find_pc_desc(pc, false); } @@ -1017,6 +1019,9 @@ class nmethod : public CodeBlob { // Avoid hiding of parent's 'decode(outputStream*)' method. void decode(outputStream* st) const { decode2(st); } // just delegate here. + // AOT cache support + static void post_delayed_compiled_method_load_events() NOT_CDS_RETURN; + // printing support void print_on_impl(outputStream* st) const; void print_code(); diff --git a/src/hotspot/share/compiler/compilerOracle.cpp b/src/hotspot/share/compiler/compilerOracle.cpp index 868ae8bfa41..23bb754f432 100644 --- a/src/hotspot/share/compiler/compilerOracle.cpp +++ b/src/hotspot/share/compiler/compilerOracle.cpp @@ -617,18 +617,44 @@ static void usage() { tty->cr(); print_commands(); tty->cr(); - tty->print_cr("Method patterns has the format:"); - tty->print_cr(" package/Class.method()"); + tty->print_cr("The has the format '.'."); + tty->cr(); + tty->print_cr("For example, the "); + tty->cr(); + tty->print_cr(" package/Class.method(Lpackage/Parameter;)Lpackage/Return;"); + tty->cr(); + tty->print_cr("matches the 'method' in 'package/Class' with "); + tty->print_cr("'(Lpackage/Parameter;)Lpackage/Return;'"); tty->cr(); tty->print_cr("For backward compatibility this form is also allowed:"); - tty->print_cr(" package.Class::method()"); tty->cr(); - tty->print_cr("The signature can be separated by an optional whitespace or comma:"); - tty->print_cr(" package/Class.method ()"); + tty->print_cr(" package.Class::method(Lpackage.Parameter;)Lpackage.Return;"); + tty->cr(); + tty->print_cr("A whitespace or comma can optionally separate the from the"); + tty->print_cr(":"); + tty->cr(); + tty->print_cr(" package/Class.method (Lpackage/Parameter;)Lpackage/Return;"); + tty->print_cr(" package/Class.method,(Lpackage/Parameter;)Lpackage/Return;"); + tty->cr(); + tty->print_cr("The and accept leading and trailing '*' wildcards"); + tty->print_cr("matching:"); + tty->cr(); + tty->print_cr(" *ackage/Clas*.*etho*(Lpackage/Parameter;)Lpackage/Return;"); + tty->cr(); + tty->print_cr("The does not support explicit wildcards and"); + tty->print_cr("always has an implicit trailing wildcard. Therefore,"); + tty->cr(); + tty->print_cr(" package/Class.method(Lpackage/Parameter;)Lpackage/Return;"); + tty->cr(); + tty->print_cr("matches a subset of"); + tty->cr(); + tty->print_cr(" package/Class.method(Lpackage/Parameter;)"); + tty->cr(); + tty->print_cr("which matches a subset of"); + tty->cr(); + tty->print_cr(" package/Class.method"); tty->cr(); - tty->print_cr("The class and method identifier can be used together with leading or"); - tty->print_cr("trailing *'s for wildcard matching:"); - tty->print_cr(" *ackage/Clas*.*etho*()"); + tty->print_cr("which matches all possible descriptors."); tty->cr(); tty->print_cr("It is possible to use more than one CompileCommand on the command line:"); tty->print_cr(" -XX:CompileCommand=exclude,java/*.* -XX:CompileCommand=log,java*.*"); diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp index 16cae714cb9..f3d411e34ba 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 ab7d6febf4c..622651ce0d8 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 20642cfc7e6..58a70ed6a60 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.hpp @@ -84,10 +84,6 @@ class G1BarrierSet: public CardTableBarrierSet { // Update the given thread's card table (byte map) base to the current card table's. void update_card_table_base(Thread* thread); - virtual bool card_mark_must_follow_store() const { - return true; - } - // Add "pre_val" to a set of objects that may have been disconnected from the // pre-marking object graph. Prefer the version that takes location, as it // can avoid touching the heap unnecessarily. @@ -103,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 0888fc58937..ffba561f11f 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 c1b18a71cfb..485caa9f6c0 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -1391,7 +1391,6 @@ jint G1CollectedHeap::initialize() { G1CardTable* refinement_table = new G1CardTable(_reserved); G1BarrierSet* bs = new G1BarrierSet(card_table, refinement_table); - bs->initialize(); assert(bs->is_a(BarrierSet::G1BarrierSet), "sanity"); // Create space mappers. diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp index df4312ebd75..36412ce5efe 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 dc37b10292a..02850376592 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 71fddf2c4da..a8f47a387e3 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 d09a2b2df89..785bfe27228 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 d120c71d2fa..255ee0c56ec 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 780185952b4..2d267951f79 100644 --- a/src/hotspot/share/gc/parallel/parallelArguments.cpp +++ b/src/hotspot/share/gc/parallel/parallelArguments.cpp @@ -103,15 +103,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 +118,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 18cbe2403d8..eef9dfbc97c 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); @@ -79,7 +86,6 @@ jint ParallelScavengeHeap::initialize() { card_table->initialize(old_rs.base(), young_rs.base()); CardTableBarrierSet* const barrier_set = new CardTableBarrierSet(card_table); - barrier_set->initialize(); BarrierSet::set_barrier_set(barrier_set); // Set up WorkerThreads diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index bf777bda29e..84732a86880 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; @@ -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 b013238a9f8..613361c7039 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 13a29986b66..03b7f128442 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 89f22b72b69..2d4b0698ad0 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 3be90b370d1..f4b24fa51af 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 a54a513a117..ca94f4d83b6 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 c26fdf4740c..b2cce11398d 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 fa019aa5b42..f5e7375fca1 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 5b7bc744236..413d80bebf4 100644 --- a/src/hotspot/share/gc/serial/defNewGeneration.cpp +++ b/src/hotspot/share/gc/serial/defNewGeneration.cpp @@ -225,16 +225,12 @@ DefNewGeneration::DefNewGeneration(ReservedSpace rs, _promo_failure_drain_in_progress(false), _string_dedup_requests() { - MemRegion cmr((HeapWord*)_virtual_space.low(), - (HeapWord*)_virtual_space.high()); - SerialHeap* gch = SerialHeap::heap(); - - gch->rem_set()->resize_covered_region(cmr); - _eden_space = new ContiguousSpace(); _from_space = new ContiguousSpace(); _to_space = new ContiguousSpace(); + init_spaces(); + // Compute the maximum eden and survivor space sizes. These sizes // are computed assuming the entire reserved space is committed. // These values are exported as performance counters. @@ -256,7 +252,6 @@ DefNewGeneration::DefNewGeneration(ReservedSpace rs, _to_counters = new CSpaceCounters("s1", 2, _max_survivor_size, _to_space, _gen_counters); - compute_space_boundaries(0, SpaceDecorator::Clear, SpaceDecorator::Mangle); update_counters(); _old_gen = nullptr; _tenuring_threshold = MaxTenuringThreshold; @@ -268,74 +263,51 @@ DefNewGeneration::DefNewGeneration(ReservedSpace rs, _gc_tracer = new DefNewTracer(); } -void DefNewGeneration::compute_space_boundaries(uintx minimum_eden_size, - bool clear_space, - bool mangle_space) { - // If the spaces are being cleared (only done at heap initialization - // currently), the survivor spaces need not be empty. - // Otherwise, no care is taken for used areas in the survivor spaces - // so check. - assert(clear_space || (to()->is_empty() && from()->is_empty()), - "Initialization of the survivor spaces assumes these are empty"); +void DefNewGeneration::init_spaces() { + // Using layout: from, to, eden, so only from can be non-empty. + assert(eden()->is_empty(), "precondition"); + assert(to()->is_empty(), "precondition"); - // Compute sizes - uintx size = _virtual_space.committed_size(); - uintx survivor_size = compute_survivor_size(size, SpaceAlignment); - uintx eden_size = size - (2*survivor_size); - if (eden_size > max_eden_size()) { - // Need to reduce eden_size to satisfy the max constraint. The delta needs - // to be 2*SpaceAlignment aligned so that both survivors are properly - // aligned. - uintx eden_delta = align_up(eden_size - max_eden_size(), 2*SpaceAlignment); - eden_size -= eden_delta; - survivor_size += eden_delta/2; + if (!from()->is_empty()) { + assert((char*) from()->bottom() == _virtual_space.low(), "inv"); } - assert(eden_size > 0 && survivor_size <= eden_size, "just checking"); - if (eden_size < minimum_eden_size) { - // May happen due to 64Kb rounding, if so adjust eden size back up - minimum_eden_size = align_up(minimum_eden_size, SpaceAlignment); - uintx maximum_survivor_size = (size - minimum_eden_size) / 2; - uintx unaligned_survivor_size = - align_down(maximum_survivor_size, SpaceAlignment); - survivor_size = MAX2(unaligned_survivor_size, SpaceAlignment); - eden_size = size - (2*survivor_size); - assert(eden_size > 0 && survivor_size <= eden_size, "just checking"); - assert(eden_size >= minimum_eden_size, "just checking"); - } + // Compute sizes + size_t size = _virtual_space.committed_size(); + size_t survivor_size = compute_survivor_size(size, SpaceAlignment); + assert(survivor_size >= from()->used(), "inv"); + assert(size > 2 * survivor_size, "inv"); + size_t eden_size = size - (2 * survivor_size); + assert(eden_size > 0 && survivor_size <= eden_size, "just checking"); - char *eden_start = _virtual_space.low(); - char *from_start = eden_start + eden_size; - char *to_start = from_start + survivor_size; - char *to_end = to_start + survivor_size; + // layout: from, to, eden + char* from_start = _virtual_space.low(); + char* to_start = from_start + survivor_size; + char* eden_start = to_start + survivor_size; + char* eden_end = eden_start + eden_size; - assert(to_end == _virtual_space.high(), "just checking"); - assert(is_aligned(eden_start, SpaceAlignment), "checking alignment"); + assert(eden_end == _virtual_space.high(), "just checking"); assert(is_aligned(from_start, SpaceAlignment), "checking alignment"); assert(is_aligned(to_start, SpaceAlignment), "checking alignment"); + assert(is_aligned(eden_start, SpaceAlignment), "checking alignment"); + assert(is_aligned(eden_end, SpaceAlignment), "checking alignment"); - MemRegion edenMR((HeapWord*)eden_start, (HeapWord*)from_start); MemRegion fromMR((HeapWord*)from_start, (HeapWord*)to_start); - MemRegion toMR ((HeapWord*)to_start, (HeapWord*)to_end); - - // A minimum eden size implies that there is a part of eden that - // is being used and that affects the initialization of any - // newly formed eden. - bool live_in_eden = minimum_eden_size > 0; + MemRegion toMR ((HeapWord*)to_start, (HeapWord*)eden_start); + MemRegion edenMR((HeapWord*)eden_start, (HeapWord*)eden_end); // Reset the spaces for their new regions. - eden()->initialize(edenMR, - clear_space && !live_in_eden, - SpaceDecorator::Mangle); - // If clear_space and live_in_eden, we will not have cleared any - // portion of eden above its top. This can cause newly - // expanded space not to be mangled if using ZapUnusedHeapArea. - // We explicitly do such mangling here. - if (ZapUnusedHeapArea && clear_space && live_in_eden && mangle_space) { - eden()->mangle_unused_area(); - } - from()->initialize(fromMR, clear_space, mangle_space); - to()->initialize(toMR, clear_space, mangle_space); + from()->initialize(fromMR, from()->is_empty()); + to()->initialize(toMR, true); + eden()->initialize(edenMR, true); + + post_resize(); +} + +void DefNewGeneration::post_resize() { + MemRegion cmr((HeapWord*)_virtual_space.low(), + (HeapWord*)_virtual_space.high()); + SerialHeap::heap()->rem_set()->resize_covered_region(cmr); } void DefNewGeneration::swap_spaces() { @@ -351,20 +323,28 @@ void DefNewGeneration::swap_spaces() { } bool DefNewGeneration::expand(size_t bytes) { - HeapWord* prev_high = (HeapWord*) _virtual_space.high(); + assert(bytes != 0, "precondition"); + assert(is_aligned(bytes, SpaceAlignment), "precondition"); + bool success = _virtual_space.expand_by(bytes); - if (success && ZapUnusedHeapArea) { - // Mangle newly committed space immediately because it - // can be done here more simply that after the new - // spaces have been computed. - HeapWord* new_high = (HeapWord*) _virtual_space.high(); - MemRegion mangle_region(prev_high, new_high); - SpaceMangler::mangle_region(mangle_region); + if (!success) { + log_info(gc)("Failed to expand young-gen by %zu bytes", bytes); } return success; } +void DefNewGeneration::expand_eden_by(size_t delta_bytes) { + if (!expand(delta_bytes)) { + return; + } + + MemRegion eden_mr{eden()->bottom(), (HeapWord*)_virtual_space.high()}; + eden()->initialize(eden_mr, eden()->is_empty()); + + post_resize(); +} + size_t DefNewGeneration::calculate_thread_increase_size(int threads_count) const { size_t thread_increase_size = 0; // Check an overflow at 'threads_count * NewSizeThreadIncrease'. @@ -397,18 +377,8 @@ size_t DefNewGeneration::adjust_for_thread_increase(size_t new_size_candidate, return desired_new_size; } -void DefNewGeneration::compute_new_size() { - // This is called after a GC that includes the old generation, so from-space - // will normally be empty. - // Note that we check both spaces, since if scavenge failed they revert roles. - // If not we bail out (otherwise we would have to relocate the objects). - if (!from()->is_empty() || !to()->is_empty()) { - return; - } - - SerialHeap* gch = SerialHeap::heap(); - - size_t old_size = gch->old_gen()->capacity(); +size_t DefNewGeneration::calculate_desired_young_gen_bytes() const { + size_t old_size = SerialHeap::heap()->old_gen()->capacity(); size_t new_size_before = _virtual_space.committed_size(); size_t min_new_size = NewSize; size_t max_new_size = reserved().byte_size(); @@ -429,46 +399,82 @@ void DefNewGeneration::compute_new_size() { // Adjust new generation size desired_new_size = clamp(desired_new_size, min_new_size, max_new_size); - assert(desired_new_size <= max_new_size, "just checking"); - - bool changed = false; - if (desired_new_size > new_size_before) { - size_t change = desired_new_size - new_size_before; - assert(change % alignment == 0, "just checking"); - if (expand(change)) { - changed = true; + if (!from()->is_empty()) { + // Mininum constraint to hold all live objs inside from-space. + size_t min_survivor_size = align_up(from()->used(), alignment); + + // SurvivorRatio := eden_size / survivor_size + // young-gen-size = eden_size + 2 * survivor_size + // = SurvivorRatio * survivor_size + 2 * survivor_size + // = (SurvivorRatio + 2) * survivor_size + size_t min_young_gen_size = min_survivor_size * (SurvivorRatio + 2); + + desired_new_size = MAX2(min_young_gen_size, desired_new_size); + } + assert(is_aligned(desired_new_size, alignment), "postcondition"); + + return desired_new_size; +} + +void DefNewGeneration::resize_inner() { + assert(eden()->is_empty(), "precondition"); + assert(to()->is_empty(), "precondition"); + + size_t current_young_gen_size_bytes = _virtual_space.committed_size(); + size_t desired_young_gen_size_bytes = calculate_desired_young_gen_bytes(); + if (current_young_gen_size_bytes == desired_young_gen_size_bytes) { + return; + } + + // Commit/uncommit + if (desired_young_gen_size_bytes > current_young_gen_size_bytes) { + size_t delta_bytes = desired_young_gen_size_bytes - current_young_gen_size_bytes; + if (!expand(delta_bytes)) { + return; } - // If the heap failed to expand to the desired size, - // "changed" will be false. If the expansion failed - // (and at this point it was expected to succeed), - // ignore the failure (leaving "changed" as false). - } - if (desired_new_size < new_size_before && eden()->is_empty()) { - // bail out of shrinking if objects in eden - size_t change = new_size_before - desired_new_size; - assert(change % alignment == 0, "just checking"); - _virtual_space.shrink_by(change); - changed = true; - } - if (changed) { - // The spaces have already been mangled at this point but - // may not have been cleared (set top = bottom) and should be. - // Mangling was done when the heap was being expanded. - compute_space_boundaries(eden()->used(), - SpaceDecorator::Clear, - SpaceDecorator::DontMangle); - MemRegion cmr((HeapWord*)_virtual_space.low(), - (HeapWord*)_virtual_space.high()); - gch->rem_set()->resize_covered_region(cmr); - - log_debug(gc, ergo, heap)( - "New generation size %zuK->%zuK [eden=%zuK,survivor=%zuK]", - new_size_before/K, _virtual_space.committed_size()/K, - eden()->capacity()/K, from()->capacity()/K); - log_trace(gc, ergo, heap)( - " [allowed %zuK extra for %d threads]", - thread_increase_size/K, threads_count); - } + } else { + size_t delta_bytes = current_young_gen_size_bytes - desired_young_gen_size_bytes; + _virtual_space.shrink_by(delta_bytes); + } + + assert(desired_young_gen_size_bytes == _virtual_space.committed_size(), "inv"); + + init_spaces(); + + log_debug(gc, ergo, heap)("New generation size %zuK->%zuK [eden=%zuK,survivor=%zuK]", + current_young_gen_size_bytes/K, _virtual_space.committed_size()/K, + eden()->capacity()/K, from()->capacity()/K); +} + +void DefNewGeneration::resize_after_young_gc() { + // Called only after successful young-gc. + assert(eden()->is_empty(), "precondition"); + assert(to()->is_empty(), "precondition"); + + if ((char*)to()->bottom() == _virtual_space.low()) { + // layout: to, from, eden; can't resize. + return; + } + + assert((char*)from()->bottom() == _virtual_space.low(), "inv"); + resize_inner(); +} + +void DefNewGeneration::resize_after_full_gc() { + if (eden()->is_empty() && from()->is_empty() && to()->is_empty()) { + resize_inner(); + return; + } + + // Usually the young-gen is empty after full-gc. + // This is the extreme case; expand young-gen to its max size. + if (_virtual_space.uncommitted_size() == 0) { + // Already at its max size. + return; + } + + // Keep from/to and expand eden. + expand_eden_by(_virtual_space.uncommitted_size()); } void DefNewGeneration::ref_processor_init() { @@ -483,13 +489,11 @@ size_t DefNewGeneration::capacity() const { + from()->capacity(); // to() is only used during scavenge } - size_t DefNewGeneration::used() const { return eden()->used() + from()->used(); // to() is only used during scavenge } - size_t DefNewGeneration::free() const { return eden()->free() + from()->free(); // to() is only used during scavenge @@ -497,7 +501,8 @@ size_t DefNewGeneration::free() const { size_t DefNewGeneration::max_capacity() const { const size_t reserved_bytes = reserved().byte_size(); - return reserved_bytes - compute_survivor_size(reserved_bytes, SpaceAlignment); + const size_t min_survivor_bytes = SpaceAlignment; + return reserved_bytes - min_survivor_bytes; } bool DefNewGeneration::is_in(const void* p) const { @@ -589,7 +594,6 @@ bool DefNewGeneration::collect(bool clear_all_soft_refs) { IsAliveClosure is_alive(this); age_table()->clear(); - to()->clear(SpaceDecorator::Mangle); YoungGenScanClosure young_gen_cl(this); OldGenScanClosure old_gen_cl(this); @@ -839,13 +843,18 @@ void DefNewGeneration::print_on(outputStream* st) const { to()->print_on(st, "to "); } -HeapWord* DefNewGeneration::allocate(size_t word_size) { - // This is the slow-path allocation for the DefNewGeneration. - // Most allocations are fast-path in compiled code. - // We try to allocate from the eden. If that works, we are happy. - // Note that since DefNewGeneration supports lock-free allocation, we - // have to use it here, as well. - HeapWord* result = eden()->par_allocate(word_size); +HeapWord* DefNewGeneration::expand_and_allocate(size_t word_size) { + assert(SafepointSynchronize::is_at_safepoint(), "precondition"); + assert(Thread::current()->is_VM_thread(), "precondition"); + + size_t eden_free_bytes = eden()->free(); + size_t requested_bytes = word_size * HeapWordSize; + if (eden_free_bytes < requested_bytes) { + size_t expand_bytes = requested_bytes - eden_free_bytes; + expand_eden_by(align_up(expand_bytes, SpaceAlignment)); + } + + HeapWord* result = eden()->allocate(word_size); return result; } diff --git a/src/hotspot/share/gc/serial/defNewGeneration.hpp b/src/hotspot/share/gc/serial/defNewGeneration.hpp index 32b6b32f42f..40d2116cb58 100644 --- a/src/hotspot/share/gc/serial/defNewGeneration.hpp +++ b/src/hotspot/share/gc/serial/defNewGeneration.hpp @@ -131,6 +131,13 @@ class DefNewGeneration: public Generation { return n > alignment ? align_down(n, alignment) : alignment; } + size_t calculate_desired_young_gen_bytes() const; + + void expand_eden_by(size_t delta_bytes); + + void resize_inner(); + void post_resize(); + public: DefNewGeneration(ReservedSpace rs, size_t initial_byte_size, @@ -183,9 +190,8 @@ class DefNewGeneration: public Generation { HeapWord* block_start(const void* p) const; - // Allocate requested size or return null; single-threaded and lock-free versions. - HeapWord* allocate(size_t word_size); HeapWord* par_allocate(size_t word_size); + HeapWord* expand_and_allocate(size_t word_size); void gc_epilogue(); @@ -196,8 +202,8 @@ class DefNewGeneration: public Generation { // Reset for contribution of "to-space". void reset_scratch(); - // GC support - void compute_new_size(); + void resize_after_young_gc(); + void resize_after_full_gc(); bool collect(bool clear_all_soft_refs); @@ -220,13 +226,9 @@ class DefNewGeneration: public Generation { DefNewTracer* gc_tracer() const { return _gc_tracer; } - protected: - // If clear_space is true, clear the survivor spaces. Eden is - // cleared if the minimum size of eden is 0. If mangle_space - // is true, also mangle the space in debug mode. - void compute_space_boundaries(uintx minimum_eden_size, - bool clear_space, - bool mangle_space); + private: + // Initialize eden/from/to spaces. + void init_spaces(); // Return adjusted new size for NewSizeThreadIncrease. // If any overflow happens, revert to previous new size. diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index 3ab88da4633..8022b317ca6 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -182,7 +182,6 @@ jint SerialHeap::initialize() { _rem_set->initialize(young_rs.base(), old_rs.base()); CardTableBarrierSet *bs = new CardTableBarrierSet(_rem_set); - bs->initialize(); BarrierSet::set_barrier_set(bs); _young_gen = new DefNewGeneration(young_rs, NewSize, MinNewSize, MaxNewSize); @@ -269,9 +268,9 @@ size_t SerialHeap::max_capacity() const { } HeapWord* SerialHeap::expand_heap_and_allocate(size_t size, bool is_tlab) { - HeapWord* result = _young_gen->allocate(size); + HeapWord* result = _young_gen->expand_and_allocate(size); - if (result == nullptr) { + if (result == nullptr && !is_tlab) { result = _old_gen->expand_and_allocate(size); } @@ -388,14 +387,13 @@ bool SerialHeap::do_young_collection(bool clear_soft_refs) { // Only update stats for successful young-gc if (result) { _old_gen->update_promote_stats(); + _young_gen->resize_after_young_gc(); } if (should_verify && VerifyAfterGC) { Universe::verify("After GC"); } - _young_gen->compute_new_size(); - print_heap_change(pre_gc_values); // Track memory usage and detect low memory after GC finishes @@ -581,7 +579,7 @@ void SerialHeap::do_full_collection(bool clear_all_soft_refs) { // Adjust generation sizes. _old_gen->compute_new_size(); - _young_gen->compute_new_size(); + _young_gen->resize_after_full_gc(); _old_gen->update_promote_stats(); diff --git a/src/hotspot/share/gc/serial/serialHeap.hpp b/src/hotspot/share/gc/serial/serialHeap.hpp index 27053b4cc81..3915f8c4af9 100644 --- a/src/hotspot/share/gc/serial/serialHeap.hpp +++ b/src/hotspot/share/gc/serial/serialHeap.hpp @@ -55,10 +55,10 @@ class TenuredGeneration; // +-- generation boundary (fixed after startup) // | // |<- young gen (reserved MaxNewSize) ->|<- old gen (reserved MaxOldSize) ->| -// +-----------------+--------+--------+--------+---------------+-------------------+ -// | eden | from | to | | old | | -// | | (to) | (from) | | | | -// +-----------------+--------+--------+--------+---------------+-------------------+ +// +--------+--------+-----------------+--------+---------------+-------------------+ +// | from | to | eden | | old | | +// | (to) | (from) | | | | | +// +--------+--------+-----------------+--------+---------------+-------------------+ // |<- committed ->| |<- committed ->| // class SerialHeap : public CollectedHeap { diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.cpp b/src/hotspot/share/gc/serial/tenuredGeneration.cpp index a28a8c8e1cb..f68847ed1a6 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/c2/barrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp index 68f232ac2f7..31d1576d1ab 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp @@ -119,7 +119,7 @@ uint8_t BarrierStubC2::barrier_data() const { void BarrierStubC2::preserve(Register r) { const VMReg vm_reg = r->as_VMReg(); assert(vm_reg->is_Register(), "r must be a general-purpose register"); - _preserve.Insert(OptoReg::as_OptoReg(vm_reg)); + _preserve.insert(OptoReg::as_OptoReg(vm_reg)); } void BarrierStubC2::dont_preserve(Register r) { @@ -128,7 +128,7 @@ void BarrierStubC2::dont_preserve(Register r) { // Subtract the given register and all its sub-registers (e.g. {R11, R11_H} // for r11 in aarch64). do { - _preserve.Remove(OptoReg::as_OptoReg(vm_reg)); + _preserve.remove(OptoReg::as_OptoReg(vm_reg)); vm_reg = vm_reg->next(); } while (vm_reg->is_Register() && !vm_reg->is_concrete()); } @@ -1175,7 +1175,7 @@ void BarrierSetC2::compute_liveness_at_stubs() const { // Initialize to union of successors for (uint i = 0; i < block->_num_succs; i++) { const uint succ_id = block->_succs[i]->_pre_order; - new_live.OR(live[succ_id]); + new_live.or_with(live[succ_id]); } // Walk block backwards, computing liveness @@ -1186,7 +1186,7 @@ void BarrierSetC2::compute_liveness_at_stubs() const { if (!bs_state->needs_livein_data()) { RegMask* const regs = bs_state->live(node); if (regs != nullptr) { - regs->OR(new_live); + regs->or_with(new_live); } } @@ -1194,10 +1194,10 @@ void BarrierSetC2::compute_liveness_at_stubs() const { const OptoReg::Name first = bs->refine_register(node, regalloc->get_reg_first(node)); const OptoReg::Name second = bs->refine_register(node, regalloc->get_reg_second(node)); if (first != OptoReg::Bad) { - new_live.Remove(first); + new_live.remove(first); } if (second != OptoReg::Bad) { - new_live.Remove(second); + new_live.remove(second); } // Add use bits @@ -1206,10 +1206,10 @@ void BarrierSetC2::compute_liveness_at_stubs() const { const OptoReg::Name first = bs->refine_register(use, regalloc->get_reg_first(use)); const OptoReg::Name second = bs->refine_register(use, regalloc->get_reg_second(use)); if (first != OptoReg::Bad) { - new_live.Insert(first); + new_live.insert(first); } if (second != OptoReg::Bad) { - new_live.Insert(second); + new_live.insert(second); } } @@ -1217,16 +1217,16 @@ void BarrierSetC2::compute_liveness_at_stubs() const { if (bs_state->needs_livein_data()) { RegMask* const regs = bs_state->live(node); if (regs != nullptr) { - regs->OR(new_live); + regs->or_with(new_live); } } } // Now at block top, see if we have any changes - new_live.SUBTRACT(old_live); - if (!new_live.is_Empty()) { + new_live.subtract(old_live); + if (!new_live.is_empty()) { // Liveness has refined, update and propagate to prior blocks - old_live.OR(new_live); + old_live.or_with(new_live); for (uint i = 1; i < block->num_preds(); ++i) { Block* const pred = cfg->get_block_for_node(block->pred(i)); worklist.push(pred); diff --git a/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp b/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp index dfa00636dec..de514f64be2 100644 --- a/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp +++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp @@ -57,7 +57,6 @@ CardTableBarrierSet::CardTableBarrierSet(BarrierSetAssembler* barrier_set_assemb barrier_set_c1, barrier_set_c2, fake_rtti.add_tag(BarrierSet::CardTableBarrierSet)), - _defer_initial_card_mark(false), _card_table(card_table) {} @@ -66,14 +65,9 @@ CardTableBarrierSet::CardTableBarrierSet(CardTable* card_table) : make_barrier_set_c1(), make_barrier_set_c2(), BarrierSet::FakeRtti(BarrierSet::CardTableBarrierSet)), - _defer_initial_card_mark(false), _card_table(card_table) {} -void CardTableBarrierSet::initialize() { - initialize_deferred_card_mark_barriers(); -} - CardTableBarrierSet::~CardTableBarrierSet() { delete _card_table; } @@ -108,9 +102,7 @@ void CardTableBarrierSet::print_on(outputStream* st) const { // to the post-barrier, we note that G1 needs a RS update barrier // which simply enqueues a (sequence of) dirty cards which may // optionally be refined by the concurrent update threads. Note -// that this barrier need only be applied to a non-young write, -// but, because of the presence of concurrent refinement, -// must strictly follow the oop-store. +// that this barrier need only be applied to a non-young write. // // For any future collector, this code should be reexamined with // that specific collector in mind, and the documentation above suitably @@ -120,72 +112,13 @@ void CardTableBarrierSet::on_slowpath_allocation_exit(JavaThread* thread, oop ne if (!ReduceInitialCardMarks) { return; } - // If a previous card-mark was deferred, flush it now. - flush_deferred_card_mark_barrier(thread); if (new_obj->is_typeArray() || _card_table->is_in_young(new_obj)) { // Arrays of non-references don't need a post-barrier. - // The deferred_card_mark region should be empty - // following the flush above. - assert(thread->deferred_card_mark().is_empty(), "Error"); } else { MemRegion mr(cast_from_oop(new_obj), new_obj->size()); assert(!mr.is_empty(), "Error"); - if (_defer_initial_card_mark) { - // Defer the card mark - thread->set_deferred_card_mark(mr); - } else { - // Do the card mark - write_region(mr); - } + // Do the card mark + write_region(mr); } #endif // COMPILER2_OR_JVMCI } - -void CardTableBarrierSet::initialize_deferred_card_mark_barriers() { - // Used for ReduceInitialCardMarks (when COMPILER2 or JVMCI is used); - // otherwise remains unused. -#if COMPILER2_OR_JVMCI - _defer_initial_card_mark = CompilerConfig::is_c2_or_jvmci_compiler_enabled() && ReduceInitialCardMarks - && (DeferInitialCardMark || card_mark_must_follow_store()); -#else - assert(_defer_initial_card_mark == false, "Who would set it?"); -#endif -} - -void CardTableBarrierSet::flush_deferred_card_mark_barrier(JavaThread* thread) { -#if COMPILER2_OR_JVMCI - MemRegion deferred = thread->deferred_card_mark(); - if (!deferred.is_empty()) { - assert(_defer_initial_card_mark, "Otherwise should be empty"); - { - // Verify that the storage points to a parsable object in heap - DEBUG_ONLY(oop old_obj = cast_to_oop(deferred.start());) - assert(!_card_table->is_in_young(old_obj), - "Else should have been filtered in on_slowpath_allocation_exit()"); - assert(oopDesc::is_oop(old_obj), "Not an oop"); - assert(deferred.word_size() == old_obj->size(), - "Mismatch: multiple objects?"); - } - write_region(thread, deferred); - // "Clear" the deferred_card_mark field - thread->set_deferred_card_mark(MemRegion()); - } - assert(thread->deferred_card_mark().is_empty(), "invariant"); -#else - assert(!_defer_initial_card_mark, "Should be false"); - assert(thread->deferred_card_mark().is_empty(), "Should be empty"); -#endif -} - -void CardTableBarrierSet::on_thread_detach(Thread* thread) { - // The deferred store barriers must all have been flushed to the - // card-table (or other remembered set structure) before GC starts - // processing the card-table (or other remembered set). - if (thread->is_Java_thread()) { // Only relevant for Java threads. - flush_deferred_card_mark_barrier(JavaThread::cast(thread)); - } -} - -bool CardTableBarrierSet::card_mark_must_follow_store() const { - return false; -} diff --git a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp index 13f3e0783a6..a5646c303f3 100644 --- a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp +++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp @@ -47,9 +47,6 @@ class CardTableBarrierSet: public ModRefBarrierSet { protected: typedef CardTable::CardValue CardValue; - // Used in support of ReduceInitialCardMarks; only consulted if COMPILER2 - // or INCLUDE_JVMCI is being used - bool _defer_initial_card_mark; CardTable* _card_table; CardTableBarrierSet(BarrierSetAssembler* barrier_set_assembler, @@ -64,13 +61,6 @@ class CardTableBarrierSet: public ModRefBarrierSet { CardTable* card_table() const { return _card_table; } - void initialize(); - - void write_region(JavaThread* thread, MemRegion mr) { - write_region(mr); - } - - public: // 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 @@ -80,29 +70,7 @@ class CardTableBarrierSet: public ModRefBarrierSet { virtual void write_region(MemRegion mr); - // ReduceInitialCardMarks - void initialize_deferred_card_mark_barriers(); - - // If the CollectedHeap was asked to defer a store barrier above, - // this informs it to flush such a deferred store barrier to the - // remembered set. - void flush_deferred_card_mark_barrier(JavaThread* thread); - - // If a compiler is eliding store barriers for TLAB-allocated objects, - // we will be informed of a slow-path allocation by a call - // to on_slowpath_allocation_exit() below. Such a call precedes the - // initialization of the object itself, and no post-store-barriers will - // be issued. Some heap types require that the barrier strictly follows - // the initializing stores. (This is currently implemented by deferring the - // barrier until the next slow-path allocation or gc-related safepoint.) - // This interface answers whether a particular barrier type needs the card - // mark to be thus strictly sequenced after the stores. - virtual bool card_mark_must_follow_store() const; - virtual void on_slowpath_allocation_exit(JavaThread* thread, oop new_obj); - virtual void on_thread_detach(Thread* thread); - - virtual void make_parsable(JavaThread* thread) { flush_deferred_card_mark_barrier(thread); } virtual void print_on(outputStream* st) const; diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index 0b245026d68..956bffde156 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.hpp @@ -418,10 +418,6 @@ "dictionary, classloader_data_graph, metaspace, jni_handles, " \ "codecache_oops, resolved_method_table, stringdedup") \ \ - product(bool, DeferInitialCardMark, false, DIAGNOSTIC, \ - "When +ReduceInitialCardMarks, explicitly defer any that " \ - "may arise from new_pre_store_barrier") \ - \ product(bool, UseCondCardMark, false, \ "Check for already marked card before updating card table") \ \ diff --git a/src/hotspot/share/gc/shared/modRefBarrierSet.hpp b/src/hotspot/share/gc/shared/modRefBarrierSet.hpp index fee652c8899..f4198c7dec4 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 08476cb2a3a..011a0f5cfd8 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), @@ -53,7 +52,10 @@ void ContiguousSpace::initialize(MemRegion mr, set_bottom(bottom); set_end(end); if (clear_space) { - clear(mangle_space); + clear(SpaceDecorator::DontMangle); + } + if (ZapUnusedHeapArea) { + mangle_unused_area(); } } diff --git a/src/hotspot/share/gc/shared/space.hpp b/src/hotspot/share/gc/shared/space.hpp index 75dd3f998d6..7f2887275b3 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/gc/shared/vmStructs_gc.hpp b/src/hotspot/share/gc/shared/vmStructs_gc.hpp index bba9c9e099f..9d84a56fbd7 100644 --- a/src/hotspot/share/gc/shared/vmStructs_gc.hpp +++ b/src/hotspot/share/gc/shared/vmStructs_gc.hpp @@ -88,7 +88,6 @@ nonstatic_field(CardTable, _byte_map_size, const size_t) \ nonstatic_field(CardTable, _byte_map, CardTable::CardValue*) \ nonstatic_field(CardTable, _byte_map_base, CardTable::CardValue*) \ - nonstatic_field(CardTableBarrierSet, _defer_initial_card_mark, bool) \ nonstatic_field(CardTableBarrierSet, _card_table, CardTable*) \ \ static_field(CollectedHeap, _lab_alignment_reserve, size_t) \ diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp index 014a4d99131..66bfc3375a3 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp @@ -32,25 +32,62 @@ #include "memory/allocation.hpp" #include "utilities/numberSeq.hpp" +/** + * ShenanoahAllocationRate maintains a truncated history of recently sampled allocation rates for the purpose of providing + * informed estimates of current and future allocation rates based on weighted averages and standard deviations of the + * truncated history. More recently sampled allocations are weighted more heavily than older samples when computing + * averages and standard deviations. + */ class ShenandoahAllocationRate : public CHeapObj { public: explicit ShenandoahAllocationRate(); + + // Reset the _last_sample_value to zero, _last_sample_time to current time. void allocation_counter_reset(); + // Force an allocation rate sample to be taken, even if the time since last sample is not greater than + // 1s/ShenandoahAdaptiveSampleFrequencyHz, except when current_time - _last_sample_time < MinSampleTime (2 ms). + // The sampled allocation rate is computed from (allocated - _last_sample_value) / (current_time - _last_sample_time). + // Return the newly computed rate if the sample is taken, zero if it is not an appropriate time to add a sample. + // In the case that a new sample is not taken, overwrite unaccounted_bytes_allocated with bytes allocated since + // the previous sample was taken (allocated - _last_sample_value). Otherwise, overwrite unaccounted_bytes_allocated + // with 0. double force_sample(size_t allocated, size_t &unaccounted_bytes_allocated); + + // Add an allocation rate sample if the time since last sample is greater than 1s/ShenandoahAdaptiveSampleFrequencyHz. + // The sampled allocation rate is computed from (allocated - _last_sample_value) / (current_time - _last_sample_time). + // Return the newly computed rate if the sample is taken, zero if it is not an appropriate time to add a sample. double sample(size_t allocated); + // Return an estimate of the upper bound on allocation rate, with the upper bound computed as the weighted average + // of recently sampled instantaneous allocation rates added to sds times the standard deviation computed for the + // sequence of recently sampled average allocation rates. double upper_bound(double sds) const; + + // Test whether rate significantly diverges from the computed average allocation rate. If so, return true. + // Otherwise, return false. Significant divergence is recognized if (rate - _rate.avg()) / _rate.sd() > threshold. bool is_spiking(double rate, double threshold) const; private: + // Return the instantaneous rate calculated from (allocated - _last_sample_value) / (time - _last_sample_time). + // Return Sentinel value 0.0 if (time - _last_sample_time) == 0 or if (allocated <= _last_sample_value). double instantaneous_rate(double time, size_t allocated) const; + // Time at which previous allocation rate sample was collected. double _last_sample_time; + + // Bytes allocated as of the time at which previous allocation rate sample was collected. size_t _last_sample_value; + + // The desired interval of time between consecutive samples of the allocation rate. double _interval_sec; + + // Holds a sequence of the most recently sampled instantaneous allocation rates TruncatedSeq _rate; + + // Holds a sequence of the most recently computed weighted average of allocation rates, with each weighted average + // computed immediately after an instantaneous rate was sampled TruncatedSeq _rate_avg; }; @@ -154,6 +191,8 @@ class ShenandoahAdaptiveHeuristics : public ShenandoahHeuristics { } public: + // Sample the allocation rate at GC trigger time if possible. Return the number of allocated bytes that were + // not accounted for in the sample. This must be called before resetting bytes allocated since gc start. virtual size_t force_alloc_rate_sample(size_t bytes_allocated) override { size_t unaccounted_bytes; _allocation_rate.force_sample(bytes_allocated, unaccounted_bytes); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp index 5d19a6a34e3..2aa37d7c575 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp @@ -89,10 +89,20 @@ bool ShenandoahBarrierSet::need_keep_alive_barrier(DecoratorSet decorators, Basi void ShenandoahBarrierSet::on_slowpath_allocation_exit(JavaThread* thread, oop new_obj) { #if COMPILER2_OR_JVMCI - assert(!ReduceInitialCardMarks || !ShenandoahCardBarrier || ShenandoahGenerationalHeap::heap()->is_in_young(new_obj), - "Allocating new object outside of young generation: " INTPTR_FORMAT, p2i(new_obj)); + if (ReduceInitialCardMarks && ShenandoahCardBarrier && !ShenandoahHeap::heap()->is_in_young(new_obj)) { + log_debug(gc)("Newly allocated object (" PTR_FORMAT ") is not in the young generation", p2i(new_obj)); + // This can happen when an object is newly allocated, but we come to a safepoint before returning + // the object. If the safepoint runs a degenerated cycle that is upgraded to a full GC, this object + // will have survived two GC cycles. If the tenuring age is very low (1), this object may be promoted. + // In this case, we have an allocated object, but it has received no stores yet. If card marking barriers + // have been elided, we could end up with an object in old holding pointers to young that won't be in + // the remembered set. The solution here is conservative, but this problem should be rare, and it will + // correct itself on subsequent cycles when the remembered set is updated. + ShenandoahGenerationalHeap::heap()->old_generation()->card_scan()->mark_range_as_dirty( + cast_from_oop(new_obj), new_obj->size() + ); + } #endif // COMPILER2_OR_JVMCI - assert(thread->deferred_card_mark().is_empty(), "We don't use this"); } void ShenandoahBarrierSet::on_thread_create(Thread* thread) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp index e6597b3c1e4..76e4412cfed 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp @@ -142,6 +142,11 @@ class ShenandoahGeneration : public CHeapObj, public ShenandoahSpaceInfo { size_t soft_available() const override; size_t bytes_allocated_since_gc_start() const override; + + // Reset the bytes allocated within this generation since the start of GC. The argument initial_bytes_allocated + // is normally zero. In the case that some memory was allocated following the last allocation rate sample that + // precedes the start of GC, the number of bytes allocated is supplied as the initial value of bytes_allocated_since_gc_start. + // We will behave as if these bytes were allocated after the start of GC. void reset_bytes_allocated_since_gc_start(size_t initial_bytes_allocated); void increase_allocated(size_t bytes); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index b2fd32d2fd0..84f5c3362ac 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1576,8 +1576,8 @@ void ShenandoahHeap::collect_as_vm_thread(GCCause::Cause cause) { // cycle. We _could_ cancel the concurrent cycle and then try to run a cycle directly // on the VM thread, but this would confuse the control thread mightily and doesn't // seem worth the trouble. Instead, we will have the caller thread run (and wait for) a - // concurrent cycle in the prologue of the heap inspect/dump operation. This is how - // other concurrent collectors in the JVM handle this scenario as well. + // concurrent cycle in the prologue of the heap inspect/dump operation (see VM_HeapDumper::doit_prologue). + // This is how other concurrent collectors in the JVM handle this scenario as well. assert(Thread::current()->is_VM_thread(), "Should be the VM thread"); guarantee(cause == GCCause::_heap_dump || cause == GCCause::_heap_inspection, "Invalid cause"); } @@ -1587,7 +1587,10 @@ void ShenandoahHeap::collect(GCCause::Cause cause) { } void ShenandoahHeap::do_full_collection(bool clear_all_soft_refs) { - //assert(false, "Shouldn't need to do full collections"); + // This method is only called by `CollectedHeap::collect_as_vm_thread`, which we have + // overridden to do nothing. See the comment there for an explanation of how heap inspections + // work for Shenandoah. + ShouldNotReachHere(); } HeapWord* ShenandoahHeap::block_start(const void* addr) const { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp index 0babeaffd3e..40eee8c342b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp @@ -74,8 +74,8 @@ void ShenandoahMarkingContext::initialize_top_at_mark_start(ShenandoahHeapRegion _top_at_mark_starts_base[idx] = bottom; _top_bitmaps[idx] = bottom; - log_debug(gc)("SMC:initialize_top_at_mark_start for Region %zu, TAMS: " PTR_FORMAT ", TopOfBitMap: " PTR_FORMAT, - r->index(), p2i(bottom), p2i(r->end())); + log_debug(gc, mark)("SMC:initialize_top_at_mark_start for Region %zu, TAMS: " PTR_FORMAT ", TopOfBitMap: " PTR_FORMAT, + r->index(), p2i(bottom), p2i(r->end())); } HeapWord* ShenandoahMarkingContext::top_bitmap(ShenandoahHeapRegion* r) { @@ -86,8 +86,8 @@ void ShenandoahMarkingContext::clear_bitmap(ShenandoahHeapRegion* r) { HeapWord* bottom = r->bottom(); HeapWord* top_bitmap = _top_bitmaps[r->index()]; - log_debug(gc)("SMC:clear_bitmap for %s Region %zu, top_bitmap: " PTR_FORMAT, - r->affiliation_name(), r->index(), p2i(top_bitmap)); + log_debug(gc, mark)("SMC:clear_bitmap for %s Region %zu, top_bitmap: " PTR_FORMAT, + r->affiliation_name(), r->index(), p2i(top_bitmap)); if (top_bitmap > bottom) { _mark_bit_map.clear_range_large(MemRegion(bottom, top_bitmap)); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp index e3ba774283c..bff4afc9ce9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp @@ -104,8 +104,8 @@ inline void ShenandoahMarkingContext::capture_top_at_mark_start(ShenandoahHeapRe "Region %zu, bitmap should be clear while adjusting TAMS: " PTR_FORMAT " -> " PTR_FORMAT, idx, p2i(old_tams), p2i(new_tams)); - log_debug(gc)("Capturing TAMS for %s Region %zu, was: " PTR_FORMAT ", now: " PTR_FORMAT, - r->affiliation_name(), idx, p2i(old_tams), p2i(new_tams)); + log_debug(gc, mark)("Capturing TAMS for %s Region %zu, was: " PTR_FORMAT ", now: " PTR_FORMAT, + r->affiliation_name(), idx, p2i(old_tams), p2i(new_tams)); _top_at_mark_starts_base[idx] = new_tams; _top_bitmaps[idx] = new_tams; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp index 774c4f7d941..964b6f0a10a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp @@ -58,6 +58,7 @@ void ShenandoahRegulatorThread::run_service() { void ShenandoahRegulatorThread::regulate_young_and_old_cycles() { while (!should_terminate()) { + SuspendibleThreadSetJoiner joiner; ShenandoahGenerationalControlThread::GCMode mode = _control_thread->gc_mode(); if (mode == ShenandoahGenerationalControlThread::none) { if (should_start_metaspace_gc()) { @@ -95,6 +96,7 @@ void ShenandoahRegulatorThread::regulate_young_and_old_cycles() { void ShenandoahRegulatorThread::regulate_young_and_global_cycles() { while (!should_terminate()) { + SuspendibleThreadSetJoiner joiner; if (_control_thread->gc_mode() == ShenandoahGenerationalControlThread::none) { if (start_global_cycle()) { log_debug(gc)("Heuristics request for global collection accepted."); @@ -122,6 +124,7 @@ void ShenandoahRegulatorThread::regulator_sleep() { _last_sleep_adjust_time = current; } + SuspendibleThreadSetLeaver leaver; os::naked_short_sleep(_sleep); if (LogTarget(Debug, gc, thread)::is_enabled()) { double elapsed = os::elapsedTime() - current; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp index a56113868be..9e6b1960708 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp @@ -58,9 +58,9 @@ void ShenandoahJavaThreadsIterator::threads_do(ThreadClosure* cl, uint worker_id } ShenandoahThreadRoots::ShenandoahThreadRoots(ShenandoahPhaseTimings::Phase phase, bool is_par) : - _phase(phase), _is_par(is_par) { - Threads::change_thread_claim_token(); -} + _phase(phase), + _is_par(is_par), + _threads_claim_token_scope() {} void ShenandoahThreadRoots::oops_do(OopClosure* oops_cl, NMethodClosure* code_cl, uint worker_id) { ShenandoahWorkerTimingsTracker timer(_phase, ShenandoahPhaseTimings::ThreadRoots, worker_id); @@ -74,10 +74,6 @@ void ShenandoahThreadRoots::threads_do(ThreadClosure* tc, uint worker_id) { Threads::possibly_parallel_threads_do(_is_par, tc); } -ShenandoahThreadRoots::~ShenandoahThreadRoots() { - Threads::assert_all_threads_claimed(); -} - ShenandoahCodeCacheRoots::ShenandoahCodeCacheRoots(ShenandoahPhaseTimings::Phase phase) : _phase(phase) { } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp index 40d4077256d..29d8c9fac2d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp @@ -33,6 +33,7 @@ #include "gc/shenandoah/shenandoahSharedVariables.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "memory/iterator.hpp" +#include "runtime/threads.hpp" template class ShenandoahVMWeakRoots { @@ -87,10 +88,10 @@ class ShenandoahJavaThreadsIterator { class ShenandoahThreadRoots { private: ShenandoahPhaseTimings::Phase _phase; - const bool _is_par; + const bool _is_par; + ThreadsClaimTokenScope _threads_claim_token_scope; public: ShenandoahThreadRoots(ShenandoahPhaseTimings::Phase phase, bool is_par); - ~ShenandoahThreadRoots(); void oops_do(OopClosure* oops_cl, NMethodClosure* code_cl, uint worker_id); void threads_do(ThreadClosure* tc, uint worker_id); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp index 23c705348c4..4a0215f15f1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp @@ -683,9 +683,9 @@ void ShenandoahScanRememberedTask::do_work(uint worker_id) { struct ShenandoahRegionChunk assignment; while (_work_list->next(&assignment)) { ShenandoahHeapRegion* region = assignment._r; - log_debug(gc)("ShenandoahScanRememberedTask::do_work(%u), processing slice of region " - "%zu at offset %zu, size: %zu", - worker_id, region->index(), assignment._chunk_offset, assignment._chunk_size); + log_debug(gc, remset)("ShenandoahScanRememberedTask::do_work(%u), processing slice of region " + "%zu at offset %zu, size: %zu", + worker_id, region->index(), assignment._chunk_offset, assignment._chunk_size); if (region->is_old()) { size_t cluster_size = CardTable::card_size_in_words() * ShenandoahCardCluster::CardsPerCluster; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp index 8ca32a32338..f3fafdc6a2a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp @@ -343,9 +343,9 @@ ShenandoahScanRemembered::process_region_slice(ShenandoahHeapRegion *region, siz } } - log_debug(gc)("Remembered set scan processing Region %zu, from " PTR_FORMAT " to " PTR_FORMAT ", using %s table", - region->index(), p2i(start_of_range), p2i(end_of_range), - use_write_table? "read/write (updating)": "read (marking)"); + log_debug(gc, remset)("Remembered set scan processing Region %zu, from " PTR_FORMAT " to " PTR_FORMAT ", using %s table", + region->index(), p2i(start_of_range), p2i(end_of_range), + use_write_table? "read/write (updating)": "read (marking)"); // Note that end_of_range may point to the middle of a cluster because we limit scanning to // region->top() or region->get_update_watermark(). We avoid processing past end_of_range. diff --git a/src/hotspot/share/gc/z/zNUMA.hpp b/src/hotspot/share/gc/z/zNUMA.hpp index de74086b10a..838a114c210 100644 --- a/src/hotspot/share/gc/z/zNUMA.hpp +++ b/src/hotspot/share/gc/z/zNUMA.hpp @@ -53,6 +53,8 @@ class ZNUMA : public AllStatic { static size_t calculate_share(uint32_t numa_id, size_t total, size_t granule = ZGranuleSize, uint32_t ignore_count = 0); static const char* to_string(); + + static int numa_id_to_node(uint32_t numa_id); }; #endif // SHARE_GC_Z_ZNUMA_HPP diff --git a/src/hotspot/share/gc/z/zPhysicalMemoryManager.cpp b/src/hotspot/share/gc/z/zPhysicalMemoryManager.cpp index 1a38efb89fd..2e7a97028ff 100644 --- a/src/hotspot/share/gc/z/zPhysicalMemoryManager.cpp +++ b/src/hotspot/share/gc/z/zPhysicalMemoryManager.cpp @@ -108,7 +108,7 @@ void ZPhysicalMemoryManager::try_enable_uncommit(size_t min_capacity, size_t max // Test if uncommit is supported by the operating system by committing // and then uncommitting a granule. const ZVirtualMemory vmem(zoffset(0), ZGranuleSize); - if (!commit(vmem, (uint32_t)-1) || !uncommit(vmem)) { + if (!commit(vmem, 0) || !uncommit(vmem)) { log_info_p(gc, init)("Uncommit: Implicitly Disabled (Not supported by operating system)"); FLAG_SET_ERGO(ZUncommit, false); return; @@ -293,7 +293,7 @@ void ZPhysicalMemoryManager::map(const ZVirtualMemory& vmem, uint32_t numa_id) c // Setup NUMA preferred for large pages if (ZNUMA::is_enabled() && ZLargePages::is_explicit()) { - os::numa_make_local((char*)addr, size, (int)numa_id); + os::numa_make_local((char*)addr, size, ZNUMA::numa_id_to_node(numa_id)); } } diff --git a/src/hotspot/share/interpreter/abstractInterpreter.cpp b/src/hotspot/share/interpreter/abstractInterpreter.cpp index 63f622eaa83..68b596b807d 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.cpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp @@ -264,7 +264,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 89d97053b45..412951691c5 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 5163bc7f6a5..309ae961808 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 f6f5627d722..2d2a910fced 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -958,7 +958,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", @@ -969,15 +969,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 506fb1f8a4e..e317ed02d04 100644 --- a/src/hotspot/share/memory/universe.hpp +++ b/src/hotspot/share/memory/universe.hpp @@ -319,7 +319,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/nmt/mallocHeader.cpp b/src/hotspot/share/nmt/mallocHeader.cpp index 2b59a2b6648..d88b5c790fb 100644 --- a/src/hotspot/share/nmt/mallocHeader.cpp +++ b/src/hotspot/share/nmt/mallocHeader.cpp @@ -26,6 +26,7 @@ #include "nmt/mallocHeader.inline.hpp" #include "nmt/mallocSiteTable.hpp" #include "nmt/memTag.hpp" +#include "nmt/memTracker.hpp" #include "runtime/os.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -36,7 +37,7 @@ // fitting into eight bits. STATIC_ASSERT(sizeof(MemTag) == sizeof(uint8_t)); -void MallocHeader::print_block_on_error(outputStream* st, address bad_address) const { +void MallocHeader::print_block_on_error(outputStream* st, address bad_address, address block_address) const { assert(bad_address >= (address)this, "sanity"); // This function prints block information, including hex dump, in case of a detected @@ -48,6 +49,18 @@ void MallocHeader::print_block_on_error(outputStream* st, address bad_address) c st->print_cr("NMT Block at " PTR_FORMAT ", corruption at: " PTR_FORMAT ": ", p2i(this), p2i(bad_address)); + if (MemTracker::tracking_level() == NMT_TrackingLevel::NMT_detail) { + MallocHeader* mh = (MallocHeader*)block_address; + NativeCallStack stack; + if (MallocSiteTable::access_stack(stack, *mh)) { + st->print_cr("allocated from:"); + stack.print_on(st); + } else { + st->print_cr("allocation-site cannot be shown since the marker is also corrupted."); + } + st->print_cr(""); + } + static const size_t min_dump_length = 256; address from1 = align_down((address)this, sizeof(void*)) - (min_dump_length / 2); address to1 = from1 + min_dump_length; diff --git a/src/hotspot/share/nmt/mallocHeader.hpp b/src/hotspot/share/nmt/mallocHeader.hpp index 8472b5f8ce8..acfc7401268 100644 --- a/src/hotspot/share/nmt/mallocHeader.hpp +++ b/src/hotspot/share/nmt/mallocHeader.hpp @@ -106,7 +106,7 @@ class MallocHeader { // We discount sizes larger than these static const size_t max_reasonable_malloc_size = LP64_ONLY(256 * G) NOT_LP64(3500 * M); - void print_block_on_error(outputStream* st, address bad_address) const; + void print_block_on_error(outputStream* st, address bad_address, address block_address) const; static uint16_t build_footer(uint8_t b1, uint8_t b2) { return (uint16_t)(((uint16_t)b1 << 8) | (uint16_t)b2); } diff --git a/src/hotspot/share/nmt/mallocHeader.inline.hpp b/src/hotspot/share/nmt/mallocHeader.inline.hpp index 8b1862332fc..7bc8a25028c 100644 --- a/src/hotspot/share/nmt/mallocHeader.inline.hpp +++ b/src/hotspot/share/nmt/mallocHeader.inline.hpp @@ -103,7 +103,7 @@ inline OutTypeParam MallocHeader::resolve_checked_impl(InTypeParam memblock) { } OutTypeParam header_pointer = (OutTypeParam)memblock - 1; if (!header_pointer->check_block_integrity(msg, sizeof(msg), &corruption)) { - header_pointer->print_block_on_error(tty, corruption != nullptr ? corruption : (address)header_pointer); + header_pointer->print_block_on_error(tty, corruption != nullptr ? corruption : (address)header_pointer, (address)header_pointer); fatal("NMT has detected a memory corruption bug. Block at " PTR_FORMAT ": %s", p2i(memblock), msg); } return header_pointer; diff --git a/src/hotspot/share/nmt/mallocSiteTable.cpp b/src/hotspot/share/nmt/mallocSiteTable.cpp index c9ddffce5ec..0150a25cae3 100644 --- a/src/hotspot/share/nmt/mallocSiteTable.cpp +++ b/src/hotspot/share/nmt/mallocSiteTable.cpp @@ -163,13 +163,17 @@ MallocSite* MallocSiteTable::lookup_or_add(const NativeCallStack& key, uint32_t* // Access malloc site MallocSite* MallocSiteTable::malloc_site(uint32_t marker) { uint16_t bucket_idx = bucket_idx_from_marker(marker); - assert(bucket_idx < table_size, "Invalid bucket index"); + if (bucket_idx >= table_size) { + return nullptr; + } const uint16_t pos_idx = pos_idx_from_marker(marker); MallocSiteHashtableEntry* head = _table[bucket_idx]; for (size_t index = 0; index < pos_idx && head != nullptr; index++, head = (MallocSiteHashtableEntry*)head->next()) {} - assert(head != nullptr, "Invalid position index"); + if (head == nullptr) { + return nullptr; + } return head->data(); } diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 92fbc4363da..7ff9e31a516 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -541,18 +541,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 41d95467af2..fa94a0d2c0b 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -431,26 +431,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); } @@ -471,32 +470,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); } @@ -511,29 +509,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/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index dd5cd6a6dd3..9dbe17f7400 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -75,7 +75,7 @@ void StartNode::calling_convention(BasicType* sig_bt, VMRegPair *parm_regs, uint //------------------------------Registers-------------------------------------- const RegMask &StartNode::in_RegMask(uint) const { - return RegMask::Empty; + return RegMask::EMPTY; } //------------------------------match------------------------------------------ @@ -85,7 +85,7 @@ Node *StartNode::match(const ProjNode *proj, const Matcher *match, const RegMask case TypeFunc::Control: case TypeFunc::I_O: case TypeFunc::Memory: - return new MachProjNode(this,proj->_con,RegMask::Empty,MachProjNode::unmatched_proj); + return new MachProjNode(this,proj->_con,RegMask::EMPTY,MachProjNode::unmatched_proj); case TypeFunc::FramePtr: return new MachProjNode(this,proj->_con,Matcher::c_frame_ptr_mask, Op_RegP); case TypeFunc::ReturnAdr: @@ -806,20 +806,20 @@ Node *CallNode::match(const ProjNode *proj, const Matcher *match, const RegMask* if(ideal_reg >= Op_VecA && ideal_reg <= Op_VecZ) { if(OptoReg::is_valid(regs.second())) { for (OptoReg::Name r = regs.first(); r <= regs.second(); r = OptoReg::add(r, 1)) { - rm.Insert(r); + rm.insert(r); } } } } if (OptoReg::is_valid(regs.second())) { - rm.Insert(regs.second()); + rm.insert(regs.second()); } return new MachProjNode(this,con,rm,ideal_reg); } else { assert(con == TypeFunc::Parms+1, "only one return value"); assert(range_cc->field_at(TypeFunc::Parms+1) == Type::HALF, ""); - return new MachProjNode(this,con, RegMask::Empty, (uint)OptoReg::Bad); + return new MachProjNode(this,con, RegMask::EMPTY, (uint)OptoReg::Bad); } } } @@ -828,7 +828,7 @@ Node *CallNode::match(const ProjNode *proj, const Matcher *match, const RegMask* case TypeFunc::Control: case TypeFunc::I_O: case TypeFunc::Memory: - return new MachProjNode(this,proj->_con,RegMask::Empty,MachProjNode::unmatched_proj); + return new MachProjNode(this,proj->_con,RegMask::EMPTY,MachProjNode::unmatched_proj); case TypeFunc::ReturnAdr: case TypeFunc::FramePtr: @@ -1693,12 +1693,14 @@ void SafePointNode::dump_spec(outputStream *st) const { #endif const RegMask &SafePointNode::in_RegMask(uint idx) const { - if( idx < TypeFunc::Parms ) return RegMask::Empty; + if (idx < TypeFunc::Parms) { + return RegMask::EMPTY; + } // Values outside the domain represent debug info return *(Compile::current()->matcher()->idealreg2debugmask[in(idx)->ideal_reg()]); } const RegMask &SafePointNode::out_RegMask() const { - return RegMask::Empty; + return RegMask::EMPTY; } @@ -1809,7 +1811,7 @@ const RegMask &SafePointScalarObjectNode::in_RegMask(uint idx) const { } const RegMask &SafePointScalarObjectNode::out_RegMask() const { - return RegMask::Empty; + return RegMask::EMPTY; } uint SafePointScalarObjectNode::match_edge(uint idx) const { @@ -1860,7 +1862,7 @@ const RegMask &SafePointScalarMergeNode::in_RegMask(uint idx) const { } const RegMask &SafePointScalarMergeNode::out_RegMask() const { - return RegMask::Empty; + return RegMask::EMPTY; } uint SafePointScalarMergeNode::match_edge(uint idx) const { diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 3a3f5b3bc21..77daf2d844d 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -1017,7 +1017,7 @@ bool RegionNode::optimize_trichotomy(PhaseIterGVN* igvn) { } const RegMask &RegionNode::out_RegMask() const { - return RegMask::Empty; + return RegMask::EMPTY; } #ifndef PRODUCT @@ -3072,13 +3072,15 @@ bool PhiNode::is_tripcount(BasicType bt) const { //------------------------------out_RegMask------------------------------------ const RegMask &PhiNode::in_RegMask(uint i) const { - return i ? out_RegMask() : RegMask::Empty; + return i ? out_RegMask() : RegMask::EMPTY; } const RegMask &PhiNode::out_RegMask() const { uint ideal_reg = _type->ideal_reg(); assert( ideal_reg != Node::NotAMachineReg, "invalid type at Phi" ); - if( ideal_reg == 0 ) return RegMask::Empty; + if (ideal_reg == 0) { + return RegMask::EMPTY; + } assert(ideal_reg != Op_RegFlags, "flags register is not spillable"); return *(Compile::current()->matcher()->idealreg2spillmask[ideal_reg]); } @@ -3105,22 +3107,22 @@ Node* GotoNode::Identity(PhaseGVN* phase) { } const RegMask &GotoNode::out_RegMask() const { - return RegMask::Empty; + return RegMask::EMPTY; } //============================================================================= const RegMask &JumpNode::out_RegMask() const { - return RegMask::Empty; + return RegMask::EMPTY; } //============================================================================= const RegMask &JProjNode::out_RegMask() const { - return RegMask::Empty; + return RegMask::EMPTY; } //============================================================================= const RegMask &CProjNode::out_RegMask() const { - return RegMask::Empty; + return RegMask::EMPTY; } diff --git a/src/hotspot/share/opto/cfgnode.hpp b/src/hotspot/share/opto/cfgnode.hpp index 8cd5ad4a058..913e15de2af 100644 --- a/src/hotspot/share/opto/cfgnode.hpp +++ b/src/hotspot/share/opto/cfgnode.hpp @@ -755,7 +755,7 @@ class BlackholeNode : public MultiNode { // Fake the incoming arguments mask for blackholes: accept all registers // and all stack slots. This would avoid any redundant register moves // for blackhole inputs. - return RegMask::All; + return RegMask::ALL; } #ifndef PRODUCT virtual void format(PhaseRegAlloc* ra, outputStream* st) const; diff --git a/src/hotspot/share/opto/chaitin.cpp b/src/hotspot/share/opto/chaitin.cpp index 6f2812d51c6..ebd529988c8 100644 --- a/src/hotspot/share/opto/chaitin.cpp +++ b/src/hotspot/share/opto/chaitin.cpp @@ -49,9 +49,11 @@ void LRG::dump() const { _mask.dump(); if( _msize_valid ) { if( mask_size() == compute_mask_size() ) tty->print(", #%d ",_mask_size); - else tty->print(", #!!!_%d_vs_%d ",_mask_size,_mask.Size()); + else { + tty->print(", #!!!_%d_vs_%d ", _mask_size, _mask.size()); + } } else { - tty->print(", #?(%d) ",_mask.Size()); + tty->print(", #?(%d) ", _mask.size()); } tty->print("EffDeg: "); @@ -741,7 +743,7 @@ void PhaseChaitin::Register_Allocate() { } } else { // Misaligned; extract 2 bits OptoReg::Name hi = lrg.reg(); // Get hi register - lrg.Remove(hi); // Yank from mask + lrg.remove(hi); // Yank from mask int lo = lrg.mask().find_first_elem(); // Find lo set_pair(i, hi, lo); } @@ -773,7 +775,7 @@ void PhaseChaitin::de_ssa() { Node *n = block->get_node(j); // Pre-color to the zero live range, or pick virtual register const RegMask &rm = n->out_RegMask(); - _lrg_map.map(n->_idx, !rm.is_Empty() ? lr_counter++ : 0); + _lrg_map.map(n->_idx, !rm.is_empty() ? lr_counter++ : 0); } } @@ -794,7 +796,7 @@ void PhaseChaitin::mark_ssa() { Node *n = block->get_node(j); // Pre-color to the zero live range, or pick virtual register const RegMask &rm = n->out_RegMask(); - _lrg_map.map(n->_idx, !rm.is_Empty() ? n->_idx : 0); + _lrg_map.map(n->_idx, !rm.is_empty() ? n->_idx : 0); max_idx = (n->_idx > max_idx) ? n->_idx : max_idx; } } @@ -879,7 +881,7 @@ void PhaseChaitin::gather_lrg_masks( bool after_aggressive ) { // Limit result register mask to acceptable registers const RegMask &rm = n->out_RegMask(); - lrg.AND( rm ); + lrg.and_with(rm); uint ireg = n->ideal_reg(); assert( !n->bottom_type()->isa_oop_ptr() || ireg == Op_RegP, @@ -935,7 +937,7 @@ void PhaseChaitin::gather_lrg_masks( bool after_aggressive ) { switch (ireg) { case MachProjNode::fat_proj: // Fat projections have size equal to number of registers killed - lrg.set_num_regs(rm.Size()); + lrg.set_num_regs(rm.size()); lrg.set_reg_pressure(lrg.num_regs()); lrg._fat_proj = 1; lrg._is_bound = 1; @@ -1126,7 +1128,7 @@ void PhaseChaitin::gather_lrg_masks( bool after_aggressive ) { // Later, AFTER aggressive, this live range will have to spill // but the spiller handles slow-path calls very nicely. } else { - lrg.AND( rm ); + lrg.and_with(rm); } // Check for bound register masks @@ -1164,7 +1166,7 @@ void PhaseChaitin::gather_lrg_masks( bool after_aggressive ) { if (!is_vect && !n->is_SpillCopy() && (lrg._def == nullptr || lrg.is_multidef() || !lrg._def->is_SpillCopy()) && lrgmask.is_misaligned_pair()) { - lrg.Clear(); + lrg.clear(); } // Check for maximum frequency value @@ -1405,7 +1407,7 @@ void PhaseChaitin::Simplify( ) { // Is 'reg' register legal for 'lrg'? static bool is_legal_reg(LRG& lrg, OptoReg::Name reg) { - if (lrg.mask().can_represent(reg) && lrg.mask().Member(reg)) { + if (lrg.mask().can_represent(reg) && lrg.mask().member(reg)) { // RA uses OptoReg which represent the highest element of a registers set. // For example, vectorX (128bit) on x86 uses [XMM,XMMb,XMMc,XMMd] set // in which XMMd is used by RA to represent such vectors. A double value @@ -1459,7 +1461,7 @@ static OptoReg::Name find_first_set(LRG& lrg, RegMask& mask) { return assigned; } else { // Remove more for each iteration - mask.Remove(assigned - num_regs + 1); // Unmask the lowest reg + mask.remove(assigned - num_regs + 1); // Unmask the lowest reg mask.clear_to_sets(RegMask::SlotsPerVecA); // Align by SlotsPerVecA bits assigned = mask.find_first_set(lrg, num_regs); } @@ -1510,7 +1512,7 @@ OptoReg::Name PhaseChaitin::bias_color(LRG& lrg) { // Choose a color which is legal for him ResourceMark rm(C->regmask_arena()); RegMask tempmask(lrg.mask(), C->regmask_arena()); - tempmask.AND(lrgs(copy_lrg).mask()); + tempmask.and_with(lrgs(copy_lrg).mask()); tempmask.clear_to_sets(lrg.num_regs()); OptoReg::Name reg = find_first_set(lrg, tempmask); if (OptoReg::is_valid(reg)) @@ -1533,9 +1535,9 @@ OptoReg::Name PhaseChaitin::bias_color(LRG& lrg) { if( (++_alternate & 1) && OptoReg::is_valid(reg) ) { // This 'Remove; find; Insert' idiom is an expensive way to find the // SECOND element in the mask. - lrg.Remove(reg); + lrg.remove(reg); OptoReg::Name reg2 = lrg.mask().find_first_elem(); - lrg.Insert(reg); + lrg.insert(reg); if (OptoReg::is_reg(reg2)) { reg = reg2; } @@ -1545,8 +1547,8 @@ OptoReg::Name PhaseChaitin::bias_color(LRG& lrg) { // Choose a color in the current chunk OptoReg::Name PhaseChaitin::choose_color(LRG& lrg) { - assert(C->in_preserve_stack_slots() == 0 || lrg.mask().is_offset() || lrg._is_bound || lrg.mask().is_bound1() || !lrg.mask().Member(OptoReg::Name(_matcher._old_SP - 1)), "must not allocate stack0 (inside preserve area)"); - assert(C->out_preserve_stack_slots() == 0 || lrg.mask().is_offset() || lrg._is_bound || lrg.mask().is_bound1() || !lrg.mask().Member(OptoReg::Name(_matcher._old_SP + 0)), "must not allocate stack0 (inside preserve area)"); + assert(C->in_preserve_stack_slots() == 0 || lrg.mask().is_offset() || lrg._is_bound || lrg.mask().is_bound1() || !lrg.mask().member(OptoReg::Name(_matcher._old_SP - 1)), "must not allocate stack0 (inside preserve area)"); + assert(C->out_preserve_stack_slots() == 0 || lrg.mask().is_offset() || lrg._is_bound || lrg.mask().is_bound1() || !lrg.mask().member(OptoReg::Name(_matcher._old_SP + 0)), "must not allocate stack0 (inside preserve area)"); if( lrg.num_regs() == 1 || // Common Case !lrg._fat_proj ) // Aligned+adjacent pairs ok @@ -1622,20 +1624,20 @@ uint PhaseChaitin::Select( ) { // at retry_next_chunk. if (nreg < LRG::SPILL_REG) { #ifndef PRODUCT - uint size = lrg->mask().Size(); + uint size = lrg->mask().size(); ResourceMark rm(C->regmask_arena()); RegMask trace_mask(lrg->mask(), C->regmask_arena()); #endif - lrg->SUBTRACT_inner(nlrg.mask()); + lrg->subtract_inner(nlrg.mask()); #ifndef PRODUCT - if (trace_spilling() && lrg->mask().Size() != size) { + if (trace_spilling() && lrg->mask().size() != size) { ttyLocker ttyl; tty->print("L%d ", lidx); trace_mask.dump(); tty->print(" intersected L%d ", neighbor); nlrg.mask().dump(); tty->print(" removed "); - trace_mask.SUBTRACT(lrg->mask()); + trace_mask.subtract(lrg->mask()); trace_mask.dump(); tty->print(" leaving "); lrg->mask().dump(); @@ -1701,15 +1703,15 @@ uint PhaseChaitin::Select( ) { } else { assert(!lrg->_is_vector || n_regs <= RegMask::SlotsPerVecZ, "sanity"); } - lrg->Clear(); // Clear the mask - lrg->Insert(reg); // Set regmask to match selected reg + lrg->clear(); // Clear the mask + lrg->insert(reg); // Set regmask to match selected reg // For vectors and pairs, also insert the low bit of the pair // We always choose the high bit, then mask the low bits by register size if (lrg->is_scalable() && OptoReg::is_stack(lrg->reg())) { // stack n_regs = lrg->scalable_reg_slots(); } for (int i = 1; i < n_regs; i++) { - lrg->Insert(OptoReg::add(reg,-i)); + lrg->insert(OptoReg::add(reg, -i)); } lrg->set_mask_size(n_regs); } else { // Else fatproj diff --git a/src/hotspot/share/opto/chaitin.hpp b/src/hotspot/share/opto/chaitin.hpp index 9b3f8123ac2..b477c54fcae 100644 --- a/src/hotspot/share/opto/chaitin.hpp +++ b/src/hotspot/share/opto/chaitin.hpp @@ -103,11 +103,11 @@ class LRG : public ResourceObj { private: RegMask _mask; // Allowed registers for this LRG - uint _mask_size; // cache of _mask.Size(); + uint _mask_size; // cache of _mask.size(); public: - int compute_mask_size() const { return _mask.is_infinite_stack() ? INFINITE_STACK_SIZE : _mask.Size(); } + int compute_mask_size() const { return _mask.is_infinite_stack() ? INFINITE_STACK_SIZE : _mask.size(); } void set_mask_size( int size ) { - assert((size == (int)INFINITE_STACK_SIZE) || (size == (int)_mask.Size()), ""); + assert((size == (int)INFINITE_STACK_SIZE) || (size == (int)_mask.size()), ""); _mask_size = size; #ifdef ASSERT _msize_valid=1; @@ -128,17 +128,17 @@ 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( const RegMask &rm ) { _mask.AND(rm); DEBUG_ONLY(_msize_valid=0;)} - void SUBTRACT( const RegMask &rm ) { _mask.SUBTRACT(rm); DEBUG_ONLY(_msize_valid=0;)} - void SUBTRACT_inner(const RegMask& rm) { _mask.SUBTRACT_inner(rm); DEBUG_ONLY(_msize_valid = 0;) } - void Clear() { _mask.Clear() ; DEBUG_ONLY(_msize_valid=1); _mask_size = 0; } - void Set_All() { _mask.Set_All(); DEBUG_ONLY(_msize_valid = 1); _mask_size = _mask.rm_size_in_bits(); } + 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;)} + void subtract_inner(const RegMask& rm) { _mask.subtract_inner(rm); DEBUG_ONLY(_msize_valid = 0;) } + void clear() { _mask.clear() ; DEBUG_ONLY(_msize_valid=1); _mask_size = 0; } + void set_all() { _mask.set_all(); DEBUG_ONLY(_msize_valid = 1); _mask_size = _mask.rm_size_in_bits(); } bool rollover() { DEBUG_ONLY(_msize_valid = 1); _mask_size = _mask.rm_size_in_bits(); return _mask.rollover(); } - void Insert( OptoReg::Name reg ) { _mask.Insert(reg); DEBUG_ONLY(_msize_valid=0;) } - void Remove( OptoReg::Name reg ) { _mask.Remove(reg); DEBUG_ONLY(_msize_valid=0;) } + void insert( OptoReg::Name reg ) { _mask.insert(reg); DEBUG_ONLY(_msize_valid=0;) } + void remove( OptoReg::Name reg ) { _mask.remove(reg); DEBUG_ONLY(_msize_valid=0;) } void clear_to_sets() { _mask.clear_to_sets(_num_regs); DEBUG_ONLY(_msize_valid=0;) } private: @@ -624,7 +624,7 @@ class PhaseChaitin : public PhaseRegAlloc { void check_pressure_at_fatproj(uint fatproj_location, RegMask& fatproj_mask) { // this pressure is only valid at this instruction, i.e. we don't need to lower // the register pressure since the fat proj was never live before (going backwards) - uint new_pressure = current_pressure() + fatproj_mask.Size(); + uint new_pressure = current_pressure() + fatproj_mask.size(); if (new_pressure > final_pressure()) { _final_pressure = new_pressure; } diff --git a/src/hotspot/share/opto/coalesce.cpp b/src/hotspot/share/opto/coalesce.cpp index 90a2dd0e152..82c1f7050c7 100644 --- a/src/hotspot/share/opto/coalesce.cpp +++ b/src/hotspot/share/opto/coalesce.cpp @@ -118,7 +118,7 @@ void PhaseCoalesce::combine_these_two(Node *n1, Node *n2) { // Merge in the IFG _phc._ifg->Union( lr1, lr2 ); // Combine register restrictions - lrg1->AND(lrg2->mask()); + lrg1->and_with(lrg2->mask()); } } } @@ -503,8 +503,8 @@ void PhaseConservativeCoalesce::union_helper( Node *lr1_node, Node *lr2_node, ui lrgs(lr2).is_multidef() ) ? NodeSentinel : src_def; lrgs(lr2)._def = nullptr; // No def for lrg 2 - lrgs(lr2).Clear(); // Force empty mask for LRG 2 - //lrgs(lr2)._size = 0; // Live-range 2 goes dead + lrgs(lr2).clear(); // Force empty mask for LRG 2 + // lrgs(lr2)._size = 0; // Live-range 2 goes dead lrgs(lr1)._is_oop |= lrgs(lr2)._is_oop; lrgs(lr2)._is_oop = 0; // In particular, not an oop for GC info @@ -570,9 +570,9 @@ uint PhaseConservativeCoalesce::compute_separating_interferences(Node *dst_copy, // If we attempt to coalesce across a bound def if( lrgs(lidx).is_bound() ) { // Do not let the coalesced LRG expect to get the bound color - rm.SUBTRACT( lrgs(lidx).mask() ); + rm.subtract(lrgs(lidx).mask()); // Recompute rm_size - rm_size = rm.Size(); + rm_size = rm.size(); //if( rm._flags ) rm_size += 1000000; if( reg_degree >= rm_size ) return max_juint; } @@ -695,9 +695,9 @@ bool PhaseConservativeCoalesce::copy_copy(Node *dst_copy, Node *src_copy, Block // intersecting their allowed register sets. ResourceMark rm(C->regmask_arena()); RegMask mask(lrgs(lr1).mask(), C->regmask_arena()); - mask.AND(lrgs(lr2).mask()); + mask.and_with(lrgs(lr2).mask()); // Number of bits free - uint rm_size = mask.Size(); + uint rm_size = mask.size(); if (UseFPUForSpilling && mask.is_infinite_stack() ) { // Don't coalesce when frequency difference is large diff --git a/src/hotspot/share/opto/connode.hpp b/src/hotspot/share/opto/connode.hpp index 47888587960..8cf3eea7570 100644 --- a/src/hotspot/share/opto/connode.hpp +++ b/src/hotspot/share/opto/connode.hpp @@ -43,8 +43,8 @@ class ConNode : public TypeNode { } virtual int Opcode() const; virtual uint hash() const; - virtual const RegMask &out_RegMask() const { return RegMask::Empty; } - virtual const RegMask &in_RegMask(uint) const { return RegMask::Empty; } + virtual const RegMask& out_RegMask() const { return RegMask::EMPTY; } + virtual const RegMask& in_RegMask(uint) const { return RegMask::EMPTY; } virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) { return Node::Ideal(phase, can_reshape); diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index 5ae87eae507..b3b21961b56 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, const RegMa 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, const RegMa 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, const RegM 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, const Reg 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 dd6c73643d1..46125b620d8 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -1334,9 +1334,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/gcm.cpp b/src/hotspot/share/opto/gcm.cpp index d00bd151f6e..b1aeb670934 100644 --- a/src/hotspot/share/opto/gcm.cpp +++ b/src/hotspot/share/opto/gcm.cpp @@ -1449,8 +1449,9 @@ Block* PhaseCFG::hoist_to_cheaper_block(Block* LCA, Block* early, Node* self) { // single register. Hoisting stretches the live range of the // single register and may force spilling. MachNode* mach = self->is_Mach() ? self->as_Mach() : nullptr; - if (mach != nullptr && mach->out_RegMask().is_bound1() && !mach->out_RegMask().is_Empty()) + if (mach != nullptr && mach->out_RegMask().is_bound1() && !mach->out_RegMask().is_empty()) { in_latency = true; + } #ifndef PRODUCT if (trace_opto_pipelining()) { diff --git a/src/hotspot/share/opto/ifg.cpp b/src/hotspot/share/opto/ifg.cpp index 438209df8f8..1480e806f76 100644 --- a/src/hotspot/share/opto/ifg.cpp +++ b/src/hotspot/share/opto/ifg.cpp @@ -55,7 +55,7 @@ void PhaseIFG::init( uint maxlrg ) { for( uint i = 0; i < maxlrg; i++ ) { _adjs[i].initialize(maxlrg); _lrgs[i].init_mask(_arena); - _lrgs[i].Set_All(); + _lrgs[i].set_all(); } } @@ -655,7 +655,7 @@ bool PhaseChaitin::remove_node_if_not_used(Block* b, uint location, Node* n, uin void PhaseChaitin::check_for_high_pressure_transition_at_fatproj(uint& block_reg_pressure, uint location, LRG& lrg, Pressure& pressure, const int op_regtype) { ResourceMark rm(C->regmask_arena()); RegMask mask_tmp(lrg.mask(), C->regmask_arena()); - mask_tmp.AND(*Matcher::idealreg2regmask[op_regtype]); + mask_tmp.and_with(*Matcher::idealreg2regmask[op_regtype]); pressure.check_pressure_at_fatproj(location, mask_tmp); } @@ -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,21 +738,21 @@ 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. - interfering_lrg.SUBTRACT(r2mask); + interfering_lrg.subtract(r2mask); interfering_lrg.compute_set_mask_size(); } else if (r_size != 1) { // fat proj - interfering_lrg.SUBTRACT(mask); + interfering_lrg.subtract(mask); interfering_lrg.compute_set_mask_size(); } else { // Common case: size 1 bound removal OptoReg::Name r_reg = mask.find_first_elem(); - if (interfering_lrg.mask().Member(r_reg)) { - interfering_lrg.Remove(r_reg); + if (interfering_lrg.mask().member(r_reg)) { + interfering_lrg.remove(r_reg); interfering_lrg.set_mask_size(interfering_lrg.mask().is_infinite_stack() ? LRG::INFINITE_STACK_SIZE : old_size - 1); } } @@ -933,7 +933,7 @@ uint PhaseChaitin::build_ifg_physical( ResourceArea *a ) { // Since rematerializable DEFs are not bound but the live range is, // some uses must be bound. If we spill live range 'r', it can // rematerialize at each use site according to its bindings. - if (lrg.is_bound() && !n->rematerialize() && !lrg.mask().is_Empty()) { + if (lrg.is_bound() && !n->rematerialize() && !lrg.mask().is_empty()) { remove_bound_register_from_interfering_live_ranges(lrg, &liveout, must_spill); } interfere_with_live(lid, &liveout); diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 93611630df1..1e56df4809a 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -82,7 +82,7 @@ const Type* IfNode::Value(PhaseGVN* phase) const { } const RegMask &IfNode::out_RegMask() const { - return RegMask::Empty; + return RegMask::EMPTY; } //------------------------------split_if--------------------------------------- diff --git a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp index 8087f14d08d..5fd87c714e5 100644 --- a/src/hotspot/share/opto/lcm.cpp +++ b/src/hotspot/share/opto/lcm.cpp @@ -882,12 +882,12 @@ void PhaseCFG::needed_for_next_call(Block* block, Node* this_call, VectorSet& ne static void add_call_kills(MachProjNode *proj, RegMask& regs, const char* save_policy, bool exclude_soe) { // Fill in the kill mask for the call for( OptoReg::Name r = OptoReg::Name(0); r < _last_Mach_Reg; r=OptoReg::add(r,1) ) { - if( !regs.Member(r) ) { // Not already defined by the call + if (!regs.member(r)) { // Not already defined by the call // Save-on-call register? if ((save_policy[r] == 'C') || (save_policy[r] == 'A') || ((save_policy[r] == 'E') && exclude_soe)) { - proj->_rout.Insert(r); + proj->_rout.insert(r); } } } @@ -911,7 +911,7 @@ uint PhaseCFG::sched_call(Block* block, uint node_cnt, Node_List& worklist, Grow // Schedule next to call block->map_node(n, node_cnt++); // Collect defined registers - regs.OR(n->out_RegMask()); + regs.or_with(n->out_RegMask()); // Check for scheduling the next control-definer if( n->bottom_type() == Type::CONTROL ) // Warm up next pile of heuristic bits @@ -934,12 +934,12 @@ uint PhaseCFG::sched_call(Block* block, uint node_cnt, Node_List& worklist, Grow // Act as if the call defines the Frame Pointer. // Certainly the FP is alive and well after the call. - regs.Insert(_matcher.c_frame_pointer()); + regs.insert(_matcher.c_frame_pointer()); // Set all registers killed and not already defined by the call. uint r_cnt = mcall->tf()->range_cc()->cnt(); int op = mcall->ideal_Opcode(); - MachProjNode *proj = new MachProjNode( mcall, r_cnt+1, RegMask::Empty, MachProjNode::fat_proj ); + MachProjNode* proj = new MachProjNode(mcall, r_cnt + 1, RegMask::EMPTY, MachProjNode::fat_proj); map_node_to_block(proj, block); block->insert_node(proj, node_cnt++); @@ -1191,10 +1191,10 @@ bool PhaseCFG::schedule_local(Block* block, GrowableArray& ready_cnt, Vecto if (n->is_Mach() && n->as_Mach()->has_call()) { RegMask regs; - regs.Insert(_matcher.c_frame_pointer()); - regs.OR(n->out_RegMask()); + regs.insert(_matcher.c_frame_pointer()); + regs.or_with(n->out_RegMask()); - MachProjNode *proj = new MachProjNode( n, 1, RegMask::Empty, MachProjNode::fat_proj ); + MachProjNode* proj = new MachProjNode(n, 1, RegMask::EMPTY, MachProjNode::fat_proj); map_node_to_block(proj, block); block->insert_node(proj, phi_cnt++); diff --git a/src/hotspot/share/opto/loopUnswitch.cpp b/src/hotspot/share/opto/loopUnswitch.cpp index 6687eea8873..d5c50f4caa1 100644 --- a/src/hotspot/share/opto/loopUnswitch.cpp +++ b/src/hotspot/share/opto/loopUnswitch.cpp @@ -703,7 +703,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/machnode.cpp b/src/hotspot/share/opto/machnode.cpp index d4600d93f13..5e931756a26 100644 --- a/src/hotspot/share/opto/machnode.cpp +++ b/src/hotspot/share/opto/machnode.cpp @@ -546,7 +546,7 @@ bool MachNode::rematerialize() const { uint idx = oper_input_base(); if (req() > idx) { const RegMask &rm = in_RegMask(idx); - if (!rm.is_Empty() && rm.is_bound(ideal_reg())) { + if (!rm.is_empty() && rm.is_bound(ideal_reg())) { return false; } } @@ -640,8 +640,11 @@ void MachNullCheckNode::save_label( Label** label, uint* block_num ) { } const RegMask &MachNullCheckNode::in_RegMask( uint idx ) const { - if( idx == 0 ) return RegMask::Empty; - else return in(1)->as_Mach()->out_RegMask(); + if (idx == 0) { + return RegMask::EMPTY; + } else { + return in(1)->as_Mach()->out_RegMask(); + } } //============================================================================= diff --git a/src/hotspot/share/opto/machnode.hpp b/src/hotspot/share/opto/machnode.hpp index 1d6b26ad152..1be0208d430 100644 --- a/src/hotspot/share/opto/machnode.hpp +++ b/src/hotspot/share/opto/machnode.hpp @@ -773,7 +773,7 @@ class MachNullCheckNode : public MachBranchNode { virtual const class Type *bottom_type() const { return TypeTuple::IFBOTH; } virtual uint ideal_reg() const { return NotAMachineReg; } virtual const RegMask &in_RegMask(uint) const; - virtual const RegMask &out_RegMask() const { return RegMask::Empty; } + virtual const RegMask& out_RegMask() const { return RegMask::EMPTY; } #ifndef PRODUCT virtual const char *Name() const { return "NullCheck"; } virtual void format( PhaseRegAlloc *, outputStream *st ) const; @@ -805,7 +805,7 @@ class MachProjNode : public ProjNode { virtual int Opcode() const; virtual const Type *bottom_type() const; virtual const TypePtr *adr_type() const; - virtual const RegMask &in_RegMask(uint) const { return RegMask::Empty; } + virtual const RegMask& in_RegMask(uint) const { return RegMask::EMPTY; } virtual const RegMask &out_RegMask() const { return _rout; } virtual uint ideal_reg() const { return _ideal_reg; } // Need size_of() for virtual ProjNode::clone() diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index 05c48d4149f..87865164260 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -191,19 +191,19 @@ RegMask* Matcher::return_values_mask(const TypeFunc* tf) { if (C->needs_stack_repair()) { slot -= 2; // Account for stack increment value } - mask[--cnt].Clear(); - mask[cnt].Insert(OptoReg::stack2reg(slot)); + mask[--cnt].clear(); + mask[cnt].insert(OptoReg::stack2reg(slot)); } for (uint i = 0; i < cnt; i++) { - mask[i].Clear(); + mask[i].clear(); OptoReg::Name reg1 = OptoReg::as_OptoReg(vm_parm_regs[i].first()); if (OptoReg::is_valid(reg1)) { - mask[i].Insert(reg1); + mask[i].insert(reg1); } OptoReg::Name reg2 = OptoReg::as_OptoReg(vm_parm_regs[i].second()); if (OptoReg::is_valid(reg2)) { - mask[i].Insert(reg2); + mask[i].insert(reg2); } } @@ -221,12 +221,12 @@ void Matcher::match( ) { if (C->failing()) { return; } - assert(_return_addr_mask.is_Empty(), + assert(_return_addr_mask.is_empty(), "return address mask must be empty initially"); - _return_addr_mask.Insert(return_addr()); + _return_addr_mask.insert(return_addr()); #ifdef _LP64 // Pointers take 2 slots in 64-bit land - _return_addr_mask.Insert(OptoReg::add(return_addr(),1)); + _return_addr_mask.insert(OptoReg::add(return_addr(), 1)); #endif // Map Java-signature return types into return register-value @@ -294,7 +294,7 @@ void Matcher::match( ) { assert( is_even(_in_arg_limit), "out_preserve must be even" ); for( i = 0; i < argcnt; i++ ) { // Permit args to have no register - _calling_convention_mask[i].Clear(); + _calling_convention_mask[i].clear(); if( !vm_parm_regs[i].first()->is_valid() && !vm_parm_regs[i].second()->is_valid() ) { _parm_regs[i].set_bad(); continue; @@ -306,11 +306,11 @@ void Matcher::match( ) { OptoReg::Name reg1 = warp_incoming_stk_arg(vm_parm_regs[i].first()); if( OptoReg::is_valid(reg1)) - _calling_convention_mask[i].Insert(reg1); + _calling_convention_mask[i].insert(reg1); OptoReg::Name reg2 = warp_incoming_stk_arg(vm_parm_regs[i].second()); if( OptoReg::is_valid(reg2)) - _calling_convention_mask[i].Insert(reg2); + _calling_convention_mask[i].insert(reg2); // Saved biased stack-slot register number _parm_regs[i].set_pair(reg2, reg1); @@ -455,11 +455,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; } @@ -504,16 +504,16 @@ void Matcher::init_first_stack_mask() { assert(index == NOF_STACK_MASKS, "wrong size"); // At first, start with the empty mask - C->FIRST_STACK_mask().Clear(); + C->FIRST_STACK_mask().clear(); // Add in the incoming argument area OptoReg::Name init_in = OptoReg::add(_old_SP, C->out_preserve_stack_slots()); for (OptoReg::Name i = init_in; i < _in_arg_limit; i = OptoReg::add(i, 1)) { - C->FIRST_STACK_mask().Insert(i); + C->FIRST_STACK_mask().insert(i); } // Add in all bits past the outgoing argument area - C->FIRST_STACK_mask().Set_All_From(_out_arg_limit); + C->FIRST_STACK_mask().set_all_from(_out_arg_limit); // Make spill masks. Registers for their class, plus FIRST_STACK_mask. RegMask aligned_stack_mask(C->FIRST_STACK_mask(), C->comp_arena()); @@ -522,44 +522,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(C->FIRST_STACK_mask()); - idealreg2spillmask[Op_RegP]->OR(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(C->FIRST_STACK_mask()); + idealreg2spillmask[Op_RegP]->or_with(C->FIRST_STACK_mask()); #endif - *idealreg2spillmask[Op_RegI] = *idealreg2regmask[Op_RegI]; - idealreg2spillmask[Op_RegI]->OR(C->FIRST_STACK_mask()); - *idealreg2spillmask[Op_RegL] = *idealreg2regmask[Op_RegL]; - idealreg2spillmask[Op_RegL]->OR(aligned_stack_mask); - *idealreg2spillmask[Op_RegF] = *idealreg2regmask[Op_RegF]; - idealreg2spillmask[Op_RegF]->OR(C->FIRST_STACK_mask()); - *idealreg2spillmask[Op_RegD] = *idealreg2regmask[Op_RegD]; - idealreg2spillmask[Op_RegD]->OR(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(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(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(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)) { @@ -572,45 +572,45 @@ void Matcher::init_first_stack_mask() { // otherwise vector spills could stomp over stack slots in caller frame. OptoReg::Name in = OptoReg::add(_in_arg_limit, -1); for (int k = 1; (in >= init_in) && (k < RegMask::SlotsPerVecX); k++) { - aligned_stack_mask.Remove(in); + 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(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)) { // For VecY we need octo alignment and 32 bytes (8 slots) for spills. OptoReg::Name in = OptoReg::add(_in_arg_limit, -1); for (int k = 1; (in >= init_in) && (k < RegMask::SlotsPerVecY); k++) { - aligned_stack_mask.Remove(in); + 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(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)) { // For VecZ we need enough alignment and 64 bytes (16 slots) for spills. OptoReg::Name in = OptoReg::add(_in_arg_limit, -1); for (int k = 1; (in >= init_in) && (k < RegMask::SlotsPerVecZ); k++) { - aligned_stack_mask.Remove(in); + 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(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()) { @@ -620,31 +620,31 @@ void Matcher::init_first_stack_mask() { // Exclude last input arg stack slots to avoid spilling vector register there, // otherwise RegVectMask spills could stomp over stack slots in caller frame. for (; (in >= init_in) && (k < scalable_predicate_reg_slots()); k++) { - scalable_stack_mask.Remove(in); + scalable_stack_mask.remove(in); in = OptoReg::add(in, -1); } // 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]->OR(scalable_stack_mask); + idealreg2spillmask[Op_RegVectMask]->assignFrom(*idealreg2regmask[Op_RegVectMask]); + idealreg2spillmask[Op_RegVectMask]->or_with(scalable_stack_mask); } // Exclude last input arg stack slots to avoid spilling vector register there, // otherwise vector spills could stomp over stack slots in caller frame. for (; (in >= init_in) && (k < scalable_vector_reg_size(T_FLOAT)); k++) { - scalable_stack_mask.Remove(in); + scalable_stack_mask.remove(in); in = OptoReg::add(in, -1); } // 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(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) { @@ -652,20 +652,20 @@ void Matcher::init_first_stack_mask() { // symmetric and that the registers involved are the same size. // On sparc for instance we may have to use 64 bit moves will // kill 2 registers when used with F0-F31. - idealreg2spillmask[Op_RegI]->OR(*idealreg2regmask[Op_RegF]); - idealreg2spillmask[Op_RegF]->OR(*idealreg2regmask[Op_RegI]); + idealreg2spillmask[Op_RegI]->or_with(*idealreg2regmask[Op_RegF]); + idealreg2spillmask[Op_RegF]->or_with(*idealreg2regmask[Op_RegI]); #ifdef _LP64 - idealreg2spillmask[Op_RegN]->OR(*idealreg2regmask[Op_RegF]); - idealreg2spillmask[Op_RegL]->OR(*idealreg2regmask[Op_RegD]); - idealreg2spillmask[Op_RegD]->OR(*idealreg2regmask[Op_RegL]); - idealreg2spillmask[Op_RegP]->OR(*idealreg2regmask[Op_RegD]); + idealreg2spillmask[Op_RegN]->or_with(*idealreg2regmask[Op_RegF]); + idealreg2spillmask[Op_RegL]->or_with(*idealreg2regmask[Op_RegD]); + idealreg2spillmask[Op_RegD]->or_with(*idealreg2regmask[Op_RegL]); + idealreg2spillmask[Op_RegP]->or_with(*idealreg2regmask[Op_RegD]); #else - idealreg2spillmask[Op_RegP]->OR(*idealreg2regmask[Op_RegF]); + idealreg2spillmask[Op_RegP]->or_with(*idealreg2regmask[Op_RegF]); #ifdef ARM // ARM has support for moving 64bit values between a pair of // integer registers and a double register - idealreg2spillmask[Op_RegL]->OR(*idealreg2regmask[Op_RegD]); - idealreg2spillmask[Op_RegD]->OR(*idealreg2regmask[Op_RegL]); + idealreg2spillmask[Op_RegL]->or_with(*idealreg2regmask[Op_RegD]); + idealreg2spillmask[Op_RegD]->or_with(*idealreg2regmask[Op_RegL]); #endif #endif } @@ -673,40 +673,40 @@ 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 bool exclude_soe = !Compile::current()->is_method_compilation(); RegMask* caller_save_mask = exclude_soe ? &caller_save_regmask_exclude_soe : &caller_save_regmask; - idealreg2debugmask[Op_RegN]->SUBTRACT(*caller_save_mask); - idealreg2debugmask[Op_RegI]->SUBTRACT(*caller_save_mask); - idealreg2debugmask[Op_RegL]->SUBTRACT(*caller_save_mask); - idealreg2debugmask[Op_RegF]->SUBTRACT(*caller_save_mask); - idealreg2debugmask[Op_RegD]->SUBTRACT(*caller_save_mask); - idealreg2debugmask[Op_RegP]->SUBTRACT(*caller_save_mask); - idealreg2debugmask[Op_RegVectMask]->SUBTRACT(*caller_save_mask); - - idealreg2debugmask[Op_VecA]->SUBTRACT(*caller_save_mask); - idealreg2debugmask[Op_VecS]->SUBTRACT(*caller_save_mask); - idealreg2debugmask[Op_VecD]->SUBTRACT(*caller_save_mask); - idealreg2debugmask[Op_VecX]->SUBTRACT(*caller_save_mask); - idealreg2debugmask[Op_VecY]->SUBTRACT(*caller_save_mask); - idealreg2debugmask[Op_VecZ]->SUBTRACT(*caller_save_mask); + idealreg2debugmask[Op_RegN]->subtract(*caller_save_mask); + idealreg2debugmask[Op_RegI]->subtract(*caller_save_mask); + idealreg2debugmask[Op_RegL]->subtract(*caller_save_mask); + idealreg2debugmask[Op_RegF]->subtract(*caller_save_mask); + idealreg2debugmask[Op_RegD]->subtract(*caller_save_mask); + idealreg2debugmask[Op_RegP]->subtract(*caller_save_mask); + idealreg2debugmask[Op_RegVectMask]->subtract(*caller_save_mask); + + idealreg2debugmask[Op_VecA]->subtract(*caller_save_mask); + idealreg2debugmask[Op_VecS]->subtract(*caller_save_mask); + idealreg2debugmask[Op_VecD]->subtract(*caller_save_mask); + idealreg2debugmask[Op_VecX]->subtract(*caller_save_mask); + idealreg2debugmask[Op_VecY]->subtract(*caller_save_mask); + idealreg2debugmask[Op_VecZ]->subtract(*caller_save_mask); } //---------------------------is_save_on_entry---------------------------------- @@ -735,7 +735,7 @@ void Matcher::Fixup_Save_On_Entry( ) { uint ret_edge_cnt = C->tf()->range_cc()->cnt(); RegMask *ret_rms = init_input_masks( ret_edge_cnt + soe_cnt, _return_addr_mask, c_frame_ptr_mask ); for (i = TypeFunc::Parms; i < ret_edge_cnt; i++) { - ret_rms[i] = _return_values_mask[i-TypeFunc::Parms]; + ret_rms[i].assignFrom(_return_values_mask[i-TypeFunc::Parms]); } // Input RegMask array shared by all ForwardExceptions @@ -748,10 +748,10 @@ 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)); + reth_rms[TypeFunc::Parms].insert(OptoReg::add(OptoReg::Name(reg), 1)); #endif } @@ -770,8 +770,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; } } @@ -783,8 +783,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; } } @@ -817,14 +817,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; @@ -835,12 +835,12 @@ void Matcher::Fixup_Save_On_Entry( ) { _register_save_type[i+1] == Op_RegF && is_save_on_entry(i+1) ) { // Add other bit for double - ret_rms [ ret_edge_cnt].Insert(OptoReg::Name(i+1)); - reth_rms [ reth_edge_cnt].Insert(OptoReg::Name(i+1)); - tail_call_rms[tail_call_edge_cnt].Insert(OptoReg::Name(i+1)); - tail_jump_rms[tail_jump_edge_cnt].Insert(OptoReg::Name(i+1)); - forw_exc_rms [ forw_exc_edge_cnt].Insert(OptoReg::Name(i+1)); - halt_rms [ halt_edge_cnt].Insert(OptoReg::Name(i+1)); + ret_rms [ ret_edge_cnt].insert(OptoReg::Name(i+1)); + reth_rms [ reth_edge_cnt].insert(OptoReg::Name(i+1)); + tail_call_rms[tail_call_edge_cnt].insert(OptoReg::Name(i+1)); + tail_jump_rms[tail_jump_edge_cnt].insert(OptoReg::Name(i+1)); + forw_exc_rms [ forw_exc_edge_cnt].insert(OptoReg::Name(i+1)); + halt_rms [ halt_edge_cnt].insert(OptoReg::Name(i+1)); mproj = new MachProjNode( start, proj_cnt, ret_rms[ret_edge_cnt], Op_RegD ); proj_cnt += 2; // Skip 2 for doubles } @@ -848,12 +848,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 @@ -863,12 +863,12 @@ void Matcher::Fixup_Save_On_Entry( ) { _register_save_type[i+1] == Op_RegI && is_save_on_entry(i+1) ) { // Add other bit for long - ret_rms [ ret_edge_cnt].Insert(OptoReg::Name(i+1)); - reth_rms [ reth_edge_cnt].Insert(OptoReg::Name(i+1)); - tail_call_rms[tail_call_edge_cnt].Insert(OptoReg::Name(i+1)); - tail_jump_rms[tail_jump_edge_cnt].Insert(OptoReg::Name(i+1)); - forw_exc_rms [ forw_exc_edge_cnt].Insert(OptoReg::Name(i+1)); - halt_rms [ halt_edge_cnt].Insert(OptoReg::Name(i+1)); + ret_rms [ ret_edge_cnt].insert(OptoReg::Name(i+1)); + reth_rms [ reth_edge_cnt].insert(OptoReg::Name(i+1)); + tail_call_rms[tail_call_edge_cnt].insert(OptoReg::Name(i+1)); + tail_jump_rms[tail_jump_edge_cnt].insert(OptoReg::Name(i+1)); + forw_exc_rms [ forw_exc_edge_cnt].insert(OptoReg::Name(i+1)); + halt_rms [ halt_edge_cnt].insert(OptoReg::Name(i+1)); mproj = new MachProjNode( start, proj_cnt, ret_rms[ret_edge_cnt], Op_RegL ); proj_cnt += 2; // Skip 2 for longs } @@ -876,12 +876,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 @@ -908,34 +908,34 @@ 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)); + c_frame_ptr_mask.insert(OptoReg::add(c_frame_pointer(), 1)); #endif // Start at OptoReg::stack0() - STACK_ONLY_mask.Clear(); + STACK_ONLY_mask.clear(); // STACK_ONLY_mask is all stack bits - STACK_ONLY_mask.Set_All_From(OptoReg::stack2reg(0)); + STACK_ONLY_mask.set_all_from(OptoReg::stack2reg(0)); for (OptoReg::Name i = OptoReg::Name(0); i < OptoReg::Name(_last_Mach_Reg); i = OptoReg::add(i, 1)) { // Copy the register names over into the shared world. // SharedInfo::regName[i] = regName[i]; // Handy RegMasks per machine register - mreg2regmask[i].Insert(i); + mreg2regmask[i].insert(i); // Set up regmasks used to exclude save-on-call (and always-save) registers from debug masks. if (_register_save_policy[i] == 'C' || _register_save_policy[i] == 'A') { - caller_save_regmask.Insert(i); + caller_save_regmask.insert(i); } // Exclude save-on-entry registers from debug masks for stub compilations. if (_register_save_policy[i] == 'C' || _register_save_policy[i] == 'A' || _register_save_policy[i] == 'E') { - caller_save_regmask_exclude_soe.Insert(i); + caller_save_regmask_exclude_soe.insert(i); } } @@ -1277,8 +1277,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()); @@ -1355,18 +1355,18 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { OptoReg::Name reg_snd = OptoReg::as_OptoReg(second); assert (reg_fst <= reg_snd, "fst=%d snd=%d", reg_fst, reg_snd); for (OptoReg::Name r = reg_fst; r <= reg_snd; r++) { - rm->Insert(r); + rm->insert(r); } } // Grab first register, adjust stack slots and insert in mask. OptoReg::Name reg1 = warp_outgoing_stk_arg(first, begin_out_arg_area, out_arg_limit_per_call ); if (OptoReg::is_valid(reg1)) { - rm->Insert( reg1 ); + rm->insert( reg1 ); } // Grab second register (if any), adjust stack slots and insert in mask. OptoReg::Name reg2 = warp_outgoing_stk_arg(second, begin_out_arg_area, out_arg_limit_per_call ); if (OptoReg::is_valid(reg2)) { - rm->Insert( reg2 ); + rm->insert( reg2 ); } } // End of for all arguments } @@ -1384,11 +1384,11 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { // is excluded on the max-per-method basis, debug info cannot land in // this killed area. uint r_cnt = mcall->tf()->range_sig()->cnt(); - MachProjNode *proj = new MachProjNode( mcall, r_cnt+10000, RegMask::Empty, MachProjNode::fat_proj ); + MachProjNode *proj = new MachProjNode( mcall, r_cnt+10000, RegMask::EMPTY, MachProjNode::fat_proj ); for (int i = begin_out_arg_area; i < out_arg_limit_per_call; i++) { - proj->_rout.Insert(OptoReg::Name(i)); + proj->_rout.insert(OptoReg::Name(i)); } - if (!proj->_rout.is_Empty()) { + if (!proj->_rout.is_empty()) { push_projection(proj); } } diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index 9cae8fcd136..5bbe8a112f3 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -410,14 +410,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/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 04944a85836..ef7f73a0728 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -4549,7 +4549,7 @@ Node *MemBarNode::match(const ProjNode *proj, const Matcher *m, const RegMask* m switch (proj->_con) { case TypeFunc::Control: case TypeFunc::Memory: - return new MachProjNode(this,proj->_con,RegMask::Empty,MachProjNode::unmatched_proj); + return new MachProjNode(this, proj->_con, RegMask::EMPTY, MachProjNode::unmatched_proj); } ShouldNotReachHere(); return nullptr; @@ -4796,7 +4796,7 @@ const RegMask &InitializeNode::in_RegMask(uint idx) const { // This edge should be set to top, by the set_complete. But be conservative. if (idx == InitializeNode::RawAddress) return *(Compile::current()->matcher()->idealreg2spillmask[in(idx)->ideal_reg()]); - return RegMask::Empty; + return RegMask::EMPTY; } Node* InitializeNode::memory(uint alias_idx) { @@ -6020,7 +6020,7 @@ void MergeMemNode::set_base_memory(Node *new_base) { //------------------------------out_RegMask------------------------------------ const RegMask &MergeMemNode::out_RegMask() const { - return RegMask::Empty; + return RegMask::EMPTY; } //------------------------------dump_spec-------------------------------------- diff --git a/src/hotspot/share/opto/mempointer.cpp b/src/hotspot/share/opto/mempointer.cpp index a63ba8ef701..68abaffe642 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/multnode.cpp b/src/hotspot/share/opto/multnode.cpp index 1e16138a940..a6a5d635503 100644 --- a/src/hotspot/share/opto/multnode.cpp +++ b/src/hotspot/share/opto/multnode.cpp @@ -36,7 +36,7 @@ //============================================================================= //------------------------------MultiNode-------------------------------------- const RegMask &MultiNode::out_RegMask() const { - return RegMask::Empty; + return RegMask::EMPTY; } Node *MultiNode::match(const ProjNode *proj, const Matcher *m, const RegMask* mask) { return proj->clone(); } @@ -185,7 +185,7 @@ const Type* ProjNode::Value(PhaseGVN* phase) const { //------------------------------out_RegMask------------------------------------ // Pass the buck uphill const RegMask &ProjNode::out_RegMask() const { - return RegMask::Empty; + return RegMask::EMPTY; } //------------------------------ideal_reg-------------------------------------- diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 4e1d8b03673..6d43e40b485 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -2810,12 +2810,12 @@ uint Node::match_edge(uint idx) const { // Register classes are defined for specific machines const RegMask &Node::out_RegMask() const { ShouldNotCallThis(); - return RegMask::Empty; + return RegMask::EMPTY; } const RegMask &Node::in_RegMask(uint) const { ShouldNotCallThis(); - return RegMask::Empty; + return RegMask::EMPTY; } void Node_Array::grow(uint i) { diff --git a/src/hotspot/share/opto/postaloc.cpp b/src/hotspot/share/opto/postaloc.cpp index 56d3ba6bbe0..c961340e71a 100644 --- a/src/hotspot/share/opto/postaloc.cpp +++ b/src/hotspot/share/opto/postaloc.cpp @@ -173,7 +173,7 @@ int PhaseChaitin::use_prior_register( Node *n, uint idx, Node *def, Block *curre const LRG &def_lrg = lrgs(_lrg_map.live_range_id(def)); OptoReg::Name def_reg = def_lrg.reg(); const RegMask &use_mask = n->in_RegMask(idx); - bool can_use = use_mask.Member(def_reg); + bool can_use = use_mask.member(def_reg); if (!RegMask::is_vector(def->ideal_reg())) { // Check for a copy to or from a misaligned pair. // It is workaround for a sparc with misaligned pairs. @@ -678,7 +678,7 @@ void PhaseChaitin::post_allocate_copy_removal() { int n_regs = RegMask::num_registers(def_ideal_reg, lrgs(_lrg_map.live_range_id(def))); for (int l = 1; l < n_regs; l++) { OptoReg::Name ureg_lo = OptoReg::add(ureg,-l); - bool is_adjacent = lrgs(useidx).mask().Member(ureg_lo); + bool is_adjacent = lrgs(useidx).mask().member(ureg_lo); assert(is_adjacent || OptoReg::is_reg(ureg_lo), "only registers can be non-adjacent"); if (value[ureg_lo] == nullptr && is_adjacent) { // Nearly always adjacent @@ -762,13 +762,13 @@ void PhaseChaitin::post_allocate_copy_removal() { // If the value occupies a register pair, record same info // in both registers. OptoReg::Name nreg_lo = OptoReg::add(nreg,-1); - bool is_adjacent = lrgs(lidx).mask().Member(nreg_lo); + bool is_adjacent = lrgs(lidx).mask().member(nreg_lo); assert(is_adjacent || OptoReg::is_reg(nreg_lo), "only registers can be non-adjacent"); if (!is_adjacent) { // Nearly always adjacent // Sparc occasionally has non-adjacent pairs. // Find the actual other value - RegMask tmp = lrgs(lidx).mask(); - tmp.Remove(nreg); + RegMask tmp(lrgs(lidx).mask()); + tmp.remove(nreg); nreg_lo = tmp.find_first_elem(); } if (value[nreg] != val || value[nreg_lo] != val) { diff --git a/src/hotspot/share/opto/reg_split.cpp b/src/hotspot/share/opto/reg_split.cpp index 327c30b152e..96f87fe6947 100644 --- a/src/hotspot/share/opto/reg_split.cpp +++ b/src/hotspot/share/opto/reg_split.cpp @@ -476,7 +476,7 @@ bool PhaseChaitin::prompt_use( Block *b, uint lidx ) { return true; // Found 1st use! } } - if (!n->out_RegMask().is_Empty()) { + if (!n->out_RegMask().is_empty()) { return false; } } @@ -1038,7 +1038,7 @@ uint PhaseChaitin::Split(uint maxlrg, ResourceArea* split_arena) { // bound use if we can't rematerialize the def, or if we need the // split to form a misaligned pair. if (!umask.is_infinite_stack() && - (int)umask.Size() <= lrgs(useidx).num_regs() && + (int)umask.size() <= lrgs(useidx).num_regs() && (!def->rematerialize() || (!is_vect && umask.is_misaligned_pair()))) { // These need a Split regardless of overlap or pressure @@ -1128,7 +1128,7 @@ uint PhaseChaitin::Split(uint maxlrg, ResourceArea* split_arena) { if( n->is_SpillCopy() ) { ResourceMark rm(C->regmask_arena()); RegMask tmp_rm(umask, C->regmask_arena()); - tmp_rm.SUBTRACT(Matcher::STACK_ONLY_mask); + tmp_rm.subtract(Matcher::STACK_ONLY_mask); if( dmask.overlap(tmp_rm) ) { if( def != n->in(inpidx) ) { n->set_req(inpidx, def); diff --git a/src/hotspot/share/opto/regmask.cpp b/src/hotspot/share/opto/regmask.cpp index 57cf13a8b31..dcbc4dbac8e 100644 --- a/src/hotspot/share/opto/regmask.cpp +++ b/src/hotspot/share/opto/regmask.cpp @@ -47,9 +47,9 @@ void OptoReg::dump(int r, outputStream *st) { //============================================================================= -const RegMask RegMask::Empty; +const RegMask RegMask::EMPTY; -const RegMask RegMask::All( +const RegMask RegMask::ALL( # define BODY(I) -1, FORALL_BODY # undef BODY @@ -126,7 +126,7 @@ void RegMask::clear_to_pairs() { } bool RegMask::is_misaligned_pair() const { - return Size() == 2 && !is_aligned_pairs(); + return size() == 2 && !is_aligned_pairs(); } bool RegMask::is_aligned_pairs() const { @@ -227,7 +227,7 @@ bool RegMask::is_bound(uint ireg) const { // for current regmask, where reg is the highest number. bool RegMask::is_valid_reg(OptoReg::Name reg, const int size) const { for (int i = 0; i < size; i++) { - if (!Member(reg - i)) { + if (!member(reg - i)) { return false; } } diff --git a/src/hotspot/share/opto/regmask.hpp b/src/hotspot/share/opto/regmask.hpp index 67e160940cc..453fbb45d33 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) { @@ -449,31 +416,62 @@ class RegMask { RegMask(OptoReg::Name reg, Arena* arena DEBUG_ONLY(COMMA bool read_only = false)) : RegMask(arena DEBUG_ONLY(COMMA read_only)) { - Insert(reg); + insert(reg); } 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 // ---------------- - bool Member(OptoReg::Name reg) const { + bool member(OptoReg::Name reg) const { reg = reg - offset_bits(); if (reg < 0) { return false; @@ -486,7 +484,7 @@ class RegMask { } // Empty mask check. Ignores registers included through the infinite_stack flag. - bool is_Empty() const { + bool is_empty() const { assert(valid_watermarks(), "sanity"); for (unsigned i = _lwm; i <= _hwm; i++) { if (rm_word(i) != 0) { @@ -642,7 +640,7 @@ class RegMask { bool is_UP() const; // Clear a register mask. Does not clear any offset. - void Clear() { + void clear() { _lwm = rm_word_max_index(); _hwm = 0; set_range(0, 0, _rm_size_in_words); @@ -651,13 +649,13 @@ class RegMask { } // Fill a register mask with 1's - void Set_All() { + void set_all() { assert(_offset == 0, "offset non-zero"); - Set_All_From_Offset(); + set_all_from_offset(); } // Fill a register mask with 1's from the current offset. - void Set_All_From_Offset() { + void set_all_from_offset() { _lwm = 0; _hwm = rm_word_max_index(); set_range(0, 0xFF, _rm_size_in_words); @@ -666,7 +664,7 @@ class RegMask { } // Fill a register mask with 1's starting from the given register. - void Set_All_From(OptoReg::Name reg) { + void set_all_from(OptoReg::Name reg) { reg = reg - offset_bits(); assert(reg != OptoReg::Bad, "sanity"); assert(reg != OptoReg::Special, "sanity"); @@ -689,7 +687,7 @@ class RegMask { } // Insert register into mask - void Insert(OptoReg::Name reg) { + void insert(OptoReg::Name reg) { reg = reg - offset_bits(); assert(reg != OptoReg::Bad, "sanity"); assert(reg != OptoReg::Special, "sanity"); @@ -706,7 +704,7 @@ class RegMask { } // Remove register from mask - void Remove(OptoReg::Name reg) { + void remove(OptoReg::Name reg) { reg = reg - offset_bits(); assert(reg >= 0, "register outside mask"); assert(reg < (int)rm_size_in_bits(), "register outside mask"); @@ -714,8 +712,8 @@ class RegMask { rm_word(r >> LogBitsPerWord) &= ~(uintptr_t(1) << (r & WORD_BIT_MASK)); } - // OR 'rm' into 'this' - void OR(const RegMask &rm) { + // Or 'rm' into 'this' + void or_with(const RegMask& rm) { assert(_offset == rm._offset, "offset mismatch"); assert(valid_watermarks() && rm.valid_watermarks(), "sanity"); grow(rm._rm_size_in_words); @@ -736,8 +734,8 @@ class RegMask { assert(valid_watermarks(), "sanity"); } - // AND 'rm' into 'this' - void AND(const RegMask &rm) { + // And 'rm' into 'this' + void and_with(const RegMask& rm) { assert(_offset == rm._offset, "offset mismatch"); assert(valid_watermarks() && rm.valid_watermarks(), "sanity"); grow(rm._rm_size_in_words); @@ -768,7 +766,7 @@ class RegMask { } // Subtract 'rm' from 'this'. - void SUBTRACT(const RegMask &rm) { + void subtract(const RegMask& rm) { assert(_offset == rm._offset, "offset mismatch"); assert(valid_watermarks() && rm.valid_watermarks(), "sanity"); grow(rm._rm_size_in_words); @@ -791,7 +789,7 @@ class RegMask { // Subtract 'rm' from 'this', but ignore everything in 'rm' that does not // overlap with us and do not modify our infinite_stack flag. Supports masks of // differing offsets. Does not support 'rm' with the infinite_stack flag set. - void SUBTRACT_inner(const RegMask& rm) { + void subtract_inner(const RegMask& rm) { assert(valid_watermarks() && rm.valid_watermarks(), "sanity"); assert(!rm.is_infinite_stack(), "not supported"); // Various translations due to differing offsets @@ -821,12 +819,12 @@ class RegMask { return false; } _offset += _rm_size_in_words; - Set_All_From_Offset(); + set_all_from_offset(); return true; } // Compute size of register mask: number of bits - uint Size() const { + uint size() const { uint sum = 0; assert(valid_watermarks(), "sanity"); for (unsigned i = _lwm; i <= _hwm; i++) { @@ -895,8 +893,8 @@ class RegMask { void dump_hex(outputStream* st = tty) const; // Print a mask (raw hex) #endif - static const RegMask Empty; // Common empty mask - static const RegMask All; // Common all mask + static const RegMask EMPTY; // Common empty mask + static const RegMask ALL; // Common all mask bool can_represent(OptoReg::Name reg, unsigned int size = 1) const { reg = reg - offset_bits(); diff --git a/src/hotspot/share/opto/rootnode.cpp b/src/hotspot/share/opto/rootnode.cpp index 4ced13abdb1..60167c5436a 100644 --- a/src/hotspot/share/opto/rootnode.cpp +++ b/src/hotspot/share/opto/rootnode.cpp @@ -88,5 +88,5 @@ const Type* HaltNode::Value(PhaseGVN* phase) const { } const RegMask &HaltNode::out_RegMask() const { - return RegMask::Empty; + return RegMask::EMPTY; } diff --git a/src/hotspot/share/prims/jvmtiAgentList.cpp b/src/hotspot/share/prims/jvmtiAgentList.cpp index 8da5b75be46..41fc9c0f359 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/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index fa6ede86cd9..0884fce2ff7 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -879,7 +879,6 @@ class JvmtiClassFileLoadHookPoster : public StackObj { JvmtiThreadState * _state; Klass* _class_being_redefined; JvmtiClassLoadKind _load_kind; - bool _has_been_modified; public: inline JvmtiClassFileLoadHookPoster(Symbol* h_name, Handle class_loader, @@ -896,7 +895,6 @@ class JvmtiClassFileLoadHookPoster : public StackObj { _curr_data = *data_ptr; _curr_env = nullptr; _cached_class_file_ptr = cache_ptr; - _has_been_modified = false; _state = JvmtiExport::get_jvmti_thread_state(_thread); if (_state != nullptr) { @@ -935,8 +933,6 @@ class JvmtiClassFileLoadHookPoster : public StackObj { copy_modified_data(); } - bool has_been_modified() { return _has_been_modified; } - private: void post_all_envs() { if (_load_kind != jvmti_class_load_kind_retransform) { @@ -983,7 +979,6 @@ class JvmtiClassFileLoadHookPoster : public StackObj { } if (new_data != nullptr) { // this agent has modified class data. - _has_been_modified = true; if (caching_needed && *_cached_class_file_ptr == nullptr) { // data has been changed by the new retransformable agent // and it hasn't already been cached, cache it @@ -1058,18 +1053,18 @@ bool JvmtiExport::_should_post_class_file_load_hook = false; int JvmtiExport::_should_notify_object_alloc = 0; // this entry is for class file load hook on class load, redefine and retransform -bool JvmtiExport::post_class_file_load_hook(Symbol* h_name, +void JvmtiExport::post_class_file_load_hook(Symbol* h_name, Handle class_loader, Handle h_protection_domain, unsigned char **data_ptr, unsigned char **end_ptr, JvmtiCachedClassFileData **cache_ptr) { if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) { - return false; + return; } if (JavaThread::current()->should_hide_jvmti_events()) { - return false; + return; } JvmtiClassFileLoadHookPoster poster(h_name, class_loader, @@ -1077,7 +1072,6 @@ bool JvmtiExport::post_class_file_load_hook(Symbol* h_name, data_ptr, end_ptr, cache_ptr); poster.post(); - return poster.has_been_modified(); } void JvmtiExport::report_unsupported(bool on) { diff --git a/src/hotspot/share/prims/jvmtiExport.hpp b/src/hotspot/share/prims/jvmtiExport.hpp index 062057c70ab..8906d6b81df 100644 --- a/src/hotspot/share/prims/jvmtiExport.hpp +++ b/src/hotspot/share/prims/jvmtiExport.hpp @@ -377,11 +377,10 @@ class JvmtiExport : public AllStatic { static bool is_early_phase() NOT_JVMTI_RETURN_(false); static bool has_early_class_hook_env() NOT_JVMTI_RETURN_(false); static bool has_early_vmstart_env() NOT_JVMTI_RETURN_(false); - // Return true if the class was modified by the hook. - static bool post_class_file_load_hook(Symbol* h_name, Handle class_loader, + static void post_class_file_load_hook(Symbol* h_name, Handle class_loader, Handle h_protection_domain, unsigned char **data_ptr, unsigned char **end_ptr, - JvmtiCachedClassFileData **cache_ptr) NOT_JVMTI_RETURN_(false); + JvmtiCachedClassFileData **cache_ptr) NOT_JVMTI_RETURN; static void post_native_method_bind(Method* method, address* function_ptr) NOT_JVMTI_RETURN; static void post_compiled_method_load(JvmtiEnv* env, nmethod *nm) NOT_JVMTI_RETURN; static void post_compiled_method_load(nmethod *nm) NOT_JVMTI_RETURN; diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index eae475b7461..bd66bc71948 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -561,6 +561,7 @@ static SpecialFlag const special_jvm_flags[] = { { "ZGenerational", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::undefined() }, { "ZMarkStackSpaceLimit", JDK_Version::undefined(), JDK_Version::jdk(25), JDK_Version::undefined() }, { "G1UpdateBufferSize", JDK_Version::undefined(), JDK_Version::jdk(26), JDK_Version::jdk(27) }, + { "ShenandoahPacing", JDK_Version::jdk(25), JDK_Version::jdk(26), JDK_Version::jdk(27) }, #if defined(AARCH64) { "NearCpool", JDK_Version::undefined(), JDK_Version::jdk(25), JDK_Version::undefined() }, #endif diff --git a/src/hotspot/share/runtime/continuationEntry.hpp b/src/hotspot/share/runtime/continuationEntry.hpp index 8361f2f912b..490293f5b11 100644 --- a/src/hotspot/share/runtime/continuationEntry.hpp +++ b/src/hotspot/share/runtime/continuationEntry.hpp @@ -39,6 +39,7 @@ class RegisterMap; // Metadata stored in the continuation entry frame class ContinuationEntry { + friend class VMStructs; friend class JVMCIVMStructs; ContinuationEntryPD _pd; #ifdef ASSERT diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index a3fff42a8bb..0c2d5dc401f 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -1933,10 +1933,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... @@ -1950,6 +1951,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(); @@ -1965,16 +1975,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() || @@ -2877,10 +2886,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 556e82973eb..46a84b3dfad 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/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index 473969afd60..38a452f6518 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -536,7 +536,6 @@ JavaThread::JavaThread(MemTag mem_tag) : set_requires_cross_modify_fence(false); pd_initialize(); - assert(deferred_card_mark().is_empty(), "Default MemRegion ctor"); } JavaThread* JavaThread::create_attaching_thread() { @@ -1360,9 +1359,6 @@ void JavaThread::pop_jni_handle_block() { } void JavaThread::oops_do_no_frames(OopClosure* f, NMethodClosure* cf) { - // Verify that the deferred card marks have been flushed. - assert(deferred_card_mark().is_empty(), "Should be empty during GC"); - // Traverse the GCHandles Thread::oops_do_no_frames(f, cf); diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp index ba57ec91028..b9516083e93 100644 --- a/src/hotspot/share/runtime/javaThread.hpp +++ b/src/hotspot/share/runtime/javaThread.hpp @@ -150,11 +150,6 @@ class JavaThread: public Thread { Metadata* _vm_result_metadata; // non-oop result oop _return_buffered_value; // buffered value being returned - // See ReduceInitialCardMarks: this holds the precise space interval of - // the most recent slow path allocation for which compiled code has - // elided card-marks for performance along the fast-path. - MemRegion _deferred_card_mark; - ObjectMonitor* volatile _current_pending_monitor; // ObjectMonitor this thread is waiting to lock bool _current_pending_monitor_is_from_java; // locking is from Java code ObjectMonitor* volatile _current_waiting_monitor; // ObjectMonitor on which this thread called Object.wait() @@ -780,9 +775,6 @@ class JavaThread: public Thread { oop return_buffered_value() const { return _return_buffered_value; } void set_return_buffered_value(oop val) { _return_buffered_value = val; } - MemRegion deferred_card_mark() const { return _deferred_card_mark; } - void set_deferred_card_mark(MemRegion mr) { _deferred_card_mark = mr; } - // Is thread in scope of an InternalOOMEMark? bool is_in_internal_oome_mark() const { return _is_in_internal_oome_mark; } void set_is_in_internal_oome_mark(bool b) { _is_in_internal_oome_mark = b; } diff --git a/src/hotspot/share/runtime/perfMemory.cpp b/src/hotspot/share/runtime/perfMemory.cpp index a75a41e95a9..9594149333e 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/serviceThread.cpp b/src/hotspot/share/runtime/serviceThread.cpp index 9a0bfe03ac3..03168842e36 100644 --- a/src/hotspot/share/runtime/serviceThread.cpp +++ b/src/hotspot/share/runtime/serviceThread.cpp @@ -45,7 +45,7 @@ #include "services/lowMemoryDetector.hpp" #include "services/threadIdTable.hpp" -DEBUG_ONLY(JavaThread* ServiceThread::_instance = nullptr;) +JavaThread* ServiceThread::_instance = nullptr; JvmtiDeferredEvent* ServiceThread::_jvmti_event = nullptr; // The service thread has it's own static deferred event queue. // Events can be posted before JVMTI vm_start, so it's too early to call JvmtiThreadState::state_for @@ -62,7 +62,7 @@ void ServiceThread::initialize() { JavaThread::vm_exit_on_osthread_failure(thread); JavaThread::start_internal_daemon(THREAD, thread, thread_oop, NearMaxPriority); - DEBUG_ONLY(_instance = thread;) + _instance = thread; } static void cleanup_oopstorages() { diff --git a/src/hotspot/share/runtime/serviceThread.hpp b/src/hotspot/share/runtime/serviceThread.hpp index f65847ece00..cfce8603cd5 100644 --- a/src/hotspot/share/runtime/serviceThread.hpp +++ b/src/hotspot/share/runtime/serviceThread.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, 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 @@ -35,7 +35,7 @@ class JvmtiDeferredEvent; class ServiceThread : public JavaThread { private: - DEBUG_ONLY(static JavaThread* _instance;) + static JavaThread* _instance; static JvmtiDeferredEvent* _jvmti_event; static JvmtiDeferredEventQueue _jvmti_service_queue; @@ -44,6 +44,7 @@ class ServiceThread : public JavaThread { public: static void initialize(); + static bool has_started() { return _instance != nullptr; } // Hide this thread from external view. bool is_hidden_from_external_view() const { return true; } diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 4766fec4d46..930bef0cf2d 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -797,7 +797,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 @@ -853,6 +854,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 095d0eb245f..da94a38918e 100644 --- a/src/hotspot/share/runtime/sharedRuntime.hpp +++ b/src/hotspot/share/runtime/sharedRuntime.hpp @@ -389,10 +389,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, bool& is_optimized, bool caller_is_c1, TRAPS); - public: static DeoptimizationBlob* deopt_blob(void) { return _deopt_blob; } @@ -566,7 +562,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/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index ff8034461bb..a5f5e2201e4 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -315,14 +315,14 @@ static bool _no_progress_skip_increment = false; // These checks are required for wait, notify and exit to avoid inflating the monitor to // find out this inline type object cannot be locked. #define CHECK_THROW_NOSYNC_IMSE(obj) \ - if (EnableValhalla && (obj)->mark().is_inline_type()) { \ + if ((obj)->mark().is_inline_type()) { \ JavaThread* THREAD = current; \ ResourceMark rm(THREAD); \ THROW_MSG(vmSymbols::java_lang_IllegalMonitorStateException(), obj->klass()->external_name()); \ } #define CHECK_THROW_NOSYNC_IMSE_0(obj) \ - if (EnableValhalla && (obj)->mark().is_inline_type()) { \ + if ((obj)->mark().is_inline_type()) { \ JavaThread* THREAD = current; \ ResourceMark rm(THREAD); \ THROW_MSG_0(vmSymbols::java_lang_IllegalMonitorStateException(), obj->klass()->external_name()); \ @@ -354,7 +354,7 @@ bool ObjectSynchronizer::quick_notify(oopDesc* obj, JavaThread* current, bool al assert(current->thread_state() == _thread_in_Java, "invariant"); NoSafepointVerifier nsv; if (obj == nullptr) return false; // slow-path for invalid obj - assert(!EnableValhalla || !obj->klass()->is_inline_klass(), "monitor op on inline type"); + assert(!obj->klass()->is_inline_klass(), "monitor op on inline type"); const markWord mark = obj->mark(); if (mark.is_fast_locked() && current->lock_stack().contains(cast_to_oop(obj))) { @@ -446,7 +446,7 @@ void ObjectSynchronizer::enter_for(Handle obj, BasicLock* lock, JavaThread* lock // the locking_thread with respect to the current thread. Currently only used when // deoptimizing and re-locking locks. See Deoptimization::relock_objects assert(locking_thread == Thread::current() || locking_thread->is_obj_deopt_suspend(), "must be"); - assert(!EnableValhalla || !obj->klass()->is_inline_klass(), "JITed code should never have locked an instance of a value class"); + assert(!obj->klass()->is_inline_klass(), "JITed code should never have locked an instance of a value class"); return LightweightSynchronizer::enter_for(obj, lock, locking_thread); } @@ -463,7 +463,7 @@ void ObjectSynchronizer::jni_enter(Handle obj, JavaThread* current) { handle_sync_on_value_based_class(obj, current); } - if (EnableValhalla && obj->klass()->is_inline_klass()) { + if (obj->klass()->is_inline_klass()) { ResourceMark rm(THREAD); const char* desc = "Cannot synchronize on an instance of value class "; const char* className = obj->klass()->external_name(); @@ -681,10 +681,9 @@ static intptr_t install_hash_code(Thread* current, oop obj) { } intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) { - if (EnableValhalla && obj->klass()->is_inline_klass()) { - // VM should be calling bootstrap method - ShouldNotReachHere(); - } + // VM should be calling bootstrap method. + assert(!obj->klass()->is_inline_klass(), "FastHashCode should not be called for inline classes"); + if (UseObjectMonitorTable) { // Since the monitor isn't in the object header, the hash can simply be // installed in the object header. @@ -786,7 +785,7 @@ intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) { bool ObjectSynchronizer::current_thread_holds_lock(JavaThread* current, Handle h_obj) { - if (EnableValhalla && h_obj->mark().is_inline_type()) { + if (h_obj->mark().is_inline_type()) { return false; } assert(current == JavaThread::current(), "Can only be called on current thread"); diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index 5816ea89f04..2d175852be2 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -343,6 +343,11 @@ static void call_initPhase3(TRAPS) { void Threads::initialize_java_lang_classes(JavaThread* main_thread, TRAPS) { TraceTime timer("Initialize java.lang classes", TRACETIME_LOG(Info, startuptime)); + // This is before the execution of the very first Java bytecode. + if (CDSConfig::is_using_aot_linked_classes()) { + AOTLinkedClassBulkLoader::link_classes(THREAD); + } + initialize_class(vmSymbols::java_lang_String(), CHECK); // Inject CompactStrings value after the static initializers for String ran. @@ -743,6 +748,10 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { // and other cleanups. Needs to start before the compilers start posting events. ServiceThread::initialize(); + if (CDSConfig::is_using_aot_linked_classes()) { + nmethod::post_delayed_compiled_method_load_events(); + } + // Start the monitor deflation thread: MonitorDeflationThread::initialize(); @@ -775,7 +784,7 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { if (CDSConfig::is_using_aot_linked_classes()) { SystemDictionary::restore_archived_method_handle_intrinsics(); - AOTLinkedClassBulkLoader::link_or_init_javabase_classes(THREAD); + AOTLinkedClassBulkLoader::init_javabase_classes(THREAD); } // Start string deduplication thread if requested. @@ -794,7 +803,7 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { call_initPhase2(CHECK_JNI_ERR); if (CDSConfig::is_using_aot_linked_classes()) { - AOTLinkedClassBulkLoader::link_or_init_non_javabase_classes(THREAD); + AOTLinkedClassBulkLoader::init_non_javabase_classes(THREAD); } #ifndef PRODUCT HeapShared::initialize_test_class_from_archive(THREAD); diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 4653dc1ed8c..b03d18af72c 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -618,6 +618,7 @@ nonstatic_field(JavaThread, _active_handles, JNIHandleBlock*) \ nonstatic_field(JavaThread, _monitor_owner_id, int64_t) \ volatile_nonstatic_field(JavaThread, _terminated, JavaThread::TerminatedTypes) \ + nonstatic_field(JavaThread, _cont_entry, ContinuationEntry*) \ nonstatic_field(Thread, _osthread, OSThread*) \ \ /************/ \ @@ -798,7 +799,8 @@ nonstatic_field(Mutex, _name, const char*) \ static_field(Mutex, _mutex_array, Mutex**) \ static_field(Mutex, _num_mutex, int) \ - volatile_nonstatic_field(Mutex, _owner, Thread*) + volatile_nonstatic_field(Mutex, _owner, Thread*) \ + static_field(ContinuationEntry, _return_pc, address) //-------------------------------------------------------------------------------- // VM_TYPES @@ -1275,6 +1277,7 @@ declare_toplevel_type(FileMapHeader) \ declare_toplevel_type(CDSFileMapRegion) \ declare_toplevel_type(UpcallStub::FrameData) \ + declare_toplevel_type(ContinuationEntry) \ \ /************/ \ /* GC types */ \ @@ -1583,8 +1586,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/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp index ba390d3af26..8c5ed018331 100644 --- a/src/hotspot/share/services/diagnosticCommand.hpp +++ b/src/hotspot/share/services/diagnosticCommand.hpp @@ -376,7 +376,9 @@ class ThreadDumpDCmd : public DCmdWithParser { ThreadDumpDCmd(outputStream* output, bool heap); static const char* name() { return "Thread.print"; } static const char* description() { - return "Print all threads with stacktraces."; + return "Print all platform threads, and mounted virtual threads, " + "with stack traces. The Thread.dump_to_file command will " + "print all threads to a file."; } static const char* impact() { return "Medium: Depends on the number of threads."; @@ -788,7 +790,8 @@ class ThreadDumpToFileDCmd : public DCmdWithParser { return "Thread.dump_to_file"; } static const char *description() { - return "Dump threads, with stack traces, to a file in plain text or JSON format."; + return "Dump all threads, with stack traces, " + "to a file in plain text or JSON format."; } static const char* impact() { return "Medium: Depends on the number of threads."; diff --git a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java index d317557cbb1..f1da102236a 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/Byte.java b/src/java.base/share/classes/java/lang/Byte.java index 80df832a482..660b6ee15a6 100644 --- a/src/java.base/share/classes/java/lang/Byte.java +++ b/src/java.base/share/classes/java/lang/Byte.java @@ -64,8 +64,6 @@ * * * - * @author Nakul Saraiya - * @author Joseph D. Darcy * @see java.lang.Number * @since 1.1 */ diff --git a/src/java.base/share/classes/java/lang/Double.java b/src/java.base/share/classes/java/lang/Double.java index 0ec00f9530c..d728b903253 100644 --- a/src/java.base/share/classes/java/lang/Double.java +++ b/src/java.base/share/classes/java/lang/Double.java @@ -361,9 +361,6 @@ * @spec https://standards.ieee.org/ieee/754/6210/ * IEEE Standard for Floating-Point Arithmetic * - * @author Lee Boynton - * @author Arthur van Hoff - * @author Joseph D. Darcy * @since 1.0 */ @jdk.internal.MigratedValueClass @@ -705,7 +702,6 @@ public static String toString(double d) { * @param d the {@code double} to be converted. * @return a hex string representation of the argument. * @since 1.5 - * @author Joseph D. Darcy */ public static String toHexString(double d) { /* diff --git a/src/java.base/share/classes/java/lang/Float.java b/src/java.base/share/classes/java/lang/Float.java index a5b78a7ed55..cb5a15b5fcb 100644 --- a/src/java.base/share/classes/java/lang/Float.java +++ b/src/java.base/share/classes/java/lang/Float.java @@ -79,9 +79,6 @@ * @spec https://standards.ieee.org/ieee/754/6210/ * IEEE Standard for Floating-Point Arithmetic * - * @author Lee Boynton - * @author Arthur van Hoff - * @author Joseph D. Darcy * @since 1.0 */ @jdk.internal.MigratedValueClass @@ -421,7 +418,6 @@ public static String toString(float f) { * @param f the {@code float} to be converted. * @return a hex string representation of the argument. * @since 1.5 - * @author Joseph D. Darcy */ public static String toHexString(float f) { if (Math.abs(f) < Float.MIN_NORMAL diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 828e45d4a44..ae093ae124a 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -78,10 +78,6 @@ * Delight, (Addison Wesley, 2002) and Hacker's * Delight, Second Edition, (Pearson Education, 2013). * - * @author Lee Boynton - * @author Arthur van Hoff - * @author Josh Bloch - * @author Joseph D. Darcy * @since 1.0 */ @jdk.internal.MigratedValueClass diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 693fd96502d..9c62875c207 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -79,10 +79,6 @@ * Delight, (Addison Wesley, 2002) and Hacker's * Delight, Second Edition, (Pearson Education, 2013). * - * @author Lee Boynton - * @author Arthur van Hoff - * @author Josh Bloch - * @author Joseph D. Darcy * @since 1.0 */ @jdk.internal.MigratedValueClass diff --git a/src/java.base/share/classes/java/lang/Math.java b/src/java.base/share/classes/java/lang/Math.java index ef5d1214b11..0f39ecf0a8a 100644 --- a/src/java.base/share/classes/java/lang/Math.java +++ b/src/java.base/share/classes/java/lang/Math.java @@ -2529,7 +2529,6 @@ public static float fma(float a, float b, float c) { * * @param d the floating-point value whose ulp is to be returned * @return the size of an ulp of the argument - * @author Joseph D. Darcy * @since 1.5 */ public static double ulp(double d) { @@ -2576,7 +2575,6 @@ public static double ulp(double d) { * * @param f the floating-point value whose ulp is to be returned * @return the size of an ulp of the argument - * @author Joseph D. Darcy * @since 1.5 */ public static float ulp(float f) { @@ -2617,7 +2615,6 @@ public static float ulp(float f) { * * @param d the floating-point value whose signum is to be returned * @return the signum function of the argument - * @author Joseph D. Darcy * @since 1.5 */ @IntrinsicCandidate @@ -2639,7 +2636,6 @@ public static double signum(double d) { * * @param f the floating-point value whose signum is to be returned * @return the signum function of the argument - * @author Joseph D. Darcy * @since 1.5 */ @IntrinsicCandidate diff --git a/src/java.base/share/classes/java/lang/Short.java b/src/java.base/share/classes/java/lang/Short.java index be15f4723fb..7c954c19e7a 100644 --- a/src/java.base/share/classes/java/lang/Short.java +++ b/src/java.base/share/classes/java/lang/Short.java @@ -64,8 +64,6 @@ * * * - * @author Nakul Saraiya - * @author Joseph D. Darcy * @see java.lang.Number * @since 1.1 */ diff --git a/src/java.base/share/classes/java/lang/StrictMath.java b/src/java.base/share/classes/java/lang/StrictMath.java index 266d98e3947..499fce73aee 100644 --- a/src/java.base/share/classes/java/lang/StrictMath.java +++ b/src/java.base/share/classes/java/lang/StrictMath.java @@ -101,7 +101,6 @@ * @spec https://standards.ieee.org/ieee/754/6210/ * IEEE Standard for Floating-Point Arithmetic * - * @author Joseph D. Darcy * @since 1.3 */ public final class StrictMath { @@ -493,7 +492,6 @@ private static double floorOrCeil(double a, * @param a a value. * @return the closest floating-point value to {@code a} that is * equal to a mathematical integer. - * @author Joseph D. Darcy */ public static double rint(double a) { /* @@ -2014,7 +2012,6 @@ public static float fma(float a, float b, float c) { * * @param d the floating-point value whose ulp is to be returned * @return the size of an ulp of the argument - * @author Joseph D. Darcy * @since 1.5 */ public static double ulp(double d) { @@ -2041,7 +2038,6 @@ public static double ulp(double d) { * * @param f the floating-point value whose ulp is to be returned * @return the size of an ulp of the argument - * @author Joseph D. Darcy * @since 1.5 */ public static float ulp(float f) { @@ -2062,7 +2058,6 @@ public static float ulp(float f) { * * @param d the floating-point value whose signum is to be returned * @return the signum function of the argument - * @author Joseph D. Darcy * @since 1.5 */ public static double signum(double d) { @@ -2083,7 +2078,6 @@ public static double signum(double d) { * * @param f the floating-point value whose signum is to be returned * @return the signum function of the argument - * @author Joseph D. Darcy * @since 1.5 */ public static float signum(float f) { 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 611ef4e0e5d..8abd3f32b9a 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,17 +66,20 @@ 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); /* package-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 { @@ -78,12 +89,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)); @@ -123,23 +134,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); @@ -161,24 +155,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"; } /** @@ -187,8 +198,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 @@ -197,8 +208,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)); } @@ -214,13 +287,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 @@ -413,12 +541,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/math/BigDecimal.java b/src/java.base/share/classes/java/math/BigDecimal.java index c24998344c1..199b6648cdd 100644 --- a/src/java.base/share/classes/java/math/BigDecimal.java +++ b/src/java.base/share/classes/java/math/BigDecimal.java @@ -327,10 +327,6 @@ * @spec https://standards.ieee.org/ieee/754/6210/ * IEEE Standard for Floating-Point Arithmetic * - * @author Josh Bloch - * @author Mike Cowlishaw - * @author Joseph D. Darcy - * @author Sergey V. Kuksenko * @since 1.1 */ public class BigDecimal extends Number implements Comparable { @@ -1779,7 +1775,6 @@ public BigDecimal divide(BigDecimal divisor, RoundingMode roundingMode) { * terminating decimal expansion, including dividing by zero * @return {@code this / divisor} * @since 1.5 - * @author Joseph D. Darcy */ public BigDecimal divide(BigDecimal divisor) { /* @@ -1948,7 +1943,6 @@ public BigDecimal divideToIntegralValue(BigDecimal divisor) { * @throws ArithmeticException if {@code mc.precision} {@literal >} 0 and the result * requires a precision of more than {@code mc.precision} digits. * @since 1.5 - * @author Joseph D. Darcy */ public BigDecimal divideToIntegralValue(BigDecimal divisor, MathContext mc) { if (mc.precision == 0 || // exact result diff --git a/src/java.base/share/classes/java/math/MathContext.java b/src/java.base/share/classes/java/math/MathContext.java index d0c1cb4a5a9..f80fcc3e076 100644 --- a/src/java.base/share/classes/java/math/MathContext.java +++ b/src/java.base/share/classes/java/math/MathContext.java @@ -51,8 +51,6 @@ * @spec https://standards.ieee.org/ieee/754/6210/ * IEEE Standard for Floating-Point Arithmetic * - * @author Mike Cowlishaw - * @author Joseph D. Darcy * @since 1.5 */ diff --git a/src/java.base/share/classes/java/math/RoundingMode.java b/src/java.base/share/classes/java/math/RoundingMode.java index e66a64e143f..4188c781cab 100644 --- a/src/java.base/share/classes/java/math/RoundingMode.java +++ b/src/java.base/share/classes/java/math/RoundingMode.java @@ -115,9 +115,6 @@ * IEEE Standard for Floating-Point Arithmetic * @jls 15.4 Floating-point Expressions * - * @author Josh Bloch - * @author Mike Cowlishaw - * @author Joseph D. Darcy * @since 1.5 */ @SuppressWarnings("deprecation") // Legacy rounding mode constants in BigDecimal diff --git a/src/java.base/share/classes/java/nio/ByteOrder.java b/src/java.base/share/classes/java/nio/ByteOrder.java index 96f2317b956..ab6876448be 100644 --- a/src/java.base/share/classes/java/nio/ByteOrder.java +++ b/src/java.base/share/classes/java/nio/ByteOrder.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 @@ -35,28 +35,19 @@ * @since 1.4 */ -public final class ByteOrder { - - private final String name; - - private ByteOrder(String name) { - this.name = name; - } - - /** - * Constant denoting big-endian byte order. In this order, the bytes of a - * multibyte value are ordered from most significant to least significant. - */ - public static final ByteOrder BIG_ENDIAN - = new ByteOrder("BIG_ENDIAN"); - +public enum ByteOrder { /** * Constant denoting little-endian byte order. In this order, the bytes of * a multibyte value are ordered from least significant to most * significant. */ - public static final ByteOrder LITTLE_ENDIAN - = new ByteOrder("LITTLE_ENDIAN"); + LITTLE_ENDIAN, + /** + * Constant denoting big-endian byte order. In this order, the bytes of a + * multibyte value are ordered from most significant to least significant. + */ + BIG_ENDIAN; + // Retrieve the native byte order. It's used early during bootstrap, and // must be initialized after BIG_ENDIAN and LITTLE_ENDIAN. @@ -78,18 +69,4 @@ private ByteOrder(String name) { public static ByteOrder nativeOrder() { return NATIVE_ORDER; } - - /** - * Constructs a string describing this object. - * - *

This method returns the string - * {@code "BIG_ENDIAN"} for {@link #BIG_ENDIAN} and - * {@code "LITTLE_ENDIAN"} for {@link #LITTLE_ENDIAN}. - * - * @return The specified string - */ - public String toString() { - return name; - } - } 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 4e3b0cf136d..e2e97562dee 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 ef8efa5037c..5284c72b37b 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 54f29c5db4b..d1a3529c5fa 100644 --- a/src/java.base/share/classes/java/time/Duration.java +++ b/src/java.base/share/classes/java/time/Duration.java @@ -146,6 +146,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 8f94e061d4d..6e944b296da 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/jdk/internal/jimage/ImageHeader.java b/src/java.base/share/classes/jdk/internal/jimage/ImageHeader.java index f63665119e2..b41fec188cd 100644 --- a/src/java.base/share/classes/jdk/internal/jimage/ImageHeader.java +++ b/src/java.base/share/classes/jdk/internal/jimage/ImageHeader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, 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 @@ -30,6 +30,23 @@ import java.util.Objects; /** + * Defines the header and version information for jimage files. + * + *

Version number changes must be synced in a single change across all code + * which reads/writes jimage files, and code which tries to open a jimage file + * with an unexpected version should fail. + * + *

Known jimage file code which needs updating on version change: + *

    + *
  • src/java.base/share/native/libjimage/imageFile.hpp + *
+ * + *

Version history: + *

    + *
  • {@code 1.0}: Original version. + *
  • {@code 1.1}: Support preview mode with new flags. + *
+ * * @implNote This class needs to maintain JDK 8 source compatibility. * * It is used internally in the JDK to implement jimage/jrtfs access, @@ -39,7 +56,7 @@ public final class ImageHeader { public static final int MAGIC = 0xCAFEDADA; public static final int MAJOR_VERSION = 1; - public static final int MINOR_VERSION = 0; + public static final int MINOR_VERSION = 1; private static final int HEADER_SLOTS = 7; private final int magic; @@ -52,15 +69,14 @@ public final class ImageHeader { private final int stringsSize; public ImageHeader(int resourceCount, int tableCount, - int locationsSize, int stringsSize) { + int locationsSize, int stringsSize) { this(MAGIC, MAJOR_VERSION, MINOR_VERSION, 0, resourceCount, tableCount, locationsSize, stringsSize); } public ImageHeader(int magic, int majorVersion, int minorVersion, - int flags, int resourceCount, - int tableLength, int locationsSize, int stringsSize) - { + int flags, int resourceCount, + int tableLength, int locationsSize, int stringsSize) { this.magic = magic; this.majorVersion = majorVersion; this.minorVersion = minorVersion; @@ -72,7 +88,7 @@ public ImageHeader(int magic, int majorVersion, int minorVersion, } public static int getHeaderSize() { - return HEADER_SLOTS * 4; + return HEADER_SLOTS * 4; } static ImageHeader readFrom(IntBuffer buffer) { @@ -80,7 +96,7 @@ static ImageHeader readFrom(IntBuffer buffer) { if (buffer.capacity() != HEADER_SLOTS) { throw new InternalError( - "jimage header not the correct size: " + buffer.capacity()); + "jimage header not the correct size: " + buffer.capacity()); } int magic = buffer.get(0); @@ -94,7 +110,7 @@ static ImageHeader readFrom(IntBuffer buffer) { int stringsSize = buffer.get(6); return new ImageHeader(magic, majorVersion, minorVersion, flags, - resourceCount, tableLength, locationsSize, stringsSize); + resourceCount, tableLength, locationsSize, stringsSize); } public void writeTo(ImageStream stream) { @@ -156,10 +172,10 @@ public int getStringsSize() { public int getIndexSize() { return getHeaderSize() + - getRedirectSize() + - getOffsetsSize() + - getLocationsSize() + - getStringsSize(); + getRedirectSize() + + getOffsetsSize() + + getLocationsSize() + + getStringsSize(); } int getRedirectOffset() { @@ -168,16 +184,16 @@ int getRedirectOffset() { int getOffsetsOffset() { return getRedirectOffset() + - getRedirectSize(); + getRedirectSize(); } int getLocationsOffset() { return getOffsetsOffset() + - getOffsetsSize(); + getOffsetsSize(); } int getStringsOffset() { return getLocationsOffset() + - getLocationsSize(); + getLocationsSize(); } } diff --git a/src/java.base/share/classes/jdk/internal/jimage/ImageLocation.java b/src/java.base/share/classes/jdk/internal/jimage/ImageLocation.java index f31c7291927..711c5f2ea3c 100644 --- a/src/java.base/share/classes/jdk/internal/jimage/ImageLocation.java +++ b/src/java.base/share/classes/jdk/internal/jimage/ImageLocation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, 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 @@ -26,7 +26,9 @@ package jdk.internal.jimage; import java.nio.ByteBuffer; +import java.util.List; import java.util.Objects; +import java.util.function.Predicate; /** * @implNote This class needs to maintain JDK 8 source compatibility. @@ -36,15 +38,169 @@ * to the jimage file provided by the shipped JDK by tools running on JDK 8. */ public class ImageLocation { + // Also defined in src/java.base/share/native/libjimage/imageFile.hpp + + /** End of attribute stream marker. */ public static final int ATTRIBUTE_END = 0; + /** String table offset of module name. */ public static final int ATTRIBUTE_MODULE = 1; + /** String table offset of resource path parent. */ public static final int ATTRIBUTE_PARENT = 2; + /** String table offset of resource path base. */ public static final int ATTRIBUTE_BASE = 3; + /** String table offset of resource path extension. */ public static final int ATTRIBUTE_EXTENSION = 4; + /** Container byte offset of resource. */ public static final int ATTRIBUTE_OFFSET = 5; + /** In-image byte size of the compressed resource. */ public static final int ATTRIBUTE_COMPRESSED = 6; + /** In-memory byte size of the uncompressed resource. */ public static final int ATTRIBUTE_UNCOMPRESSED = 7; - public static final int ATTRIBUTE_COUNT = 8; + /** Flags relating to preview mode resources. */ + public static final int ATTRIBUTE_PREVIEW_FLAGS = 8; + /** Number of attribute kinds. */ + public static final int ATTRIBUTE_COUNT = 9; + + // Flag masks for the ATTRIBUTE_PREVIEW_FLAGS attribute. Defined so + // that zero is the overwhelmingly common case for normal resources. + + /** + * Indicates that a non-preview location is associated with preview + * resources. + * + *

This can apply to both resources and directories in the + * {@code /modules/xxx/...} namespace, as well as {@code /packages/xxx} + * directories. + * + *

For {@code /packages/xxx} directories, it indicates that the package + * has preview resources in one of the modules in which it exists. + */ + private static final int FLAGS_HAS_PREVIEW_VERSION = 0x1; + /** + * Set on all locations in the {@code /modules/xxx/META-INF/preview/...} + * namespace. + * + *

This flag is mutually exclusive with {@link #FLAGS_HAS_PREVIEW_VERSION}. + */ + private static final int FLAGS_IS_PREVIEW_VERSION = 0x2; + /** + * Indicates that a location only exists due to preview resources. + * + *

This can apply to both resources and directories in the + * {@code /modules/xxx/...} namespace, as well as {@code /packages/xxx} + * directories. + * + *

For {@code /packages/xxx} directories it indicates that, for every + * module in which the package exists, it is preview only. + * + *

This flag is mutually exclusive with {@link #FLAGS_HAS_PREVIEW_VERSION} + * and need not imply that {@link #FLAGS_IS_PREVIEW_VERSION} is set (i.e. + * for {@code /packages/xxx} directories). + */ + private static final int FLAGS_IS_PREVIEW_ONLY = 0x4; + + // Also used in ImageReader. + static final String MODULES_PREFIX = "/modules"; + static final String PACKAGES_PREFIX = "/packages"; + static final String PREVIEW_INFIX = "/META-INF/preview"; + + /** + * Helper function to calculate preview flags (ATTRIBUTE_PREVIEW_FLAGS). + * + *

Since preview flags are calculated separately for resource nodes and + * directory nodes (in two quite different places) it's useful to have a + * common helper. + * + *

Based on the entry name, the flags are: + *

    + *
  • {@code "[/modules]//"} normal resource or directory:
    + * Zero, or {@code FLAGS_HAS_PREVIEW_VERSION} if a preview entry exists. + *
  • {@code "[/modules]//META-INF/preview/"} preview + * resource or directory:
    + * {@code FLAGS_IS_PREVIEW_VERSION}, and additionally {@code + * FLAGS_IS_PREVIEW_ONLY} if no normal version of the resource exists. + *
  • In all other cases, returned flags are zero (note that {@code + * "/packages/xxx"} entries may have flags, but these are calculated + * elsewhere). + *
+ * + * @param name the jimage name of the resource or directory. + * @param hasEntry a predicate for jimage names returning whether an entry + * is present. + * @return flags for the ATTRIBUTE_PREVIEW_FLAGS attribute. + */ + public static int getFlags(String name, Predicate hasEntry) { + if (name.startsWith(PACKAGES_PREFIX + "/")) { + throw new IllegalArgumentException( + "Package sub-directory flags handled separately: " + name); + } + // Find suffix for either '/modules/xxx/suffix' or '/xxx/suffix' paths. + int idx = name.startsWith(MODULES_PREFIX + "/") ? MODULES_PREFIX.length() + 1 : 1; + int suffixStart = name.indexOf('/', idx); + if (suffixStart == -1) { + // No flags for '[/modules]/xxx' paths (esp. '/modules', '/packages'). + // '/packages/xxx' entries have flags, but not calculated here. + return 0; + } + // Prefix is either '/modules/xxx' or '/xxx', and suffix starts with '/'. + String prefix = name.substring(0, suffixStart); + String suffix = name.substring(suffixStart); + if (suffix.startsWith(PREVIEW_INFIX + "/")) { + // Preview resources/directories. + String nonPreviewName = prefix + suffix.substring(PREVIEW_INFIX.length()); + return FLAGS_IS_PREVIEW_VERSION + | (hasEntry.test(nonPreviewName) ? 0 : FLAGS_IS_PREVIEW_ONLY); + } else if (!suffix.startsWith("/META-INF/")) { + // Non-preview resources/directories. + String previewName = prefix + PREVIEW_INFIX + suffix; + return hasEntry.test(previewName) ? FLAGS_HAS_PREVIEW_VERSION : 0; + } else { + // Suffix is '/META-INF/xxx' and no preview version is even possible. + return 0; + } + } + + /** + * Helper function to calculate package flags for {@code "/packages/xxx"} + * directory entries. + * + *

Based on the module references, the flags are: + *

    + *
  • {@code FLAGS_HAS_PREVIEW_VERSION} if any referenced + * package has a preview version. + *
  • {@code FLAGS_IS_PREVIEW_ONLY} if all referenced packages + * are preview only. + *
+ * + * @return package flags for {@code "/packages/xxx"} directory entries. + */ + public static int getPackageFlags(List moduleReferences) { + boolean hasPreviewVersion = + moduleReferences.stream().anyMatch(ModuleReference::hasPreviewVersion); + boolean isPreviewOnly = + moduleReferences.stream().allMatch(ModuleReference::isPreviewOnly); + return (hasPreviewVersion ? ImageLocation.FLAGS_HAS_PREVIEW_VERSION : 0) + | (isPreviewOnly ? ImageLocation.FLAGS_IS_PREVIEW_ONLY : 0); + } + + /** + * Tests a non-preview image location's flags to see if it has preview + * content associated with it. + */ + public static boolean hasPreviewVersion(int flags) { + return (flags & FLAGS_HAS_PREVIEW_VERSION) != 0; + } + + /** + * Tests an image location's flags to see if it only exists in preview mode. + */ + public static boolean isPreviewOnly(int flags) { + return (flags & FLAGS_IS_PREVIEW_ONLY) != 0; + } + + public enum LocationType { + RESOURCE, MODULES_ROOT, MODULES_DIR, PACKAGES_ROOT, PACKAGES_DIR; + } protected final long[] attributes; @@ -72,7 +228,7 @@ static long[] decompress(ByteBuffer bytes, int offset) { int kind = data >>> 3; if (ATTRIBUTE_COUNT <= kind) { throw new InternalError( - "Invalid jimage attribute kind: " + kind); + "Invalid jimage attribute kind: " + kind); } int length = (data & 0x7) + 1; @@ -94,7 +250,7 @@ public static byte[] compress(long[] attributes) { stream.put((kind << 3) | n); for (int i = n; i >= 0; i--) { - stream.put((int)(value >> (i << 3))); + stream.put((int) (value >> (i << 3))); } } } @@ -102,7 +258,7 @@ public static byte[] compress(long[] attributes) { stream.put(ATTRIBUTE_END << 3); return stream.toArray(); - } + } public boolean verify(String name) { return verify(name, attributes, strings); @@ -117,7 +273,7 @@ static boolean verify(String name, long[] attributes, ImageStrings strings) { Objects.requireNonNull(name); final int length = name.length(); int index = 0; - int moduleOffset = (int)attributes[ATTRIBUTE_MODULE]; + int moduleOffset = (int) attributes[ATTRIBUTE_MODULE]; if (moduleOffset != 0 && length >= 1) { int moduleLen = strings.match(moduleOffset, name, 1); index = moduleLen + 1; @@ -188,7 +344,7 @@ private static long readValue(int length, ByteBuffer buffer, int offset, int lim } static boolean verify(String module, String name, long[] attributes, - ImageStrings strings) { + ImageStrings strings) { Objects.requireNonNull(module); Objects.requireNonNull(name); return verifyName(module, name, 0, name.length(), @@ -200,7 +356,7 @@ static boolean verify(String module, String name, long[] attributes, } private static boolean verifyName(String module, String name, int index, int length, - int moduleOffset, int parentOffset, int baseOffset, int extOffset, ImageStrings strings) { + int moduleOffset, int parentOffset, int baseOffset, int extOffset, ImageStrings strings) { if (moduleOffset != 0) { if (strings.match(moduleOffset, module, 0) != module.length()) { @@ -240,7 +396,7 @@ private static boolean verifyName(String module, String name, int index, int len long getAttribute(int kind) { if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) { throw new InternalError( - "Invalid jimage attribute kind: " + kind); + "Invalid jimage attribute kind: " + kind); } return attributes[kind]; } @@ -248,9 +404,9 @@ long getAttribute(int kind) { String getAttributeString(int kind) { if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) { throw new InternalError( - "Invalid jimage attribute kind: " + kind); + "Invalid jimage attribute kind: " + kind); } - return getStrings().get((int)attributes[kind]); + return getStrings().get((int) attributes[kind]); } public String getModule() { @@ -258,7 +414,7 @@ public String getModule() { } public int getModuleOffset() { - return (int)getAttribute(ATTRIBUTE_MODULE); + return (int) getAttribute(ATTRIBUTE_MODULE); } public String getBase() { @@ -266,7 +422,7 @@ public String getBase() { } public int getBaseOffset() { - return (int)getAttribute(ATTRIBUTE_BASE); + return (int) getAttribute(ATTRIBUTE_BASE); } public String getParent() { @@ -274,7 +430,7 @@ public String getParent() { } public int getParentOffset() { - return (int)getAttribute(ATTRIBUTE_PARENT); + return (int) getAttribute(ATTRIBUTE_PARENT); } public String getExtension() { @@ -282,7 +438,11 @@ public String getExtension() { } public int getExtensionOffset() { - return (int)getAttribute(ATTRIBUTE_EXTENSION); + return (int) getAttribute(ATTRIBUTE_EXTENSION); + } + + public int getFlags() { + return (int) getAttribute(ATTRIBUTE_PREVIEW_FLAGS); } public String getFullName() { @@ -294,7 +454,7 @@ public String getFullName(boolean modulesPrefix) { if (getModuleOffset() != 0) { if (modulesPrefix) { - builder.append("/modules"); + builder.append(MODULES_PREFIX); } builder.append('/'); @@ -317,36 +477,6 @@ public String getFullName(boolean modulesPrefix) { return builder.toString(); } - String buildName(boolean includeModule, boolean includeParent, - boolean includeName) { - StringBuilder builder = new StringBuilder(); - - if (includeModule && getModuleOffset() != 0) { - builder.append("/modules/"); - builder.append(getModule()); - } - - if (includeParent && getParentOffset() != 0) { - builder.append('/'); - builder.append(getParent()); - } - - if (includeName) { - if (includeModule || includeParent) { - builder.append('/'); - } - - builder.append(getBase()); - - if (getExtensionOffset() != 0) { - builder.append('.'); - builder.append(getExtension()); - } - } - - return builder.toString(); - } - public long getContentOffset() { return getAttribute(ATTRIBUTE_OFFSET); } @@ -359,6 +489,42 @@ public long getUncompressedSize() { return getAttribute(ATTRIBUTE_UNCOMPRESSED); } + // Fast (zero allocation) type determination for locations. + public LocationType getType() { + switch (getModuleOffset()) { + case ImageStrings.MODULES_STRING_OFFSET: + // Locations in /modules/... namespace are directory entries. + return LocationType.MODULES_DIR; + case ImageStrings.PACKAGES_STRING_OFFSET: + // Locations in /packages/... namespace are always 2-level + // "/packages/xxx" directories. + return LocationType.PACKAGES_DIR; + case ImageStrings.EMPTY_STRING_OFFSET: + // Only 2 choices, either the "/modules" or "/packages" root. + assert isRootDir() : "Invalid root directory: " + getFullName(); + return getBase().charAt(1) == 'p' + ? LocationType.PACKAGES_ROOT + : LocationType.MODULES_ROOT; + default: + // Anything else is // and references a resource. + return LocationType.RESOURCE; + } + } + + private boolean isRootDir() { + if (getModuleOffset() == 0 && getParentOffset() == 0) { + String name = getFullName(); + return name.equals(MODULES_PREFIX) || name.equals(PACKAGES_PREFIX); + } + return false; + } + + @Override + public String toString() { + // Cannot use String.format() (too early in startup for locale code). + return "ImageLocation[name='" + getFullName() + "', type=" + getType() + ", flags=" + getFlags() + "]"; + } + static ImageLocation readFrom(BasicImageReader reader, int offset) { Objects.requireNonNull(reader); long[] attributes = reader.getAttributes(offset); diff --git a/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java b/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java index e062e1629ff..a8ab2ff465b 100644 --- a/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java +++ b/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java @@ -24,6 +24,8 @@ */ package jdk.internal.jimage; +import jdk.internal.jimage.ImageLocation.LocationType; + import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -34,16 +36,26 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +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.Set; +import java.util.TreeMap; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Stream; +import static jdk.internal.jimage.ImageLocation.LocationType.MODULES_DIR; +import static jdk.internal.jimage.ImageLocation.LocationType.MODULES_ROOT; +import static jdk.internal.jimage.ImageLocation.LocationType.PACKAGES_DIR; +import static jdk.internal.jimage.ImageLocation.LocationType.RESOURCE; +import static jdk.internal.jimage.ImageLocation.MODULES_PREFIX; +import static jdk.internal.jimage.ImageLocation.PACKAGES_PREFIX; +import static jdk.internal.jimage.ImageLocation.PREVIEW_INFIX; + /** * A view over the entries of a jimage file with a unified namespace suitable * for file system use. The jimage entries (resources, module and package @@ -86,22 +98,27 @@ private ImageReader(SharedImageReader reader) { } /** - * Opens an image reader for a jimage file at the specified path, using the - * given byte order. + * Opens an image reader for a jimage file at the specified path. + * + * @param imagePath file system path of the jimage file. + * @param mode whether to return preview resources. */ - public static ImageReader open(Path imagePath, ByteOrder byteOrder) throws IOException { - Objects.requireNonNull(imagePath); - Objects.requireNonNull(byteOrder); - - return SharedImageReader.open(imagePath, byteOrder); + public static ImageReader open(Path imagePath, PreviewMode mode) throws IOException { + return open(imagePath, ByteOrder.nativeOrder(), mode); } /** - * Opens an image reader for a jimage file at the specified path, using the - * platform native byte order. + * Opens an image reader for a jimage file at the specified path. + * + * @param imagePath file system path of the jimage file. + * @param byteOrder the byte-order to be used when reading the jimage file. + * @param mode controls whether preview resources are visible. */ - public static ImageReader open(Path imagePath) throws IOException { - return open(imagePath, ByteOrder.nativeOrder()); + public static ImageReader open(Path imagePath, ByteOrder byteOrder, PreviewMode mode) + throws IOException { + Objects.requireNonNull(imagePath); + Objects.requireNonNull(byteOrder); + return SharedImageReader.open(imagePath, byteOrder, mode.isPreviewModeEnabled()); } @Override @@ -214,14 +231,38 @@ public ByteBuffer getResourceBuffer(Node node) { } private static final class SharedImageReader extends BasicImageReader { - private static final Map OPEN_FILES = new HashMap<>(); - private static final String MODULES_ROOT = "/modules"; - private static final String PACKAGES_ROOT = "/packages"; // There are >30,000 nodes in a complete jimage tree, and even relatively // common tasks (e.g. starting up javac) load somewhere in the region of // 1000 classes. Thus, an initial capacity of 2000 is a reasonable guess. private static final int INITIAL_NODE_CACHE_CAPACITY = 2000; + static final class ReaderKey { + private final Path imagePath; + private final boolean previewMode; + + public ReaderKey(Path imagePath, boolean previewMode) { + this.imagePath = imagePath; + this.previewMode = previewMode; + } + + @Override + public boolean equals(Object obj) { + // No pattern variables here (Java 8 compatible source). + if (obj instanceof ReaderKey) { + ReaderKey other = (ReaderKey) obj; + return this.imagePath.equals(other.imagePath) && this.previewMode == other.previewMode; + } + return false; + } + + @Override + public int hashCode() { + return imagePath.hashCode() ^ Boolean.hashCode(previewMode); + } + } + + private static final Map OPEN_FILES = new HashMap<>(); + // List of openers for this shared image. private final Set openers = new HashSet<>(); @@ -232,55 +273,139 @@ private static final class SharedImageReader extends BasicImageReader { // Cache of all user visible nodes, guarded by synchronizing 'this' instance. private final Map nodes; - // Used to classify ImageLocation instances without string comparison. - private final int modulesStringOffset; - private final int packagesStringOffset; - private SharedImageReader(Path imagePath, ByteOrder byteOrder) throws IOException { + // Preview mode support. + private final boolean previewMode; + // A relativized mapping from non-preview name to directories containing + // preview-only nodes. This is used to add preview-only content to + // directories as they are completed. + private final HashMap previewDirectoriesToMerge; + + private SharedImageReader(Path imagePath, ByteOrder byteOrder, boolean previewMode) throws IOException { super(imagePath, byteOrder); this.imageFileAttributes = Files.readAttributes(imagePath, BasicFileAttributes.class); this.nodes = new HashMap<>(INITIAL_NODE_CACHE_CAPACITY); - // Pick stable jimage names from which to extract string offsets (we cannot - // use "/modules" or "/packages", since those have a module offset of zero). - this.modulesStringOffset = getModuleOffset("/modules/java.base"); - this.packagesStringOffset = getModuleOffset("/packages/java.lang"); + this.previewMode = previewMode; // Node creation is very lazy, so we can just make the top-level directories // now without the risk of triggering the building of lots of other nodes. - Directory packages = newDirectory(PACKAGES_ROOT); - nodes.put(packages.getName(), packages); - Directory modules = newDirectory(MODULES_ROOT); - nodes.put(modules.getName(), modules); + Directory packages = ensureCached(newDirectory(PACKAGES_PREFIX)); + Directory modules = ensureCached(newDirectory(MODULES_PREFIX)); Directory root = newDirectory("/"); root.setChildren(Arrays.asList(packages, modules)); - nodes.put(root.getName(), root); + ensureCached(root); + + // By scanning the /packages directory information early we can determine + // which module/package pairs have preview resources, and build the (small) + // set of preview nodes early. This also ensures that preview-only entries + // in the /packages directory are not present in non-preview mode. + this.previewDirectoriesToMerge = previewMode ? new HashMap<>() : null; + packages.setChildren(processPackagesDirectory(previewMode)); } /** - * Returns the offset of the string denoting the leading "module" segment in - * the given path (e.g. {@code /}). We can't just pass in the - * {@code /} string here because that has a module offset of zero. + * Process {@code "/packages/xxx"} entries to build the child nodes for the + * root {@code "/packages"} node. Preview-only entries will be skipped if + * {@code previewMode == false}. + * + *

If {@code previewMode == true}, this method also populates the {@link + * #previewDirectoriesToMerge} map with any preview-only nodes, to be merged + * into directories as they are completed. It also caches preview resources + * and preview-only directories for direct lookup. */ - private int getModuleOffset(String path) { - ImageLocation location = findLocation(path); - assert location != null : "Cannot find expected jimage location: " + path; - int offset = location.getModuleOffset(); - assert offset != 0 : "Invalid module offset for jimage location: " + path; - return offset; + private ArrayList processPackagesDirectory(boolean previewMode) { + ImageLocation pkgRoot = findLocation(PACKAGES_PREFIX); + assert pkgRoot != null : "Invalid jimage file"; + IntBuffer offsets = getOffsetBuffer(pkgRoot); + ArrayList pkgDirs = new ArrayList<>(offsets.capacity()); + // Package path to module map, sorted in reverse order so that + // longer child paths get processed first. + Map> previewPackagesToModules = + new TreeMap<>(Comparator.reverseOrder()); + for (int i = 0; i < offsets.capacity(); i++) { + ImageLocation pkgDir = getLocation(offsets.get(i)); + int flags = pkgDir.getFlags(); + // A package subdirectory is "preview only" if all the modules + // it references have that package marked as preview only. + // Skipping these entries avoids empty package subdirectories. + if (previewMode || !ImageLocation.isPreviewOnly(flags)) { + pkgDirs.add(ensureCached(newDirectory(pkgDir.getFullName()))); + } + if (previewMode && ImageLocation.hasPreviewVersion(flags)) { + // Only do this in preview mode for the small set of packages with + // preview versions (the number of preview entries should be small). + List moduleNames = new ArrayList<>(); + ModuleReference.readNameOffsets(getOffsetBuffer(pkgDir), /*normal*/ false, /*preview*/ true) + .forEachRemaining(n -> moduleNames.add(getString(n))); + previewPackagesToModules.put(pkgDir.getBase().replace('.', '/'), moduleNames); + } + } + // Reverse sorted map means child directories are processed first. + previewPackagesToModules.forEach((pkgPath, modules) -> + modules.forEach(modName -> processPreviewDir(MODULES_PREFIX + "/" + modName, pkgPath))); + // We might have skipped some preview-only package entries. + pkgDirs.trimToSize(); + return pkgDirs; } - private static ImageReader open(Path imagePath, ByteOrder byteOrder) throws IOException { + void processPreviewDir(String namePrefix, String pkgPath) { + String previewDirName = namePrefix + PREVIEW_INFIX + "/" + pkgPath; + ImageLocation previewLoc = findLocation(previewDirName); + assert previewLoc != null : "Missing preview directory location: " + previewDirName; + String nonPreviewDirName = namePrefix + "/" + pkgPath; + List previewOnlyChildren = createChildNodes(previewLoc, 0, childLoc -> { + String baseName = getBaseName(childLoc); + String nonPreviewChildName = nonPreviewDirName + "/" + baseName; + boolean isPreviewOnly = ImageLocation.isPreviewOnly(childLoc.getFlags()); + LocationType type = childLoc.getType(); + if (type == RESOURCE) { + // Preview resources are cached to override non-preview versions. + Node childNode = ensureCached(newResource(nonPreviewChildName, childLoc)); + return isPreviewOnly ? childNode : null; + } else { + // Child directories are not cached here (they are either cached + // already or have been added to previewDirectoriesToMerge). + assert type == MODULES_DIR : "Invalid location type: " + childLoc; + Node childNode = nodes.get(nonPreviewChildName); + assert isPreviewOnly == (childNode != null) : + "Inconsistent child node: " + nonPreviewChildName; + return childNode; + } + }); + Directory previewDir = newDirectory(nonPreviewDirName); + previewDir.setChildren(previewOnlyChildren); + if (ImageLocation.isPreviewOnly(previewLoc.getFlags())) { + // If we are preview-only, our children are also preview-only, so + // this directory is a complete hierarchy and should be cached. + assert !previewOnlyChildren.isEmpty() : "Invalid empty preview-only directory: " + nonPreviewDirName; + ensureCached(previewDir); + } else if (!previewOnlyChildren.isEmpty()) { + // A partial directory containing extra preview-only nodes + // to be merged when the non-preview directory is completed. + previewDirectoriesToMerge.put(nonPreviewDirName, previewDir); + } + } + + // Adds a node to the cache, ensuring that no matching entry already existed. + private T ensureCached(T node) { + Node existingNode = nodes.put(node.getName(), node); + assert existingNode == null : "Unexpected node already cached for: " + node; + return node; + } + + private static ImageReader open(Path imagePath, ByteOrder byteOrder, boolean previewMode) throws IOException { Objects.requireNonNull(imagePath); Objects.requireNonNull(byteOrder); synchronized (OPEN_FILES) { - SharedImageReader reader = OPEN_FILES.get(imagePath); + ReaderKey key = new ReaderKey(imagePath, previewMode); + SharedImageReader reader = OPEN_FILES.get(key); if (reader == null) { // Will fail with an IOException if wrong byteOrder. - reader = new SharedImageReader(imagePath, byteOrder); - OPEN_FILES.put(imagePath, reader); + reader = new SharedImageReader(imagePath, byteOrder, previewMode); + OPEN_FILES.put(key, reader); } else if (reader.getByteOrder() != byteOrder) { throw new IOException("\"" + reader.getName() + "\" is not an image file"); } @@ -304,7 +429,7 @@ public void close(ImageReader image) throws IOException { close(); nodes.clear(); - if (!OPEN_FILES.remove(this.getImagePath(), this)) { + if (!OPEN_FILES.remove(new ReaderKey(getImagePath(), previewMode), this)) { throw new IOException("image file not found in open list"); } } @@ -322,20 +447,14 @@ public void close(ImageReader image) throws IOException { * "/modules" or "/packages". */ synchronized Node findNode(String name) { + // Root directories "/", "/modules" and "/packages", as well + // as all "/packages/xxx" subdirectories are already cached. Node node = nodes.get(name); if (node == null) { - // We cannot get the root paths ("/modules" or "/packages") here - // because those nodes are already in the nodes cache. - if (name.startsWith(MODULES_ROOT + "/")) { - // This may perform two lookups, one for a directory (in - // "/modules/...") and one for a non-prefixed resource - // (with "/modules" removed). - node = buildModulesNode(name); - } else if (name.startsWith(PACKAGES_ROOT + "/")) { - node = buildPackagesNode(name); - } - if (node != null) { - nodes.put(node.getName(), node); + if (name.startsWith(MODULES_PREFIX + "/")) { + node = buildAndCacheModulesNode(name); + } else if (name.startsWith(PACKAGES_PREFIX + "/")) { + node = buildAndCacheLinkNode(name); } } else if (!node.isCompleted()) { // Only directories can be incomplete. @@ -359,13 +478,13 @@ Node findResourceNode(String moduleName, String resourcePath) { if (moduleName.indexOf('/') >= 0) { throw new IllegalArgumentException("invalid module name: " + moduleName); } - String nodeName = MODULES_ROOT + "/" + moduleName + "/" + resourcePath; + String nodeName = MODULES_PREFIX + "/" + moduleName + "/" + resourcePath; // Synchronize as tightly as possible to reduce locking contention. synchronized (this) { Node node = nodes.get(nodeName); if (node == null) { ImageLocation loc = findLocation(moduleName, resourcePath); - if (loc != null && isResource(loc)) { + if (loc != null && loc.getType() == RESOURCE) { node = newResource(nodeName, loc); nodes.put(node.getName(), node); } @@ -381,18 +500,29 @@ Node findResourceNode(String moduleName, String resourcePath) { * *

This method is expected to be called frequently for resources * which do not exist in the given module (e.g. as part of classpath - * search). As such, it skips checking the nodes cache and only checks - * for an entry in the jimage file, as this is faster if the resource - * is not present. This also means it doesn't need synchronization. + * search). As such, it skips checking the nodes cache if possible, and + * only checks for an entry in the jimage file, as this is faster if the + * resource is not present. This also means it doesn't need + * synchronization most of the time. */ boolean containsResource(String moduleName, String resourcePath) { if (moduleName.indexOf('/') >= 0) { throw new IllegalArgumentException("invalid module name: " + moduleName); } - // If the given module name is 'modules', then 'isResource()' - // returns false to prevent false positives. + // In preview mode, preview-only resources are eagerly added to the + // cache, so we must check that first. + if (previewMode) { + String nodeName = MODULES_PREFIX + "/" + moduleName + "/" + resourcePath; + // Synchronize as tightly as possible to reduce locking contention. + synchronized (this) { + Node node = nodes.get(nodeName); + if (node != null) { + return node.isResource(); + } + } + } ImageLocation loc = findLocation(moduleName, resourcePath); - return loc != null && isResource(loc); + return loc != null && loc.getType() == RESOURCE; } /** @@ -401,55 +531,82 @@ boolean containsResource(String moduleName, String resourcePath) { *

Called by {@link #findNode(String)} if a {@code /modules/...} node * is not present in the cache. */ - private Node buildModulesNode(String name) { - assert name.startsWith(MODULES_ROOT + "/") : "Invalid module node name: " + name; + private Node buildAndCacheModulesNode(String name) { + assert name.startsWith(MODULES_PREFIX + "/") : "Invalid module node name: " + name; + if (isPreviewName(name)) { + return null; + } // Returns null for non-directory resources, since the jimage name does not // start with "/modules" (e.g. "/java.base/java/lang/Object.class"). ImageLocation loc = findLocation(name); if (loc != null) { assert name.equals(loc.getFullName()) : "Mismatched location for directory: " + name; - assert isModulesSubdirectory(loc) : "Invalid modules directory: " + name; - return completeModuleDirectory(newDirectory(name), loc); + assert loc.getType() == MODULES_DIR : "Invalid modules directory: " + name; + return ensureCached(completeModuleDirectory(newDirectory(name), loc)); } // Now try the non-prefixed resource name, but be careful to avoid false // positives for names like "/modules/modules/xxx" which could return a // location of a directory entry. - loc = findLocation(name.substring(MODULES_ROOT.length())); - return loc != null && isResource(loc) ? newResource(name, loc) : null; + loc = findLocation(name.substring(MODULES_PREFIX.length())); + return loc != null && loc.getType() == RESOURCE + ? ensureCached(newResource(name, loc)) + : null; } /** - * Builds a node in the "/packages/..." namespace. + * Returns whether a directory name in the "/modules/" directory could be referencing + * the "META-INF" directory". + */ + private boolean isMetaInf(Directory dir) { + String name = dir.getName(); + int pathStart = name.indexOf('/', MODULES_PREFIX.length() + 1); + return name.length() == pathStart + "/META-INF".length() + && name.endsWith("/META-INF"); + } + + /** + * Returns whether a node name in the "/modules/" directory could be referencing + * a preview resource or directory under "META-INF/preview". + */ + private boolean isPreviewName(String name) { + int pathStart = name.indexOf('/', MODULES_PREFIX.length() + 1); + int previewEnd = pathStart + PREVIEW_INFIX.length(); + return pathStart > 0 + && name.regionMatches(pathStart, PREVIEW_INFIX, 0, PREVIEW_INFIX.length()) + && (name.length() == previewEnd || name.charAt(previewEnd) == '/'); + } + + private String getBaseName(ImageLocation loc) { + // Matches logic in ImageLocation#getFullName() regarding extensions. + String trailingParts = loc.getBase() + + ((loc.getExtensionOffset() != 0) ? "." + loc.getExtension() : ""); + return trailingParts.substring(trailingParts.lastIndexOf('/') + 1); + } + + /** + * Builds a link node of the form "/packages/xxx/yyy". * - *

Called by {@link #findNode(String)} if a {@code /packages/...} node - * is not present in the cache. + *

Called by {@link #findNode(String)} if a {@code /packages/...} + * node is not present in the cache (the name is not trusted). */ - private Node buildPackagesNode(String name) { - // There are only locations for the root "/packages" or "/packages/xxx" - // directories, but not the symbolic links below them (the links can be - // entirely derived from the name information in the parent directory). - // However, unlike resources this means that we do not have a constant - // time lookup for link nodes when creating them. - int packageStart = PACKAGES_ROOT.length() + 1; + private Node buildAndCacheLinkNode(String name) { + // There are only locations for "/packages" or "/packages/xxx" + // directories, but not the symbolic links below them (links are + // derived from the name information in the parent directory). + int packageStart = PACKAGES_PREFIX.length() + 1; int packageEnd = name.indexOf('/', packageStart); - if (packageEnd == -1) { - ImageLocation loc = findLocation(name); - return loc != null ? completePackageDirectory(newDirectory(name), loc) : null; - } else { - // We cannot assume that the parent directory exists for a link node, since - // the given name is untrusted and could reference a non-existent link. - // However, if the parent directory is present, we can conclude that the - // given name was not a valid link (or else it would already be cached). + // We already built the 2-level "/packages/xxx" directories, + // so if this is a 2-level name, it cannot reference a node. + if (packageEnd >= 0) { String dirName = name.substring(0, packageEnd); - if (!nodes.containsKey(dirName)) { - ImageLocation loc = findLocation(dirName); - // If the parent location doesn't exist, the link node cannot exist. - if (loc != null) { - nodes.put(dirName, completePackageDirectory(newDirectory(dirName), loc)); - // When the parent is created its child nodes are created and cached, - // but this can still return null if given name wasn't a valid link. - return nodes.get(name); + // If no parent exists here, the name cannot be valid. + Directory parent = (Directory) nodes.get(dirName); + if (parent != null) { + if (!parent.isCompleted()) { + // This caches all child links of the parent directory. + completePackageSubdirectory(parent, findLocation(dirName)); } + return nodes.get(name); } } return null; @@ -461,127 +618,125 @@ private void completeDirectory(Directory dir) { // Since the node exists, we can assert that its name starts with // either "/modules" or "/packages", making differentiation easy. // It also means that the name is valid, so it must yield a location. - assert name.startsWith(MODULES_ROOT) || name.startsWith(PACKAGES_ROOT); + assert name.startsWith(MODULES_PREFIX) || name.startsWith(PACKAGES_PREFIX); ImageLocation loc = findLocation(name); assert loc != null && name.equals(loc.getFullName()) : "Invalid location for name: " + name; - // We cannot use 'isXxxSubdirectory()' methods here since we could - // be given a top-level directory (for which that test doesn't work). - // The string MUST start "/modules" or "/packages" here. - if (name.charAt(1) == 'm') { + LocationType type = loc.getType(); + if (type == MODULES_DIR || type == MODULES_ROOT) { completeModuleDirectory(dir, loc); } else { - completePackageDirectory(dir, loc); + assert type == PACKAGES_DIR : "Invalid location type: " + loc; + completePackageSubdirectory(dir, loc); } assert dir.isCompleted() : "Directory must be complete by now: " + dir; } - /** - * Completes a modules directory by setting the list of child nodes. - * - *

The given directory can be the top level {@code /modules} directory, - * so it is NOT safe to use {@code isModulesSubdirectory(loc)} here. - */ + /** Completes a modules directory by setting the list of child nodes. */ private Directory completeModuleDirectory(Directory dir, ImageLocation loc) { assert dir.getName().equals(loc.getFullName()) : "Mismatched location for directory: " + dir; - List children = createChildNodes(loc, childLoc -> { - if (isModulesSubdirectory(childLoc)) { - return nodes.computeIfAbsent(childLoc.getFullName(), this::newDirectory); + List previewOnlyNodes = getPreviewNodesToMerge(dir); + // We hide preview names from direct lookup, but must also prevent + // the preview directory from appearing in any META-INF directories. + boolean parentIsMetaInfDir = isMetaInf(dir); + List children = createChildNodes(loc, previewOnlyNodes.size(), childLoc -> { + LocationType type = childLoc.getType(); + if (type == MODULES_DIR) { + String name = childLoc.getFullName(); + return parentIsMetaInfDir && name.endsWith("/preview") + ? null + : nodes.computeIfAbsent(name, this::newDirectory); } else { + assert type == RESOURCE : "Invalid location type: " + loc; // Add "/modules" prefix to image location paths to get node names. String resourceName = childLoc.getFullName(true); return nodes.computeIfAbsent(resourceName, n -> newResource(n, childLoc)); } }); + children.addAll(previewOnlyNodes); dir.setChildren(children); return dir; } + /** Completes a package directory by setting the list of child nodes. */ + private void completePackageSubdirectory(Directory dir, ImageLocation loc) { + assert dir.getName().equals(loc.getFullName()) : "Mismatched location for directory: " + dir; + assert !dir.isCompleted() : "Directory already completed: " + dir; + assert loc.getType() == PACKAGES_DIR : "Invalid location type: " + loc.getType(); + + // In non-preview mode we might skip a very small number of preview-only + // entries, but it's not worth "right-sizing" the array for that. + IntBuffer offsets = getOffsetBuffer(loc); + List children = new ArrayList<>(offsets.capacity() / 2); + ModuleReference.readNameOffsets(offsets, /*normal*/ true, previewMode) + .forEachRemaining(n -> { + String modName = getString(n); + Node link = newLinkNode(dir.getName() + "/" + modName, MODULES_PREFIX + "/" + modName); + children.add(ensureCached(link)); + }); + // If the parent directory exists, there must be at least one child node. + assert !children.isEmpty() : "Invalid empty package directory: " + dir; + dir.setChildren(children); + } + /** - * Completes a package directory by setting the list of child nodes. + * Returns the list of child preview nodes to be merged into the given directory. * - *

The given directory can be the top level {@code /packages} directory, - * so it is NOT safe to use {@code isPackagesSubdirectory(loc)} here. + *

Because this is only called once per-directory (since the result is cached + * indefinitely) we can remove any entries we find from the cache. If ever the + * node cache allowed entries to expire, this would have to be changed so that + * directories could be completed more than once. */ - private Directory completePackageDirectory(Directory dir, ImageLocation loc) { - assert dir.getName().equals(loc.getFullName()) : "Mismatched location for directory: " + dir; - // The only directories in the "/packages" namespace are "/packages" or - // "/packages/". However, unlike "/modules" directories, the - // location offsets mean different things. - List children; - if (dir.getName().equals(PACKAGES_ROOT)) { - // Top-level directory just contains a list of subdirectories. - children = createChildNodes(loc, c -> nodes.computeIfAbsent(c.getFullName(), this::newDirectory)); - } else { - // A package directory's content is array of offset PAIRS in the - // Strings table, but we only need the 2nd value of each pair. - IntBuffer intBuffer = getOffsetBuffer(loc); - int offsetCount = intBuffer.capacity(); - assert (offsetCount & 0x1) == 0 : "Offset count must be even: " + offsetCount; - children = new ArrayList<>(offsetCount / 2); - // Iterate the 2nd offset in each pair (odd indices). - for (int i = 1; i < offsetCount; i += 2) { - String moduleName = getString(intBuffer.get(i)); - children.add(nodes.computeIfAbsent( - dir.getName() + "/" + moduleName, - n -> newLinkNode(n, MODULES_ROOT + "/" + moduleName))); + List getPreviewNodesToMerge(Directory dir) { + if (previewDirectoriesToMerge != null) { + Directory mergeDir = previewDirectoriesToMerge.remove(dir.getName()); + if (mergeDir != null) { + return mergeDir.children; } } - // This only happens once and "completes" the directory. - dir.setChildren(children); - return dir; + return Collections.emptyList(); } /** - * Creates the list of child nodes for a {@code Directory} based on a given + * Creates the list of child nodes for a modules {@code Directory} from + * its parent location. + * + *

The {@code getChildFn} may return existing cached nodes rather + * than creating them, and if newly created nodes are to be cached, + * it is the job of {@code getChildFn}, or the caller of this method, + * to do that. * - *

Note: This cannot be used for package subdirectories as they have - * child offsets stored differently to other directories. + * @param loc a location relating to a "/modules" directory. + * @param extraNodesCount a known number of preview-only child nodes + * which will be merged onto the end of the returned list later. + * @param getChildFn a function to return a node for each child location + * (or null to skip putting anything in the list). + * @return the list of the non-null child nodes, returned by + * {@code getChildFn}, in the order of the locations entries. */ - private List createChildNodes(ImageLocation loc, Function newChildFn) { + private List createChildNodes(ImageLocation loc, int extraNodesCount, Function getChildFn) { + LocationType type = loc.getType(); + assert type == MODULES_DIR || type == MODULES_ROOT : "Invalid location type: " + loc; IntBuffer offsets = getOffsetBuffer(loc); int childCount = offsets.capacity(); - List children = new ArrayList<>(childCount); + List children = new ArrayList<>(childCount + extraNodesCount); for (int i = 0; i < childCount; i++) { - children.add(newChildFn.apply(getLocation(offsets.get(i)))); + Node childNode = getChildFn.apply(getLocation(offsets.get(i))); + if (childNode != null) { + children.add(childNode); + } } return children; } /** Helper to extract the integer offset buffer from a directory location. */ private IntBuffer getOffsetBuffer(ImageLocation dir) { - assert !isResource(dir) : "Not a directory: " + dir.getFullName(); + assert dir.getType() != RESOURCE : "Not a directory: " + dir.getFullName(); byte[] offsets = getResource(dir); ByteBuffer buffer = ByteBuffer.wrap(offsets); buffer.order(getByteOrder()); return buffer.asIntBuffer(); } - /** - * Efficiently determines if an image location is a resource. - * - *

A resource must have a valid module associated with it, so its - * module offset must be non-zero, and not equal to the offsets for - * "/modules/..." or "/packages/..." entries. - */ - private boolean isResource(ImageLocation loc) { - int moduleOffset = loc.getModuleOffset(); - return moduleOffset != 0 - && moduleOffset != modulesStringOffset - && moduleOffset != packagesStringOffset; - } - - /** - * Determines if an image location is a directory in the {@code /modules} - * namespace (if so, the location name is the node name). - * - *

In jimage, every {@code ImageLocation} under {@code /modules/} is a - * directory and has the same value for {@code getModule()}, and {@code - * getModuleOffset()}. - */ - private boolean isModulesSubdirectory(ImageLocation loc) { - return loc.getModuleOffset() == modulesStringOffset; - } - /** * Creates an "incomplete" directory node with no child nodes set. * Directories need to be "completed" before they are returned by @@ -597,7 +752,6 @@ private Directory newDirectory(String name) { * In image files, resource locations are NOT prefixed by {@code /modules}. */ private Resource newResource(String name, ImageLocation loc) { - assert name.equals(loc.getFullName(true)) : "Mismatched location for resource: " + name; return new Resource(name, loc, imageFileAttributes); } @@ -829,11 +983,12 @@ public Stream getChildNames() { throw new IllegalStateException("Cannot get child nodes of an incomplete directory: " + getName()); } - private void setChildren(List children) { + private void setChildren(List children) { assert this.children == null : this + ": Cannot set child nodes twice!"; this.children = Collections.unmodifiableList(children); } } + /** * Resource node (e.g. a ".class" entry, or any other data resource). * diff --git a/src/java.base/share/classes/jdk/internal/jimage/ImageStrings.java b/src/java.base/share/classes/jdk/internal/jimage/ImageStrings.java index eea62e444de..b259613defa 100644 --- a/src/java.base/share/classes/jdk/internal/jimage/ImageStrings.java +++ b/src/java.base/share/classes/jdk/internal/jimage/ImageStrings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, 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 @@ -33,6 +33,23 @@ * to the jimage file provided by the shipped JDK by tools running on JDK 8. */ public interface ImageStrings { + // String offset constants are useful for efficient classification + // of location entries without string comparison. + // They are validated during initialization of ImageStringsWriter. + // + // Adding new strings (with larger offsets) is possible without changing + // the jimage version number, but any change to existing strings must be + // accompanied by a jimage version number change. + + /** Fixed offset for the empty string in the strings table. */ + int EMPTY_STRING_OFFSET = 0; + /** Fixed offset for the string "class" in the strings table. */ + int CLASS_STRING_OFFSET = 1; + /** Fixed offset for the string "modules" in the strings table. */ + int MODULES_STRING_OFFSET = 7; + /** Fixed offset for the string "packages" in the strings table. */ + int PACKAGES_STRING_OFFSET = 15; + String get(int offset); int add(final String string); diff --git a/src/java.base/share/classes/jdk/internal/jimage/ModuleReference.java b/src/java.base/share/classes/jdk/internal/jimage/ModuleReference.java new file mode 100644 index 00000000000..7117d4708a1 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/jimage/ModuleReference.java @@ -0,0 +1,260 @@ +/* + * 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.internal.jimage; + +import java.nio.IntBuffer; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.function.Function; + +/** + * Represents the module entries stored in the buffer of {@code "/packages/xxx"} + * image locations (package subdirectories). These entries use flags which are + * similar to, but distinct from, the {@link ImageLocation} flags, so + * encapsulating them here helps avoid confusion. + * + * @implNote This class needs to maintain JDK 8 source compatibility. + * + * It is used internally in the JDK to implement jimage/jrtfs access, + * but also compiled and delivered as part of the jrtfs.jar to support access + * to the jimage file provided by the shipped JDK by tools running on JDK 8. + */ +public final class ModuleReference implements Comparable { + // These flags are additive (hence "has-content" rather than "is-empty"). + + /** If set, this package exists in preview mode. */ + private static final int FLAGS_PKG_HAS_PREVIEW_VERSION = 0x1; + /** If set, this package exists in non-preview mode. */ + private static final int FLAGS_PKG_HAS_NORMAL_VERSION = 0x2; + /** If set, the associated module has resources (in normal or preview mode). */ + private static final int FLAGS_PKG_HAS_RESOURCES = 0x4; + + /** + * References are ordered with preview versions first which permits early + * exit when processing preview entries (it's reversed because the default + * order for a boolean is {@code false < true}). + */ + private static final Comparator PREVIEW_FIRST = + Comparator.comparing(ModuleReference::hasPreviewVersion).reversed() + .thenComparing(ModuleReference::name); + + /** + * Returns a reference for non-empty packages (those with resources) in a + * given module. + * + *

The same reference can be used for multiple packages in the same module. + */ + public static ModuleReference forPackageIn(String moduleName, boolean isPreview) { + return new ModuleReference(moduleName, FLAGS_PKG_HAS_RESOURCES | previewFlag(isPreview)); + } + + /** + * Returns a reference for empty packages in a given module. + * + *

The same reference can be used for multiple packages in the same module. + */ + public static ModuleReference forEmptyPackageIn(String moduleName, boolean isPreview) { + return new ModuleReference(moduleName, previewFlag(isPreview)); + } + + private static int previewFlag(boolean isPreview) { + return isPreview ? FLAGS_PKG_HAS_PREVIEW_VERSION : FLAGS_PKG_HAS_NORMAL_VERSION; + } + + /** Merges two references for the same module (combining their flags). */ + public ModuleReference merge(ModuleReference other) { + if (!name.equals(other.name)) { + throw new IllegalArgumentException("Cannot merge " + other + " with " + this); + } + // Because flags are additive, we can just OR them here. + return new ModuleReference(name, flags | other.flags); + } + + private final String name; + private final int flags; + + private ModuleReference(String moduleName, int flags) { + this.name = Objects.requireNonNull(moduleName); + this.flags = flags; + } + + /** Returns the module name of this reference. */ + public String name() { + return name; + } + + /** + * Returns whether the package associated with this reference contains + * resources in this reference's module. + * + *

An invariant of the module system is that while a package may exist + * under many modules, it only has resources in one. + */ + public boolean hasResources() { + return (flags & FLAGS_PKG_HAS_RESOURCES) != 0; + } + + /** + * Returns whether the package associated with this reference has a preview + * version (empty or otherwise) in this reference's module. + */ + public boolean hasPreviewVersion() { + return (flags & FLAGS_PKG_HAS_PREVIEW_VERSION) != 0; + } + + /** Returns whether this reference exists only in preview mode. */ + public boolean isPreviewOnly() { + return (flags & FLAGS_PKG_HAS_NORMAL_VERSION) == 0; + } + + @Override + public int compareTo(ModuleReference rhs) { + return PREVIEW_FIRST.compare(this, rhs); + } + + @Override + public String toString() { + return "ModuleReference{ module=" + name + ", flags=" + flags + " }"; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ModuleReference)) { + return false; + } + ModuleReference other = (ModuleReference) obj; + return name.equals(other.name) && flags == other.flags; + } + + @Override + public int hashCode() { + return Objects.hash(name, flags); + } + + /** + * Reads the content buffer of a package subdirectory to return a sequence + * of module name offsets in the jimage. + * + * @param buffer the content buffer of an {@link ImageLocation} with type + * {@link ImageLocation.LocationType#PACKAGES_DIR PACKAGES_DIR}. + * @param includeNormal whether to include name offsets for modules present + * in normal (non-preview) mode. + * @param includePreview whether to include name offsets for modules present + * in preview mode. + * @return an iterator of module name offsets. + */ + public static Iterator readNameOffsets( + IntBuffer buffer, boolean includeNormal, boolean includePreview) { + int bufferSize = buffer.capacity(); + if (bufferSize == 0 || (bufferSize & 0x1) != 0) { + throw new IllegalArgumentException("Invalid buffer size"); + } + int includeMask = (includeNormal ? FLAGS_PKG_HAS_NORMAL_VERSION : 0) + + (includePreview ? FLAGS_PKG_HAS_PREVIEW_VERSION : 0); + if (includeMask == 0) { + throw new IllegalArgumentException("Invalid flags"); + } + + return new Iterator() { + private int idx = nextIdx(0); + + int nextIdx(int idx) { + for (; idx < bufferSize; idx += 2) { + // If any of the test flags are set, include this entry. + if ((buffer.get(idx) & includeMask) != 0) { + return idx; + } else if (!includeNormal) { + // Preview entries are first in the offset buffer, so we + // can exit early (by returning the end index) if we are + // only iterating preview entries, and have run out. + break; + } + } + return bufferSize; + } + + @Override + public boolean hasNext() { + return idx < bufferSize; + } + + @Override + public Integer next() { + if (idx < bufferSize) { + int nameOffset = buffer.get(idx + 1); + idx = nextIdx(idx + 2); + return nameOffset; + } + throw new NoSuchElementException(); + } + }; + } + + /** + * Writes a list of module references to a given buffer. The given references + * list is checked carefully to ensure the written buffer will be valid. + * + *

Entries are written in order, taking two integer slots per entry as + * {@code [, ]}. + * + * @param refs the references to write, correctly ordered. + * @param buffer destination buffer. + * @param nameEncoder encoder for module names. + * @throws IllegalArgumentException in the references are invalid in any way. + */ + public static void write( + List refs, IntBuffer buffer, Function nameEncoder) { + if (refs.isEmpty()) { + throw new IllegalArgumentException("References list must be non-empty"); + } + int expectedCapacity = 2 * refs.size(); + if (buffer.capacity() != expectedCapacity) { + throw new IllegalArgumentException( + "Invalid buffer capacity: expected " + expectedCapacity + ", got " + buffer.capacity()); + } + // This catches exact duplicates in the list. + refs.stream().reduce((lhs, rhs) -> { + if (lhs.compareTo(rhs) >= 0) { + throw new IllegalArgumentException("References must be strictly ordered: " + refs); + } + return rhs; + }); + // Distinct references can have the same name (but we don't allow this). + if (refs.stream().map(ModuleReference::name).distinct().count() != refs.size()) { + throw new IllegalArgumentException("Reference names must be unique: " + refs); + } + if (refs.stream().filter(ModuleReference::hasResources).count() > 1) { + throw new IllegalArgumentException("At most one reference can have resources: " + refs); + } + for (ModuleReference modRef : refs) { + buffer.put(modRef.flags); + buffer.put(nameEncoder.apply(modRef.name)); + } + } +} diff --git a/src/java.base/share/classes/jdk/internal/jimage/PreviewMode.java b/src/java.base/share/classes/jdk/internal/jimage/PreviewMode.java new file mode 100644 index 00000000000..baf75261d8f --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/jimage/PreviewMode.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. 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.internal.jimage; + +import java.lang.reflect.InvocationTargetException; + +/** + * Specifies the preview mode used to open a jimage file via {@link ImageReader}. + * + * @implNote This class needs to maintain JDK 8 source compatibility. + * + * It is used internally in the JDK to implement jimage/jrtfs access, + * but also compiled and delivered as part of the jrtfs.jar to support access + * to the jimage file provided by the shipped JDK by tools running on JDK 8. + * */ +public enum PreviewMode { + /** + * Preview mode is disabled. No preview classes or resources will be available + * in this mode. + */ + DISABLED, + /** + * Preview mode is enabled. If preview classes or resources exist in the jimage file, + * they will be made available. + */ + ENABLED, + /** + * The preview mode of the current run-time, typically determined by the + * {@code --enable-preview} flag. + */ + FOR_RUNTIME; + + /** + * Resolves whether preview mode should be enabled for an {@link ImageReader}. + */ + public boolean isPreviewModeEnabled() { + if (!ENABLE_PREVIEW_MODE) { + return false; + } + // A switch, instead of an abstract method, saves 3 subclasses. + switch (this) { + case DISABLED: + return false; + case ENABLED: + return true; + case FOR_RUNTIME: + // We want to call jdk.internal.misc.PreviewFeatures.isEnabled(), but + // is not available in older JREs, so we must look to it reflectively. + Class clazz; + try { + clazz = Class.forName("jdk.internal.misc.PreviewFeatures"); + } catch (ClassNotFoundException e) { + // It is valid and expected that the class might not exist (JDK-8). + return false; + } + try { + return (Boolean) clazz.getDeclaredMethod("isEnabled").invoke(null); + } catch (NoSuchMethodException | IllegalAccessException | + InvocationTargetException e) { + // But if the class exists, the method must exist and be callable. + throw new ExceptionInInitializerError(e); + } + default: + throw new IllegalStateException("Invalid mode: " + this); + } + } + ; + + // Temporary system property to disable preview patching and enable the new preview mode + // feature for testing/development. Once the preview mode feature is finished, the value + // will be always 'true' and this code, and all related dead-code can be removed. + private static final boolean DISABLE_PREVIEW_PATCHING_DEFAULT = false; + private static final boolean ENABLE_PREVIEW_MODE = Boolean.parseBoolean( + System.getProperty( + "DISABLE_PREVIEW_PATCHING", + Boolean.toString(DISABLE_PREVIEW_PATCHING_DEFAULT))); +} diff --git a/src/java.base/share/classes/jdk/internal/jimage/ImageReaderFactory.java b/src/java.base/share/classes/jdk/internal/jimage/SystemImageReader.java similarity index 56% rename from src/java.base/share/classes/jdk/internal/jimage/ImageReaderFactory.java rename to src/java.base/share/classes/jdk/internal/jimage/SystemImageReader.java index 2ecec20d6f9..38bf786e533 100644 --- a/src/java.base/share/classes/jdk/internal/jimage/ImageReaderFactory.java +++ b/src/java.base/share/classes/jdk/internal/jimage/SystemImageReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,14 +29,9 @@ import java.io.UncheckedIOException; import java.nio.file.FileSystem; import java.nio.file.FileSystems; -import java.nio.file.Path; -import java.util.concurrent.ConcurrentHashMap; -import java.util.Map; -import java.util.Objects; -import java.util.function.Function; /** - * Factory to get ImageReader + * Static holder class for singleton {@link ImageReader} instance. * * @implNote This class needs to maintain JDK 8 source compatibility. * @@ -44,15 +39,13 @@ * but also compiled and delivered as part of the jrtfs.jar to support access * to the jimage file provided by the shipped JDK by tools running on JDK 8. */ -public class ImageReaderFactory { - private ImageReaderFactory() {} - - private static final String JAVA_HOME = System.getProperty("java.home"); - private static final Path BOOT_MODULES_JIMAGE; +public class SystemImageReader { + private static final ImageReader SYSTEM_IMAGE_READER; static { + String javaHome = System.getProperty("java.home"); FileSystem fs; - if (ImageReaderFactory.class.getClassLoader() == null) { + if (SystemImageReader.class.getClassLoader() == null) { try { fs = (FileSystem) Class.forName("sun.nio.fs.DefaultFileSystemProvider") .getMethod("theFileSystem") @@ -63,44 +56,22 @@ private ImageReaderFactory() {} } else { fs = FileSystems.getDefault(); } - BOOT_MODULES_JIMAGE = fs.getPath(JAVA_HOME, "lib", "modules"); - } - - private static final Map readers = new ConcurrentHashMap<>(); - - /** - * Returns an {@code ImageReader} to read from the given image file - */ - public static ImageReader get(Path jimage) throws IOException { - Objects.requireNonNull(jimage); try { - return readers.computeIfAbsent(jimage, OPENER); - } catch (UncheckedIOException io) { - throw io.getCause(); + SYSTEM_IMAGE_READER = ImageReader.open(fs.getPath(javaHome, "lib", "modules"), PreviewMode.FOR_RUNTIME); + } catch (IOException e) { + throw new ExceptionInInitializerError(e); } } - private static Function OPENER = new Function() { - public ImageReader apply(Path path) { - try { - return ImageReader.open(path); - } catch (IOException io) { - throw new UncheckedIOException(io); - } - } - }; - /** - * Returns the {@code ImageReader} to read the image file in this - * run-time image. + * Returns the singleton {@code ImageReader} to read the image file in this + * run-time image. The returned instance must not be closed. * * @throws UncheckedIOException if an I/O error occurs */ - public static ImageReader getImageReader() { - try { - return get(BOOT_MODULES_JIMAGE); - } catch (IOException ioe) { - throw new UncheckedIOException(ioe); - } + public static ImageReader get() { + return SYSTEM_IMAGE_READER; } + + private SystemImageReader() {} } diff --git a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java index c405801506f..8899df551bf 100644 --- a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java +++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java @@ -58,11 +58,12 @@ import java.util.Collections; import java.util.HashSet; import java.util.Iterator; -import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.regex.Pattern; + import jdk.internal.jimage.ImageReader.Node; +import jdk.internal.jimage.PreviewMode; /** * jrt file system implementation built on System jimage files. @@ -81,13 +82,34 @@ class JrtFileSystem extends FileSystem { private volatile boolean isClosable; private SystemImage image; - JrtFileSystem(JrtFileSystemProvider provider, Map env) - throws IOException - { + /** + * Special constructor for the singleton system jrt file system. This creates + * a non-closable instance, and should only be called once by {@link + * JrtFileSystemProvider}. + * + * @param provider the provider opening the file system. + */ + JrtFileSystem(JrtFileSystemProvider provider) + throws IOException { + this.provider = provider; + this.image = SystemImage.open(PreviewMode.FOR_RUNTIME); // open image file + this.isOpen = true; + // Only the system singleton jrt file system is "unclosable". + this.isClosable = false; + } + + /** + * Creates a new, non-system, instance of the jrt file system. + * + * @param provider the provider opening the file system. + * @param mode controls whether preview resources are visible. + */ + JrtFileSystem(JrtFileSystemProvider provider, PreviewMode mode) + throws IOException { this.provider = provider; - this.image = SystemImage.open(); // open image file + this.image = SystemImage.open(mode); // open image file this.isOpen = true; - this.isClosable = env != null; + this.isClosable = true; } // FileSystem method implementations @@ -153,7 +175,7 @@ public final Iterable getFileStores() { private static final Set supportedFileAttributeViews = Collections.unmodifiableSet( - new HashSet(Arrays.asList("basic", "jrt"))); + new HashSet(Arrays.asList("basic", "jrt"))); @Override public final Set supportedFileAttributeViews() { @@ -184,8 +206,8 @@ public PathMatcher getPathMatcher(String syntaxAndInput) { } else if (syntax.equalsIgnoreCase("regex")) { expr = input; } else { - throw new UnsupportedOperationException("Syntax '" + syntax - + "' not recognized"); + throw new UnsupportedOperationException("Syntax '" + syntax + + "' not recognized"); } // return matcher final Pattern pattern = Pattern.compile(expr); @@ -277,11 +299,11 @@ static void checkOptions(Set options) { Objects.requireNonNull(option); if (!(option instanceof StandardOpenOption)) { throw new IllegalArgumentException( - "option class: " + option.getClass()); + "option class: " + option.getClass()); } } if (options.contains(StandardOpenOption.WRITE) || - options.contains(StandardOpenOption.APPEND)) { + options.contains(StandardOpenOption.APPEND)) { throw readOnly(); } } @@ -322,8 +344,8 @@ final void copyFile(boolean deletesrc, JrtPath srcPath, JrtPath dstPath, CopyOpt } final FileChannel newFileChannel(JrtPath path, - Set options, - FileAttribute... attrs) + Set options, + FileAttribute... attrs) throws IOException { throw new UnsupportedOperationException("newFileChannel"); } @@ -333,8 +355,8 @@ final InputStream newInputStream(JrtPath path) throws IOException { } final SeekableByteChannel newByteChannel(JrtPath path, - Set options, - FileAttribute... attrs) + Set options, + FileAttribute... attrs) throws IOException { checkOptions(options); diff --git a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java index 9ee620f4137..8e54b41cb4f 100644 --- a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java +++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java @@ -24,6 +24,8 @@ */ package jdk.internal.jrtfs; +import jdk.internal.jimage.PreviewMode; + import java.io.*; import java.net.MalformedURLException; import java.net.URL; @@ -44,7 +46,7 @@ /** * File system provider for jrt file systems. Conditionally creates jrt fs on - * .jimage file or exploded modules directory of underlying JDK. + * a jimage file, or exploded modules directory of underlying JDK. * * @implNote This class needs to maintain JDK 8 source compatibility. * @@ -69,7 +71,7 @@ public String getScheme() { */ @SuppressWarnings("removal") private void checkPermission() { - @SuppressWarnings({ "removal", "suppression" }) + @SuppressWarnings({"removal", "suppression"}) SecurityManager sm = System.getSecurityManager(); if (sm != null) { RuntimePermission perm = new RuntimePermission("accessSystemModules"); @@ -105,10 +107,22 @@ public FileSystem newFileSystem(URI uri, Map env) checkPermission(); checkUri(uri); if (env.containsKey("java.home")) { - return newFileSystem((String)env.get("java.home"), uri, env); + return newFileSystem((String) env.get("java.home"), uri, env); } else { - return new JrtFileSystem(this, env); + return new JrtFileSystem(this, parsePreviewMode(env.get("previewMode"))); + } + } + + // Currently this does not support specifying "for runtime", because it is + // expected that callers creating non-standard image readers will not be + // using them to read resources for the current runtime (they would just + // use "jrt:" URLs if they were doing that). + private static PreviewMode parsePreviewMode(Object envValue) { + if (envValue instanceof String && Boolean.parseBoolean((String) envValue)) { + return PreviewMode.ENABLED; } + // Default (unspecified/null or bad parameter) is to not use preview mode. + return PreviewMode.DISABLED; } private static final String JRT_FS_JAR = "jrt-fs.jar"; @@ -119,14 +133,14 @@ private FileSystem newFileSystem(String targetHome, URI uri, Map env) if (Files.notExists(jrtfs)) { throw new IOException(jrtfs.toString() + " not exist"); } - Map newEnv = new HashMap<>(env); + Map newEnv = new HashMap<>(env); newEnv.remove("java.home"); ClassLoader cl = newJrtFsLoader(jrtfs); try { Class c = Class.forName(JrtFileSystemProvider.class.getName(), false, cl); - @SuppressWarnings({ "deprecation", "suppression" }) + @SuppressWarnings({"deprecation", "suppression"}) Object tmp = c.newInstance(); - return ((FileSystemProvider)tmp).newFileSystem(uri, newEnv); + return ((FileSystemProvider) tmp).newFileSystem(uri, newEnv); } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { @@ -140,8 +154,7 @@ private static class JrtFsLoader extends URLClassLoader { } @Override protected Class loadClass(String cn, boolean resolve) - throws ClassNotFoundException - { + throws ClassNotFoundException { Class c = findLoadedClass(cn); if (c == null) { URL u = findResource(cn.replace('.', '/') + ".class"); @@ -157,7 +170,7 @@ protected Class loadClass(String cn, boolean resolve) } } - @SuppressWarnings({ "removal", "suppression" }) + @SuppressWarnings({"removal", "suppression"}) private static URLClassLoader newJrtFsLoader(Path jrtfs) { final URL url; try { @@ -166,7 +179,7 @@ private static URLClassLoader newJrtFsLoader(Path jrtfs) { throw new IllegalArgumentException(mue); } - final URL[] urls = new URL[] { url }; + final URL[] urls = new URL[]{url}; return AccessController.doPrivileged( new PrivilegedAction() { @Override @@ -208,7 +221,8 @@ private FileSystem getTheFileSystem() { fs = this.theFileSystem; if (fs == null) { try { - this.theFileSystem = fs = new JrtFileSystem(this, null); + // Special constructor call for singleton instance. + this.theFileSystem = fs = new JrtFileSystem(this); } catch (IOException ioe) { throw new InternalError(ioe); } @@ -226,7 +240,7 @@ public FileSystem getFileSystem(URI uri) { } // Checks that the given file is a JrtPath - static final JrtPath toJrtPath(Path path) { + static JrtPath toJrtPath(Path path) { Objects.requireNonNull(path, "path"); if (!(path instanceof JrtPath)) { throw new ProviderMismatchException(); @@ -257,13 +271,13 @@ public void createDirectory(Path path, FileAttribute... attrs) } @Override - public final void delete(Path path) throws IOException { + public void delete(Path path) throws IOException { toJrtPath(path).delete(); } @Override public V - getFileAttributeView(Path path, Class type, LinkOption... options) { + getFileAttributeView(Path path, Class type, LinkOption... options) { return JrtFileAttributeView.get(toJrtPath(path), type, options); } @@ -290,17 +304,17 @@ public void move(Path src, Path target, CopyOption... options) @Override public AsynchronousFileChannel newAsynchronousFileChannel(Path path, - Set options, - ExecutorService exec, - FileAttribute... attrs) + Set options, + ExecutorService exec, + FileAttribute... attrs) throws IOException { throw new UnsupportedOperationException(); } @Override public SeekableByteChannel newByteChannel(Path path, - Set options, - FileAttribute... attrs) + Set options, + FileAttribute... attrs) throws IOException { return toJrtPath(path).newByteChannel(options, attrs); } @@ -313,8 +327,8 @@ public DirectoryStream newDirectoryStream( @Override public FileChannel newFileChannel(Path path, - Set options, - FileAttribute... attrs) + Set options, + FileAttribute... attrs) throws IOException { return toJrtPath(path).newFileChannel(options, attrs); } @@ -334,7 +348,7 @@ public OutputStream newOutputStream(Path path, OpenOption... options) @Override @SuppressWarnings("unchecked") // Cast to A public A - readAttributes(Path path, Class type, LinkOption... options) + readAttributes(Path path, Class type, LinkOption... options) throws IOException { if (type == BasicFileAttributes.class || type == JrtFileAttributes.class) { return (A) toJrtPath(path).getAttributes(options); @@ -344,14 +358,14 @@ public OutputStream newOutputStream(Path path, OpenOption... options) @Override public Map - readAttributes(Path path, String attribute, LinkOption... options) + readAttributes(Path path, String attribute, LinkOption... options) throws IOException { return toJrtPath(path).readAttributes(attribute, options); } @Override public void setAttribute(Path path, String attribute, - Object value, LinkOption... options) + Object value, LinkOption... options) throws IOException { toJrtPath(path).setAttribute(attribute, value, options); } diff --git a/src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java b/src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java index b38e953a5f9..852949170f5 100644 --- a/src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java +++ b/src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java @@ -39,6 +39,7 @@ import jdk.internal.jimage.ImageReader; import jdk.internal.jimage.ImageReader.Node; +import jdk.internal.jimage.PreviewMode; /** * @implNote This class needs to maintain JDK 8 source compatibility. @@ -47,17 +48,17 @@ * but also compiled and delivered as part of the jrtfs.jar to support access * to the jimage file provided by the shipped JDK by tools running on JDK 8. */ -@SuppressWarnings({ "removal", "suppression"} ) +@SuppressWarnings({"removal", "suppression"}) abstract class SystemImage { abstract Node findNode(String path) throws IOException; abstract byte[] getResource(Node node) throws IOException; abstract void close() throws IOException; - static SystemImage open() throws IOException { + static SystemImage open(PreviewMode mode) throws IOException { if (modulesImageExists) { // open a .jimage and build directory structure - final ImageReader image = ImageReader.open(moduleImageFile); + final ImageReader image = ImageReader.open(moduleImageFile, mode); return new SystemImage() { @Override Node findNode(String path) throws IOException { @@ -73,8 +74,13 @@ void close() throws IOException { } }; } + if (Files.notExists(explodedModulesDir)) throw new FileSystemNotFoundException(explodedModulesDir.toString()); + // TODO: Support preview mode in ExplodedImage and remove this check. + if (mode.isPreviewModeEnabled()) + throw new UnsupportedOperationException( + "Preview mode not yet supported for exploded images"); return new ExplodedImage(explodedModulesDir); } @@ -95,12 +101,12 @@ void close() throws IOException { explodedModulesDir = fs.getPath(RUNTIME_HOME, "modules"); modulesImageExists = AccessController.doPrivileged( - new PrivilegedAction() { - @Override - public Boolean run() { - return Files.isRegularFile(moduleImageFile); - } - }); + new PrivilegedAction() { + @Override + public Boolean run() { + return Files.isRegularFile(moduleImageFile); + } + }); } /** diff --git a/src/java.base/share/classes/jdk/internal/math/DoubleConsts.java b/src/java.base/share/classes/jdk/internal/math/DoubleConsts.java index d3a271fdd07..168e99d4ef5 100644 --- a/src/java.base/share/classes/jdk/internal/math/DoubleConsts.java +++ b/src/java.base/share/classes/jdk/internal/math/DoubleConsts.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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,8 +32,6 @@ /** * This class contains additional constants documenting limits of the * {@code double} type. - * - * @author Joseph D. Darcy */ public class DoubleConsts { diff --git a/src/java.base/share/classes/jdk/internal/math/FloatConsts.java b/src/java.base/share/classes/jdk/internal/math/FloatConsts.java index fd304c7871a..2bd484e99f3 100644 --- a/src/java.base/share/classes/jdk/internal/math/FloatConsts.java +++ b/src/java.base/share/classes/jdk/internal/math/FloatConsts.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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,8 +32,6 @@ /** * This class contains additional constants documenting limits of the * {@code float} type. - * - * @author Joseph D. Darcy */ public class FloatConsts { diff --git a/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java b/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java index 370c151af84..f5f7bc4f55c 100644 --- a/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java +++ b/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java @@ -54,7 +54,7 @@ import java.util.stream.StreamSupport; import jdk.internal.jimage.ImageReader; -import jdk.internal.jimage.ImageReaderFactory; +import jdk.internal.jimage.SystemImageReader; import jdk.internal.access.JavaNetUriAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.util.StaticProperty; @@ -86,7 +86,7 @@ public final class SystemModuleFinders { // cached ModuleFinder returned from ofSystem private static volatile ModuleFinder cachedSystemModuleFinder; - private SystemModuleFinders() { } + private SystemModuleFinders() {} /** * Returns the SystemModules object to reconstitute all modules. Returns @@ -148,7 +148,7 @@ static ModuleFinder of(SystemModules systemModules) { ModuleReference[] mrefs = new ModuleReference[moduleCount]; @SuppressWarnings(value = {"rawtypes", "unchecked"}) Map.Entry[] map - = (Map.Entry[])new Map.Entry[moduleCount]; + = (Map.Entry[]) new Map.Entry[moduleCount]; Map nameToHash = generateNameToHash(recordedHashes); @@ -156,10 +156,10 @@ static ModuleFinder of(SystemModules systemModules) { String name = descriptors[i].name(); HashSupplier hashSupplier = hashSupplier(nameToHash, name); ModuleReference mref = toModuleReference(descriptors[i], - targets[i], - recordedHashes[i], - hashSupplier, - moduleResolutions[i]); + targets[i], + recordedHashes[i], + hashSupplier, + moduleResolutions[i]); mrefs[i] = mref; map[i] = Map.entry(name, mref); } @@ -236,10 +236,10 @@ private static ModuleFinder ofModuleInfos() { ModuleInfo.Attributes attrs = e.getValue(); HashSupplier hashSupplier = hashSupplier(nameToHash, mn); ModuleReference mref = toModuleReference(attrs.descriptor(), - attrs.target(), - attrs.recordedHashes(), - hashSupplier, - attrs.moduleResolution()); + attrs.target(), + attrs.recordedHashes(), + hashSupplier, + attrs.moduleResolution()); mrefs.add(mref); nameToModule.put(mn, mref); } @@ -331,13 +331,13 @@ public ModuleReader get() { }; ModuleReference mref = new ModuleReferenceImpl(descriptor, - uri, - readerSupplier, - null, - target, - recordedHashes, - hasher, - mres); + uri, + readerSupplier, + null, + target, + recordedHashes, + hasher, + mres); // may need a reference to a patched module if --patch-module specified mref = ModuleBootstrap.patcher().patchIfNeeded(mref); @@ -392,7 +392,7 @@ public byte[] generate(String algorithm) { * Holder class for the ImageReader. */ private static class SystemImage { - static final ImageReader READER = ImageReaderFactory.getImageReader(); + static final ImageReader READER = SystemImageReader.get(); static ImageReader reader() { return READER; } @@ -515,7 +515,7 @@ private static class ModuleContentSpliterator implements Spliterator { * there are no remaining nodes to visit. */ private String next() throws IOException { - for (;;) { + for (; ; ) { while (iterator.hasNext()) { String name = iterator.next(); ImageReader.Node node = SystemImage.reader().findNode(name); diff --git a/src/java.base/share/classes/sun/net/www/protocol/jrt/JavaRuntimeURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/jrt/JavaRuntimeURLConnection.java index 71080950b80..bf67df58e2a 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/jrt/JavaRuntimeURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/jrt/JavaRuntimeURLConnection.java @@ -34,7 +34,7 @@ import jdk.internal.jimage.ImageReader; import jdk.internal.jimage.ImageReader.Node; -import jdk.internal.jimage.ImageReaderFactory; +import jdk.internal.jimage.SystemImageReader; import sun.net.www.ParseUtil; import sun.net.www.URLConnection; @@ -48,7 +48,7 @@ public class JavaRuntimeURLConnection extends URLConnection { // ImageReader to access resources in jimage. - private static final ImageReader READER = ImageReaderFactory.getImageReader(); + private static final ImageReader READER = SystemImageReader.get(); // The module and resource name in the URL (i.e. "jrt:/[$MODULE[/$PATH]]"). // @@ -109,9 +109,11 @@ public InputStream getInputStream() throws IOException { @Override public long getContentLengthLong() { + // Note: UncheckedIOException is thrown by the Node subclass in + // ExplodedImage (this not obvious, so worth calling out). try { return connectResourceNode().size(); - } catch (IOException ioe) { + } catch (IOException | UncheckedIOException ioe) { return -1L; } } @@ -119,11 +121,15 @@ public long getContentLengthLong() { @Override public int getContentLength() { long len = getContentLengthLong(); - return len > Integer.MAX_VALUE ? -1 : (int)len; + return len > Integer.MAX_VALUE ? -1 : (int) len; } // Perform percent decoding of the resource name/path from the URL. private static String percentDecode(String path) throws MalformedURLException { + if (path.indexOf('%') == -1) { + // Nothing to decode (overwhelmingly common case). + return path; + } // Any additional special case decoding logic should go here. try { return ParseUtil.decode(path); 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 19e7083180b..ec8b482b07d 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 915421c76f2..214ae718288 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 7d525a9add7..8d2c761a011 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/libjimage/imageFile.cpp b/src/java.base/share/native/libjimage/imageFile.cpp index 60c2cf44e79..31ac4ebc8d4 100644 --- a/src/java.base/share/native/libjimage/imageFile.cpp +++ b/src/java.base/share/native/libjimage/imageFile.cpp @@ -319,12 +319,7 @@ bool ImageFileReader::open() { !read_at((u1*)&_header, header_size, 0) || _header.magic(_endian) != IMAGE_MAGIC || _header.major_version(_endian) != MAJOR_VERSION || - // Temporarily, we allow either version (1.1 or 1.0) of the file to - // be read so this code can be committed before image writing changes - // for preview mode. Preview mode changes do not modify any structure, - // so a 1.0 file will look like a jimage without any preview resources. - // TODO: Restore equality check for MINOR_VERSION. - _header.minor_version(_endian) > MINOR_VERSION) { + _header.minor_version(_endian) != MINOR_VERSION) { close(); return false; } diff --git a/src/java.base/share/native/libverify/check_code.c b/src/java.base/share/native/libverify/check_code.c index cc6e96582d0..7ad8c3cc3ed 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/share/classes/java/awt/font/NumericShaper.java b/src/java.desktop/share/classes/java/awt/font/NumericShaper.java index ae507036112..99b59cc2e0e 100644 --- a/src/java.desktop/share/classes/java/awt/font/NumericShaper.java +++ b/src/java.desktop/share/classes/java/awt/font/NumericShaper.java @@ -346,6 +346,19 @@ private static Range indexToRange(int index) { return index < NUM_KEYS ? Range.values()[index] : null; } + private static int toRangeHash(Set ranges) { + int m = 0; + for (Range range : ranges) { + int index = range.ordinal(); + if (index < NUM_KEYS) { + m |= 1 << index; + } else { + m |= (1 << NUM_KEYS) + index; + } + } + return m; + } + private static int toRangeMask(Set ranges) { int m = 0; for (Range range : ranges) { @@ -576,7 +589,7 @@ private boolean inRange(int c) { // and a linear probe is ok. private static int ctCache = 0; - private static int ctCacheLimit = contexts.length - 2; + private static final int ctCacheLimit = contexts.length - 2; // warning, synchronize access to this as it modifies state private static int getContextKey(char c) { @@ -1510,6 +1523,9 @@ public static NumericShaper getContextualShaper(Set ranges, private NumericShaper(int key, int mask) { this.key = key; this.mask = mask; + if (((this.mask & ARABIC) != 0) && ((this.mask & EASTERN_ARABIC) != 0)) { + this.mask &= ~ARABIC; + } } private NumericShaper(Range defaultContext, Set ranges) { @@ -1795,15 +1811,7 @@ private void shapeContextually(char[] text, int start, int count, Range ctxKey) * @see java.lang.Object#hashCode */ public int hashCode() { - int hash = mask; - if (rangeSet != null) { - // Use the CONTEXTUAL_MASK bit only for the enum-based - // NumericShaper. A deserialized NumericShaper might have - // bit masks. - hash &= CONTEXTUAL_MASK; - hash ^= rangeSet.hashCode(); - } - return hash; + return (rangeSet != null) ? Range.toRangeHash(rangeSet) : (mask & ~CONTEXTUAL_MASK); } /** diff --git a/src/java.desktop/share/classes/sun/java2d/marlin/Curve.java b/src/java.desktop/share/classes/sun/java2d/marlin/Curve.java index 2ce0cd4672c..9d2c8dc2a72 100644 --- a/src/java.desktop/share/classes/sun/java2d/marlin/Curve.java +++ b/src/java.desktop/share/classes/sun/java2d/marlin/Curve.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2021, 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 @@ -144,7 +144,9 @@ int yPoints(final double[] ts, final int off, final double y) // finds points where the first and second derivative are // perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where // * is a dot product). Unfortunately, we have to solve a cubic. - private int perpendiculardfddf(final double[] pts, final int off) { + private int perpendiculardfddf(final double[] pts, final int off, + final double A, final double B) + { assert pts.length >= off + 4; // these are the coefficients of some multiple of g(t) (not g(t), @@ -155,7 +157,7 @@ private int perpendiculardfddf(final double[] pts, final int off) { final double c = 2.0d * (dax * cx + day * cy) + dbx * dbx + dby * dby; final double d = dbx * cx + dby * cy; - return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0.0d, 1.0d); + return Helpers.cubicRootsInAB(a, b, c, d, pts, off, A, B); } // Tries to find the roots of the function ROC(t)-w in [0, 1). It uses @@ -171,35 +173,43 @@ private int perpendiculardfddf(final double[] pts, final int off) { // at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection // points, so roc-w can have at least 6 roots. This shouldn't be a // problem for what we're trying to do (draw a nice looking curve). - int rootsOfROCMinusW(final double[] roots, final int off, final double w2, final double err) { + int rootsOfROCMinusW(final double[] roots, final int off, final double w2, + final double A, final double B) + { // no OOB exception, because by now off<=6, and roots.length >= 10 assert off <= 6 && roots.length >= 10; int ret = off; - final int end = off + perpendiculardfddf(roots, off); + final int end = off + perpendiculardfddf(roots, off, A, B); + Helpers.isort(roots, off, end); roots[end] = 1.0d; // always check interval end points - double t0 = 0.0d, ft0 = ROCsq(t0) - w2; + double t0 = 0.0d; + double ft0 = eliminateInf(ROCsq(t0) - w2); + double t1, ft1; for (int i = off; i <= end; i++) { - double t1 = roots[i], ft1 = ROCsq(t1) - w2; + t1 = roots[i]; + ft1 = eliminateInf(ROCsq(t1) - w2); if (ft0 == 0.0d) { roots[ret++] = t0; } else if (ft1 * ft0 < 0.0d) { // have opposite signs // (ROC(t)^2 == w^2) == (ROC(t) == w) is true because // ROC(t) >= 0 for all t. - roots[ret++] = falsePositionROCsqMinusX(t0, t1, w2, err); + roots[ret++] = falsePositionROCsqMinusX(t0, t1, ft0, ft1, w2, A); // A = err } t0 = t1; ft0 = ft1; } - return ret - off; } - private static double eliminateInf(final double x) { - return (x == Double.POSITIVE_INFINITY ? Double.MAX_VALUE : - (x == Double.NEGATIVE_INFINITY ? Double.MIN_VALUE : x)); + private final static double MAX_ROC_SQ = 1e20; + + private static double eliminateInf(final double x2) { + // limit the value of x to avoid numerical problems (smaller step): + // must handle NaN and +Infinity: + return (x2 <= MAX_ROC_SQ) ? x2 : MAX_ROC_SQ; } // A slight modification of the false position algorithm on wikipedia. @@ -210,17 +220,18 @@ private static double eliminateInf(final double x) { // and turn out. Same goes for the newton's method // algorithm in Helpers.java private double falsePositionROCsqMinusX(final double t0, final double t1, + final double ft0, final double ft1, final double w2, final double err) { final int iterLimit = 100; int side = 0; - double t = t1, ft = eliminateInf(ROCsq(t) - w2); - double s = t0, fs = eliminateInf(ROCsq(s) - w2); + double s = t0, fs = eliminateInf(ft0); + double t = t1, ft = eliminateInf(ft1); double r = s, fr; - for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) { + for (int i = 0; i < iterLimit && Math.abs(t - s) > err; i++) { r = (fs * t - ft * s) / (fs - ft); - fr = ROCsq(r) - w2; + fr = eliminateInf(ROCsq(r) - w2); if (sameSign(fr, ft)) { ft = fr; t = r; if (side < 0) { @@ -241,7 +252,7 @@ private double falsePositionROCsqMinusX(final double t0, final double t1, break; } } - return r; + return (Math.abs(ft) <= Math.abs(fs)) ? t : s; } private static boolean sameSign(final double x, final double y) { @@ -256,9 +267,9 @@ private double ROCsq(final double t) { final double dy = t * (t * day + dby) + cy; final double ddx = 2.0d * dax * t + dbx; final double ddy = 2.0d * day * t + dby; - final double dx2dy2 = dx * dx + dy * dy; - final double ddx2ddy2 = ddx * ddx + ddy * ddy; - final double ddxdxddydy = ddx * dx + ddy * dy; - return dx2dy2 * ((dx2dy2 * dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy * ddxdxddydy)); + final double dx2dy2 = dx * dx + dy * dy; // positive + final double dxddyddxdy = dx * ddy - dy * ddx; + // may return +Infinity if dxddyddxdy = 0 or NaN if 0/0: + return (dx2dy2 * dx2dy2 * dx2dy2) / (dxddyddxdy * dxddyddxdy); // both positive } } diff --git a/src/java.desktop/share/classes/sun/java2d/marlin/DMarlinRenderingEngine.java b/src/java.desktop/share/classes/sun/java2d/marlin/DMarlinRenderingEngine.java index 66eb9334e86..f829872a8a8 100644 --- a/src/java.desktop/share/classes/sun/java2d/marlin/DMarlinRenderingEngine.java +++ b/src/java.desktop/share/classes/sun/java2d/marlin/DMarlinRenderingEngine.java @@ -564,7 +564,7 @@ void strokeTo(final RendererContext rdrCtx, } private static boolean nearZero(final double num) { - return Math.abs(num) < 2.0d * Math.ulp(num); + return Math.abs(num) < 2.0d * Helpers.ulp(num); } abstract static class NormalizingPathIterator implements PathIterator { diff --git a/src/java.desktop/share/classes/sun/java2d/marlin/Helpers.java b/src/java.desktop/share/classes/sun/java2d/marlin/Helpers.java index 0aed05ab506..926533cdb2b 100644 --- a/src/java.desktop/share/classes/sun/java2d/marlin/Helpers.java +++ b/src/java.desktop/share/classes/sun/java2d/marlin/Helpers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2022, 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 @@ -31,12 +31,19 @@ final class Helpers implements MarlinConst { + private final static double T_ERR = 1e-4; + private final static double T_A = T_ERR; + private final static double T_B = 1.0 - T_ERR; + private static final double EPS = 1e-9d; private Helpers() { throw new Error("This is a non instantiable class"); } + /** use lower precision like former Pisces and Marlin (float-precision) */ + static double ulp(final double value) { return Math.ulp((float)value); } + static boolean within(final double x, final double y) { return within(x, y, EPS); } @@ -322,10 +329,10 @@ static int findSubdivPoints(final Curve c, final double[] pts, // now we must subdivide at points where one of the offset curves will have // a cusp. This happens at ts where the radius of curvature is equal to w. - ret += c.rootsOfROCMinusW(ts, ret, w2, 0.0001d); + ret += c.rootsOfROCMinusW(ts, ret, w2, T_A, T_B); - ret = filterOutNotInAB(ts, 0, ret, 0.0001d, 0.9999d); - isort(ts, ret); + ret = filterOutNotInAB(ts, 0, ret, T_A, T_B); + isort(ts, 0, ret); return ret; } @@ -354,7 +361,7 @@ static int findClipPoints(final Curve curve, final double[] pts, if ((outCodeOR & OUTCODE_BOTTOM) != 0) { ret += curve.yPoints(ts, ret, clipRect[1]); } - isort(ts, ret); + isort(ts, 0, ret); return ret; } @@ -374,11 +381,11 @@ static void subdivide(final double[] src, } } - static void isort(final double[] a, final int len) { - for (int i = 1, j; i < len; i++) { + static void isort(final double[] a, final int off, final int len) { + for (int i = off + 1, j; i < len; i++) { final double ai = a[i]; j = i - 1; - for (; j >= 0 && a[j] > ai; j--) { + for (; j >= off && a[j] > ai; j--) { a[j + 1] = a[j]; } a[j + 1] = ai; diff --git a/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java b/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java index 59f93ed7d6d..1c257bc13d9 100644 --- a/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java +++ b/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2023, 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 @@ -886,8 +886,8 @@ private int computeOffsetCubic(final double[] pts, final int off, // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4, // in which case ignore if p1 == p2 - final boolean p1eqp2 = Helpers.withinD(dx1, dy1, 6.0d * Math.ulp(y2)); - final boolean p3eqp4 = Helpers.withinD(dx4, dy4, 6.0d * Math.ulp(y4)); + final boolean p1eqp2 = Helpers.withinD(dx1, dy1, 6.0d * Helpers.ulp(y2)); + final boolean p3eqp4 = Helpers.withinD(dx4, dy4, 6.0d * Helpers.ulp(y4)); if (p1eqp2 && p3eqp4) { return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff); @@ -905,7 +905,7 @@ private int computeOffsetCubic(final double[] pts, final int off, final double l1sq = dx1 * dx1 + dy1 * dy1; final double l4sq = dx4 * dx4 + dy4 * dy4; - if (Helpers.within(dotsq, l1sq * l4sq, 4.0d * Math.ulp(dotsq))) { + if (Helpers.within(dotsq, l1sq * l4sq, 4.0d * Helpers.ulp(dotsq))) { return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff); } @@ -1078,8 +1078,8 @@ private int computeOffsetQuad(final double[] pts, final int off, // equal if they're very close to each other. // if p1 == p2 or p2 == p3: draw line from p1->p3 - final boolean p1eqp2 = Helpers.withinD(dx12, dy12, 6.0d * Math.ulp(y2)); - final boolean p2eqp3 = Helpers.withinD(dx23, dy23, 6.0d * Math.ulp(y3)); + final boolean p1eqp2 = Helpers.withinD(dx12, dy12, 6.0d * Helpers.ulp(y2)); + final boolean p2eqp3 = Helpers.withinD(dx23, dy23, 6.0d * Helpers.ulp(y3)); if (p1eqp2 || p2eqp3) { return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff); @@ -1091,7 +1091,7 @@ private int computeOffsetQuad(final double[] pts, final int off, final double l1sq = dx12 * dx12 + dy12 * dy12; final double l3sq = dx23 * dx23 + dy23 * dy23; - if (Helpers.within(dotsq, l1sq * l3sq, 4.0d * Math.ulp(dotsq))) { + if (Helpers.within(dotsq, l1sq * l3sq, 4.0d * Helpers.ulp(dotsq))) { return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff); } diff --git a/src/java.desktop/share/conf/psfontj2d.properties b/src/java.desktop/share/conf/psfontj2d.properties index 9efe8864428..8030a82bc4f 100644 --- a/src/java.desktop/share/conf/psfontj2d.properties +++ b/src/java.desktop/share/conf/psfontj2d.properties @@ -59,7 +59,6 @@ avantgarde_book_oblique=avantgarde_book_oblique avantgarde_demi_oblique=avantgarde_demi_oblique # itcavantgarde=avantgarde_book -itcavantgarde=avantgarde_book itcavantgarde_demi=avantgarde_demi itcavantgarde_oblique=avantgarde_book_oblique itcavantgarde_demi_oblique=avantgarde_demi_oblique 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 f28ae2a9326..3a2578b3e0b 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 a9b09085ad1..041bdb5adaa 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 754b394d4ac..130b09227cc 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 06ef5db23a1..78768c29ab3 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/Http1Request.java b/src/java.net.http/share/classes/jdk/internal/net/http/Http1Request.java index 815b6bad20c..8d28b664036 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http1Request.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http1Request.java @@ -290,7 +290,8 @@ List headers() { } String uriString = requestURI(); StringBuilder sb = new StringBuilder(64); - sb.append(request.method()) + String method = request.method(); + sb.append(method) .append(' ') .append(uriString) .append(" HTTP/1.1\r\n"); @@ -300,11 +301,15 @@ List headers() { systemHeadersBuilder.setHeader("Host", hostString()); } - // GET, HEAD and DELETE with no request body should not set the Content-Length header if (requestPublisher != null) { contentLength = requestPublisher.contentLength(); if (contentLength == 0) { - systemHeadersBuilder.setHeader("Content-Length", "0"); + // PUT and POST with no request body should set the Content-Length header + // even when the content is empty. + // Other methods defined in RFC 9110 should not send the header in that case. + if ("POST".equals(method) || "PUT".equals(method)) { + systemHeadersBuilder.setHeader("Content-Length", "0"); + } } else if (contentLength > 0) { systemHeadersBuilder.setHeader("Content-Length", Long.toString(contentLength)); streaming = false; 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 b14d76d8dba..a02506cff5c 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 392385136b0..48f23953ad0 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 385b8e29439..bc8e93b4f0b 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 1288f1dbac3..2f4d2ade545 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 53099ad078e..c2faf90ce2e 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 a92090900fa..3de72f3f68b 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 93b63a746f1..9e718b264e4 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 5ca4073e20f..a1687c420c3 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 00000000000..7a8067ce983 --- /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 85775b84f93..615248a4926 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,8 @@ package com.sun.tools.javac.comp; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Map.Entry; import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; import java.util.function.Consumer; @@ -52,20 +49,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 @@ -214,8 +203,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; private final UnsetFieldsInfo unsetFieldsInfo; private final boolean allowValueClasses; @@ -339,7 +328,6 @@ 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); unsetFieldsInfo = UnsetFieldsInfo.instance(context); @@ -347,6 +335,7 @@ protected Flow(Context context) { Source source = Source.instance(context); allowValueClasses = (!preview.isPreview(Source.Feature.VALUE_CLASSES) || preview.isEnabled()) && Source.Feature.VALUE_CLASSES.allowedInSource(source); + exhaustiveness = ExhaustivenessComputer.instance(context); } /** @@ -736,7 +725,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); } @@ -775,7 +764,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) { @@ -785,429 +774,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<>(); @@ -1353,18 +919,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 @@ -3548,93 +3102,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.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index b7985a7f6ba..2336dcab137 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -397,11 +397,11 @@ compiler.err.cant.inherit.from.final=\ # 0: symbol or name compiler.err.cant.ref.before.ctor.called=\ - cannot reference {0} before constructor has been called + reference to {0} may only appear after an explicit constructor invocation # 0: symbol or name compiler.err.cant.assign.initialized.before.ctor.called=\ - cannot assign initialized field ''{0}'' before constructor has been called + assignment to initialized field ''{0}'' may only appear after an explicit constructor invocation compiler.err.cant.select.static.class.from.param.type=\ cannot select a static class from a parameterized type diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeBlob.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeBlob.java index 20c5fabf8bc..12469efc67e 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeBlob.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeBlob.java @@ -180,6 +180,8 @@ public ImmutableOopMapSet getOopMaps() { public boolean isUpcallStub() { return getKind() == UpcallKind; } + public boolean isContinuationStub() { return getName().equals("StubRoutines (continuation stubs)"); } + public boolean isJavaMethod() { return false; } public boolean isNativeMethod() { return false; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ContinuationEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ContinuationEntry.java new file mode 100644 index 00000000000..73152bdee84 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ContinuationEntry.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, NTT DATA. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; + + +public class ContinuationEntry extends VMObject { + private static long size; + private static Address returnPC; + + static { + VM.registerVMInitializedObserver((o, d) -> initialize(VM.getVM().getTypeDataBase())); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("ContinuationEntry"); + size = type.getSize(); + returnPC = type.getAddressField("_return_pc").getValue(); + } + + public ContinuationEntry(Address addr) { + super(addr); + } + + public Address getEntryPC() { + return returnPC; + } + + public Address getEntrySP(){ + return this.getAddress(); + } + + public Address getEntryFP(){ + return this.getAddress().addOffsetTo(size); + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java index d92f464f0d2..826b5cecfd5 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java @@ -47,6 +47,7 @@ public class JavaThread extends Thread { private static AddressField stackBaseField; private static CIntegerField stackSizeField; private static CIntegerField terminatedField; + private static AddressField contEntryField; private static AddressField activeHandlesField; private static CIntegerField monitorOwnerIDField; private static long oopPtrSize; @@ -95,6 +96,7 @@ private static synchronized void initialize(TypeDataBase db) { stackBaseField = type.getAddressField("_stack_base"); stackSizeField = type.getCIntegerField("_stack_size"); terminatedField = type.getCIntegerField("_terminated"); + contEntryField = type.getAddressField("_cont_entry"); activeHandlesField = type.getAddressField("_active_handles"); monitorOwnerIDField = type.getCIntegerField("_monitor_owner_id"); @@ -340,6 +342,10 @@ public int getTerminated() { return (int) terminatedField.getValue(addr); } + public ContinuationEntry getContEntry() { + return VMObjectFactory.newObject(ContinuationEntry.class, contEntryField.getValue(addr)); + } + /** Gets the Java-side thread object for this JavaThread */ public Oop getThreadObj() { Oop obj = null; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java index a5aa7ce4405..5ae4cb703b3 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java @@ -270,7 +270,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 @@ -356,6 +362,16 @@ private void updateMapWithSavedLink(RegisterMap map, Address savedFPAddr) { map.setLocation(fp, savedFPAddr); } + private Frame senderForContinuationStub(AARCH64RegisterMap map, CodeBlob cb) { + var contEntry = map.getThread().getContEntry(); + + Address senderSP = contEntry.getEntrySP(); + Address senderPC = contEntry.getEntryPC(); + Address senderFP = contEntry.getEntryFP(); + + return new AARCH64Frame(senderSP, senderFP, senderPC); + } + private Frame senderForCompiledFrame(AARCH64RegisterMap map, CodeBlob cb) { if (DEBUG) { System.out.println("senderForCompiledFrame"); 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 f4ce5337e2f..cae034c9613 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.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java index e02e056f028..44c8f4c679c 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java @@ -262,7 +262,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 @@ -348,6 +354,16 @@ private void updateMapWithSavedLink(RegisterMap map, Address savedFPAddr) { map.setLocation(fp, savedFPAddr); } + private Frame senderForContinuationStub(RISCV64RegisterMap map, CodeBlob cb) { + var contEntry = map.getThread().getContEntry(); + + Address senderSP = contEntry.getEntrySP(); + Address senderPC = contEntry.getEntryPC(); + Address senderFP = contEntry.getEntryFP(); + + return new RISCV64Frame(senderSP, senderFP, senderPC); + } + private Frame senderForCompiledFrame(RISCV64RegisterMap map, CodeBlob cb) { if (DEBUG) { System.out.println("senderForCompiledFrame"); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/x86/X86Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/x86/X86Frame.java index 3ee4f0a8158..2d972d3df17 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/x86/X86Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/x86/X86Frame.java @@ -270,7 +270,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 @@ -356,6 +362,16 @@ private void updateMapWithSavedLink(RegisterMap map, Address savedFPAddr) { map.setLocation(rbp, savedFPAddr); } + private Frame senderForContinuationStub(X86RegisterMap map, CodeBlob cb) { + var contEntry = map.getThread().getContEntry(); + + Address senderSP = contEntry.getEntrySP(); + Address senderPC = contEntry.getEntryPC(); + Address senderFP = contEntry.getEntryFP(); + + return new X86Frame(senderSP, senderFP, senderPC); + } + private Frame senderForCompiledFrame(X86RegisterMap map, CodeBlob cb) { if (DEBUG) { System.out.println("senderForCompiledFrame"); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java index f364702ca12..66fcd90e2e8 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java @@ -331,6 +331,7 @@ protected void generateOtherFiles(ClassTree classTree) copyResource(DocPaths.LINK_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.LINK_SVG), true); copyResource(DocPaths.MOON_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.MOON_SVG), true); copyResource(DocPaths.SUN_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.SUN_SVG), true); + copyResource(DocPaths.SORT_A_Z_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.SORT_A_Z_SVG), true); if (options.createIndex()) { copyResource(DocPaths.SEARCH_JS_TEMPLATE, DocPaths.SCRIPT_FILES.resolve(DocPaths.SEARCH_JS), true); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java index c257e1e153c..a3fba7eca14 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java @@ -120,6 +120,7 @@ public class HtmlIds { static final HtmlId THEME_OS = HtmlId.of("theme-os"); static final HtmlId THEME_PANEL = HtmlId.of("theme-panel"); static final HtmlId UNNAMED_PACKAGE_ANCHOR = HtmlId.of("unnamed-package"); + static final HtmlId TOC_ORDER_TOGGLE = HtmlId.of("toc-lexical-order-toggle"); private static final String FIELDS_INHERITANCE = "fields-inherited-from-class-"; private static final String METHODS_INHERITANCE = "methods-inherited-from-class-"; private static final String NESTED_CLASSES_INHERITANCE = "nested-classes-inherited-from-class-"; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TableOfContents.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TableOfContents.java index 6a3dff8a5d6..fab81914dbd 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TableOfContents.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TableOfContents.java @@ -112,6 +112,16 @@ protected Content toContent(boolean hasFilterInput) { .add(HtmlTree.INPUT(HtmlAttr.InputType.RESET, HtmlStyles.resetFilter) .put(HtmlAttr.TABINDEX, "-1") .put(HtmlAttr.VALUE, writer.resources.getText("doclet.filter_reset"))); + + header.add(Entity.NO_BREAK_SPACE) + .add(HtmlTree.BUTTON(HtmlStyles.tocSortToggle) + .setId(HtmlIds.TOC_ORDER_TOGGLE) + .add(HtmlTree.IMG(writer.pathToRoot.resolve(DocPaths.RESOURCE_FILES) + .resolve(DocPaths.SORT_A_Z_SVG), + writer.resources.getText("doclet.sort_table_of_contents") + )) + ); + } content.add(header); content.add(listBuilder); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyles.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyles.java index 358f490334b..7f33ebedfa4 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyles.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyles.java @@ -164,6 +164,16 @@ public enum HtmlStyles implements HtmlStyle { */ tocList, + /** + * The class used for lexical order toggle in the table of contents. + */ + tocSortToggle, + + /** + * The class used to indicate the state of the lexical sort toggle. + */ + tocSortIsActive, + // // diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template index b91f99b2c42..235d0a87d24 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template @@ -23,6 +23,14 @@ const linkIcon = "##REPLACE:doclet.Link_icon##"; const linkToSection = "##REPLACE:doclet.Link_to_section##"; const toggleMemberListing = "##REPLACE:doclet.Toggle_member_listing##"; +const sortLexicalLabel = "##REPLACE:doclet.Sort_lexicographically##"; +const sortSourceLabel = "##REPLACE:doclet.Sort_by_source_order##"; +const TOC_ALPHA = "alpha"; +const TOC_SOURCE = "source"; +var origOlOrder = new Map(); +var origContainerOrder = new Map(); +var snapshotted = false; + if (typeof hljs !== "undefined") { try { hljs.highlightAll(); @@ -409,6 +417,9 @@ document.addEventListener("DOMContentLoaded", function(e) { makeFilterWidget(tocMenu, updateToc); var menuInput = tocMenu.querySelector("input.filter-input"); } + + snapshotAllOnce(); + document.addEventListener("keydown", (e) => { if (e.ctrlKey || e.altKey || e.metaKey) { return; @@ -640,6 +651,168 @@ document.addEventListener("DOMContentLoaded", function(e) { } window.addEventListener("scroll", handleScroll); } + + function allTocNavs() { + return Array.from(document.querySelectorAll("nav.toc")); + } + + function nestedTocLists(scope) { + const listsToSort = []; + const sectionLinks = scope.querySelectorAll("ol.toc-list > li > a"); + + sectionLinks.forEach(function(link) { + const href = link.getAttribute("href"); + if (href === "#constructor-detail" || href === "#method-detail" + || href === "#field-detail" || href === "#annotation-interface-element-detail" + || href === "#enum-constant-detail" || href === "#property-detail") { + const memberList = link.nextElementSibling; + if (memberList && memberList.tagName === 'OL') { + listsToSort.push(memberList); + } + } + }); + + return listsToSort; + } + + function textForLi(li) { + return li.querySelector(":scope > a").textContent.trim(); + } + + function alphaCompare(a, b) { + return textForLi(a).localeCompare(textForLi(b), undefined, { + numeric: true, + sensitivity: "base" + }); + } + + function snapshotTocOnce(nav){ + nestedTocLists(nav).forEach(function(ol){ + if (!origOlOrder.has(ol)) origOlOrder.set(ol, Array.from(ol.children)); + }); + } + function restoreToc(nav){ + nestedTocLists(nav).forEach(function(ol){ + var orig = origOlOrder.get(ol); + if (orig) orig.forEach(function(li){ ol.appendChild(li); }); + }); + } + function sortTocAlpha(nav){ + nestedTocLists(nav).forEach(function(ol){ + var lis = Array.from(ol.children); + if (lis.length < 2) return; + lis.slice().sort(alphaCompare).forEach(function(li){ ol.appendChild(li); }); + }); + } + + function snapshotAllOnce() { + if (snapshotted) return; + allTocNavs().forEach(snapshotTocOnce); + snapshotted = true; + } + + function restoreAllMemberContainers(){ + origContainerOrder.forEach(function(kids, container){ + kids.forEach(function(ch){ container.appendChild(ch); }); + }); + } + + function reorderMemberDetailsAlpha() { + var sidebarNav = document.querySelector(".main-grid nav.toc"); + var mainRoot = document.querySelector(".main-grid main"); + if (!sidebarNav || !mainRoot) return; + + var containers = Array.from( + mainRoot.querySelectorAll("ul.member-list") + ); + + containers.forEach(function(container) { + var links = Array.from(sidebarNav.querySelectorAll("a[href^='#']")).filter(function(a) { + var id = a.getAttribute("href").slice(1); + if (!id) return false; + var target = document.getElementById(decodeURI(id)); + return target && container.contains(target); + }); + if (links.length < 2) return; + + var items = links.map(function(a) { + var id = a.getAttribute("href").slice(1); + var target = document.getElementById(decodeURI(id)); + if (!target) return null; + var block = target.closest("section.detail, div.detail") || target; + var li = block.closest("li"); + if (li.parentElement !== container) return null; + return { + node: li, + text: (a.textContent || "").trim() + }; + }).filter(Boolean); + + if (items.length < 2) return; + + if (!origContainerOrder.has(container)) { + origContainerOrder.set(container, Array.from(container.children)); + } + + items.slice() + .sort(function(x, y) { + return x.text.localeCompare(y.text, undefined, { + numeric: true, + sensitivity: "base" + }); + }) + .forEach(function(it) { + container.appendChild(it.node); + }); + }); + } + + function updateToggleButtons(order){ + const next = (order === TOC_ALPHA) ? sortSourceLabel : sortLexicalLabel; + document.querySelectorAll(".toc-sort-toggle").forEach(function(btn){ + btn.setAttribute("aria-label", next); + btn.setAttribute("title", next); + btn.setAttribute("aria-pressed", order === TOC_ALPHA); + + if (order === TOC_ALPHA) { + btn.classList.add("toc-sort-is-active"); + } else { + btn.classList.remove("toc-sort-is-active"); + } + + var img = btn.querySelector("img"); + if (img) img.alt = next; + }); + } + + var tocOrder = TOC_SOURCE; + updateToggleButtons(tocOrder); + + function applyAlpha(){ + snapshotAllOnce(); + reorderMemberDetailsAlpha(); + allTocNavs().forEach(sortTocAlpha); + initSectionData(); handleScroll(); + updateToggleButtons(TOC_ALPHA); + tocOrder = TOC_ALPHA; + } + + function applySource(){ + snapshotAllOnce(); + restoreAllMemberContainers(); + allTocNavs().forEach(restoreToc); + initSectionData(); handleScroll(); + updateToggleButtons(TOC_SOURCE); + tocOrder = TOC_SOURCE; + } + + document.querySelectorAll(".toc-sort-toggle").forEach(function(btn) { + btn.addEventListener("click", function() { + if (tocOrder === TOC_SOURCE) applyAlpha(); else applySource(); + if (typeof btn.blur === "function") btn.blur(); + }); + }); + // Resize handler new ResizeObserver((entries) => { if (expanded) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/sort-a-z.svg b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/sort-a-z.svg new file mode 100644 index 00000000000..ea1ed38942e --- /dev/null +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/sort-a-z.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css index 4bb0fad4306..8da885a12cc 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css @@ -693,10 +693,10 @@ dl.name-value > dd { background-color: var(--toc-background-color); color: #666666; font-size: 0.76rem; - border: none; cursor: pointer; padding: 6px 10px; white-space: nowrap; + border: 1px solid transparent; } .main-grid nav.toc button > img { vertical-align: middle; @@ -1901,6 +1901,11 @@ table.striped > tbody > tr > th { .ui-autocomplete .search-result-desc { display: block; } + .top-nav nav.toc .toc-sort-toggle { + background: transparent; + border: 0; + margin-left: 4px; + } } @media screen and (max-width: 800px) { .about-language { @@ -1957,6 +1962,33 @@ pre.snippet .highlighted { background-color: var(--snippet-highlight-color); border-radius: 10%; } + +nav.toc div.toc-header input.filter-input { + flex: 1 1 auto; + min-width: 0; +} + +nav.toc div.toc-header .toc-sort-toggle { + flex: 0 0 auto; + position: static; + display: inline-flex; + align-items: center; + padding: .5em; + cursor: pointer; +} + +nav.toc div.toc-header .toc-sort-toggle > img { + width: 22px; + height: 22px; + vertical-align: middle; + filter: var(--icon-filter); +} + +nav.toc div.toc-header .toc-sort-toggle.toc-sort-is-active { + background-color: var(--toc-highlight-color); + border-radius: 4px; +} + /* * Hide navigation links and search box in print layout */ diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties index 138a8fd0040..6a15025044a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties @@ -251,11 +251,14 @@ doclet.Description=Description doclet.ConstantField=Constant Field doclet.Value=Value doclet.table_of_contents=Table of contents +doclet.Sort_lexicographically=Sort member details lexicographically +doclet.Sort_by_source_order=Sort member details by source order doclet.hide_sidebar=Hide sidebar doclet.show_sidebar=Show sidebar doclet.filter_label=Filter contents (type .) doclet.filter_table_of_contents=Filter table of contents doclet.filter_reset=Reset +doclet.sort_table_of_contents=Sort member details in lexicographical order doclet.linkMismatch_PackagedLinkedtoModule=The code being documented uses packages in the unnamed module, \ but the packages defined in {0} are in named modules. doclet.linkMismatch_ModuleLinkedtoPackage=The code being documented uses modules but the packages defined \ diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java index dbe91585aff..f5672dcbf96 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java @@ -115,6 +115,9 @@ public static DocPath indexN(int n) { /** The name of the link icon file. */ public static final DocPath LINK_SVG = DocPath.create("link.svg"); + /** The name of the table of contents toggle icon file. */ + public static final DocPath SORT_A_Z_SVG = DocPath.create("sort-a-z.svg"); + /** The name of the right pointing angle icon. */ public static final DocPath RIGHT_SVG = DocPath.create("right.svg"); diff --git a/src/jdk.jcmd/share/man/jcmd.md b/src/jdk.jcmd/share/man/jcmd.md index 4e67e7a4502..af3886a915c 100644 --- a/src/jdk.jcmd/share/man/jcmd.md +++ b/src/jdk.jcmd/share/man/jcmd.md @@ -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 @@ -704,7 +704,7 @@ The following commands are available: Impact: Low `Thread.dump_to_file` \[*options*\] *filepath* -: Dump threads, with stack traces, to a file in plain text or JSON format. +: Dump all threads, with stack traces, to a file in plain text or JSON format. Impact: Medium: Depends on the number of threads. @@ -723,7 +723,8 @@ The following commands are available: - *filepath*: The file path to the output file. If %p is specified in the filename, it is expanded to the JVM's PID. (FILE, no default value) `Thread.print` \[*options*\] -: Prints all threads with stacktraces. +: Print all platform threads, and mounted virtual threads, with stack traces. + The Thread.dump_to_file command will print all threads to a file. Impact: Medium --- depends on the number of threads. 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 ec2290421a4..0ff2d0e3665 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.jdwp.agent/share/native/libjdwp/debugInit.c b/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c index 73ea9a295e6..2bed6b6bb28 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -890,8 +890,6 @@ printUsage(void) " everything = 0xfff")); TTY_MESSAGE(( - "debugflags=flags debug flags (bitmask) none\n" - " USE_ITERATE_THROUGH_HEAP 0x01\n" "\n" "Environment Variables\n" "---------------------\n" @@ -1192,13 +1190,6 @@ parseOptions(char *options) } /*LINTED*/ logflags = (unsigned)strtol(current, NULL, 0); - } else if (strcmp(buf, "debugflags") == 0) { - /*LINTED*/ - if (!get_tok(&str, current, (int)(end - current), ',')) { - goto syntax_error; - } - /*LINTED*/ - gdata->debugflags = (unsigned)strtol(current, NULL, 0); } else if ( strcmp(buf, "suspend")==0 ) { if ( !get_boolean(&str, &suspendOnInit) ) { goto syntax_error; diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/util.c b/src/jdk.jdwp.agent/share/native/libjdwp/util.c index 4d6d69ad408..3146d0cfe16 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/util.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/util.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -2729,10 +2729,6 @@ typedef struct ClassCountData { jvmtiError error; } ClassCountData; -/* Two different cbObjectCounter's, one for FollowReferences, one for - * IterateThroughHeap. Pick a card, any card. - */ - /* Callback for object count heap traversal (heap_reference_callback) */ static jint JNICALL cbObjectCounterFromRef(jvmtiHeapReferenceKind reference_kind, @@ -2794,38 +2790,6 @@ cbObjectCounterFromRef(jvmtiHeapReferenceKind reference_kind, return JVMTI_VISIT_OBJECTS; } -/* Callback for instance count heap traversal (heap_iteration_callback) */ -static jint JNICALL -cbObjectCounter(jlong class_tag, jlong size, jlong* tag_ptr, jint length, - void* user_data) -{ - ClassCountData *data; - int index; - - /* Check data structure */ - data = (ClassCountData*)user_data; - if (data == NULL) { - return JVMTI_VISIT_ABORT; - } - - /* Classes with no tag should be filtered out. */ - if ( class_tag == (jlong)0 ) { - data->error = AGENT_ERROR_INTERNAL; - return JVMTI_VISIT_ABORT; - } - - /* Class tag is actually an index into data arrays */ - index = CLASSTAG2INDEX(class_tag); - if (index < 0 || index >= data->classCount) { - data->error = AGENT_ERROR_ILLEGAL_ARGUMENT; - return JVMTI_VISIT_ABORT; - } - - /* Bump instance count on this class */ - data->counts[index]++; - return JVMTI_VISIT_OBJECTS; -} - /* Get instance counts for a set of classes */ jvmtiError classInstanceCounts(jint classCount, jclass *classes, jlong *counts) @@ -2878,53 +2842,27 @@ classInstanceCounts(jint classCount, jclass *classes, jlong *counts) /* Clear out callbacks structure */ (void)memset(&heap_callbacks,0,sizeof(heap_callbacks)); - /* Check debug flags to see how to do this. */ - if ( (gdata->debugflags & USE_ITERATE_THROUGH_HEAP) == 0 ) { - - /* Using FollowReferences only gives us live objects, but we - * need to tag the objects to avoid counting them twice since - * the callback is per reference. - * The jclass objects have been tagged with their index in the - * supplied list, and that tag may flip to negative if it - * is also an object of interest. - * All other objects being counted that weren't in the - * supplied classes list will have a negative classCount - * tag value. So all objects counted will have negative tags. - * If the absolute tag value is an index in the supplied - * list, then it's one of the supplied classes. - */ - data.negObjTag = -INDEX2CLASSTAG(classCount); - - /* Setup callbacks, only using object reference callback */ - heap_callbacks.heap_reference_callback = &cbObjectCounterFromRef; - - /* Follow references, no initiating object, tagged classes only */ - error = JVMTI_FUNC_PTR(jvmti,FollowReferences) - (jvmti, JVMTI_HEAP_FILTER_CLASS_UNTAGGED, - NULL, NULL, &heap_callbacks, &data); - - } else { - - /* Using IterateThroughHeap means that we will visit each object - * once, so no special tag tricks here. Just simple counting. - * However in this case the object might not be live, so we do - * a GC beforehand to make sure we minimize this. - */ - - /* FIXUP: Need some kind of trigger here to avoid excessive GC's? */ - error = JVMTI_FUNC_PTR(jvmti,ForceGarbageCollection)(jvmti); - if ( error != JVMTI_ERROR_NONE ) { - - /* Setup callbacks, just need object callback */ - heap_callbacks.heap_iteration_callback = &cbObjectCounter; + /* Using FollowReferences only gives us live objects, but we + * need to tag the objects to avoid counting them twice since + * the callback is per reference. + * The jclass objects have been tagged with their index in the + * supplied list, and that tag may flip to negative if it + * is also an object of interest. + * All other objects being counted that weren't in the + * supplied classes list will have a negative classCount + * tag value. So all objects counted will have negative tags. + * If the absolute tag value is an index in the supplied + * list, then it's one of the supplied classes. + */ + data.negObjTag = -INDEX2CLASSTAG(classCount); - /* Iterate through entire heap, tagged classes only */ - error = JVMTI_FUNC_PTR(jvmti,IterateThroughHeap) - (jvmti, JVMTI_HEAP_FILTER_CLASS_UNTAGGED, - NULL, &heap_callbacks, &data); + /* Setup callbacks, only using object reference callback */ + heap_callbacks.heap_reference_callback = &cbObjectCounterFromRef; - } - } + /* Follow references, no initiating object, tagged classes only */ + error = JVMTI_FUNC_PTR(jvmti,FollowReferences) + (jvmti, JVMTI_HEAP_FILTER_CLASS_UNTAGGED, + NULL, NULL, &heap_callbacks, &data); /* Use data error if needed */ if ( error == JVMTI_ERROR_NONE ) { diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/util.h b/src/jdk.jdwp.agent/share/native/libjdwp/util.h index 3d499d7d569..a48c8ba2c09 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/util.h +++ b/src/jdk.jdwp.agent/share/native/libjdwp/util.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -92,12 +92,6 @@ typedef struct { jboolean quiet; jboolean jvmti_data_dump; /* If true, then support JVMTI DATA_DUMP_REQUEST events. */ - /* Debug flags (bit mask) */ - int debugflags; - - /* Possible debug flags */ - #define USE_ITERATE_THROUGH_HEAP 0X001 - char * options; jclass classClass; 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 46dab564ac0..c8db9aef169 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 56d9fe01d79..82712fcbedc 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/classes/jdk/tools/jlink/internal/BasicImageWriter.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/BasicImageWriter.java index ca576227692..5966571408a 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/BasicImageWriter.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/BasicImageWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2020, 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 @@ -36,21 +36,17 @@ public final class BasicImageWriter { public static final String MODULES_IMAGE_NAME = "modules"; - private ByteOrder byteOrder; - private ImageStringsWriter strings; + private final ByteOrder byteOrder; + private final ImageStringsWriter strings; private int length; private int[] redirect; private ImageLocationWriter[] locations; - private List input; - private ImageStream headerStream; - private ImageStream redirectStream; - private ImageStream locationOffsetStream; - private ImageStream locationStream; - private ImageStream allIndexStream; - - public BasicImageWriter() { - this(ByteOrder.nativeOrder()); - } + private final List input; + private final ImageStream headerStream; + private final ImageStream redirectStream; + private final ImageStream locationOffsetStream; + private final ImageStream locationStream; + private final ImageStream allIndexStream; public BasicImageWriter(ByteOrder byteOrder) { this.byteOrder = Objects.requireNonNull(byteOrder); @@ -75,11 +71,15 @@ public String getString(int offset) { return strings.get(offset); } - public void addLocation(String fullname, long contentOffset, - long compressedSize, long uncompressedSize) { + public void addLocation( + String fullname, + long contentOffset, + long compressedSize, + long uncompressedSize, + int previewFlags) { ImageLocationWriter location = ImageLocationWriter.newLocation(fullname, strings, - contentOffset, compressedSize, uncompressedSize); + contentOffset, compressedSize, uncompressedSize, previewFlags); input.add(location); length++; } @@ -88,10 +88,6 @@ ImageLocationWriter[] getLocations() { return locations; } - int getLocationsCount() { - return input.size(); - } - private void generatePerfectHash() { PerfectHashBuilder builder = new PerfectHashBuilder<>( @@ -174,16 +170,4 @@ public byte[] getBytes() { return allIndexStream.toArray(); } - - ImageLocationWriter find(String key) { - int index = redirect[ImageStringsReader.hashCode(key) % length]; - - if (index < 0) { - index = -index - 1; - } else { - index = ImageStringsReader.hashCode(key, index) % length; - } - - return locations[index]; - } } diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java index 9e05fe31aa9..c8345d13358 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, 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 @@ -49,6 +49,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.internal.jimage.ImageLocation; import jdk.tools.jlink.internal.Archive.Entry; import jdk.tools.jlink.internal.Archive.Entry.EntryType; import jdk.tools.jlink.internal.JRTArchive.ResourceFileEntry; @@ -227,31 +228,8 @@ private static ResourcePool generateJImage(ResourcePoolManager allContent, DataOutputStream out, boolean generateRuntimeImage ) throws IOException { - ResourcePool resultResources; - try { - resultResources = pluginSupport.visitResources(allContent); - if (generateRuntimeImage) { - // Keep track of non-modules resources for linking from a run-time image - resultResources = addNonClassResourcesTrackFiles(resultResources, - writer); - // Generate the diff between the input resources from packaged - // modules in 'allContent' to the plugin- or otherwise - // generated-content in 'resultResources' - resultResources = addResourceDiffFiles(allContent.resourcePool(), - resultResources, - writer); - } - } catch (PluginException pe) { - if (JlinkTask.DEBUG) { - pe.printStackTrace(); - } - throw pe; - } catch (Exception ex) { - if (JlinkTask.DEBUG) { - ex.printStackTrace(); - } - throw new IOException(ex); - } + ResourcePool resultResources = + getResourcePool(allContent, writer, pluginSupport, generateRuntimeImage); Set duplicates = new HashSet<>(); long[] offset = new long[1]; @@ -282,8 +260,10 @@ private static ResourcePool generateJImage(ResourcePoolManager allContent, offset[0] += onFileSize; return; } + int locFlags = ImageLocation.getFlags( + res.path(), p -> resultResources.findEntry(p).isPresent()); duplicates.add(path); - writer.addLocation(path, offset[0], compressedSize, uncompressedSize); + writer.addLocation(path, offset[0], compressedSize, uncompressedSize, locFlags); paths.add(path); offset[0] += onFileSize; } @@ -307,6 +287,40 @@ private static ResourcePool generateJImage(ResourcePoolManager allContent, return resultResources; } + private static ResourcePool getResourcePool( + ResourcePoolManager allContent, + BasicImageWriter writer, + ImagePluginStack pluginSupport, + boolean generateRuntimeImage) + throws IOException { + ResourcePool resultResources; + try { + resultResources = pluginSupport.visitResources(allContent); + if (generateRuntimeImage) { + // Keep track of non-modules resources for linking from a run-time image + resultResources = addNonClassResourcesTrackFiles(resultResources, + writer); + // Generate the diff between the input resources from packaged + // modules in 'allContent' to the plugin- or otherwise + // generated-content in 'resultResources' + resultResources = addResourceDiffFiles(allContent.resourcePool(), + resultResources, + writer); + } + } catch (PluginException pe) { + if (JlinkTask.DEBUG) { + pe.printStackTrace(); + } + throw pe; + } catch (Exception ex) { + if (JlinkTask.DEBUG) { + ex.printStackTrace(); + } + throw new IOException(ex); + } + return resultResources; + } + /** * Support for creating a runtime suitable for linking from the run-time * image. diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageLocationWriter.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageLocationWriter.java index f2c7f102027..e5ea763e47e 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageLocationWriter.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageLocationWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, 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 @@ -53,9 +53,13 @@ private ImageLocationWriter addAttribute(int kind, String value) { return addAttribute(kind, strings.add(value)); } - static ImageLocationWriter newLocation(String fullName, + static ImageLocationWriter newLocation( + String fullName, ImageStringsWriter strings, - long contentOffset, long compressedSize, long uncompressedSize) { + long contentOffset, + long compressedSize, + long uncompressedSize, + int previewFlags) { String moduleName = ""; String parentName = ""; String baseName; @@ -90,13 +94,14 @@ static ImageLocationWriter newLocation(String fullName, } return new ImageLocationWriter(strings) - .addAttribute(ATTRIBUTE_MODULE, moduleName) - .addAttribute(ATTRIBUTE_PARENT, parentName) - .addAttribute(ATTRIBUTE_BASE, baseName) - .addAttribute(ATTRIBUTE_EXTENSION, extensionName) - .addAttribute(ATTRIBUTE_OFFSET, contentOffset) - .addAttribute(ATTRIBUTE_COMPRESSED, compressedSize) - .addAttribute(ATTRIBUTE_UNCOMPRESSED, uncompressedSize); + .addAttribute(ATTRIBUTE_MODULE, moduleName) + .addAttribute(ATTRIBUTE_PARENT, parentName) + .addAttribute(ATTRIBUTE_BASE, baseName) + .addAttribute(ATTRIBUTE_EXTENSION, extensionName) + .addAttribute(ATTRIBUTE_OFFSET, contentOffset) + .addAttribute(ATTRIBUTE_COMPRESSED, compressedSize) + .addAttribute(ATTRIBUTE_UNCOMPRESSED, uncompressedSize) + .addAttribute(ATTRIBUTE_PREVIEW_FLAGS, previewFlags); } @Override diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageResourcesTree.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageResourcesTree.java index 8a4288f0cfd..870978a410e 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageResourcesTree.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageResourcesTree.java @@ -24,33 +24,38 @@ */ package jdk.tools.jlink.internal; +import jdk.internal.jimage.ImageLocation; +import jdk.internal.jimage.ModuleReference; + import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; +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.Set; import java.util.TreeMap; -import java.util.TreeSet; +import java.util.stream.Collectors; /** * A class to build a sorted tree of Resource paths as a tree of ImageLocation. - * */ // XXX Public only due to the JImageTask / JImageTask code duplication public final class ImageResourcesTree { public static boolean isTreeInfoResource(String path) { - return path.startsWith("/packages") || path.startsWith("/modules"); + return path.startsWith("/packages/") || path.startsWith("/modules/"); } /** * Path item tree node. */ - private static class Node { + // Visible for testing only. + static class Node { private final String name; private final Map children = new TreeMap<>(); @@ -66,6 +71,14 @@ private Node(String name, Node parent) { } } + private void setLocation(ImageLocationWriter loc) { + // This *can* be called more than once, but only with the same instance. + if (this.loc != null && loc != this.loc) { + throw new IllegalStateException("Cannot add different locations: " + name); + } + this.loc = Objects.requireNonNull(loc); + } + public String getPath() { if (parent == null) { return "/"; @@ -95,215 +108,207 @@ private static String buildPath(Node item) { } } - private static final class ResourceNode extends Node { + // Visible for testing only. + static final class ResourceNode extends Node { public ResourceNode(String name, Node parent) { super(name, parent); } } - private static class PackageNode extends Node { - /** - * A reference to a package. Empty packages can be located inside one or - * more modules. A package with classes exist in only one module. - */ - static final class PackageReference { - - private final String name; - private final boolean isEmpty; + /** + * A 2nd level package directory, {@code "/packages/"}. + * + *

    While package paths can exist within many modules, for each package + * there is at most one module in which that package has resources. + * + *

    For example, the package path {@code java/util} exists in both the + * {@code java.base} and {@code java.logging} modules. This means both + * {@code "/packages/java.util/java.base"} and + * {@code "/packages/java.util/java.logging"} will exist, but only + * {@code "java.base"} entry will be marked as having content. + * + *

    When processing module references in non-preview mode, entries marked + * as {@link ModuleReference#isPreviewOnly() preview-only} must be ignored. + * + *

    If all entries in a package are preview-only, then the package's flags + * have {@link ImageLocation#FLAGS_IS_PREVIEW_ONLY FLAGS_IS_PREVIEW_ONLY} + * set, and the entire package must be ignored. + */ + // Visible for testing only. + static final class PackageNode extends Node { + private final List moduleReferences; - PackageReference(String name, boolean isEmpty) { - this.name = Objects.requireNonNull(name); - this.isEmpty = isEmpty; + PackageNode(String name, List moduleReferences, Node parent) { + super(name, parent); + if (moduleReferences.isEmpty()) { + throw new IllegalStateException("Package must be associated with modules: " + name); } - - @Override - public String toString() { - return name + "[empty:" + isEmpty + "]"; + if (moduleReferences.stream().filter(ModuleReference::hasResources).count() > 1) { + throw new IllegalStateException("Multiple modules contain non-empty package: " + name); } + this.moduleReferences = Collections.unmodifiableList(moduleReferences); } - private final Map references = new TreeMap<>(); - - PackageNode(String name, Node parent) { - super(name, parent); - } - - private void addReference(String name, boolean isEmpty) { - PackageReference ref = references.get(name); - if (ref == null || ref.isEmpty) { - references.put(name, new PackageReference(name, isEmpty)); - } + List getModuleReferences() { + return moduleReferences; } + } - private void validate() { - boolean exists = false; - for (PackageReference ref : references.values()) { - if (!ref.isEmpty) { - if (exists) { - throw new RuntimeException("Multiple modules to contain package " - + getName()); - } else { - exists = true; - } - } - } + // Not serialized, and never stored in any field of any class that is. + @SuppressWarnings("serial") + private static final class InvalidTreeException extends Exception { + public InvalidTreeException(Node badNode) { + super("Resources tree, invalid data structure, skipping: " + badNode.getPath()); } + // Exception only used for program flow, not debugging. + @Override + public Throwable fillInStackTrace() {return this;} } /** * Tree of nodes. */ - private static final class Tree { + // Visible for testing only. + static final class Tree { + private static final String PREVIEW_PREFIX = "META-INF/preview/"; private final Map directAccess = new HashMap<>(); private final List paths; private final Node root; - private Node modules; - private Node packages; + private Node packagesRoot; - private Tree(List paths) { - this.paths = paths; + // Visible for testing only. + Tree(List paths) { + this.paths = paths.stream().sorted(Comparator.reverseOrder()).toList(); + // Root node is not added to the directAccess map. root = new Node("", null); buildTree(); } private void buildTree() { - modules = new Node("modules", root); - directAccess.put(modules.getPath(), modules); - - Map> moduleToPackage = new TreeMap<>(); - Map> packageToModule = new TreeMap<>(); - - for (String p : paths) { - if (!p.startsWith("/")) { - continue; - } - String[] split = p.split("/"); - // minimum length is 3 items: // - if (split.length < 3) { - System.err.println("Resources tree, invalid data structure, " - + "skipping " + p); - continue; - } - Node current = modules; - String module = null; - for (int i = 0; i < split.length; i++) { - // When a non terminal node is marked as being a resource, something is wrong. + Node modulesRoot = new Node("modules", root); + directAccess.put(modulesRoot.getPath(), modulesRoot); + packagesRoot = new Node("packages", root); + directAccess.put(packagesRoot.getPath(), packagesRoot); + + // Map of dot-separated package names to module references (those + // in which the package appear). References are merged after to + // ensure each module name appears only once, but temporarily a + // module may have several entries per package (e.g. with-content, + // without-content, normal, preview-only etc..). + Map> packageToModules = new TreeMap<>(); + for (String fullPath : paths) { + try { + processPath(fullPath, modulesRoot, packageToModules); + } catch (InvalidTreeException err) { // It has been observed some badly created jar file to contain - // invalid directory entry marled as not directory (see 8131762) - if (current instanceof ResourceNode) { - System.err.println("Resources tree, invalid data structure, " - + "skipping " + p); - continue; - } - String s = split[i]; - if (!s.isEmpty()) { - // First item, this is the module, simply add a new node to the - // tree. - if (module == null) { - module = s; - } - Node n = current.children.get(s); - if (n == null) { - if (i == split.length - 1) { // Leaf - n = new ResourceNode(s, current); - String pkg = toPackageName(n.parent); - //System.err.println("Adding a resource node. pkg " + pkg + ", name " + s); - if (pkg != null && !pkg.startsWith("META-INF")) { - Set pkgs = moduleToPackage.get(module); - if (pkgs == null) { - pkgs = new TreeSet<>(); - moduleToPackage.put(module, pkgs); - } - pkgs.add(pkg); - } - } else { // put only sub trees, no leaf - n = new Node(s, current); - directAccess.put(n.getPath(), n); - String pkg = toPackageName(n); - if (pkg != null && !pkg.startsWith("META-INF")) { - Set mods = packageToModule.get(pkg); - if (mods == null) { - mods = new TreeSet<>(); - packageToModule.put(pkg, mods); - } - mods.add(module); - } - } - } - current = n; - } - } - } - packages = new Node("packages", root); - directAccess.put(packages.getPath(), packages); - // The subset of package nodes that have some content. - // These packages exist only in a single module. - for (Map.Entry> entry : moduleToPackage.entrySet()) { - for (String pkg : entry.getValue()) { - PackageNode pkgNode = new PackageNode(pkg, packages); - pkgNode.addReference(entry.getKey(), false); - directAccess.put(pkgNode.getPath(), pkgNode); + // invalid directory entry marked as not directory (see 8131762). + System.err.println(err.getMessage()); } } - // All packages - for (Map.Entry> entry : packageToModule.entrySet()) { - // Do we already have a package node? - PackageNode pkgNode = (PackageNode) packages.getChildren(entry.getKey()); - if (pkgNode == null) { - pkgNode = new PackageNode(entry.getKey(), packages); - } - for (String module : entry.getValue()) { - pkgNode.addReference(module, true); - } + // We've collected information for all "packages", including the root + // (empty) package and anything under "META-INF". However, these should + // not have entries in the "/packages" directory. + packageToModules.keySet().removeIf(p -> p.isEmpty() || p.equals("META-INF") || p.startsWith("META-INF.")); + packageToModules.forEach((pkgName, modRefs) -> { + // Merge multiple refs for the same module. + List pkgModules = modRefs.stream() + .collect(Collectors.groupingBy(ModuleReference::name)) + .values().stream() + .map(refs -> refs.stream().reduce(ModuleReference::merge).orElseThrow()) + .sorted() + .toList(); + PackageNode pkgNode = new PackageNode(pkgName, pkgModules, packagesRoot); directAccess.put(pkgNode.getPath(), pkgNode); + }); + } + + private void processPath( + String fullPath, + Node modulesRoot, + Map> packageToModules) + throws InvalidTreeException { + // Paths are untrusted, so be careful about checking expected format. + if (!fullPath.startsWith("/") || fullPath.endsWith("/") || fullPath.contains("//")) { + return; + } + int modEnd = fullPath.indexOf('/', 1); + // Ensure non-empty module name with non-empty suffix. + if (modEnd <= 1) { + return; } - // Validate that the packages are well formed. - for (Node n : packages.children.values()) { - ((PackageNode)n).validate(); + String modName = fullPath.substring(1, modEnd); + String pkgPath = fullPath.substring(modEnd + 1); + + Node parentNode = getDirectoryNode(modName, modulesRoot); + boolean isPreviewPath = false; + if (pkgPath.startsWith(PREVIEW_PREFIX)) { + // For preview paths, process nodes relative to the preview directory. + pkgPath = pkgPath.substring(PREVIEW_PREFIX.length()); + Node metaInf = getDirectoryNode("META-INF", parentNode); + parentNode = getDirectoryNode("preview", metaInf); + isPreviewPath = true; } + int pathEnd = pkgPath.lastIndexOf('/'); + // From invariants tested above, this must now be well-formed. + String fullPkgName = (pathEnd == -1) ? "" : pkgPath.substring(0, pathEnd).replace('/', '.'); + String resourceName = pkgPath.substring(pathEnd + 1); + // Intermediate packages are marked "empty" (no resources). This might + // later be merged with a non-empty reference for the same package. + ModuleReference emptyRef = ModuleReference.forEmptyPackageIn(modName, isPreviewPath); + + // Work down through empty packages to final resource. + for (int i = pkgEndIndex(fullPkgName, 0); i != -1; i = pkgEndIndex(fullPkgName, i)) { + // Due to invariants already checked, pkgName is non-empty. + String pkgName = fullPkgName.substring(0, i); + packageToModules.computeIfAbsent(pkgName, p -> new HashSet<>()).add(emptyRef); + String childNodeName = pkgName.substring(pkgName.lastIndexOf('.') + 1); + parentNode = getDirectoryNode(childNodeName, parentNode); + } + // Reached non-empty (leaf) package (could still be a duplicate). + Node resourceNode = parentNode.getChildren(resourceName); + if (resourceNode == null) { + ModuleReference resourceRef = ModuleReference.forPackageIn(modName, isPreviewPath); + packageToModules.computeIfAbsent(fullPkgName, p -> new HashSet<>()).add(resourceRef); + // Init adds new node to parent (don't add resources to directAccess). + new ResourceNode(resourceName, parentNode); + } else if (!(resourceNode instanceof ResourceNode)) { + throw new InvalidTreeException(resourceNode); + } } - public String toResourceName(Node node) { - if (!node.children.isEmpty()) { - throw new RuntimeException("Node is not a resource"); + private Node getDirectoryNode(String name, Node parent) throws InvalidTreeException { + Node child = parent.getChildren(name); + if (child == null) { + // Adds child to parent during init. + child = new Node(name, parent); + directAccess.put(child.getPath(), child); + } else if (child instanceof ResourceNode) { + throw new InvalidTreeException(child); } - return removeRadical(node); + return child; } - public String getModule(Node node) { - if (node.parent == null || node.getName().equals("modules") - || node.getName().startsWith("packages")) { - return null; - } - String path = removeRadical(node); - // "/xxx/..."; - path = path.substring(1); - int i = path.indexOf("/"); - if (i == -1) { - return path; - } else { - return path.substring(0, i); + // Helper to iterate package names up to, and including, the complete name. + private int pkgEndIndex(String s, int i) { + if (i >= 0 && i < s.length()) { + i = s.indexOf('.', i + 1); + return i != -1 ? i : s.length(); } + return -1; } - public String toPackageName(Node node) { - if (node.parent == null) { - return null; - } - String path = removeRadical(node.getPath(), "/modules/"); - String module = getModule(node); - if (path.equals(module)) { - return null; + private String toResourceName(Node node) { + if (!node.children.isEmpty()) { + throw new RuntimeException("Node is not a resource"); } - String pkg = removeRadical(path, module + "/"); - return pkg.replace('/', '.'); + return removeRadical(node); } - public String removeRadical(Node node) { + private String removeRadical(Node node) { return removeRadical(node.getPath(), "/modules"); } @@ -339,9 +344,10 @@ private static final class LocationsAdder { private int addLocations(Node current) { if (current instanceof PackageNode) { - PackageNode pkgNode = (PackageNode) current; - int size = pkgNode.references.size() * 8; - writer.addLocation(current.getPath(), offset, 0, size); + List refs = ((PackageNode) current).getModuleReferences(); + // "/packages/" entries have 8-byte entries (flags+offset). + int size = refs.size() * 8; + writer.addLocation(current.getPath(), offset, 0, size, ImageLocation.getPackageFlags(refs)); offset += size; } else { int[] ret = new int[current.children.size()]; @@ -351,8 +357,10 @@ private int addLocations(Node current) { i += 1; } if (current != tree.getRoot() && !(current instanceof ResourceNode)) { + int locFlags = ImageLocation.getFlags(current.getPath(), tree.directAccess::containsKey); + // Normal directory entries have 4-byte entries (offset only). int size = ret.length * 4; - writer.addLocation(current.getPath(), offset, 0, size); + writer.addLocation(current.getPath(), offset, 0, size, locFlags); offset += size; } } @@ -369,7 +377,7 @@ private List computeContent() { for (Map.Entry entry : outLocations.entrySet()) { Node item = tree.getMap().get(entry.getKey()); if (item != null) { - item.loc = entry.getValue(); + item.setLocation(entry.getValue()); } } computeContent(tree.getRoot(), outLocations); @@ -378,18 +386,13 @@ private List computeContent() { private int computeContent(Node current, Map outLocations) { if (current instanceof PackageNode) { - // /packages/ - PackageNode pkgNode = (PackageNode) current; - int size = pkgNode.references.size() * 8; - ByteBuffer buff = ByteBuffer.allocate(size); - buff.order(writer.getByteOrder()); - for (PackageNode.PackageReference mod : pkgNode.references.values()) { - buff.putInt(mod.isEmpty ? 1 : 0); - buff.putInt(writer.addString(mod.name)); - } - byte[] arr = buff.array(); - content.add(arr); - current.loc = outLocations.get(current.getPath()); + // "/packages/" entries have 8-byte entries (flags+offset). + List refs = ((PackageNode) current).getModuleReferences(); + ByteBuffer byteBuffer = ByteBuffer.allocate(8 * refs.size()); + byteBuffer.order(writer.getByteOrder()); + ModuleReference.write(refs, byteBuffer.asIntBuffer(), writer::addString); + content.add(byteBuffer.array()); + current.setLocation(outLocations.get(current.getPath())); } else { int[] ret = new int[current.children.size()]; int i = 0; @@ -410,10 +413,10 @@ private int computeContent(Node current, Map outLoc if (current instanceof ResourceNode) { // A resource location, remove "/modules" String s = tree.toResourceName(current); - current.loc = outLocations.get(s); + current.setLocation(outLocations.get(s)); } else { // empty "/packages" or empty "/modules" paths - current.loc = outLocations.get(current.getPath()); + current.setLocation(outLocations.get(current.getPath())); } } if (current.loc == null && current != tree.getRoot()) { diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageStringsWriter.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageStringsWriter.java index 7ba9b7db72e..b71e9589f85 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageStringsWriter.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageStringsWriter.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 @@ -27,13 +27,13 @@ import java.nio.ByteBuffer; import java.util.HashMap; + import jdk.internal.jimage.ImageStream; import jdk.internal.jimage.ImageStrings; import jdk.internal.jimage.ImageStringsReader; class ImageStringsWriter implements ImageStrings { private static final int NOT_FOUND = -1; - static final int EMPTY_OFFSET = 0; private final HashMap stringToOffsetMap; private final ImageStream stream; @@ -42,16 +42,20 @@ class ImageStringsWriter implements ImageStrings { this.stringToOffsetMap = new HashMap<>(); this.stream = new ImageStream(); - // Reserve 0 offset for empty string. - int offset = addString(""); - if (offset != 0) { - throw new InternalError("Empty string not offset zero"); - } + // Frequently used/special strings for which the offset is useful. + // New strings can be reserved after existing strings without having to + // change the jimage file version, but any change to existing entries + // requires the jimage file version to be increased at the same time. + reserveString("", ImageStrings.EMPTY_STRING_OFFSET); + reserveString("class", ImageStrings.CLASS_STRING_OFFSET); + reserveString("modules", ImageStrings.MODULES_STRING_OFFSET); + reserveString("packages", ImageStrings.PACKAGES_STRING_OFFSET); + } - // Reserve 1 offset for frequently used ".class". - offset = addString("class"); - if (offset != 1) { - throw new InternalError("'class' string not offset one"); + private void reserveString(String value, int expectedOffset) { + int offset = addString(value); + if (offset != expectedOffset) { + throw new InternalError("Reserved string \"" + value + "\" not at expected offset " + expectedOffset + "[was " + offset + "]"); } } diff --git a/src/jdk.jlink/share/man/jlink.md b/src/jdk.jlink/share/man/jlink.md index 74f2d119c69..dc256af43b5 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 571f163f682..1345597e352 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 f46b5a328fd..5c912728c32 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 7a881c846ff..e2d8750e39c 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 cf5c6a934f7..9576f6a6a99 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 131650aebb5..663b8b16265 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 141a1b5155f..9b5ed5b3b08 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/ApplicationImageUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java index 7d3250ff7b9..cda5b6c79ef 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java @@ -32,6 +32,7 @@ import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -148,6 +149,7 @@ private static void copyRecursive(Path srcDir, Path dstDir, List excludeDi } } - FileUtils.copyRecursive(srcDir, dstDir.toAbsolutePath(), excludes, LinkOption.NOFOLLOW_LINKS); + FileUtils.copyRecursive(srcDir, dstDir.toAbsolutePath(), excludes, + LinkOption.NOFOLLOW_LINKS, StandardCopyOption.REPLACE_EXISTING); } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java index f9a5429a8bf..f0323bbd841 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java @@ -125,7 +125,7 @@ public Arguments(String[] args) { for (String arg : args) { argList.add(arg); } - Log.verbose ("\njpackage argument list: \n" + argList + "\n"); + pos = 0; deployParams = new DeployParams(); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java index 56f2b46ea8f..8cfab61c9c0 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java @@ -120,7 +120,7 @@ static ApplicationBuilder createApplicationBuilder(Map p final var runtimeBuilderBuilder = new RuntimeBuilderBuilder(); - MODULE_PATH.copyInto(params, runtimeBuilderBuilder::modulePath); + runtimeBuilderBuilder.modulePath(MODULE_PATH.fetchFrom(params)); predefinedRuntimeDirectory.ifPresentOrElse(runtimeBuilderBuilder::forRuntime, () -> { final var startupInfos = launchers.asList().stream() 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 13c7a78b502..427051719bb 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 bd82b34d897..2273d385936 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 -> { @@ -215,7 +264,7 @@ private static String getPathList(List pathList) { } private static String getStringList(Set strings) { - return strings.stream().collect(Collectors.joining(",")); + return strings.stream().sorted().collect(Collectors.joining(",")); } private final List jlinkCmdLine; @@ -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 0099491fc73..73fc36d78ac 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 6b89bb3ee65..2b35a6830f8 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 af52696a546..ce2925d75e4 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 c989bcc8915..a0f5f077c70 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 3aa0c69dbd5..2a81b1c102c 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 00000000000..0a7a8cc8d4b --- /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 7bd6408183a..8a61acafe77 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/unix/classes/jdk/jpackage/internal/PackageScripts.java b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/PackageScripts.java index cd8799416f7..cef5fe05f35 100644 --- a/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/PackageScripts.java +++ b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/PackageScripts.java @@ -24,15 +24,16 @@ */ package jdk.jpackage.internal; +import static java.util.stream.Collectors.toMap; + import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Path; import java.util.EnumSet; import java.util.Map; import java.util.Optional; +import java.util.TreeMap; import java.util.function.Supplier; -import java.util.function.UnaryOperator; -import java.util.stream.Collectors; import jdk.jpackage.internal.resources.ResourceLocator; /** @@ -46,11 +47,11 @@ static & Supplier> PackageScripts cre } PackageScripts(Class scriptIdsType) { - scripts = EnumSet.allOf(scriptIdsType).stream().collect( - Collectors.toMap(UnaryOperator.identity(), scriptId -> { - return new ShellScriptResource(scriptId.name()).setResource( - scriptId.get()); - })); + scripts = EnumSet.allOf(scriptIdsType).stream().collect(toMap(x -> x, scriptId -> { + return new ShellScriptResource(scriptId.name()).setResource(scriptId.get()); + }, (a, b) -> { + throw new UnsupportedOperationException(); + }, TreeMap::new)); } PackageScripts setSubstitutionData(T id, Map data) { diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java index 9ce758eb3c7..f61a26f0774 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java @@ -65,7 +65,7 @@ public boolean isDefault() { @Override public boolean validate(Map params) throws ConfigException { - return msiBundler.validate(params); + return msiBundler.validate(params, WinFromParams.EXE_PACKAGE); } @Override diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java index 3ca26f38f82..24aa3e0573a 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java @@ -30,6 +30,7 @@ import java.nio.file.Path; import java.util.Map; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.model.PackagerException; import jdk.jpackage.internal.model.WinMsiPackage; import jdk.jpackage.internal.util.Result; @@ -80,11 +81,15 @@ public boolean isDefault() { } @Override - public boolean validate(Map params) + public boolean validate(Map params) throws ConfigException { + return validate(params, WinFromParams.MSI_PACKAGE); + } + + boolean validate(Map params, BundlerParamInfo pkgParam) throws ConfigException { try { // Order is important! - WinFromParams.APPLICATION.fetchFrom(params); + pkgParam.fetchFrom(params); BuildEnvFromParams.BUILD_ENV.fetchFrom(params); final var wixToolset = sysEnv.orElseThrow().wixToolset(); 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 63be18a5ee8..0dff5c26ae2 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 be15b202877..40160192862 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 b07c525db26..b1ad973b9ed 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixSourceConverter.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixSourceConverter.java @@ -38,6 +38,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import java.util.function.Supplier; import java.util.stream.Collectors; import javax.xml.XMLConstants; @@ -144,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) { @@ -193,7 +194,7 @@ void saveResources() throws IOException { } } - private final Map resources = new HashMap<>(); + private final Map resources = new TreeMap<>(); private final WixToolsetType wixToolsetType; } diff --git a/test/docs/ProblemList.txt b/test/docs/ProblemList.txt index 914ae21d49f..83693eacbd1 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 bf4f2111999..48b19acaa05 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 352ea33750e..34a5f8ca68e 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/nmt/test_nmt_buffer_overflow_detection.cpp b/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp index ae3f4e74516..82ddd28d56c 100644 --- a/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp +++ b/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp @@ -164,4 +164,14 @@ TEST_VM(NMT, test_realloc) { } } +TEST_VM_FATAL_ERROR_MSG(NMT, memory_corruption_call_stack, ".*header canary.*") { + if (MemTracker::tracking_level() != NMT_detail) { + guarantee(false, "fake message ignore this - header canary"); + } + const size_t SIZE = 1024; + char* p = (char*)os::malloc(SIZE, mtTest); + *(p - 1) = 0; + os::free(p); +} + #endif // !INCLUDE_ASAN diff --git a/test/hotspot/gtest/opto/test_regmask.cpp b/test/hotspot/gtest/opto/test_regmask.cpp index 0975314c33d..f367ca4bef4 100644 --- a/test/hotspot/gtest/opto/test_regmask.cpp +++ b/test/hotspot/gtest/opto/test_regmask.cpp @@ -33,11 +33,11 @@ static void contains_expected_num_of_registers(const RegMask& rm, unsigned int expected) { - ASSERT_TRUE(rm.Size() == expected); + ASSERT_TRUE(rm.size() == expected); if (expected > 0) { - ASSERT_TRUE(!rm.is_Empty()); + ASSERT_TRUE(!rm.is_empty()); } else { - ASSERT_TRUE(rm.is_Empty()); + ASSERT_TRUE(rm.is_empty()); ASSERT_TRUE(!rm.is_infinite_stack()); } @@ -60,14 +60,14 @@ TEST_VM(RegMask, empty) { TEST_VM(RegMask, iteration) { RegMask rm; - rm.Insert(30); - rm.Insert(31); - rm.Insert(32); - rm.Insert(33); - rm.Insert(62); - rm.Insert(63); - rm.Insert(64); - rm.Insert(65); + rm.insert(30); + rm.insert(31); + rm.insert(32); + rm.insert(33); + rm.insert(62); + rm.insert(63); + rm.insert(64); + rm.insert(65); RegMaskIterator rmi(rm); ASSERT_TRUE(rmi.next() == OptoReg::Name(30)); @@ -82,12 +82,12 @@ TEST_VM(RegMask, iteration) { } TEST_VM(RegMask, Set_ALL) { - // Check that Set_All doesn't add bits outside of rm.rm_size_bits() + // Check that set_all doesn't add bits outside of rm.rm_size_bits() RegMask rm; - rm.Set_All(); - ASSERT_TRUE(rm.Size() == rm.rm_size_in_bits()); - ASSERT_TRUE(!rm.is_Empty()); - // Set_All sets infinite_stack + rm.set_all(); + ASSERT_TRUE(rm.size() == rm.rm_size_in_bits()); + ASSERT_TRUE(!rm.is_empty()); + // set_all sets infinite_stack ASSERT_TRUE(rm.is_infinite_stack()); contains_expected_num_of_registers(rm, rm.rm_size_in_bits()); } @@ -95,64 +95,64 @@ TEST_VM(RegMask, Set_ALL) { TEST_VM(RegMask, Clear) { // Check that Clear doesn't leave any stray bits RegMask rm; - rm.Set_All(); - rm.Clear(); + rm.set_all(); + rm.clear(); contains_expected_num_of_registers(rm, 0); } -TEST_VM(RegMask, AND) { +TEST_VM(RegMask, and_with) { RegMask rm1; - rm1.Insert(OptoReg::Name(1)); + rm1.insert(OptoReg::Name(1)); contains_expected_num_of_registers(rm1, 1); - ASSERT_TRUE(rm1.Member(OptoReg::Name(1))); + ASSERT_TRUE(rm1.member(OptoReg::Name(1))); - rm1.AND(rm1); + rm1.and_with(rm1); contains_expected_num_of_registers(rm1, 1); RegMask rm2; - rm1.AND(rm2); + rm1.and_with(rm2); contains_expected_num_of_registers(rm1, 0); contains_expected_num_of_registers(rm2, 0); } -TEST_VM(RegMask, OR) { +TEST_VM(RegMask, or_with) { RegMask rm1; - rm1.Insert(OptoReg::Name(1)); + rm1.insert(OptoReg::Name(1)); contains_expected_num_of_registers(rm1, 1); - ASSERT_TRUE(rm1.Member(OptoReg::Name(1))); + ASSERT_TRUE(rm1.member(OptoReg::Name(1))); - rm1.OR(rm1); + rm1.or_with(rm1); contains_expected_num_of_registers(rm1, 1); RegMask rm2; - rm1.OR(rm2); + rm1.or_with(rm2); contains_expected_num_of_registers(rm1, 1); contains_expected_num_of_registers(rm2, 0); } -TEST_VM(RegMask, SUBTRACT) { +TEST_VM(RegMask, subtract) { RegMask rm1; RegMask rm2; - rm2.Set_All(); + rm2.set_all(); for (int i = 17; i < (int)rm1.rm_size_in_bits(); i++) { - rm1.Insert(i); + rm1.insert(i); } rm1.set_infinite_stack(true); ASSERT_TRUE(rm1.is_infinite_stack()); - rm2.SUBTRACT(rm1); + rm2.subtract(rm1); contains_expected_num_of_registers(rm1, rm1.rm_size_in_bits() - 17); contains_expected_num_of_registers(rm2, 17); } -TEST_VM(RegMask, SUBTRACT_inner) { +TEST_VM(RegMask, subtract_inner) { RegMask rm1; RegMask rm2; - rm2.Set_All(); + rm2.set_all(); for (int i = 17; i < (int)rm1.rm_size_in_bits(); i++) { - rm1.Insert(i); + rm1.insert(i); } - rm2.SUBTRACT_inner(rm1); + rm2.subtract_inner(rm1); contains_expected_num_of_registers(rm1, rm1.rm_size_in_bits() - 17); contains_expected_num_of_registers(rm2, 17); } @@ -161,11 +161,11 @@ TEST_VM(RegMask, is_bound1) { RegMask rm; ASSERT_FALSE(rm.is_bound1()); for (int i = 0; i < (int)rm.rm_size_in_bits() - 1; i++) { - rm.Insert(i); + rm.insert(i); ASSERT_TRUE(rm.is_bound1()) << "Index " << i; ASSERT_TRUE(rm.is_bound(Op_RegI)) << "Index " << i; contains_expected_num_of_registers(rm, 1); - rm.Remove(i); + rm.remove(i); } // infinite_stack does not count as a bound register rm.set_infinite_stack(true); @@ -176,18 +176,18 @@ TEST_VM(RegMask, is_bound_pair) { RegMask rm; ASSERT_TRUE(rm.is_bound_pair()); for (int i = 0; i < (int)rm.rm_size_in_bits() - 2; i++) { - rm.Insert(i); - rm.Insert(i + 1); + rm.insert(i); + rm.insert(i + 1); ASSERT_TRUE(rm.is_bound_pair()) << "Index " << i; ASSERT_TRUE(rm.is_bound_set(2)) << "Index " << i; ASSERT_TRUE(rm.is_bound(Op_RegI)) << "Index " << i; contains_expected_num_of_registers(rm, 2); - rm.Clear(); + rm.clear(); } // A pair with the infinite bit does not count as a bound pair - rm.Clear(); - rm.Insert(rm.rm_size_in_bits() - 2); - rm.Insert(rm.rm_size_in_bits() - 1); + rm.clear(); + rm.insert(rm.rm_size_in_bits() - 2); + rm.insert(rm.rm_size_in_bits() - 1); rm.set_infinite_stack(true); ASSERT_FALSE(rm.is_bound_pair()); } @@ -198,40 +198,40 @@ TEST_VM(RegMask, is_bound_set) { ASSERT_TRUE(rm.is_bound_set(size)); for (int i = 0; i < (int)rm.rm_size_in_bits() - size; i++) { for (int j = i; j < i + size; j++) { - rm.Insert(j); + rm.insert(j); } ASSERT_TRUE(rm.is_bound_set(size)) << "Size " << size << " Index " << i; contains_expected_num_of_registers(rm, size); - rm.Clear(); + rm.clear(); } // A set with infinite_stack does not count as a bound set for (int j = rm.rm_size_in_bits() - size; j < (int)rm.rm_size_in_bits(); j++) { - rm.Insert(j); + rm.insert(j); } rm.set_infinite_stack(true); ASSERT_FALSE(rm.is_bound_set(size)); - rm.Clear(); + rm.clear(); } } TEST_VM(RegMask, external_member) { RegMask rm; rm.set_infinite_stack(false); - ASSERT_FALSE(rm.Member(OptoReg::Name(rm.rm_size_in_bits()))); + ASSERT_FALSE(rm.member(OptoReg::Name(rm.rm_size_in_bits()))); rm.set_infinite_stack(true); - ASSERT_TRUE(rm.Member(OptoReg::Name(rm.rm_size_in_bits()))); + ASSERT_TRUE(rm.member(OptoReg::Name(rm.rm_size_in_bits()))); } TEST_VM(RegMask, find_element) { RegMask rm; - rm.Insert(OptoReg::Name(44)); - rm.Insert(OptoReg::Name(30)); - rm.Insert(OptoReg::Name(54)); + rm.insert(OptoReg::Name(44)); + rm.insert(OptoReg::Name(30)); + rm.insert(OptoReg::Name(54)); ASSERT_EQ(rm.find_first_elem(), OptoReg::Name(30)); ASSERT_EQ(rm.find_last_elem(), OptoReg::Name(54)); rm.set_infinite_stack(true); ASSERT_EQ(rm.find_last_elem(), OptoReg::Name(54)); - rm.Clear(); + rm.clear(); ASSERT_EQ(rm.find_first_elem(), OptoReg::Bad); ASSERT_EQ(rm.find_last_elem(), OptoReg::Bad); } @@ -242,58 +242,58 @@ TEST_VM(RegMask, find_first_set) { lrg._is_scalable = 0; lrg._is_vector = 0; ASSERT_EQ(rm.find_first_set(lrg, 2), OptoReg::Bad); - rm.Insert(OptoReg::Name(24)); - rm.Insert(OptoReg::Name(25)); - rm.Insert(OptoReg::Name(26)); - rm.Insert(OptoReg::Name(27)); - rm.Insert(OptoReg::Name(16)); - rm.Insert(OptoReg::Name(17)); - rm.Insert(OptoReg::Name(18)); - rm.Insert(OptoReg::Name(19)); + rm.insert(OptoReg::Name(24)); + rm.insert(OptoReg::Name(25)); + rm.insert(OptoReg::Name(26)); + rm.insert(OptoReg::Name(27)); + rm.insert(OptoReg::Name(16)); + rm.insert(OptoReg::Name(17)); + rm.insert(OptoReg::Name(18)); + rm.insert(OptoReg::Name(19)); ASSERT_EQ(rm.find_first_set(lrg, 4), OptoReg::Name(19)); } TEST_VM(RegMask, alignment) { RegMask rm; - rm.Insert(OptoReg::Name(30)); - rm.Insert(OptoReg::Name(31)); + rm.insert(OptoReg::Name(30)); + rm.insert(OptoReg::Name(31)); ASSERT_TRUE(rm.is_aligned_sets(2)); - rm.Insert(OptoReg::Name(32)); - rm.Insert(OptoReg::Name(37)); - rm.Insert(OptoReg::Name(62)); - rm.Insert(OptoReg::Name(71)); - rm.Insert(OptoReg::Name(74)); - rm.Insert(OptoReg::Name(75)); + rm.insert(OptoReg::Name(32)); + rm.insert(OptoReg::Name(37)); + rm.insert(OptoReg::Name(62)); + rm.insert(OptoReg::Name(71)); + rm.insert(OptoReg::Name(74)); + rm.insert(OptoReg::Name(75)); ASSERT_FALSE(rm.is_aligned_pairs()); rm.clear_to_pairs(); ASSERT_TRUE(rm.is_aligned_sets(2)); ASSERT_TRUE(rm.is_aligned_pairs()); contains_expected_num_of_registers(rm, 4); - ASSERT_TRUE(rm.Member(OptoReg::Name(30))); - ASSERT_TRUE(rm.Member(OptoReg::Name(31))); - ASSERT_TRUE(rm.Member(OptoReg::Name(74))); - ASSERT_TRUE(rm.Member(OptoReg::Name(75))); + ASSERT_TRUE(rm.member(OptoReg::Name(30))); + ASSERT_TRUE(rm.member(OptoReg::Name(31))); + ASSERT_TRUE(rm.member(OptoReg::Name(74))); + ASSERT_TRUE(rm.member(OptoReg::Name(75))); ASSERT_FALSE(rm.is_misaligned_pair()); - rm.Remove(OptoReg::Name(30)); - rm.Remove(OptoReg::Name(74)); + rm.remove(OptoReg::Name(30)); + rm.remove(OptoReg::Name(74)); ASSERT_TRUE(rm.is_misaligned_pair()); } TEST_VM(RegMask, clear_to_sets) { RegMask rm; - rm.Insert(OptoReg::Name(3)); - rm.Insert(OptoReg::Name(20)); - rm.Insert(OptoReg::Name(21)); - rm.Insert(OptoReg::Name(22)); - rm.Insert(OptoReg::Name(23)); - rm.Insert(OptoReg::Name(25)); - rm.Insert(OptoReg::Name(26)); - rm.Insert(OptoReg::Name(27)); - rm.Insert(OptoReg::Name(40)); - rm.Insert(OptoReg::Name(42)); - rm.Insert(OptoReg::Name(43)); - rm.Insert(OptoReg::Name(44)); - rm.Insert(OptoReg::Name(45)); + rm.insert(OptoReg::Name(3)); + rm.insert(OptoReg::Name(20)); + rm.insert(OptoReg::Name(21)); + rm.insert(OptoReg::Name(22)); + rm.insert(OptoReg::Name(23)); + rm.insert(OptoReg::Name(25)); + rm.insert(OptoReg::Name(26)); + rm.insert(OptoReg::Name(27)); + rm.insert(OptoReg::Name(40)); + rm.insert(OptoReg::Name(42)); + rm.insert(OptoReg::Name(43)); + rm.insert(OptoReg::Name(44)); + rm.insert(OptoReg::Name(45)); rm.clear_to_sets(2); ASSERT_TRUE(rm.is_aligned_sets(2)); contains_expected_num_of_registers(rm, 10); @@ -307,7 +307,7 @@ TEST_VM(RegMask, clear_to_sets) { TEST_VM(RegMask, smear_to_sets) { RegMask rm; - rm.Insert(OptoReg::Name(3)); + rm.insert(OptoReg::Name(3)); rm.smear_to_sets(2); ASSERT_TRUE(rm.is_aligned_sets(2)); contains_expected_num_of_registers(rm, 2); @@ -327,14 +327,14 @@ TEST_VM(RegMask, overlap) { RegMask rm2; ASSERT_FALSE(rm1.overlap(rm2)); ASSERT_FALSE(rm2.overlap(rm1)); - rm1.Insert(OptoReg::Name(23)); - rm1.Insert(OptoReg::Name(2)); - rm1.Insert(OptoReg::Name(12)); - rm2.Insert(OptoReg::Name(1)); - rm2.Insert(OptoReg::Name(4)); + rm1.insert(OptoReg::Name(23)); + rm1.insert(OptoReg::Name(2)); + rm1.insert(OptoReg::Name(12)); + rm2.insert(OptoReg::Name(1)); + rm2.insert(OptoReg::Name(4)); ASSERT_FALSE(rm1.overlap(rm2)); ASSERT_FALSE(rm2.overlap(rm1)); - rm1.Insert(OptoReg::Name(4)); + rm1.insert(OptoReg::Name(4)); ASSERT_TRUE(rm1.overlap(rm2)); ASSERT_TRUE(rm2.overlap(rm1)); } @@ -342,10 +342,10 @@ TEST_VM(RegMask, overlap) { TEST_VM(RegMask, valid_reg) { RegMask rm; ASSERT_FALSE(rm.is_valid_reg(OptoReg::Name(42), 1)); - rm.Insert(OptoReg::Name(3)); - rm.Insert(OptoReg::Name(5)); - rm.Insert(OptoReg::Name(6)); - rm.Insert(OptoReg::Name(7)); + rm.insert(OptoReg::Name(3)); + rm.insert(OptoReg::Name(5)); + rm.insert(OptoReg::Name(6)); + rm.insert(OptoReg::Name(7)); ASSERT_FALSE(rm.is_valid_reg(OptoReg::Name(7), 4)); ASSERT_TRUE(rm.is_valid_reg(OptoReg::Name(7), 2)); } @@ -355,19 +355,19 @@ TEST_VM(RegMask, rollover_and_insert_remove) { OptoReg::Name reg1(rm.rm_size_in_bits() + 42); OptoReg::Name reg2(rm.rm_size_in_bits() * 2 + 42); rm.set_infinite_stack(true); - ASSERT_TRUE(rm.Member(reg1)); + ASSERT_TRUE(rm.member(reg1)); rm.rollover(); - rm.Clear(); - rm.Insert(reg1); - ASSERT_TRUE(rm.Member(reg1)); - rm.Remove(reg1); - ASSERT_FALSE(rm.Member(reg1)); + rm.clear(); + rm.insert(reg1); + ASSERT_TRUE(rm.member(reg1)); + rm.remove(reg1); + ASSERT_FALSE(rm.member(reg1)); rm.set_infinite_stack(true); rm.rollover(); - rm.Clear(); - rm.Insert(reg2); - ASSERT_FALSE(rm.Member(reg1)); - ASSERT_TRUE(rm.Member(reg2)); + rm.clear(); + rm.insert(reg2); + ASSERT_FALSE(rm.member(reg1)); + ASSERT_TRUE(rm.member(reg2)); } TEST_VM(RegMask, rollover_and_find) { @@ -376,11 +376,11 @@ TEST_VM(RegMask, rollover_and_find) { OptoReg::Name reg2(rm.rm_size_in_bits() + 7); rm.set_infinite_stack(true); rm.rollover(); - rm.Clear(); + rm.clear(); ASSERT_EQ(rm.find_first_elem(), OptoReg::Bad); ASSERT_EQ(rm.find_last_elem(), OptoReg::Bad); - rm.Insert(reg1); - rm.Insert(reg2); + rm.insert(reg1); + rm.insert(reg2); ASSERT_EQ(rm.find_first_elem(), reg2); ASSERT_EQ(rm.find_last_elem(), reg1); } @@ -400,35 +400,35 @@ TEST_VM(RegMask, rollover_and_find_first_set) { OptoReg::Name reg8(rm.rm_size_in_bits() + 19); rm.set_infinite_stack(true); rm.rollover(); - rm.Clear(); + rm.clear(); ASSERT_EQ(rm.find_first_set(lrg, 2), OptoReg::Bad); - rm.Insert(reg1); - rm.Insert(reg2); - rm.Insert(reg3); - rm.Insert(reg4); - rm.Insert(reg5); - rm.Insert(reg6); - rm.Insert(reg7); - rm.Insert(reg8); + rm.insert(reg1); + rm.insert(reg2); + rm.insert(reg3); + rm.insert(reg4); + rm.insert(reg5); + rm.insert(reg6); + rm.insert(reg7); + rm.insert(reg8); ASSERT_EQ(rm.find_first_set(lrg, 4), reg8); } -TEST_VM(RegMask, rollover_and_Set_All_From) { +TEST_VM(RegMask, rollover_and_set_all_from) { RegMask rm; OptoReg::Name reg1(rm.rm_size_in_bits() + 42); rm.set_infinite_stack(true); rm.rollover(); - rm.Clear(); - rm.Set_All_From(reg1); + rm.clear(); + rm.set_all_from(reg1); contains_expected_num_of_registers(rm, rm.rm_size_in_bits() - 42); } -TEST_VM(RegMask, rollover_and_Set_All_From_Offset) { +TEST_VM(RegMask, rollover_and_set_all_from_offset) { RegMask rm; rm.set_infinite_stack(true); rm.rollover(); - rm.Clear(); - rm.Set_All_From_Offset(); + rm.clear(); + rm.set_all_from_offset(); contains_expected_num_of_registers(rm, rm.rm_size_in_bits()); } @@ -440,11 +440,11 @@ TEST_VM(RegMask, rollover_and_iterate) { OptoReg::Name reg4(rm.rm_size_in_bits() + 43); rm.set_infinite_stack(true); rm.rollover(); - rm.Clear(); - rm.Insert(reg1); - rm.Insert(reg2); - rm.Insert(reg3); - rm.Insert(reg4); + rm.clear(); + rm.insert(reg1); + rm.insert(reg2); + rm.insert(reg3); + rm.insert(reg4); RegMaskIterator rmi(rm); ASSERT_EQ(rmi.next(), reg1); ASSERT_EQ(rmi.next(), reg2); @@ -453,45 +453,45 @@ TEST_VM(RegMask, rollover_and_iterate) { ASSERT_FALSE(rmi.has_next()); } -TEST_VM(RegMask, rollover_and_SUBTRACT_inner_disjoint) { +TEST_VM(RegMask, rollover_and_subtract_inner_disjoint) { RegMask rm1; RegMask rm2; OptoReg::Name reg1(rm1.rm_size_in_bits() + 42); rm1.set_infinite_stack(true); rm1.rollover(); - rm1.Clear(); - rm1.SUBTRACT_inner(rm2); + rm1.clear(); + rm1.subtract_inner(rm2); contains_expected_num_of_registers(rm1, 0); - rm2.SUBTRACT_inner(rm1); + rm2.subtract_inner(rm1); contains_expected_num_of_registers(rm2, 0); - rm1.Insert(reg1); - rm2.Insert(42); - rm1.SUBTRACT_inner(rm2); + rm1.insert(reg1); + rm2.insert(42); + rm1.subtract_inner(rm2); contains_expected_num_of_registers(rm1, 1); - rm2.SUBTRACT_inner(rm1); + rm2.subtract_inner(rm1); contains_expected_num_of_registers(rm2, 1); } -TEST_VM(RegMask, rollover_and_SUBTRACT_inner_overlap) { +TEST_VM(RegMask, rollover_and_subtract_inner_overlap) { RegMask rm1; RegMask rm2; OptoReg::Name reg1(rm1.rm_size_in_bits() + 42); rm1.set_infinite_stack(true); rm1.rollover(); - rm1.Clear(); + rm1.clear(); rm2.set_infinite_stack(true); rm2.rollover(); - rm2.Clear(); - rm1.SUBTRACT_inner(rm2); + rm2.clear(); + rm1.subtract_inner(rm2); contains_expected_num_of_registers(rm1, 0); - rm2.SUBTRACT_inner(rm1); + rm2.subtract_inner(rm1); contains_expected_num_of_registers(rm2, 0); - rm1.Insert(reg1); - rm2.Insert(reg1); - rm1.SUBTRACT_inner(rm2); + rm1.insert(reg1); + rm2.insert(reg1); + rm1.subtract_inner(rm2); contains_expected_num_of_registers(rm1, 0); - rm1.Insert(reg1); - rm2.SUBTRACT_inner(rm1); + rm1.insert(reg1); + rm2.subtract_inner(rm1); contains_expected_num_of_registers(rm2, 0); } @@ -502,20 +502,20 @@ TEST_VM_ASSERT_MSG(RegMask, unexpected_clone, ".*clone sanity check") { RegMask rm2; // Copy contents of rm1 to rm2 inappropriately (no copy constructor) memcpy((void*)&rm2, (void*)&rm1, sizeof(RegMask)); - rm2.Member(0); // Safeguard in RegMask must catch this. + rm2.member(0); // Safeguard in RegMask must catch this. } TEST_VM_ASSERT_MSG(RegMask, unexpected_growth, ".*unexpected register mask growth") { RegMask rm; // Add clearly out of range OptoReg::Name - rm.Insert(std::numeric_limits::max()); + rm.insert(std::numeric_limits::max()); } TEST_VM_ASSERT_MSG(RegMask, not_growable, ".*register mask not growable") { RegMask rm; // Add a bit just outside the mask, without having specified an arena for // extension. - rm.Insert(rm.rm_size_in_bits()); + rm.insert(rm.rm_size_in_bits()); } TEST_VM_ASSERT_MSG(RegMask, offset_mismatch, ".*offset mismatch") { @@ -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 @@ -549,8 +549,8 @@ static int first_extended() { static void extend(RegMask& rm, unsigned int n = 4) { // Extend the given RegMask with at least n dynamically-allocated words. - rm.Insert(OptoReg::Name(first_extended() + (BitsPerWord * n) - 1)); - rm.Clear(); + rm.insert(OptoReg::Name(first_extended() + (BitsPerWord * n) - 1)); + rm.clear(); ASSERT_TRUE(rm.rm_size_in_words() >= RegMask::gtest_basic_rm_size_in_words() + n); } @@ -562,14 +562,14 @@ TEST_VM(RegMask, static_by_default) { TEST_VM(RegMask, iteration_extended) { RegMask rm(arena()); - rm.Insert(30); - rm.Insert(31); - rm.Insert(33); - rm.Insert(62); - rm.Insert(first_extended()); - rm.Insert(first_extended() + 42); - rm.Insert(first_extended() + 55); - rm.Insert(first_extended() + 456); + rm.insert(30); + rm.insert(31); + rm.insert(33); + rm.insert(62); + rm.insert(first_extended()); + rm.insert(first_extended() + 42); + rm.insert(first_extended() + 55); + rm.insert(first_extended() + 456); RegMaskIterator rmi(rm); ASSERT_TRUE(rmi.next() == OptoReg::Name(30)); @@ -583,125 +583,125 @@ TEST_VM(RegMask, iteration_extended) { ASSERT_FALSE(rmi.has_next()); } -TEST_VM(RegMask, Set_ALL_extended) { - // Check that Set_All doesn't add bits outside of rm.rm_size_bits() on +TEST_VM(RegMask, set_all_extended) { + // Check that set_all doesn't add bits outside of rm.rm_size_bits() on // extended RegMasks. RegMask rm(arena()); extend(rm); - rm.Set_All(); - ASSERT_EQ(rm.Size(), rm.rm_size_in_bits()); - ASSERT_TRUE(!rm.is_Empty()); - // Set_All sets infinite_stack bit + rm.set_all(); + ASSERT_EQ(rm.size(), rm.rm_size_in_bits()); + ASSERT_TRUE(!rm.is_empty()); + // set_all sets infinite_stack bit ASSERT_TRUE(rm.is_infinite_stack()); contains_expected_num_of_registers(rm, rm.rm_size_in_bits()); } -TEST_VM(RegMask, Set_ALL_From_extended) { +TEST_VM(RegMask, set_all_from_extended) { RegMask rm(arena()); extend(rm); - rm.Set_All_From(OptoReg::Name(42)); + rm.set_all_from(OptoReg::Name(42)); contains_expected_num_of_registers(rm, rm.rm_size_in_bits() - 42); } -TEST_VM(RegMask, Set_ALL_From_extended_grow) { +TEST_VM(RegMask, set_all_from_extended_grow) { RegMask rm(arena()); - rm.Set_All_From(first_extended() + OptoReg::Name(42)); + rm.set_all_from(first_extended() + OptoReg::Name(42)); is_extended(rm); contains_expected_num_of_registers(rm, rm.rm_size_in_bits() - first_extended() - 42); } -TEST_VM(RegMask, Clear_extended) { - // Check that Clear doesn't leave any stray bits on extended RegMasks. +TEST_VM(RegMask, clear_extended) { + // Check that clear doesn't leave any stray bits on extended RegMasks. RegMask rm(arena()); - rm.Insert(first_extended()); + rm.insert(first_extended()); is_extended(rm); - rm.Set_All(); - rm.Clear(); + rm.set_all(); + rm.clear(); contains_expected_num_of_registers(rm, 0); } -TEST_VM(RegMask, AND_extended_basic) { +TEST_VM(RegMask, and_with_extended_basic) { RegMask rm1(arena()); - rm1.Insert(OptoReg::Name(first_extended())); + rm1.insert(OptoReg::Name(first_extended())); is_extended(rm1); contains_expected_num_of_registers(rm1, 1); - ASSERT_TRUE(rm1.Member(OptoReg::Name(first_extended()))); + ASSERT_TRUE(rm1.member(OptoReg::Name(first_extended()))); - rm1.AND(rm1); + rm1.and_with(rm1); contains_expected_num_of_registers(rm1, 1); RegMask rm2; is_basic(rm2); - rm1.AND(rm2); + rm1.and_with(rm2); contains_expected_num_of_registers(rm1, 0); contains_expected_num_of_registers(rm2, 0); } -TEST_VM(RegMask, AND_extended_extended) { +TEST_VM(RegMask, and_with_extended_extended) { RegMask rm1(arena()); - rm1.Insert(OptoReg::Name(first_extended())); + rm1.insert(OptoReg::Name(first_extended())); is_extended(rm1); contains_expected_num_of_registers(rm1, 1); - ASSERT_TRUE(rm1.Member(OptoReg::Name(first_extended()))); + ASSERT_TRUE(rm1.member(OptoReg::Name(first_extended()))); - rm1.AND(rm1); + rm1.and_with(rm1); contains_expected_num_of_registers(rm1, 1); RegMask rm2(arena()); extend(rm2); - rm1.AND(rm2); + rm1.and_with(rm2); contains_expected_num_of_registers(rm1, 0); contains_expected_num_of_registers(rm2, 0); } -TEST_VM(RegMask, OR_extended_basic) { +TEST_VM(RegMask, or_with_extended_basic) { RegMask rm1(arena()); - rm1.Insert(OptoReg::Name(first_extended())); + rm1.insert(OptoReg::Name(first_extended())); is_extended(rm1); contains_expected_num_of_registers(rm1, 1); - ASSERT_TRUE(rm1.Member(OptoReg::Name(first_extended()))); + ASSERT_TRUE(rm1.member(OptoReg::Name(first_extended()))); - rm1.OR(rm1); + rm1.or_with(rm1); contains_expected_num_of_registers(rm1, 1); RegMask rm2; is_basic(rm2); - rm1.OR(rm2); + rm1.or_with(rm2); contains_expected_num_of_registers(rm1, 1); contains_expected_num_of_registers(rm2, 0); } -TEST_VM(RegMask, OR_extended_extended) { +TEST_VM(RegMask, or_with_extended_extended) { RegMask rm1(arena()); - rm1.Insert(OptoReg::Name(first_extended())); + rm1.insert(OptoReg::Name(first_extended())); is_extended(rm1); contains_expected_num_of_registers(rm1, 1); - ASSERT_TRUE(rm1.Member(OptoReg::Name(first_extended()))); + ASSERT_TRUE(rm1.member(OptoReg::Name(first_extended()))); - rm1.OR(rm1); + rm1.or_with(rm1); contains_expected_num_of_registers(rm1, 1); RegMask rm2(arena()); extend(rm2); - rm1.OR(rm2); + rm1.or_with(rm2); contains_expected_num_of_registers(rm1, 1); contains_expected_num_of_registers(rm2, 0); } -TEST_VM(RegMask, SUBTRACT_extended) { +TEST_VM(RegMask, subtract_extended) { RegMask rm1(arena()); extend(rm1); RegMask rm2(arena()); extend(rm2); - rm2.Set_All(); + rm2.set_all(); ASSERT_TRUE(rm2.is_infinite_stack()); for (int i = first_extended() + 17; i < (int)rm1.rm_size_in_bits(); i++) { - rm1.Insert(i); + rm1.insert(i); } rm1.set_infinite_stack(true); ASSERT_TRUE(rm1.is_infinite_stack()); - rm2.SUBTRACT(rm1); + rm2.subtract(rm1); contains_expected_num_of_registers(rm1, rm1.rm_size_in_bits() - first_extended() - 17); contains_expected_num_of_registers(rm2, first_extended() + 17); } @@ -710,9 +710,9 @@ TEST_VM(RegMask, external_member_extended) { RegMask rm(arena()); extend(rm); rm.set_infinite_stack(false); - ASSERT_FALSE(rm.Member(OptoReg::Name(rm.rm_size_in_bits()))); + ASSERT_FALSE(rm.member(OptoReg::Name(rm.rm_size_in_bits()))); rm.set_infinite_stack(true); - ASSERT_TRUE(rm.Member(OptoReg::Name(rm.rm_size_in_bits()))); + ASSERT_TRUE(rm.member(OptoReg::Name(rm.rm_size_in_bits()))); } TEST_VM(RegMask, overlap_extended) { @@ -722,14 +722,14 @@ TEST_VM(RegMask, overlap_extended) { extend(rm2); ASSERT_FALSE(rm1.overlap(rm2)); ASSERT_FALSE(rm2.overlap(rm1)); - rm1.Insert(OptoReg::Name(23)); - rm1.Insert(OptoReg::Name(2)); - rm1.Insert(OptoReg::Name(first_extended() + 12)); - rm2.Insert(OptoReg::Name(1)); - rm2.Insert(OptoReg::Name(first_extended() + 4)); + rm1.insert(OptoReg::Name(23)); + rm1.insert(OptoReg::Name(2)); + rm1.insert(OptoReg::Name(first_extended() + 12)); + rm2.insert(OptoReg::Name(1)); + rm2.insert(OptoReg::Name(first_extended() + 4)); ASSERT_FALSE(rm1.overlap(rm2)); ASSERT_FALSE(rm2.overlap(rm1)); - rm1.Insert(OptoReg::Name(first_extended() + 4)); + rm1.insert(OptoReg::Name(first_extended() + 4)); ASSERT_TRUE(rm1.overlap(rm2)); ASSERT_TRUE(rm2.overlap(rm1)); } @@ -738,43 +738,43 @@ TEST_VM(RegMask, up_extended) { RegMask rm(arena()); extend(rm); ASSERT_TRUE(rm.is_UP()); - rm.Insert(OptoReg::Name(1)); + rm.insert(OptoReg::Name(1)); ASSERT_TRUE(rm.is_UP()); - rm.Insert(OptoReg::Name(first_extended())); + rm.insert(OptoReg::Name(first_extended())); ASSERT_FALSE(rm.is_UP()); - rm.Clear(); + rm.clear(); rm.set_infinite_stack(true); ASSERT_FALSE(rm.is_UP()); } -TEST_VM(RegMask, SUBTRACT_inner_basic_extended) { +TEST_VM(RegMask, subtract_inner_basic_extended) { RegMask rm1; RegMask rm2(arena()); - rm1.Insert(OptoReg::Name(1)); - rm1.Insert(OptoReg::Name(42)); + rm1.insert(OptoReg::Name(1)); + rm1.insert(OptoReg::Name(42)); is_basic(rm1); - rm2.Insert(OptoReg::Name(1)); - rm2.Insert(OptoReg::Name(first_extended() + 20)); + rm2.insert(OptoReg::Name(1)); + rm2.insert(OptoReg::Name(first_extended() + 20)); is_extended(rm2); - rm1.SUBTRACT_inner(rm2); + rm1.subtract_inner(rm2); is_basic(rm1); contains_expected_num_of_registers(rm1, 1); - ASSERT_TRUE(rm1.Member(OptoReg::Name(42))); + ASSERT_TRUE(rm1.member(OptoReg::Name(42))); } -TEST_VM(RegMask, SUBTRACT_inner_extended_basic) { +TEST_VM(RegMask, subtract_inner_extended_basic) { RegMask rm1(arena()); RegMask rm2; - rm1.Insert(OptoReg::Name(1)); - rm1.Insert(OptoReg::Name(42)); - rm1.Insert(OptoReg::Name(first_extended() + 20)); + rm1.insert(OptoReg::Name(1)); + rm1.insert(OptoReg::Name(42)); + rm1.insert(OptoReg::Name(first_extended() + 20)); is_extended(rm1); - rm2.Insert(OptoReg::Name(1)); + rm2.insert(OptoReg::Name(1)); is_basic(rm2); - rm1.SUBTRACT_inner(rm2); + rm1.subtract_inner(rm2); contains_expected_num_of_registers(rm1, 2); - ASSERT_TRUE(rm1.Member(OptoReg::Name(42))); - ASSERT_TRUE(rm1.Member(OptoReg::Name(first_extended() + 20))); + ASSERT_TRUE(rm1.member(OptoReg::Name(42))); + ASSERT_TRUE(rm1.member(OptoReg::Name(first_extended() + 20))); } TEST_VM(RegMask, rollover_extended) { @@ -784,48 +784,48 @@ TEST_VM(RegMask, rollover_extended) { OptoReg::Name reg1(rm.rm_size_in_bits() + 42); rm.set_infinite_stack(true); rm.rollover(); - rm.Insert(reg1); - ASSERT_TRUE(rm.Member(reg1)); + rm.insert(reg1); + ASSERT_TRUE(rm.member(reg1)); } -TEST_VM(RegMask, rollover_and_SUBTRACT_inner_disjoint_extended) { +TEST_VM(RegMask, rollover_and_subtract_inner_disjoint_extended) { RegMask rm1(arena()); RegMask rm2; extend(rm1); OptoReg::Name reg1(rm1.rm_size_in_bits() + 42); rm1.set_infinite_stack(true); rm1.rollover(); - rm1.Clear(); - rm1.SUBTRACT_inner(rm2); + rm1.clear(); + rm1.subtract_inner(rm2); contains_expected_num_of_registers(rm1, 0); - rm2.SUBTRACT_inner(rm1); + rm2.subtract_inner(rm1); contains_expected_num_of_registers(rm2, 0); - rm1.Insert(reg1); - rm2.Insert(42); - rm1.SUBTRACT_inner(rm2); + rm1.insert(reg1); + rm2.insert(42); + rm1.subtract_inner(rm2); contains_expected_num_of_registers(rm1, 1); - rm2.SUBTRACT_inner(rm1); + rm2.subtract_inner(rm1); contains_expected_num_of_registers(rm2, 1); } -TEST_VM(RegMask, rollover_and_SUBTRACT_inner_overlap_extended) { +TEST_VM(RegMask, rollover_and_subtract_inner_overlap_extended) { RegMask rm1(arena()); RegMask rm2; OptoReg::Name reg1(rm1.rm_size_in_bits() + 42); extend(rm1); rm2.set_infinite_stack(true); rm2.rollover(); - rm2.Clear(); - rm1.SUBTRACT_inner(rm2); + rm2.clear(); + rm1.subtract_inner(rm2); contains_expected_num_of_registers(rm1, 0); - rm2.SUBTRACT_inner(rm1); + rm2.subtract_inner(rm1); contains_expected_num_of_registers(rm2, 0); - rm1.Insert(reg1); - rm2.Insert(reg1); - rm1.SUBTRACT_inner(rm2); + rm1.insert(reg1); + rm2.insert(reg1); + rm1.subtract_inner(rm2); contains_expected_num_of_registers(rm1, 0); - rm1.Insert(reg1); - rm2.SUBTRACT_inner(rm1); + rm1.insert(reg1); + rm2.subtract_inner(rm1); contains_expected_num_of_registers(rm2, 0); } @@ -855,7 +855,7 @@ static void print(const char* name, const RegMask& mask) { static void assert_equivalent(const RegMask& mask, const ResourceBitMap& mask_ref, bool infinite_stack_ref) { - ASSERT_EQ(mask_ref.count_one_bits(), mask.Size()); + ASSERT_EQ(mask_ref.count_one_bits(), mask.size()); RegMaskIterator it(mask); OptoReg::Name reg = OptoReg::Bad; while (it.has_next()) { @@ -870,7 +870,7 @@ static void populate_auxiliary_sets(RegMask& mask_aux, ResourceBitMap& mask_aux_ref, uint reg_capacity, uint offset, bool random_offset) { - mask_aux.Clear(); + mask_aux.clear(); mask_aux_ref.clear(); if (random_offset) { uint offset_in_words = offset / BitsPerWord; @@ -936,7 +936,7 @@ static void populate_auxiliary_sets(RegMask& mask_aux, } for (uint i = 0; i < regs; i++) { uint reg = (next_random() % max_size) + offset; - mask_aux.Insert(reg); + mask_aux.insert(reg); mask_aux_ref.set_bit(reg); } mask_aux.set_infinite_stack(next_random() % 2); @@ -994,7 +994,7 @@ TEST_VM(RegMask, random) { OptoReg::dump(reg); tty->cr(); } - mask.Insert(reg); + mask.insert(reg); mask_ref.set_bit(reg); if (mask.is_infinite_stack() && reg >= size_bits_before) { // Stack-extend reference bitset. @@ -1010,36 +1010,36 @@ TEST_VM(RegMask, random) { OptoReg::dump(reg); tty->cr(); } - mask.Remove(reg); + mask.remove(reg); mask_ref.clear_bit(reg); break; case 2: if (Verbose) { tty->print_cr("action: Clear"); } - mask.Clear(); + mask.clear(); mask_ref.clear(); infinite_stack_ref = false; break; case 3: if (offset_ref > 0) { - // Set_All expects a zero-offset. + // set_all expects a zero-offset. break; } if (Verbose) { - tty->print_cr("action: Set_All"); + tty->print_cr("action: set_all"); } - mask.Set_All(); + mask.set_all(); mask_ref.set_range(0, size_bits_before); infinite_stack_ref = true; break; case 4: if (Verbose) { - tty->print_cr("action: AND"); + tty->print_cr("action: and_with"); } populate_auxiliary_sets(mask_aux, mask_aux_ref, mask.rm_size_in_bits(), offset_ref, /*random_offset*/ false); - mask.AND(mask_aux); + mask.and_with(mask_aux); stack_extend_ref_masks(mask_ref, infinite_stack_ref, size_bits_before, offset_ref, mask_aux_ref, mask_aux.is_infinite_stack(), mask_aux.rm_size_in_bits(), mask_aux.offset_bits()); @@ -1048,11 +1048,11 @@ TEST_VM(RegMask, random) { break; case 5: if (Verbose) { - tty->print_cr("action: OR"); + tty->print_cr("action: or_with"); } populate_auxiliary_sets(mask_aux, mask_aux_ref, mask.rm_size_in_bits(), offset_ref, /*random_offset*/ false); - mask.OR(mask_aux); + mask.or_with(mask_aux); stack_extend_ref_masks(mask_ref, infinite_stack_ref, size_bits_before, offset_ref, mask_aux_ref, mask_aux.is_infinite_stack(), mask_aux.rm_size_in_bits(), mask_aux.offset_bits()); @@ -1061,11 +1061,11 @@ TEST_VM(RegMask, random) { break; case 6: if (Verbose) { - tty->print_cr("action: SUBTRACT"); + tty->print_cr("action: subtract"); } populate_auxiliary_sets(mask_aux, mask_aux_ref, mask.rm_size_in_bits(), offset_ref, /*random_offset*/ false); - mask.SUBTRACT(mask_aux); + mask.subtract(mask_aux); stack_extend_ref_masks(mask_ref, infinite_stack_ref, size_bits_before, offset_ref, mask_aux_ref, mask_aux.is_infinite_stack(), mask_aux.rm_size_in_bits(), mask_aux.offset_bits()); @@ -1076,15 +1076,15 @@ TEST_VM(RegMask, random) { break; case 7: if (Verbose) { - tty->print_cr("action: SUBTRACT_inner"); + tty->print_cr("action: subtract_inner"); } populate_auxiliary_sets(mask_aux, mask_aux_ref, mask.rm_size_in_bits(), offset_ref, /*random_offset*/ true); - // SUBTRACT_inner expects an argument register mask with infinite_stack = + // subtract_inner expects an argument register mask with infinite_stack = // false. mask_aux.set_infinite_stack(false); - mask.SUBTRACT_inner(mask_aux); - // SUBTRACT_inner does not have "stack-extension semantics". + mask.subtract_inner(mask_aux); + // subtract_inner does not have "stack-extension semantics". mask_ref.set_difference(mask_aux_ref); break; case 8: @@ -1106,7 +1106,7 @@ TEST_VM(RegMask, random) { tty->print_cr("action: rollover"); } // rollover expects the mask to be cleared and with infinite_stack = true - mask.Clear(); + mask.clear(); mask.set_infinite_stack(true); mask_ref.clear(); infinite_stack_ref = true; @@ -1120,28 +1120,28 @@ TEST_VM(RegMask, random) { tty->print_cr("action: reset"); } mask.gtest_set_offset(0); - mask.Clear(); + mask.clear(); mask_ref.clear(); infinite_stack_ref = false; offset_ref = 0; break; case 11: if (Verbose) { - tty->print_cr("action: Set_All_From_Offset"); + tty->print_cr("action: set_all_from_offset"); } - mask.Set_All_From_Offset(); + mask.set_all_from_offset(); mask_ref.set_range(offset_ref, offset_ref + size_bits_before); infinite_stack_ref = true; break; case 12: reg = (next_random() % size_bits_before) + offset_ref; if (Verbose) { - tty->print_cr("action: Set_All_From"); + tty->print_cr("action: set_all_from"); tty->print("value : "); OptoReg::dump(reg); tty->cr(); } - mask.Set_All_From(reg); + mask.set_all_from(reg); mask_ref.set_range(reg, offset_ref + size_bits_before); infinite_stack_ref = true; break; @@ -1154,12 +1154,12 @@ TEST_VM(RegMask, random) { // Randomly sets register mask contents. Does not change register mask size. static void randomize(RegMask& rm) { - rm.Clear(); + rm.clear(); // Uniform distribution over number of registers. uint regs = next_random() % (rm.rm_size_in_bits() + 1); for (uint i = 0; i < regs; i++) { uint reg = (next_random() % rm.rm_size_in_bits()) + rm.offset_bits(); - rm.Insert(reg); + rm.insert(reg); } rm.set_infinite_stack(next_random() % 2); } @@ -1175,10 +1175,10 @@ static uint grow_randomly(RegMask& rm, uint min_growth = 1, break; } // Force grow - rm.Insert(reg); + rm.insert(reg); if (!rm.is_infinite_stack()) { // Restore - rm.Remove(reg); + rm.remove(reg); } } // Return how many times we grew @@ -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-Virtual.txt b/test/hotspot/jtreg/ProblemList-Virtual.txt index 3c74d7bf816..31684662194 100644 --- a/test/hotspot/jtreg/ProblemList-Virtual.txt +++ b/test/hotspot/jtreg/ProblemList-Virtual.txt @@ -33,11 +33,6 @@ vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2manyDiff_a/TestDescription.java vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001/TestDescription.java 8300711 generic-all -#### -## Tests for functionality which currently is not supported for virtual threads - -vmTestbase/nsk/jvmti/GetCurrentThreadCpuTime/curthrcputime001/TestDescription.java 8348844 generic-all -vmTestbase/nsk/jvmti/GetThreadCpuTime/thrcputime001/TestDescription.java 8348844 generic-all #### ## Test fails because it expects to find vthreads in GetAllThreads @@ -82,14 +77,3 @@ vmTestbase/nsk/jdi/VMOutOfMemoryException/VMOutOfMemoryException001/VMOutOfMemor # to make progress when all other threads are currently suspended. vmTestbase/nsk/jdi/ThreadReference/isSuspended/issuspended002/TestDescription.java 8338713 generic-all -########## -## Tests incompatible with with virtual test thread factory. -## There is no goal to run all test with virtual test thread factory. -## So any test migth be added as incompatible, the bug is not required. - -gc/arguments/TestNewSizeThreadIncrease.java 0000000 generic-all -gc/g1/TestSkipRebuildRemsetPhase.java 0000000 generic-all -runtime/ErrorHandling/MachCodeFramesInErrorFile.java 0000000 generic-all -runtime/Thread/AsyncExceptionOnMonitorEnter.java 0000000 generic-all -runtime/Thread/StopAtExit.java 0000000 generic-all -runtime/handshake/HandshakeWalkStackTest.java 0000000 generic-all diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 7d0e240f7fa..6e2ba5e0220 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -105,6 +105,7 @@ compiler/whitebox/RelocateNMethodMultiplePaths.java#SerialC1 8370571 generic-all compiler/whitebox/RelocateNMethodMultiplePaths.java#SerialC2 8370571 generic-all compiler/whitebox/RelocateNMethodMultiplePaths.java#ZGCC1 8370571 generic-all compiler/whitebox/RelocateNMethodMultiplePaths.java#ZGCC2 8370571 generic-all +compiler/valhalla/inlinetypes/TestNullableArrays.java 8367553 generic-aarch64 ############################################################################# @@ -155,6 +156,7 @@ runtime/cds/TestDefaultArchiveLoading.java#nocoops_nocoh 8366774 # Valhalla + AOT runtime/cds/appcds/aotCache/HelloAOTCache.java 8369043 generic-aarch64 +runtime/cds/appcds/aotCode/AOTCodeFlags.java 8369043 generic-aarch64 runtime/cds/appcds/methodHandles/MethodHandlesGeneralTest.java#aot 8367408 generic-all ############################################################################# @@ -162,7 +164,7 @@ runtime/cds/appcds/methodHandles/MethodHandlesGeneralTest.java#aot # :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 @@ -182,9 +184,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 # Valhalla TODO: serviceability/jvmti/valhalla/HeapDump/HeapDump.java 8317416 generic-all @@ -226,6 +225,7 @@ serviceability/sa/sadebugd/DebugdConnectTest.java 8365722 generic-all serviceability/sa/ClhsdbJhisto.java 8365722 generic-all serviceability/sa/ClhsdbJstack.java#id1 8365722 generic-all serviceability/sa/ClhsdbJstackWithConcurrentLock.java 8365722 generic-all +serviceability/sa/TestJhsdbJstackWithVirtualThread.java 8365722 generic-all serviceability/sa/ClhsdbJstackXcompStress.java 8365722 generic-all serviceability/sa/ClhsdbPstack.java#process 8365722 generic-all serviceability/sa/ClhsdbPstack.java#core 8365722 generic-all diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index a6a85568d4f..c0f0d1a65da 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -590,6 +590,7 @@ hotspot_aot_classlinking = \ -runtime/cds/appcds/TestSerialGCWithCDS.java \ -runtime/cds/appcds/TestZGCWithCDS.java \ -runtime/cds/appcds/TestWithProfiler.java \ + -runtime/cds/appcds/VerifyObjArrayCloneTest.java \ -runtime/cds/serviceability/ReplaceCriticalClassesForSubgraphs.java \ -runtime/cds/serviceability/ReplaceCriticalClasses.java \ -runtime/cds/serviceability/transformRelatedClasses/TransformInterfaceAndImplementor.java \ diff --git a/test/hotspot/jtreg/compiler/c2/TestBit.java b/test/hotspot/jtreg/compiler/c2/TestBit.java index b1186a85cae..01769470d78 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 e3651129daa..f5225e8173c 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 190f08d348c..b28d2f6dc8b 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/calls/common/InvokeDynamicPatcher.java b/test/hotspot/jtreg/compiler/calls/common/InvokeDynamicPatcher.java index b82e06317d2..d5c93ebaf5e 100644 --- a/test/hotspot/jtreg/compiler/calls/common/InvokeDynamicPatcher.java +++ b/test/hotspot/jtreg/compiler/calls/common/InvokeDynamicPatcher.java @@ -23,150 +23,121 @@ package compiler.calls.common; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Handle; -import org.objectweb.asm.Label; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; - -import java.io.FileInputStream; import java.io.IOException; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassTransform; +import java.lang.classfile.CodeBuilder; +import java.lang.classfile.CodeElement; +import java.lang.classfile.CodeTransform; +import java.lang.classfile.Label; +import java.lang.constant.ClassDesc; +import java.lang.constant.DirectMethodHandleDesc; +import java.lang.constant.DynamicCallSiteDesc; +import java.lang.constant.MethodHandleDesc; +import java.lang.constant.MethodTypeDesc; import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.StandardOpenOption; +import static java.lang.constant.ConstantDescs.*; + /** * A class which patch InvokeDynamic class bytecode with invokydynamic instruction, rewriting "caller" method to call "callee" method using invokedynamic */ -public class InvokeDynamicPatcher extends ClassVisitor { +public final class InvokeDynamicPatcher { - private static final String CLASS = InvokeDynamic.class.getName() - .replace('.', '/'); + private static final ClassDesc CLASS = InvokeDynamic.class.describeConstable().orElseThrow(); private static final String CALLER_METHOD_NAME = "caller"; private static final String CALLEE_METHOD_NAME = "callee"; private static final String NATIVE_CALLEE_METHOD_NAME = "calleeNative"; private static final String BOOTSTRAP_METHOD_NAME = "bootstrapMethod"; private static final String CALL_NATIVE_FIELD = "nativeCallee"; - private static final String CALL_NATIVE_FIELD_DESC = "Z"; - private static final String CALLEE_METHOD_DESC - = "(L" + CLASS + ";IJFDLjava/lang/String;)Z"; - private static final String ASSERTTRUE_METHOD_DESC - = "(ZLjava/lang/String;)V"; - private static final String ASSERTS_CLASS = "jdk/test/lib/Asserts"; + private static final ClassDesc CALL_NATIVE_FIELD_DESC = CD_boolean; + private static final MethodTypeDesc CALLEE_METHOD_DESC = MethodTypeDesc.of( + CD_boolean, CLASS, CD_int, CD_long, CD_float, CD_double, CD_String); + private static final MethodTypeDesc ASSERTTRUE_METHOD_DESC = MethodTypeDesc.of( + CD_void, CD_boolean, CD_String); + private static final ClassDesc ASSERTS_CLASS = ClassDesc.ofInternalName("jdk/test/lib/Asserts"); private static final String ASSERTTRUE_METHOD_NAME = "assertTrue"; - public static void main(String args[]) { - ClassReader cr; - Path filePath; - try { - filePath = Paths.get(InvokeDynamic.class.getProtectionDomain().getCodeSource() - .getLocation().toURI()).resolve(CLASS + ".class"); - } catch (URISyntaxException ex) { - throw new Error("TESTBUG: Can't get code source" + ex, ex); - } - try (FileInputStream fis = new FileInputStream(filePath.toFile())) { - cr = new ClassReader(fis); - } catch (IOException e) { - throw new Error("Error reading file", e); - } - ClassWriter cw = new ClassWriter(cr, - ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); - cr.accept(new InvokeDynamicPatcher(Opcodes.ASM5, cw), 0); - try { - Files.write(filePath, cw.toByteArray(), - StandardOpenOption.WRITE); - } catch (IOException e) { - throw new Error(e); - } - } - - public InvokeDynamicPatcher(int api, ClassWriter cw) { - super(api, cw); - } + public static void main(String args[]) throws IOException, URISyntaxException { + Path filePath = Path.of(InvokeDynamic.class.getProtectionDomain().getCodeSource() + .getLocation().toURI()).resolve(InvokeDynamic.class.getName().replace('.', '/') +".class"); + var bytes = ClassFile.of().transformClass(ClassFile.of().parse(filePath), + ClassTransform.transformingMethodBodies(m -> m.methodName().equalsString(CALLER_METHOD_NAME), new CodeTransform() { + @Override + public void accept(CodeBuilder builder, CodeElement element) { + // discard + } - @Override - public MethodVisitor visitMethod(final int access, final String name, - final String desc, final String signature, - final String[] exceptions) { - /* a code generate looks like - * 0: aload_0 - * 1: ldc #125 // int 1 - * 3: ldc2_w #126 // long 2l - * 6: ldc #128 // float 3.0f - * 8: ldc2_w #129 // double 4.0d - * 11: ldc #132 // String 5 - * 13: aload_0 - * 14: getfield #135 // Field nativeCallee:Z - * 17: ifeq 28 - * 20: invokedynamic #181, 0 // InvokeDynamic #1:calleeNative:(Lcompiler/calls/common/InvokeDynamic;IJFDLjava/lang/String;)Z - * 25: goto 33 - * 28: invokedynamic #183, 0 // InvokeDynamic #1:callee:(Lcompiler/calls/common/InvokeDynamic;IJFDLjava/lang/String;)Z - * 33: ldc #185 // String Call insuccessfull - * 35: invokestatic #191 // Method jdk/test/lib/Asserts.assertTrue:(ZLjava/lang/String;)V - * 38: return - * - * or, using java-like pseudo-code - * if (this.nativeCallee == false) { - * invokedynamic-call-return-value = invokedynamic-of-callee - * } else { - * invokedynamic-call-return-value = invokedynamic-of-nativeCallee - * } - * Asserts.assertTrue(invokedynamic-call-return-value, error-message); - * return; - */ - if (name.equals(CALLER_METHOD_NAME)) { - MethodVisitor mv = cv.visitMethod(access, name, desc, - signature, exceptions); - Label nonNativeLabel = new Label(); - Label checkLabel = new Label(); - MethodType mtype = MethodType.methodType(CallSite.class, - MethodHandles.Lookup.class, String.class, MethodType.class); - Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, CLASS, - BOOTSTRAP_METHOD_NAME, mtype.toMethodDescriptorString()); - mv.visitCode(); - // push callee parameters onto stack - mv.visitVarInsn(Opcodes.ALOAD, 0);//push "this" - mv.visitLdcInsn(1); - mv.visitLdcInsn(2L); - mv.visitLdcInsn(3.0f); - mv.visitLdcInsn(4.0d); - mv.visitLdcInsn("5"); - // params loaded. let's decide what method to call - mv.visitVarInsn(Opcodes.ALOAD, 0); // push "this" - // get nativeCallee field - mv.visitFieldInsn(Opcodes.GETFIELD, CLASS, CALL_NATIVE_FIELD, - CALL_NATIVE_FIELD_DESC); - // if nativeCallee == false goto nonNativeLabel - mv.visitJumpInsn(Opcodes.IFEQ, nonNativeLabel); - // invokedynamic nativeCalleeMethod using bootstrap method - mv.visitInvokeDynamicInsn(NATIVE_CALLEE_METHOD_NAME, - CALLEE_METHOD_DESC, bootstrap); - // goto checkLabel - mv.visitJumpInsn(Opcodes.GOTO, checkLabel); - // label: nonNativeLabel - mv.visitLabel(nonNativeLabel); - // invokedynamic calleeMethod using bootstrap method - mv.visitInvokeDynamicInsn(CALLEE_METHOD_NAME, CALLEE_METHOD_DESC, - bootstrap); - mv.visitLabel(checkLabel); - mv.visitLdcInsn(CallsBase.CALL_ERR_MSG); - mv.visitMethodInsn(Opcodes.INVOKESTATIC, ASSERTS_CLASS, - ASSERTTRUE_METHOD_NAME, ASSERTTRUE_METHOD_DESC, false); - // label: return - mv.visitInsn(Opcodes.RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - return null; - } - return super.visitMethod(access, name, desc, signature, exceptions); + /* the code generated looks like + * 0: aload_0 + * 1: ldc #125 // int 1 + * 3: ldc2_w #126 // long 2l + * 6: ldc #128 // float 3.0f + * 8: ldc2_w #129 // double 4.0d + * 11: ldc #132 // String 5 + * 13: aload_0 + * 14: getfield #135 // Field nativeCallee:Z + * 17: ifeq 28 + * 20: invokedynamic #181, 0 // InvokeDynamic #1:calleeNative:(Lcompiler/calls/common/InvokeDynamic;IJFDLjava/lang/String;)Z + * 25: goto 33 + * 28: invokedynamic #183, 0 // InvokeDynamic #1:callee:(Lcompiler/calls/common/InvokeDynamic;IJFDLjava/lang/String;)Z + * 33: ldc #185 // String Call insuccessfull + * 35: invokestatic #191 // Method jdk/test/lib/Asserts.assertTrue:(ZLjava/lang/String;)V + * 38: return + * + * or, using java-like pseudo-code + * if (this.nativeCallee == false) { + * invokedynamic-call-return-value = invokedynamic-of-callee + * } else { + * invokedynamic-call-return-value = invokedynamic-of-nativeCallee + * } + * Asserts.assertTrue(invokedynamic-call-return-value, error-message); + * return; + */ + @Override + public void atEnd(CodeBuilder builder) { + Label nonNativeLabel = builder.newLabel(); + Label checkLabel = builder.newLabel(); + MethodType mtype = MethodType.methodType(CallSite.class, + MethodHandles.Lookup.class, String.class, MethodType.class); + DirectMethodHandleDesc dmh = MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC, + CLASS, BOOTSTRAP_METHOD_NAME, mtype.descriptorString()); + // push callee parameters onto stack + builder.aload(builder.receiverSlot()) + .ldc(1) + .ldc(2L) + .ldc(3.0f) + .ldc(4.0d) + .ldc("5") + // params loaded. let's decide what method to call + .aload(builder.receiverSlot()) + // get nativeCallee field + .getfield(CLASS, CALL_NATIVE_FIELD, CALL_NATIVE_FIELD_DESC) + // if nativeCallee == false goto nonNativeLabel + .ifeq(nonNativeLabel) + // invokedynamic nativeCalleeMethod using bootstrap method + .invokedynamic(DynamicCallSiteDesc.of(dmh, NATIVE_CALLEE_METHOD_NAME, CALLEE_METHOD_DESC)) + // goto checkLabel + .goto_(checkLabel) + // label: nonNativeLabel + .labelBinding(nonNativeLabel) + // invokedynamic calleeMethod using bootstrap method + .invokedynamic(DynamicCallSiteDesc.of(dmh, CALLEE_METHOD_NAME, CALLEE_METHOD_DESC)) + .labelBinding(checkLabel) + .ldc(CallsBase.CALL_ERR_MSG) + .invokestatic(ASSERTS_CLASS, ASSERTTRUE_METHOD_NAME, ASSERTTRUE_METHOD_DESC) + // label: return + .return_(); + } + })); + Files.write(filePath, bytes, StandardOpenOption.WRITE); } } diff --git a/test/hotspot/jtreg/compiler/calls/fromCompiled/CompiledInvokeDynamic2CompiledTest.java b/test/hotspot/jtreg/compiler/calls/fromCompiled/CompiledInvokeDynamic2CompiledTest.java index 914500a25d4..c20f8f44822 100644 --- a/test/hotspot/jtreg/compiler/calls/fromCompiled/CompiledInvokeDynamic2CompiledTest.java +++ b/test/hotspot/jtreg/compiler/calls/fromCompiled/CompiledInvokeDynamic2CompiledTest.java @@ -25,10 +25,10 @@ * @test * @summary check calls from compiled to compiled using InvokeDynamic * @library /test/lib / - * @library /testlibrary/asm * @modules java.base/jdk.internal.misc * * @build jdk.test.whitebox.WhiteBox + * @build compiler.calls.common.InvokeDynamic * @run driver compiler.calls.common.InvokeDynamicPatcher * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. diff --git a/test/hotspot/jtreg/compiler/calls/fromCompiled/CompiledInvokeDynamic2InterpretedTest.java b/test/hotspot/jtreg/compiler/calls/fromCompiled/CompiledInvokeDynamic2InterpretedTest.java index b6f8520a90a..ee497d10707 100644 --- a/test/hotspot/jtreg/compiler/calls/fromCompiled/CompiledInvokeDynamic2InterpretedTest.java +++ b/test/hotspot/jtreg/compiler/calls/fromCompiled/CompiledInvokeDynamic2InterpretedTest.java @@ -25,10 +25,10 @@ * @test * @summary check calls from compiled to interpreted using InvokeDynamic * @library /test/lib / - * @library /testlibrary/asm * @modules java.base/jdk.internal.misc * * @build jdk.test.whitebox.WhiteBox + * @build compiler.calls.common.InvokeDynamic * @run driver compiler.calls.common.InvokeDynamicPatcher * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. diff --git a/test/hotspot/jtreg/compiler/calls/fromCompiled/CompiledInvokeDynamic2NativeTest.java b/test/hotspot/jtreg/compiler/calls/fromCompiled/CompiledInvokeDynamic2NativeTest.java index e334f6a15f0..7dc2b423587 100644 --- a/test/hotspot/jtreg/compiler/calls/fromCompiled/CompiledInvokeDynamic2NativeTest.java +++ b/test/hotspot/jtreg/compiler/calls/fromCompiled/CompiledInvokeDynamic2NativeTest.java @@ -25,10 +25,10 @@ * @test * @summary check calls from compiled to native using InvokeDynamic * @library /test/lib / - * @library /testlibrary/asm * @modules java.base/jdk.internal.misc * * @build jdk.test.whitebox.WhiteBox + * @build compiler.calls.common.InvokeDynamic * @run driver compiler.calls.common.InvokeDynamicPatcher * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. diff --git a/test/hotspot/jtreg/compiler/calls/fromInterpreted/InterpretedInvokeDynamic2CompiledTest.java b/test/hotspot/jtreg/compiler/calls/fromInterpreted/InterpretedInvokeDynamic2CompiledTest.java index ca626fd3b01..0ba1dd1a496 100644 --- a/test/hotspot/jtreg/compiler/calls/fromInterpreted/InterpretedInvokeDynamic2CompiledTest.java +++ b/test/hotspot/jtreg/compiler/calls/fromInterpreted/InterpretedInvokeDynamic2CompiledTest.java @@ -25,10 +25,10 @@ * @test * @summary check calls from interpreted to compiled using InvokeDynamic * @library /test/lib / - * @library /testlibrary/asm * @modules java.base/jdk.internal.misc * * @build jdk.test.whitebox.WhiteBox + * @build compiler.calls.common.InvokeDynamic * @run driver compiler.calls.common.InvokeDynamicPatcher * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. diff --git a/test/hotspot/jtreg/compiler/calls/fromInterpreted/InterpretedInvokeDynamic2InterpretedTest.java b/test/hotspot/jtreg/compiler/calls/fromInterpreted/InterpretedInvokeDynamic2InterpretedTest.java index d8d877977d4..7ed8c683629 100644 --- a/test/hotspot/jtreg/compiler/calls/fromInterpreted/InterpretedInvokeDynamic2InterpretedTest.java +++ b/test/hotspot/jtreg/compiler/calls/fromInterpreted/InterpretedInvokeDynamic2InterpretedTest.java @@ -25,10 +25,10 @@ * @test * @summary check calls from interpreted to interpreted using InvokeDynamic * @library /test/lib / - * @library /testlibrary/asm * @modules java.base/jdk.internal.misc * * @build jdk.test.whitebox.WhiteBox + * @build compiler.calls.common.InvokeDynamic * @run driver compiler.calls.common.InvokeDynamicPatcher * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. diff --git a/test/hotspot/jtreg/compiler/calls/fromInterpreted/InterpretedInvokeDynamic2NativeTest.java b/test/hotspot/jtreg/compiler/calls/fromInterpreted/InterpretedInvokeDynamic2NativeTest.java index 38bca4939d4..20f94db80b1 100644 --- a/test/hotspot/jtreg/compiler/calls/fromInterpreted/InterpretedInvokeDynamic2NativeTest.java +++ b/test/hotspot/jtreg/compiler/calls/fromInterpreted/InterpretedInvokeDynamic2NativeTest.java @@ -25,10 +25,10 @@ * @test * @summary check calls from interpreted to native using InvokeDynamic * @library /test/lib / - * @library /testlibrary/asm * @modules java.base/jdk.internal.misc * * @build jdk.test.whitebox.WhiteBox + * @build compiler.calls.common.InvokeDynamic * @run driver compiler.calls.common.InvokeDynamicPatcher * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. diff --git a/test/hotspot/jtreg/compiler/exceptions/IllegalAccessInCatch.jasm b/test/hotspot/jtreg/compiler/exceptions/IllegalAccessInCatch.jasm new file mode 100644 index 00000000000..beeffa69a97 --- /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 00000000000..46541d76016 --- /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/gcbarriers/TestG1BarrierGeneration.java b/test/hotspot/jtreg/compiler/gcbarriers/TestG1BarrierGeneration.java index 9122a53af6b..6c7cfd34ca7 100644 --- a/test/hotspot/jtreg/compiler/gcbarriers/TestG1BarrierGeneration.java +++ b/test/hotspot/jtreg/compiler/gcbarriers/TestG1BarrierGeneration.java @@ -881,6 +881,16 @@ public void runAtomicTests() { Asserts.assertEquals(oldVal, oldVal2); Asserts.assertEquals(o.f, newVal); } + { + Outer o = new Outer(); + Object oldVal = new Object(); + o.f = oldVal; + Object cmpVal = new Object(); + Object newVal = new Object(); + Object oldVal2 = testCompareAndExchange(o, cmpVal, newVal); + Asserts.assertEquals(oldVal2, oldVal); + Asserts.assertEquals(o.f, oldVal); + } { Outer o = new Outer(); Object oldVal = new Object(); @@ -890,6 +900,16 @@ public void runAtomicTests() { Asserts.assertTrue(b); Asserts.assertEquals(o.f, newVal); } + { + Outer o = new Outer(); + Object oldVal = new Object(); + o.f = oldVal; + Object cmpVal = new Object(); + Object newVal = new Object(); + boolean b = testCompareAndSwap(o, cmpVal, newVal); + Asserts.assertFalse(b); + Asserts.assertEquals(o.f, oldVal); + } { Outer o = new Outer(); Object oldVal = new Object(); diff --git a/test/hotspot/jtreg/compiler/jsr292/RedefineMethodUsedByMultipleMethodHandles.java b/test/hotspot/jtreg/compiler/jsr292/RedefineMethodUsedByMultipleMethodHandles.java index 769863880f2..9a74806e621 100644 --- a/test/hotspot/jtreg/compiler/jsr292/RedefineMethodUsedByMultipleMethodHandles.java +++ b/test/hotspot/jtreg/compiler/jsr292/RedefineMethodUsedByMultipleMethodHandles.java @@ -21,12 +21,11 @@ * questions. */ -/** +/* * @test * @bug 8042235 * @summary redefining method used by multiple MethodHandles crashes VM * @library / - * @library /testlibrary/asm * @modules java.compiler * java.instrument * jdk.attach @@ -37,15 +36,13 @@ package compiler.jsr292; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; - import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassHierarchyResolver; +import java.lang.classfile.ClassTransform; +import java.lang.classfile.instruction.ConstantInstruction; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; @@ -159,28 +156,15 @@ static class FooTransformer implements ClassFileTransformer { public byte[] transform(ClassLoader cl, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if (Foo.class.equals(classBeingRedefined)) { System.out.println("redefining " + classBeingRedefined); - ClassReader cr = new ClassReader(classfileBuffer); - ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES); - ClassVisitor adapter = new ClassVisitor(Opcodes.ASM5, cw) { - @Override - public MethodVisitor visitMethod(int access, String base, String desc, String signature, String[] exceptions) { - MethodVisitor mv = cv.visitMethod(access, base, desc, signature, exceptions); - if (mv != null) { - mv = new MethodVisitor(Opcodes.ASM5, mv) { - @Override - public void visitLdcInsn(Object cst) { - System.out.println("replacing \"" + cst + "\" with \"bar\""); - mv.visitLdcInsn("bar"); - } - }; - } - return mv; + var context = ClassFile.of(ClassFile.ClassHierarchyResolverOption.of(ClassHierarchyResolver.ofResourceParsing(cl))); + return context.transformClass(context.parse(classfileBuffer), ClassTransform.transformingMethodBodies((codeBuilder, codeElement) -> { + if (codeElement instanceof ConstantInstruction.LoadConstantInstruction ldc) { + System.out.println("replacing \"" + ldc.constantEntry().constantValue() + "\" with \"bar\""); + codeBuilder.ldc("bar"); + } else { + codeBuilder.with(codeElement); } - }; - - cr.accept(adapter, ClassReader.SKIP_FRAMES); - cw.visitEnd(); - return cw.toByteArray(); + })); } return classfileBuffer; } diff --git a/test/hotspot/jtreg/compiler/jvmci/common/CTVMUtilities.java b/test/hotspot/jtreg/compiler/jvmci/common/CTVMUtilities.java index 95917f6d943..c98d9944c80 100644 --- a/test/hotspot/jtreg/compiler/jvmci/common/CTVMUtilities.java +++ b/test/hotspot/jtreg/compiler/jvmci/common/CTVMUtilities.java @@ -23,28 +23,26 @@ package compiler.jvmci.common; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Label; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; -import jdk.test.lib.Utils; import jdk.vm.ci.code.InstalledCode; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.hotspot.CompilerToVMHelper; -import jdk.vm.ci.hotspot.HotSpotNmethod; import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; import java.io.IOException; +import java.lang.classfile.Attributes; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.classfile.MethodModel; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.constant.MethodTypeDesc; +import java.lang.invoke.MethodType; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; -import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.lang.reflect.Parameter; -import java.util.HashMap; +import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -71,76 +69,55 @@ public static InstalledCode getInstalledCode(ResolvedJavaMethod method, String n } public static Map getBciToLineNumber(Executable method) { - Map lineNumbers = new TreeMap<>(); - Class aClass = method.getDeclaringClass(); - ClassReader cr; - try { - Module aModule = aClass.getModule(); - String name = aClass.getName(); - cr = new ClassReader(aModule.getResourceAsStream( - name.replace('.', '/') + ".class")); - } catch (IOException e) { - throw new Error("TEST BUG: can read " + aClass.getName() + " : " + e, e); - } - ClassNode cn = new ClassNode(); - cr.accept(cn, ClassReader.EXPAND_FRAMES); + ClassModel classModel = findClassBytes(method.getDeclaringClass()); + MethodModel methodModel = findMethod(classModel, method); + if (methodModel == null) + return Map.of(); - Map labels = new HashMap<>(); - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - ClassVisitor cv = new ClassVisitorForLabels(cw, labels, method); - cr.accept(cv, ClassReader.EXPAND_FRAMES); - labels.forEach((k, v) -> lineNumbers.put(k.getOffset(), v)); - boolean isEmptyMethod = Modifier.isAbstract(method.getModifiers()) - || Modifier.isNative(method.getModifiers()); - if (lineNumbers.isEmpty() && !isEmptyMethod) { - throw new Error(method + " doesn't contains the line numbers table " - +"(the method marked neither abstract nor native)"); + var foundLineNumberTable = methodModel.code().flatMap(code -> + code.findAttribute(Attributes.lineNumberTable())); + if (foundLineNumberTable.isEmpty()) { + boolean isEmptyMethod = Modifier.isAbstract(method.getModifiers()) + || Modifier.isNative(method.getModifiers()); + if (!isEmptyMethod) { + throw new Error(method + " doesn't contains the line numbers table " + + "(the method marked neither abstract nor native)"); + } + return Map.of(); } + + Map lineNumbers = new TreeMap<>(); + foundLineNumberTable.get().lineNumbers().forEach(ln -> + lineNumbers.put(ln.startPc(), ln.lineNumber())); return lineNumbers; } - private static class ClassVisitorForLabels extends ClassVisitor { - private final Map lineNumbers; - private final String targetName; - private final String targetDesc; - - public ClassVisitorForLabels(ClassWriter cw, Map lines, - Executable target) { - super(Opcodes.ASM7, cw); - this.lineNumbers = lines; - - StringBuilder builder = new StringBuilder("("); - for (Parameter parameter : target.getParameters()) { - builder.append(Utils.toJVMTypeSignature(parameter.getType())); - } - builder.append(")"); - if (target instanceof Constructor) { - targetName = ""; - builder.append("V"); - } else { - targetName = target.getName(); - builder.append(Utils.toJVMTypeSignature( - ((Method) target).getReturnType())); - } - targetDesc = builder.toString(); + // Finds the ClassFile API model of a given class, or fail with an Error. + public static ClassModel findClassBytes(Class clazz) { + String binaryName = clazz.getName(); + byte[] fileBytes; + try (var inputStream = clazz.getModule().getResourceAsStream( + binaryName.replace('.', '/') + ".class")) { + fileBytes = inputStream.readAllBytes(); + } catch (IOException e) { + throw new Error("TEST BUG: cannot read " + binaryName, e); } + return ClassFile.of().parse(fileBytes); + } + + // Finds a matching method in a class model, or null if none match. + public static MethodModel findMethod(ClassModel classModel, Executable method) { + MethodTypeDesc methodType = MethodType.methodType( + method instanceof Method m ? m.getReturnType() : void.class, + method.getParameterTypes()).describeConstable().orElseThrow(); + String methodName = method instanceof Method m ? m.getName() : ConstantDescs.INIT_NAME; - @Override - public final MethodVisitor visitMethod(int access, String name, - String desc, String signature, - String[] exceptions) { - MethodVisitor mv = cv.visitMethod(access, name, desc, signature, - exceptions); - if (targetDesc.equals(desc) && targetName.equals(name)) { - return new MethodVisitor(Opcodes.ASM7, mv) { - @Override - public void visitLineNumber(int i, Label label) { - super.visitLineNumber(i, label); - lineNumbers.put(label, i); - } - }; + for (var methodModel : classModel.methods()) { + if (methodModel.methodName().equalsString(methodName) + && methodModel.methodType().isMethodType(methodType)) { + return methodModel; } - return mv; } + return null; } } diff --git a/test/hotspot/jtreg/compiler/lib/generators/UniformDoubleGenerator.java b/test/hotspot/jtreg/compiler/lib/generators/UniformDoubleGenerator.java index d160bf319d8..b5729aeec7d 100644 --- a/test/hotspot/jtreg/compiler/lib/generators/UniformDoubleGenerator.java +++ b/test/hotspot/jtreg/compiler/lib/generators/UniformDoubleGenerator.java @@ -35,6 +35,9 @@ final class UniformDoubleGenerator extends UniformIntersectionRestrictableGenera */ public UniformDoubleGenerator(Generators g, double lo, double hi) { super(g, lo, hi); + if (Double.compare(lo, hi) >= 0) { + throw new EmptyGeneratorException(); + } } @Override diff --git a/test/hotspot/jtreg/compiler/lib/generators/UniformFloatGenerator.java b/test/hotspot/jtreg/compiler/lib/generators/UniformFloatGenerator.java index 1b72ad5adc9..4405b120619 100644 --- a/test/hotspot/jtreg/compiler/lib/generators/UniformFloatGenerator.java +++ b/test/hotspot/jtreg/compiler/lib/generators/UniformFloatGenerator.java @@ -35,6 +35,9 @@ final class UniformFloatGenerator extends UniformIntersectionRestrictableGenerat */ public UniformFloatGenerator(Generators g, float lo, float hi) { super(g, lo, hi); + if (Float.compare(lo, hi) >= 0) { + throw new EmptyGeneratorException(); + } } @Override diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 94f754241e8..d76b48d76fe 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -2897,6 +2897,36 @@ public static void anyStoreOfNodes(String irNodePlaceholder, String fieldHolder) 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/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java index 0588f65ffd2..d323519e869 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java @@ -327,8 +327,10 @@ public TestFramework addScenarios(Scenario... scenarios) { for (Scenario scenario : scenarios) { int scenarioIndex = scenario.getIndex(); - TestFormat.checkNoThrow(scenarioIndices.add(scenarioIndex), - "Cannot define two scenarios with the same index " + scenarioIndex); + if (!scenarioIndices.add(scenarioIndex)) { + TestFormat.failNoThrow("Cannot define two scenarios with the same index " + scenarioIndex); + continue; + } this.scenarios.add(scenario); } TestFormat.throwIfAnyFailures(); @@ -336,9 +338,12 @@ public TestFramework addScenarios(Scenario... scenarios) { } /** - * Add the cross-product (cartesian product) of sets of flags as Scenarios. Unlike when when constructing + * Add the cross-product (cartesian product) of sets of flags as Scenarios. Unlike when constructing * scenarios directly a string can contain multiple flags separated with a space. This allows grouping - * flags that have to be specified togeher. Further, an empty string in a set stands in for "no flag". + * flags that have to be specified together. Further, an empty string in a set stands in for "no flag". + *

    + * Passing a single set will create a scenario for each of the provided flags in the set (i.e. the same as + * passing an additional set with an empty string only). *

    * Example: *

    @@ -355,7 +360,7 @@ public TestFramework addScenarios(Scenario... scenarios) {
          *     Scenario(5, "-Xbatch -XX:-TieredCompilation", "-XX:+UseNewCode2")
          * 
    * - * @param sets sets of flags to generate the cross product for. + * @param flagSets sets of flags to generate the cross product for. * @return the same framework instance. */ @SafeVarargs @@ -376,7 +381,7 @@ final public TestFramework addCrossProductScenarios(Set... flagSets) { Stream> crossProduct = Arrays.stream(flagSets) .reduce( - Stream.of(Collections.emptyList()), // Initialize Stream> acc with a Stream containing an empty list of Strings. + Stream.of(Collections.emptyList()), // Initialize Stream> acc with a Stream containing an empty list of Strings. (Stream> acc, Set set) -> acc.flatMap(lAcc -> // For each List> lAcc in acc... set.stream().map(flag -> { // ...and each flag in the current set... @@ -384,19 +389,19 @@ final public TestFramework addCrossProductScenarios(Set... flagSets) { newList.add(flag); // ...and append the flag. return newList; }) // This results in one List> for each lAcc... - ), // ...that get flattend into one big List>. - (a, b) -> Stream.concat(a, b)); // combiner; if any reduction steps are executed in parallel, just concat two streams. + ), // ...that get flattened into one big List>. + Stream::concat); // combiner; if any reduction steps are executed in parallel, just concat two streams. Scenario[] newScenarios = crossProduct .map(flags -> new Scenario( // For each List flags in crossProduct create a new Scenario. idx.getAndIncrement(), flags.stream() // Process flags - .map(s -> Set.of(s.split("[ ]"))) // Split muliple flags in the same string into separate strings. + .map(s -> Set.of(s.split("[ ]"))) // Split multiple flags in the same string into separate strings. .flatMap(Collection::stream) // Flatten the Stream> into Stream>. .filter(s -> !s.isEmpty()) // Remove empty string flags. - .collect(Collectors.toList()) + .toList() .toArray(new String[0]))) - .collect(Collectors.toList()).toArray(new Scenario[0]); + .toList().toArray(new Scenario[0]); return addScenarios(newScenarios); } 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 c05124edcd7..daa2b9765f8 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 30e1f1c0619..62e474ecb2c 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 00000000000..93a1f0b56a8 --- /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 00000000000..355d8d5383c --- /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 00000000000..6d2249cc15c --- /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/valhalla/inlinetypes/TestAllocationMergeAndFolding.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestAllocationMergeAndFolding.java index a9c4ba454e5..e9bd9732799 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestAllocationMergeAndFolding.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestAllocationMergeAndFolding.java @@ -1,5 +1,5 @@ /* - * 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 @@ -27,9 +27,80 @@ * @summary Test that removing allocation merges of non-value and value object at EA is working properly. * @library /test/lib / * @enablePreview - * @run main compiler.valhalla.inlinetypes.TestAllocationMergeAndFolding + * @run main compiler.valhalla.inlinetypes.TestAllocationMergeAndFolding 0 */ +/* + * @test + * @bug 8315003 + * @summary Test that removing allocation merges of non-value and value object at EA is working properly. + * @library /test/lib / + * @enablePreview + * @run main compiler.valhalla.inlinetypes.TestAllocationMergeAndFolding 1 + */ + +/* + * @test + * @bug 8315003 + * @summary Test that removing allocation merges of non-value and value object at EA is working properly. + * @library /test/lib / + * @enablePreview + * @run main compiler.valhalla.inlinetypes.TestAllocationMergeAndFolding 2 + */ + +/* + * @test + * @bug 8315003 + * @summary Test that removing allocation merges of non-value and value object at EA is working properly. + * @library /test/lib / + * @enablePreview + * @run main compiler.valhalla.inlinetypes.TestAllocationMergeAndFolding 3 + */ + +/* + * @test + * @bug 8315003 + * @summary Test that removing allocation merges of non-value and value object at EA is working properly. + * @library /test/lib / + * @enablePreview + * @run main compiler.valhalla.inlinetypes.TestAllocationMergeAndFolding 4 + */ + +/* + * @test + * @bug 8315003 + * @summary Test that removing allocation merges of non-value and value object at EA is working properly. + * @library /test/lib / + * @enablePreview + * @run main compiler.valhalla.inlinetypes.TestAllocationMergeAndFolding 5 + */ + +/* + * @test + * @bug 8315003 + * @summary Test that removing allocation merges of non-value and value object at EA is working properly. + * @library /test/lib / + * @enablePreview + * @run main compiler.valhalla.inlinetypes.TestAllocationMergeAndFolding 6 + */ + +/* + * @test + * @bug 8315003 + * @summary Test that removing allocation merges of non-value and value object at EA is working properly. + * @library /test/lib / + * @enablePreview + * @run main compiler.valhalla.inlinetypes.TestAllocationMergeAndFolding 7 + */ + +/* + * @test + * @bug 8315003 + * @summary Test that removing allocation merges of non-value and value object at EA is working properly. + * @library /test/lib / + * @enablePreview + * @run main compiler.valhalla.inlinetypes.TestAllocationMergeAndFolding 8 + */ package compiler.valhalla.inlinetypes; import compiler.lib.ir_framework.*; @@ -41,11 +112,16 @@ public class TestAllocationMergeAndFolding { private static final Random RANDOM = Utils.getRandomInstance(); public static void main(String[] args) { - InlineTypes.getFramework() - .addScenarios(InlineTypes.DEFAULT_SCENARIOS) - .addScenarios(new Scenario(7, "--enable-preview", "-XX:-UseCompressedOops")) - .addScenarios(new Scenario(8, "--enable-preview", "-XX:+UseCompressedOops")) - .start(); + TestFramework framework = InlineTypes.getFramework(); + int index = Integer.parseInt(args[0]); + if (index < 7) { + framework.addScenarios(InlineTypes.DEFAULT_SCENARIOS[index]); + } else if (index == 7) { + framework.addScenarios(new Scenario(7, "--enable-preview", "-XX:-UseCompressedOops")); + } else { + framework.addScenarios(new Scenario(8, "--enable-preview", "-XX:+UseCompressedOops")); + } + framework.start(); } @Test diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java index 82d907addd6..f5fc83be134 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java @@ -67,7 +67,85 @@ * java.base/jdk.internal.vm.annotation * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") * @enablePreview - * @run main/timeout=300 compiler.valhalla.inlinetypes.TestArrays + * @run main compiler.valhalla.inlinetypes.TestArrays 0 + */ + +/* + * @test + * @key randomness + * @summary Test value class arrays. + * @library /test/lib / + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @run main compiler.valhalla.inlinetypes.TestArrays 1 + */ + +/* + * @test + * @key randomness + * @summary Test value class arrays. + * @library /test/lib / + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @run main compiler.valhalla.inlinetypes.TestArrays 2 + */ + +/* + * @test + * @key randomness + * @summary Test value class arrays. + * @library /test/lib / + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @run main compiler.valhalla.inlinetypes.TestArrays 3 + */ + +/* + * @test + * @key randomness + * @summary Test value class arrays. + * @library /test/lib / + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @run main compiler.valhalla.inlinetypes.TestArrays 4 + */ + +/* + * @test + * @key randomness + * @summary Test value class arrays. + * @library /test/lib / + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @run main compiler.valhalla.inlinetypes.TestArrays 5 + */ + +/* + * @test + * @key randomness + * @summary Test value class arrays. + * @library /test/lib / + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @run main compiler.valhalla.inlinetypes.TestArrays 6 */ @ForceCompileClassInitializer @@ -81,7 +159,7 @@ public static void main(String[] args) { scenarios[5].addFlags("--enable-preview", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode"); InlineTypes.getFramework() - .addScenarios(scenarios) + .addScenarios(scenarios[Integer.parseInt(args[0])]) .addHelperClasses(MyValue1.class, MyValue2.class, MyValue2Inline.class) .start(); } diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBasicFunctionality.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBasicFunctionality.java index 046c8ff3e92..9c6ea0119e9 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBasicFunctionality.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBasicFunctionality.java @@ -54,7 +54,85 @@ * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @run main/timeout=300 compiler.valhalla.inlinetypes.TestBasicFunctionality + * @run main compiler.valhalla.inlinetypes.TestBasicFunctionality 0 + */ + +/* + * @test + * @key randomness + * @bug 8327695 + * @summary Test the basic value class implementation in C2. + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @library /test/lib / + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestBasicFunctionality 1 + */ + +/* + * @test + * @key randomness + * @bug 8327695 + * @summary Test the basic value class implementation in C2. + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @library /test/lib / + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestBasicFunctionality 2 + */ + +/* + * @test + * @key randomness + * @bug 8327695 + * @summary Test the basic value class implementation in C2. + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @library /test/lib / + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestBasicFunctionality 3 + */ + +/* + * @test + * @key randomness + * @bug 8327695 + * @summary Test the basic value class implementation in C2. + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @library /test/lib / + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestBasicFunctionality 4 + */ + +/* + * @test + * @key randomness + * @bug 8327695 + * @summary Test the basic value class implementation in C2. + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @library /test/lib / + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestBasicFunctionality 5 + */ + +/* + * @test + * @key randomness + * @bug 8327695 + * @summary Test the basic value class implementation in C2. + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @library /test/lib / + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestBasicFunctionality 6 */ @ForceCompileClassInitializer @@ -62,7 +140,7 @@ public class TestBasicFunctionality { public static void main(String[] args) { InlineTypes.getFramework() - .addScenarios(InlineTypes.DEFAULT_SCENARIOS) + .addScenarios(InlineTypes.DEFAULT_SCENARIOS[Integer.parseInt(args[0])]) .addHelperClasses(MyValue1.class, MyValue2.class, MyValue2Inline.class, diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConvention.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConvention.java index dad31950aef..9334ccebd3d 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConvention.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConvention.java @@ -57,7 +57,91 @@ * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm/timeout=450 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestCallingConvention + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestCallingConvention 0 + */ + +/* + * @test + * @key randomness + * @summary Test value class calling convention optimizations. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @build jdk.test.whitebox.WhiteBox + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestCallingConvention 1 + */ + +/* + * @test + * @key randomness + * @summary Test value class calling convention optimizations. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @build jdk.test.whitebox.WhiteBox + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestCallingConvention 2 + */ + +/* + * @test + * @key randomness + * @summary Test value class calling convention optimizations. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @build jdk.test.whitebox.WhiteBox + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestCallingConvention 3 + */ + +/* + * @test + * @key randomness + * @summary Test value class calling convention optimizations. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @build jdk.test.whitebox.WhiteBox + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestCallingConvention 4 + */ + +/* + * @test + * @key randomness + * @summary Test value class calling convention optimizations. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @build jdk.test.whitebox.WhiteBox + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestCallingConvention 5 + */ + +/* + * @test + * @key randomness + * @summary Test value class calling convention optimizations. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @build jdk.test.whitebox.WhiteBox + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestCallingConvention 6 */ @ForceCompileClassInitializer @@ -100,7 +184,7 @@ public static void main(String[] args) { scenarios[4].addFlags("-XX:-UseTLAB"); InlineTypes.getFramework() - .addScenarios(scenarios) + .addScenarios(scenarios[Integer.parseInt(args[0])]) .addHelperClasses(MyValue1.class, MyValue2.class, MyValue2Inline.class, diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java index f6707c425c1..4ea23309f03 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java @@ -43,6 +43,7 @@ import static compiler.valhalla.inlinetypes.InlineTypes.rL; import static compiler.lib.ir_framework.IRNode.LOAD_KLASS; +import static compiler.valhalla.inlinetypes.InlineTypes.*; /* * @test @@ -56,7 +57,97 @@ * java.base/jdk.internal.vm.annotation * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm/timeout=300 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestIntrinsics + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestIntrinsics 0 + */ + +/* + * @test + * @key randomness + * @summary Test intrinsic support for value classes. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.misc + * java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestIntrinsics 1 + */ + +/* + * @test + * @key randomness + * @summary Test intrinsic support for value classes. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.misc + * java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestIntrinsics 2 + */ + +/* + * @test + * @key randomness + * @summary Test intrinsic support for value classes. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.misc + * java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestIntrinsics 3 + */ + +/* + * @test + * @key randomness + * @summary Test intrinsic support for value classes. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.misc + * java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestIntrinsics 4 + */ + +/* + * @test + * @key randomness + * @summary Test intrinsic support for value classes. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.misc + * java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestIntrinsics 5 + */ + +/* + * @test + * @key randomness + * @summary Test intrinsic support for value classes. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.misc + * java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.valhalla.inlinetypes.TestIntrinsics 6 */ @ForceCompileClassInitializer @@ -74,7 +165,7 @@ public static void main(String[] args) { scenarios[4].addFlags("-XX:-MonomorphicArrayCheck", "-XX:+UnlockExperimentalVMOptions", "-XX:PerMethodSpecTrapLimit=0", "-XX:PerMethodTrapLimit=0"); InlineTypes.getFramework() - .addScenarios(scenarios) + .addScenarios(scenarios[Integer.parseInt(args[0])]) .addFlags("-Xbootclasspath/a:.", "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED", "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED") diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestJNICalls.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestJNICalls.java index 6ccbe17ca8f..075a636be77 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestJNICalls.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestJNICalls.java @@ -38,7 +38,79 @@ * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @run main/timeout=300 compiler.valhalla.inlinetypes.TestJNICalls + * @run main compiler.valhalla.inlinetypes.TestJNICalls 0 + */ + +/* + * @test + * @key randomness + * @summary Test calling native methods with value class arguments from compiled code. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestJNICalls 1 + */ + +/* + * @test + * @key randomness + * @summary Test calling native methods with value class arguments from compiled code. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestJNICalls 2 + */ + +/* + * @test + * @key randomness + * @summary Test calling native methods with value class arguments from compiled code. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestJNICalls 3 + */ + +/* + * @test + * @key randomness + * @summary Test calling native methods with value class arguments from compiled code. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestJNICalls 4 + */ + +/* + * @test + * @key randomness + * @summary Test calling native methods with value class arguments from compiled code. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestJNICalls 5 + */ + +/* + * @test + * @key randomness + * @summary Test calling native methods with value class arguments from compiled code. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestJNICalls 6 */ @ForceCompileClassInitializer @@ -48,7 +120,7 @@ public static void main(String[] args) { Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS; InlineTypes.getFramework() - .addScenarios(scenarios) + .addScenarios(scenarios[Integer.parseInt(args[0])]) .addHelperClasses(MyValue1.class) .start(); } diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java index aa6062247f3..ee123a4e385 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java @@ -70,7 +70,85 @@ * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation * @build test.java.lang.invoke.lib.InstructionHelper - * @run main/timeout=600 compiler.valhalla.inlinetypes.TestLWorld + * @run main compiler.valhalla.inlinetypes.TestLWorld 0 + */ + +/* + * @test + * @key randomness + * @summary Test inline types in LWorld. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build test.java.lang.invoke.lib.InstructionHelper + * @run main compiler.valhalla.inlinetypes.TestLWorld 1 + */ + +/* + * @test + * @key randomness + * @summary Test inline types in LWorld. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build test.java.lang.invoke.lib.InstructionHelper + * @run main compiler.valhalla.inlinetypes.TestLWorld 2 + */ + +/* + * @test + * @key randomness + * @summary Test inline types in LWorld. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build test.java.lang.invoke.lib.InstructionHelper + * @run main compiler.valhalla.inlinetypes.TestLWorld 3 + */ + +/* + * @test + * @key randomness + * @summary Test inline types in LWorld. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build test.java.lang.invoke.lib.InstructionHelper + * @run main compiler.valhalla.inlinetypes.TestLWorld 4 + */ + +/* + * @test + * @key randomness + * @summary Test inline types in LWorld. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build test.java.lang.invoke.lib.InstructionHelper + * @run main compiler.valhalla.inlinetypes.TestLWorld 5 + */ + +/* + * @test + * @key randomness + * @summary Test inline types in LWorld. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build test.java.lang.invoke.lib.InstructionHelper + * @run main compiler.valhalla.inlinetypes.TestLWorld 6 */ @ForceCompileClassInitializer @@ -88,7 +166,7 @@ public static void main(String[] args) { scenarios[4].addFlags("-XX:-MonomorphicArrayCheck"); InlineTypes.getFramework() - .addScenarios(scenarios) + .addScenarios(scenarios[Integer.parseInt(args[0])]) .addHelperClasses(MyValue1.class, MyValue2.class, MyValue2Inline.class, diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMethodHandles.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMethodHandles.java index ae4e21597a4..e74b6c10bda 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMethodHandles.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMethodHandles.java @@ -53,7 +53,85 @@ * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @run main/timeout=450 compiler.valhalla.inlinetypes.TestMethodHandles + * @run main compiler.valhalla.inlinetypes.TestMethodHandles 0 + */ + +/* + * @test + * @key randomness + * @summary Test method handle support for inline types + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @requires vm.opt.AbortVMOnCompilationFailure != true + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestMethodHandles 1 + */ + +/* + * @test + * @key randomness + * @summary Test method handle support for inline types + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @requires vm.opt.AbortVMOnCompilationFailure != true + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestMethodHandles 2 + */ + +/* + * @test + * @key randomness + * @summary Test method handle support for inline types + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @requires vm.opt.AbortVMOnCompilationFailure != true + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestMethodHandles 3 + */ + +/* + * @test + * @key randomness + * @summary Test method handle support for inline types + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @requires vm.opt.AbortVMOnCompilationFailure != true + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestMethodHandles 4 + */ + +/* + * @test + * @key randomness + * @summary Test method handle support for inline types + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @requires vm.opt.AbortVMOnCompilationFailure != true + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestMethodHandles 5 + */ + +/* + * @test + * @key randomness + * @summary Test method handle support for inline types + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @requires vm.opt.AbortVMOnCompilationFailure != true + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestMethodHandles 6 */ @ForceCompileClassInitializer @@ -147,7 +225,7 @@ public static void main(String[] args) { scenarios[4].addFlags("-XX:CompileCommand=dontinline,java.lang.invoke.DirectMethodHandle::internalMemberName"); InlineTypes.getFramework() - .addScenarios(scenarios) + .addScenarios(scenarios[Integer.parseInt(args[0])]) .addFlags("-XX:CompileCommand=inline,java.lang.invoke.MethodHandleImpl::*") .addFlags("-XX:CompileCommand=inline,java.lang.invoke.LambdaForm*::*") .addHelperClasses(MyValue1.class, diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java index e5b13a989be..7346ad3afd8 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java @@ -55,7 +55,79 @@ * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @run main/timeout=600 compiler.valhalla.inlinetypes.TestNullableArrays + * @run main compiler.valhalla.inlinetypes.TestNullableArrays 0 + */ + +/* + * @test + * @key randomness + * @summary Test nullable value class arrays. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestNullableArrays 1 + */ + +/* + * @test + * @key randomness + * @summary Test nullable value class arrays. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestNullableArrays 2 + */ + +/* + * @test + * @key randomness + * @summary Test nullable value class arrays. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestNullableArrays 3 + */ + +/* + * @test + * @key randomness + * @summary Test nullable value class arrays. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestNullableArrays 4 + */ + +/* + * @test + * @key randomness + * @summary Test nullable value class arrays. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestNullableArrays 5 + */ + +/* + * @test + * @key randomness + * @summary Test nullable value class arrays. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestNullableArrays 6 */ @ForceCompileClassInitializer @@ -70,7 +142,7 @@ public static void main(String[] args) { scenarios[5].addFlags("-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode"); InlineTypes.getFramework() - .addScenarios(scenarios) + .addScenarios(scenarios[Integer.parseInt(args[0])]) .addHelperClasses(MyValue1.class, MyValue2.class, MyValue2Inline.class) diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java index 644699b08e3..9595b339fa3 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java @@ -60,7 +60,85 @@ * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation * @build test.java.lang.invoke.lib.InstructionHelper - * @run main/timeout=1000 compiler.valhalla.inlinetypes.TestNullableInlineTypes + * @run main compiler.valhalla.inlinetypes.TestNullableInlineTypes 0 + */ + +/* + * @test + * @key randomness + * @summary Test correct handling of nullable value classes. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build test.java.lang.invoke.lib.InstructionHelper + * @run main compiler.valhalla.inlinetypes.TestNullableInlineTypes 1 + */ + +/* + * @test + * @key randomness + * @summary Test correct handling of nullable value classes. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build test.java.lang.invoke.lib.InstructionHelper + * @run main compiler.valhalla.inlinetypes.TestNullableInlineTypes 2 + */ + +/* + * @test + * @key randomness + * @summary Test correct handling of nullable value classes. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build test.java.lang.invoke.lib.InstructionHelper + * @run main compiler.valhalla.inlinetypes.TestNullableInlineTypes 3 + */ + +/* + * @test + * @key randomness + * @summary Test correct handling of nullable value classes. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build test.java.lang.invoke.lib.InstructionHelper + * @run main compiler.valhalla.inlinetypes.TestNullableInlineTypes 4 + */ + +/* + * @test + * @key randomness + * @summary Test correct handling of nullable value classes. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build test.java.lang.invoke.lib.InstructionHelper + * @run main compiler.valhalla.inlinetypes.TestNullableInlineTypes 5 + */ + +/* + * @test + * @key randomness + * @summary Test correct handling of nullable value classes. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build test.java.lang.invoke.lib.InstructionHelper + * @run main compiler.valhalla.inlinetypes.TestNullableInlineTypes 6 */ @ForceCompileClassInitializer @@ -73,7 +151,7 @@ public static void main(String[] args) { scenarios[4].addFlags("-XX:-MonomorphicArrayCheck"); InlineTypes.getFramework() - .addScenarios(scenarios) + .addScenarios(scenarios[Integer.parseInt(args[0])]) .addHelperClasses(MyValue1.class, MyValue2.class, MyValue2Inline.class, diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOnStackReplacement.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOnStackReplacement.java index 2738028f4fa..74b3f77ddd5 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOnStackReplacement.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOnStackReplacement.java @@ -46,7 +46,79 @@ * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @run main/timeout=600 compiler.valhalla.inlinetypes.TestOnStackReplacement + * @run main compiler.valhalla.inlinetypes.TestOnStackReplacement 0 + */ + +/* + * @test + * @key randomness + * @summary Test on stack replacement (OSR) with value classes. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestOnStackReplacement 1 + */ + +/* + * @test + * @key randomness + * @summary Test on stack replacement (OSR) with value classes. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestOnStackReplacement 2 + */ + +/* + * @test + * @key randomness + * @summary Test on stack replacement (OSR) with value classes. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestOnStackReplacement 3 + */ + +/* + * @test + * @key randomness + * @summary Test on stack replacement (OSR) with value classes. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestOnStackReplacement 4 + */ + +/* + * @test + * @key randomness + * @summary Test on stack replacement (OSR) with value classes. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestOnStackReplacement 5 + */ + +/* + * @test + * @key randomness + * @summary Test on stack replacement (OSR) with value classes. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestOnStackReplacement 6 */ public class TestOnStackReplacement { @@ -56,7 +128,7 @@ public static void main(String[] args) throws Throwable { scenarios[3].addFlags("-XX:-UseArrayFlattening"); InlineTypes.getFramework() - .addScenarios(scenarios) + .addScenarios(scenarios[Integer.parseInt(args[0])]) .addHelperClasses(MyValue1.class, MyValue2.class, MyValue2Inline.class, diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java index fd129c62466..dec5830cf64 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java @@ -47,7 +47,79 @@ * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @run main/timeout=300 compiler.valhalla.inlinetypes.TestValueClasses + * @run main compiler.valhalla.inlinetypes.TestValueClasses 0 + */ + +/* + * @test + * @key randomness + * @summary Test correct handling of value classes. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestValueClasses 1 + */ + +/* + * @test + * @key randomness + * @summary Test correct handling of value classes. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestValueClasses 2 + */ + +/* + * @test + * @key randomness + * @summary Test correct handling of value classes. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestValueClasses 3 + */ + +/* + * @test + * @key randomness + * @summary Test correct handling of value classes. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestValueClasses 4 + */ + +/* + * @test + * @key randomness + * @summary Test correct handling of value classes. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestValueClasses 5 + */ + +/* + * @test + * @key randomness + * @summary Test correct handling of value classes. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main compiler.valhalla.inlinetypes.TestValueClasses 6 */ @ForceCompileClassInitializer @@ -62,7 +134,7 @@ public static void main(String[] args) { scenarios[4].addFlags("-XX:-UseTLAB", "-XX:-MonomorphicArrayCheck"); InlineTypes.getFramework() - .addScenarios(scenarios) + .addScenarios(scenarios[Integer.parseInt(args[0])]) .addHelperClasses(MyValueClass1.class, MyValueClass2.class, MyValueClass2Inline.class) diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorCompressTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorCompressTest.java new file mode 100644 index 00000000000..7ab60885ad2 --- /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/compiler/vectorapi/VectorMaskCompareNotTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCompareNotTest.java index 235093cceed..09185f63c69 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCompareNotTest.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCompareNotTest.java @@ -35,7 +35,7 @@ * @library /test/lib / * @summary test combining vector not operation with compare * @modules jdk.incubator.vector - * @requires (os.arch != "riscv64" | (os.arch == "riscv64" & vm.cpu.features ~= ".*rvv.*")) + * @requires vm.opt.final.MaxVectorSize == "null" | vm.opt.final.MaxVectorSize >= 16 * * @run driver compiler.vectorapi.VectorMaskCompareNotTest */ diff --git a/test/hotspot/jtreg/gc/arguments/TestNewSizeThreadIncrease.java b/test/hotspot/jtreg/gc/arguments/TestNewSizeThreadIncrease.java index efe703f9295..f885ef7e462 100644 --- a/test/hotspot/jtreg/gc/arguments/TestNewSizeThreadIncrease.java +++ b/test/hotspot/jtreg/gc/arguments/TestNewSizeThreadIncrease.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ * @library /test/lib * @library / * @requires vm.gc.Serial + * @requires test.thread.factory == null * @modules java.base/jdk.internal.misc * java.management * @run driver gc.arguments.TestNewSizeThreadIncrease diff --git a/test/hotspot/jtreg/gc/g1/TestSkipRebuildRemsetPhase.java b/test/hotspot/jtreg/gc/g1/TestSkipRebuildRemsetPhase.java index d973e897fe1..28524869edb 100644 --- a/test/hotspot/jtreg/gc/g1/TestSkipRebuildRemsetPhase.java +++ b/test/hotspot/jtreg/gc/g1/TestSkipRebuildRemsetPhase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -29,6 +29,7 @@ * Fill up a region to above the set G1MixedGCLiveThresholdPercent. * @requires vm.gc.G1 * @library /test/lib + * @requires test.thread.factory == null * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver gc.g1.TestSkipRebuildRemsetPhase diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/MachCodeFramesInErrorFile.java b/test/hotspot/jtreg/runtime/ErrorHandling/MachCodeFramesInErrorFile.java index ff9f99d6e17..423d1470956 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/MachCodeFramesInErrorFile.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/MachCodeFramesInErrorFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -26,6 +26,7 @@ * @bug 8272586 * @requires vm.flagless * @requires vm.compiler2.enabled + * @requires test.thread.factory == null * @summary Test that abstract machine code is dumped for the top frames in a hs-err log * @library /test/lib * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/runtime/MirrorFrame/Asmator.java b/test/hotspot/jtreg/runtime/MirrorFrame/Asmator.java index c5d9a4cb595..d8165d80196 100644 --- a/test/hotspot/jtreg/runtime/MirrorFrame/Asmator.java +++ b/test/hotspot/jtreg/runtime/MirrorFrame/Asmator.java @@ -21,35 +21,28 @@ * questions. */ -import org.objectweb.asm.*; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassTransform; +import java.lang.classfile.CodeBuilder; +import java.lang.classfile.CodeElement; +import java.lang.classfile.CodeTransform; class Asmator { - static byte[] fixup(byte[] buf) throws java.io.IOException { - ClassReader cr = new ClassReader(buf); - ClassWriter cw = new ClassWriter(0); - ClassVisitor cv = new ClassVisitor(Opcodes.ASM4, cw) { - public MethodVisitor visitMethod( - final int access, - final String name, - final String desc, - final String signature, - final String[] exceptions) - { - MethodVisitor mv = super.visitMethod(access, - name, - desc, - signature, - exceptions); - if (mv == null) return null; - if (name.equals("callme")) { - // make receiver go dead! - mv.visitInsn(Opcodes.ACONST_NULL); - mv.visitVarInsn(Opcodes.ASTORE, 0); + static byte[] fixup(byte[] buf) { + return ClassFile.of().transformClass(ClassFile.of().parse(buf), ClassTransform.transformingMethodBodies( + m -> m.methodName().equalsString("callme"), + new CodeTransform() { + @Override + public void atStart(CodeBuilder builder) { + // make receiver go dead! + builder.aconst_null().astore(0); + } + + @Override + public void accept(CodeBuilder builder, CodeElement element) { + builder.with(element); // pass through + } } - return mv; - } - }; - cr.accept(cv, 0); - return cw.toByteArray(); + )); } } diff --git a/test/hotspot/jtreg/runtime/MirrorFrame/Test8003720.java b/test/hotspot/jtreg/runtime/MirrorFrame/Test8003720.java index 43c8fefacd2..5bcae8e45d1 100644 --- a/test/hotspot/jtreg/runtime/MirrorFrame/Test8003720.java +++ b/test/hotspot/jtreg/runtime/MirrorFrame/Test8003720.java @@ -25,7 +25,6 @@ * @test * @bug 8003720 * @summary Method in interpreter stack frame can be deallocated - * @library /testlibrary/asm * @modules java.base/jdk.internal.misc * @compile -XDignore.symbol.file Victim.java * @run main/othervm -Xverify:all -Xint Test8003720 diff --git a/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java b/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java new file mode 100644 index 00000000000..106295960fd --- /dev/null +++ b/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java @@ -0,0 +1,115 @@ +/* + * 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 + * @summary Check the allocation-site stack trace of a corrupted memory at free() time + * @modules java.base/jdk.internal.misc + * @library /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail NMTPrintMallocSiteOfCorruptedMemory + */ + +import jdk.test.lib.Utils; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.whitebox.WhiteBox; + +public class NMTPrintMallocSiteOfCorruptedMemory { + private static final String HEADER_ARG = "header"; + private static final String FOOTER_ARG = "footer"; + private static final String HEADER_AND_SITE_ARG = "header-and-site"; + private static final String FOOTER_AND_SITE_ARG = "footer-and-site"; + private static final int MALLOC_SIZE = 10; + private static WhiteBox wb = WhiteBox.getWhiteBox(); + + static { + System.loadLibrary("MallocHeaderModifier"); + } + + public static native byte modifyHeaderCanary(long malloc_memory); + public static native byte modifyFooterCanary(long malloc_memory, long size); + public static native byte modifyHeaderCanaryAndSiteMarker(long malloc_memory); + public static native byte modifyFooterCanaryAndSiteMarker(long malloc_memory, long size); + + private static void runThisTestWith(String arg) throws Exception { + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(new String[] {"-Xbootclasspath/a:.", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-XX:NativeMemoryTracking=detail", + "-Djava.library.path=" + Utils.TEST_NATIVE_PATH, + "NMTPrintMallocSiteOfCorruptedMemory", + arg}); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldMatch("NMT Block at .*, corruption at: "); + switch(arg) { + case HEADER_AND_SITE_ARG, FOOTER_AND_SITE_ARG -> output.shouldContain("allocation-site cannot be shown since the marker is also corrupted."); + case HEADER_ARG, FOOTER_ARG -> { + output.shouldContain("allocated from:"); + output.shouldMatch("\\[.*\\]WB_NMTMalloc\\+0x.*"); + } + } + } + + private static void testModifyHeaderCanary() { + long addr = wb.NMTMalloc(MALLOC_SIZE); + modifyHeaderCanary(addr); + wb.NMTFree(addr); + } + + private static void testModifyFooterCanary() { + long addr = wb.NMTMalloc(MALLOC_SIZE); + modifyFooterCanary(addr, MALLOC_SIZE); + wb.NMTFree(addr); + } + + private static void testModifyHeaderCanaryAndSiteMarker() { + long addr = wb.NMTMalloc(MALLOC_SIZE); + modifyHeaderCanaryAndSiteMarker(addr); + wb.NMTFree(addr); + } + + private static void testModifyFooterCanaryAndSiteMarker() { + long addr = wb.NMTMalloc(MALLOC_SIZE); + modifyFooterCanaryAndSiteMarker(addr, MALLOC_SIZE); + wb.NMTFree(addr); + } + + public static void main(String args[]) throws Exception { + if (args != null && args.length == 1) { + switch (args[0]) { + case HEADER_ARG -> testModifyHeaderCanary(); + case FOOTER_ARG -> testModifyFooterCanary(); + case HEADER_AND_SITE_ARG -> testModifyHeaderCanaryAndSiteMarker(); + case FOOTER_AND_SITE_ARG -> testModifyFooterCanaryAndSiteMarker(); + default -> throw new RuntimeException("Invalid argument for NMTPrintMallocSiteOfCorruptedMemory (" + args[0] + ")"); + } + } else { + runThisTestWith(HEADER_ARG); + runThisTestWith(FOOTER_ARG); + runThisTestWith(HEADER_AND_SITE_ARG); + runThisTestWith(FOOTER_AND_SITE_ARG); + } + } +} diff --git a/test/hotspot/jtreg/runtime/NMT/libMallocHeaderModifier.c b/test/hotspot/jtreg/runtime/NMT/libMallocHeaderModifier.c new file mode 100644 index 00000000000..ca51b7212dd --- /dev/null +++ b/test/hotspot/jtreg/runtime/NMT/libMallocHeaderModifier.c @@ -0,0 +1,52 @@ +/* + * 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 "jni.h" +#include +#include + +JNIEXPORT jint JNICALL +Java_NMTPrintMallocSiteOfCorruptedMemory_modifyHeaderCanary(JNIEnv *env, jclass cls, jlong addr) { + *((jint*)(uintptr_t)addr - 1) = 0; + return 0; +} + +JNIEXPORT jint JNICALL +Java_NMTPrintMallocSiteOfCorruptedMemory_modifyFooterCanary(JNIEnv *env, jclass cls, jlong addr, jint size) { + *((jbyte*)(uintptr_t)addr + size + 1) = 0; + return 0; +} +JNIEXPORT jint JNICALL +Java_NMTPrintMallocSiteOfCorruptedMemory_modifyHeaderCanaryAndSiteMarker(JNIEnv *env, jclass cls, jlong addr) { + jbyte* p = (jbyte*)(uintptr_t)addr - 16; + memset(p, 0xFF , 16); + return 0; +} + +JNIEXPORT jint JNICALL +Java_NMTPrintMallocSiteOfCorruptedMemory_modifyFooterCanaryAndSiteMarker(JNIEnv *env, jclass cls, jlong addr, jint size) { + jbyte* p = (jbyte*)(uintptr_t)addr - 16; + memset(p, 0xFF , 16); + *((jbyte*)(uintptr_t)addr + size + 1) = 0; + return 0; +} diff --git a/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java b/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java index 8446ffb20fe..a4aa0fe3797 100644 --- a/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java +++ b/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java @@ -25,6 +25,7 @@ * @test * @bug 8283044 * @summary Stress delivery of asynchronous exceptions while target is at monitorenter + * @requires test.thread.factory == null * @library /test/hotspot/jtreg/testlibrary * @run main/othervm/native AsyncExceptionOnMonitorEnter 0 * @run main/othervm/native -agentlib:AsyncExceptionOnMonitorEnter AsyncExceptionOnMonitorEnter 1 diff --git a/test/hotspot/jtreg/runtime/Thread/StopAtExit.java b/test/hotspot/jtreg/runtime/Thread/StopAtExit.java index 3ceb955609b..68523c2c189 100644 --- a/test/hotspot/jtreg/runtime/Thread/StopAtExit.java +++ b/test/hotspot/jtreg/runtime/Thread/StopAtExit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, 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 @@ -25,6 +25,7 @@ * @test * @bug 8167108 8266130 8283467 8284632 8286830 * @summary Stress test JVM/TI StopThread() at thread exit. + * @requires test.thread.factory == null * @requires vm.jvmti * @run main/othervm/native -agentlib:StopAtExit StopAtExit */ diff --git a/test/hotspot/jtreg/runtime/cds/CDSMapReader.java b/test/hotspot/jtreg/runtime/cds/AOTMapReader.java similarity index 81% rename from test/hotspot/jtreg/runtime/cds/CDSMapReader.java rename to test/hotspot/jtreg/runtime/cds/AOTMapReader.java index f25455b2f03..e407d4e2ecc 100644 --- a/test/hotspot/jtreg/runtime/cds/CDSMapReader.java +++ b/test/hotspot/jtreg/runtime/cds/AOTMapReader.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -33,7 +34,7 @@ This is a simple parser for parsing the output of - java -Xshare:dump -Xlog:aot+map=debug,aot+map+oops=trace:file=cds.map:none:filesize=0 + java -Xshare:dump -Xlog:aot+map=debug,aot+map+oops=trace:file=aot.map:none:filesize=0 The map file contains patterns like this for the heap objects: @@ -59,8 +60,9 @@ */ -public class CDSMapReader { +public class AOTMapReader { public static class MapFile { + HashSet classes = new HashSet<>(); ArrayList heapObjects = new ArrayList<>(); HashMap oopToObject = new HashMap<>(); HashMap narrowOopToObject = new HashMap<>(); @@ -80,6 +82,20 @@ void add(HeapObject heapObject) { public int heapObjectCount() { return heapObjects.size(); } + + void addClass(String className) { + classes.add(className); + } + + public boolean hasClass(String className) { + return classes.contains(className); + } + + public void shouldHaveClass(String className) { + if (!hasClass(className)) { + throw new RuntimeException("AOT map file is missing class " + className); + } + } } public static class HeapAddress { @@ -140,13 +156,17 @@ public static class Field { this.name = name; this.offset = Integer.parseInt(offset); this.referentAddress = new HeapAddress(oopStr, narrowOopStr); - this.lineCount = CDSMapReader.lineCount; + this.lineCount = AOTMapReader.lineCount; } } // 0x00000007ffc00000: 4a5b8701 00000063 00010290 00000000 00010100 fff80003 static Pattern rawDataPattern = Pattern.compile("^0x([0-9a-f]+): *( [0-9a-f]+)+ *$"); + // ------------------------------------------------------------------------------- + // Patterns for heap objects + // ------------------------------------------------------------------------------- + // (one address) // 0x00000007ffc00000: @@ Object java.lang.String static Pattern objPattern1 = Pattern.compile("^0x([0-9a-f]+): @@ Object ([^ ]*)"); @@ -179,6 +199,15 @@ public static class Field { // - injected 'module_entry' 'J' @16 0 (0x0000000000000000) static Pattern moduleEntryPattern = Pattern.compile("- injected 'module_entry' 'J' @[0-9]+[ ]+([0-9]+)"); + // ------------------------------------------------------------------------------- + // Patterns for metaspace objects + // ------------------------------------------------------------------------------- + + // 0x00000008000d1698: @@ Class 512 [Ljdk.internal.vm.FillerElement; + // 0x00000008000d18a0: @@ Class 520 java.lang.Cloneable + static Pattern classPattern = Pattern.compile("^0x([0-9a-f]+): @@ Class [ ]*([0-9]+) (.*)"); + + private static Matcher match(String line, Pattern pattern) { Matcher m = pattern.matcher(line); if (m.find()) { @@ -253,6 +282,11 @@ private static HeapObject parseHeapObjectImpl(String className, String oop, Stri } } + private static void parseClassObject(String className, String addr, String size) throws IOException { + mapFile.addClass(className); + nextLine(); + } + static MapFile mapFile; static BufferedReader reader; static String line = null; // current line being parsed @@ -277,6 +311,8 @@ public static MapFile read(String fileName) { parseHeapObject(m.group(3), m.group(1), m.group(2)); } else if ((m = match(line, objPattern1)) != null) { parseHeapObject(m.group(2), m.group(1), null); + } else if ((m = match(line, classPattern)) != null) { + parseClassObject(m.group(3), m.group(1), m.group(2)); // name, addr, size } else { nextLine(); } @@ -303,8 +339,15 @@ private static void mustContain(HashMap allObjects, Field fiel } } + public static void validate(MapFile mapFile, String classLoadLogFile) throws IOException { + validateOops(mapFile); + if (classLoadLogFile != null) { + validateClasses(mapFile, classLoadLogFile); + } + } + // Check that each oop fields in the HeapObjects must point to a valid HeapObject. - public static void validate(MapFile mapFile) { + static void validateOops(MapFile mapFile) { int count1 = 0; int count2 = 0; for (HeapObject heapObject : mapFile.heapObjects) { @@ -333,10 +376,10 @@ public static void validate(MapFile mapFile) { if (mapFile.heapObjectCount() > 0) { // heapObjectCount() may be zero if the selected GC doesn't support heap object archiving. if (mapFile.stringCount <= 0) { - throw new RuntimeException("CDS map file should contain at least one string"); + throw new RuntimeException("AOT map file should contain at least one string"); } if (count1 < mapFile.stringCount) { - throw new RuntimeException("CDS map file seems incorrect: " + mapFile.heapObjectCount() + + throw new RuntimeException("AOT map file seems incorrect: " + mapFile.heapObjectCount() + " objects (" + mapFile.stringCount + " strings). Each string should" + " have one non-null oop field but we found only " + count1 + " non-null oop field references"); @@ -344,8 +387,26 @@ public static void validate(MapFile mapFile) { } } - public static void main(String args[]) { + // classLoadLogFile should be generated with -Xlog:class+load:file=:none:filesize=0 + // Check that every class loaded from "source: shared objects file" have an entry inside the mapFile. + static void validateClasses(MapFile mapFile, String classLoadLogFile) throws IOException { + try (BufferedReader r = new BufferedReader(new FileReader(classLoadLogFile))) { + String line; + String suffix = " source: shared objects file"; + int suffixLen = suffix.length(); + while ((line = r.readLine()) != null) { + if (line.endsWith(suffix)) { + String className = line.substring(0, line.length() - suffixLen); + if (!mapFile.hasClass(className)) { + throw new RuntimeException("AOT map file is missing class " + className); + } + } + } + } + } + + public static void main(String args[]) throws IOException { MapFile mapFile = read(args[0]); - validate(mapFile); + validate(mapFile, null); } } diff --git a/test/hotspot/jtreg/runtime/cds/CDSMapTest.java b/test/hotspot/jtreg/runtime/cds/AOTMapTest.java similarity index 92% rename from test/hotspot/jtreg/runtime/cds/CDSMapTest.java rename to test/hotspot/jtreg/runtime/cds/AOTMapTest.java index 5a9fa82552b..dff98090859 100644 --- a/test/hotspot/jtreg/runtime/cds/CDSMapTest.java +++ b/test/hotspot/jtreg/runtime/cds/AOTMapTest.java @@ -27,7 +27,7 @@ * @summary Test the contents of -Xlog:aot+map * @requires vm.cds * @library /test/lib - * @run driver/timeout=240 CDSMapTest + * @run driver/timeout=240 AOTMapTest */ import jdk.test.lib.cds.CDSOptions; @@ -37,7 +37,7 @@ import jdk.test.lib.process.ProcessTools; import java.util.ArrayList; -public class CDSMapTest { +public class AOTMapTest { public static void main(String[] args) throws Exception { doTest(false); @@ -79,8 +79,8 @@ static String dump(ArrayList args) throws Exception { .addSuffix(args); CDSTestUtils.createArchiveAndCheck(opts); - CDSMapReader.MapFile mapFile = CDSMapReader.read(mapName); - CDSMapReader.validate(mapFile); + AOTMapReader.MapFile mapFile = AOTMapReader.read(mapName); + AOTMapReader.validate(mapFile, null); return archiveName; } @@ -98,7 +98,7 @@ static void exec(ArrayList vmArgs, String archiveFile) throws Exception OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "exec"); out.shouldHaveExitValue(0); - CDSMapReader.MapFile mapFile = CDSMapReader.read(mapName); - CDSMapReader.validate(mapFile); + AOTMapReader.MapFile mapFile = AOTMapReader.read(mapName); + AOTMapReader.validate(mapFile, null); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java index bcd2c71fea0..6cbfcbbd3c3 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java @@ -26,9 +26,10 @@ * @bug 8362566 * @summary Test the contents of -Xlog:aot+map with AOT workflow * @requires vm.cds.supports.aot.class.linking - * @library /test/lib /test/hotspot/jtreg/runtime/cds - * @build AOTMapTest + * @library /test/lib /test/hotspot/jtreg/runtime/cds /test/hotspot/jtreg/runtime/cds/appcds/test-classes + * @build AOTMapTest Hello * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar AOTMapTestApp + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar Hello * @run driver/timeout=240 AOTMapTest AOT --two-step-training */ @@ -37,15 +38,18 @@ * @bug 8362566 * @summary Test the contents of -Xlog:aot+map with dynamic CDS archive * @requires vm.cds.supports.aot.class.linking - * @library /test/lib /test/hotspot/jtreg/runtime/cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds /test/hotspot/jtreg/runtime/cds/appcds/test-classes * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @build AOTMapTest + * @build AOTMapTest Hello * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar AOTMapTestApp + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar Hello * @run main/othervm/timeout=240 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. AOTMapTest DYNAMIC */ - +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; import java.util.ArrayList; import jdk.test.lib.cds.CDSAppTester; import jdk.test.lib.helpers.ClassFileInstaller; @@ -54,6 +58,7 @@ public class AOTMapTest { static final String appJar = ClassFileInstaller.getJarPath("app.jar"); static final String mainClass = "AOTMapTestApp"; + static final String classLoadLogFile = "production.class.load.log"; public static void main(String[] args) throws Exception { doTest(args); @@ -63,13 +68,25 @@ public static void doTest(String[] args) throws Exception { Tester tester = new Tester(); tester.run(args); - validate(tester.dumpMapFile); - validate(tester.runMapFile); + if (tester.isDynamicWorkflow()) { + // For dynamic workflow, the AOT map file doesn't include classes in the base archive, so + // AOTMapReader.validateClasses() will fail. + validate(tester.dumpMapFile, false); + } else { + validate(tester.dumpMapFile, true); + } + validate(tester.runMapFile, true); } - static void validate(String mapFileName) { - CDSMapReader.MapFile mapFile = CDSMapReader.read(mapFileName); - CDSMapReader.validate(mapFile); + static void validate(String mapFileName, boolean checkClases) throws Exception { + AOTMapReader.MapFile mapFile = AOTMapReader.read(mapFileName); + if (checkClases) { + AOTMapReader.validate(mapFile, classLoadLogFile); + } else { + AOTMapReader.validate(mapFile, null); + } + mapFile.shouldHaveClass("AOTMapTestApp"); // built-in class + mapFile.shouldHaveClass("Hello"); // unregistered class } static class Tester extends CDSAppTester { @@ -97,12 +114,13 @@ public String[] vmArgs(RunMode runMode) { // filesize=0 ensures that a large map file not broken up in multiple files. String logMapPrefix = "-Xlog:aot+map=debug,aot+map+oops=trace:file="; - String logMapSuffix = ":none:filesize=0"; + String logSuffix = ":none:filesize=0"; if (runMode == RunMode.ASSEMBLY || runMode == RunMode.DUMP_DYNAMIC) { - vmArgs.add(logMapPrefix + dumpMapFile + logMapSuffix); + vmArgs.add(logMapPrefix + dumpMapFile + logSuffix); } else if (runMode == RunMode.PRODUCTION) { - vmArgs.add(logMapPrefix + runMapFile + logMapSuffix); + vmArgs.add(logMapPrefix + runMapFile + logSuffix); + vmArgs.add("-Xlog:class+load:file=" + classLoadLogFile + logSuffix); } return vmArgs.toArray(new String[vmArgs.size()]); @@ -118,7 +136,16 @@ public String[] appCommandLine(RunMode runMode) { } class AOTMapTestApp { - public static void main(String[] args) { + public static void main(String[] args) throws Exception { System.out.println("Hello AOTMapTestApp"); + testCustomLoader(); + } + + static void testCustomLoader() throws Exception { + File custJar = new File("cust.jar"); + URL[] urls = new URL[] {custJar.toURI().toURL()}; + URLClassLoader loader = new URLClassLoader(urls, AOTMapTestApp.class.getClassLoader()); + Class c = loader.loadClass("Hello"); + System.out.println(c); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java index bc2ac9db2ab..621d6383ff4 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java @@ -174,14 +174,9 @@ static void checkAssemblyOutput(String args[], OutputAnalyzer out) { // Indy References --- if (aotClassLinking) { testGroup("Indy References", out) - .shouldContain("Cannot aot-resolve Lambda proxy because OldConsumer is excluded") - .shouldContain("Cannot aot-resolve Lambda proxy because OldProvider is excluded") - .shouldContain("Cannot aot-resolve Lambda proxy because OldClass is excluded") .shouldContain("Cannot aot-resolve Lambda proxy of interface type InterfaceWithClinit") .shouldMatch("klasses.* app *NormalClass[$][$]Lambda/.* hidden aot-linked inited") - .shouldNotMatch("klasses.* app *SubOfOldClass[$][$]Lambda/") - .shouldMatch("archived indy *CP entry.*StringConcatTest .* => java/lang/invoke/StringConcatFactory.makeConcatWithConstants") - .shouldNotMatch("archived indy *CP entry.*StringConcatTestOld .* => java/lang/invoke/StringConcatFactory.makeConcatWithConstants"); + .shouldMatch("archived indy *CP entry.*StringConcatTest .* => java/lang/invoke/StringConcatFactory.makeConcatWithConstants"); } } diff --git a/test/hotspot/jtreg/runtime/handshake/HandshakeWalkStackTest.java b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkStackTest.java index 701e1ec6ec1..6644d14dbc8 100644 --- a/test/hotspot/jtreg/runtime/handshake/HandshakeWalkStackTest.java +++ b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkStackTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, 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 @@ -24,6 +24,7 @@ /* * @test HandshakeWalkStackTest + * @requires test.thread.factory == null * @library /testlibrary /test/lib * @build HandshakeWalkStackTest * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 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 00000000000..cb1596da08c --- /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 00000000000..3991926306e --- /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/jvmti/RedefineClasses/MissedStackMapFrames/MissedStackMapFrames.java b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/MissedStackMapFrames/MissedStackMapFrames.java index 534f077eaf4..941c6e79bb8 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/MissedStackMapFrames/MissedStackMapFrames.java +++ b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/MissedStackMapFrames/MissedStackMapFrames.java @@ -27,17 +27,15 @@ * @bug 8228604 * * @requires vm.jvmti - * @library /testlibrary/asm * @library /test/lib * * @run main/othervm/native -agentlib:MissedStackMapFrames MissedStackMapFrames */ -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; - +import java.lang.classfile.Attributes; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.classfile.MethodModel; public class MissedStackMapFrames { static { @@ -58,30 +56,19 @@ public class MissedStackMapFrames { private static native byte[] retransformBytes(int idx); private static int getStackMapFrameCount(byte[] classfileBuffer) { - ClassReader reader = new ClassReader(classfileBuffer); - final int[] frameCount = {0}; - ClassVisitor cv = new ClassVisitor(Opcodes.ASM9) { - @Override - public MethodVisitor visitMethod(int access, String name, - String descriptor, String signature, - String[] exceptions) { - return new MethodVisitor(Opcodes.ASM9) { - private int methodFrames = 0; - @Override - public void visitFrame(int type, int numLocal, Object[] local, - int numStack, Object[] stack) { - methodFrames++; - } - @Override - public void visitEnd() { - log(" method " + name + " - " + methodFrames + " frames"); - frameCount[0] += methodFrames; - } - }; + ClassModel clazz = ClassFile.of().parse(classfileBuffer); + int count = 0; + for (MethodModel method : clazz.methods()) { + var foundStackMapTable = method.code().flatMap(code -> code.findAttribute(Attributes.stackMapTable())); + if (foundStackMapTable.isPresent()) { + int methodFrames = foundStackMapTable.get().entries().size(); + log(" method " + method.methodName() + " - " + methodFrames + " frames"); + count += methodFrames; + } else { + log(" method " + method.methodName() + " - No StackMapTable"); } - }; - reader.accept(cv, 0); - return frameCount[0]; + } + return count; } private static int checkStackMapFrames(String mode, byte[] classfileBuffer) { diff --git a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineAnnotations.java b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineAnnotations.java index aad876d7181..320531148d4 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineAnnotations.java +++ b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineAnnotations.java @@ -24,7 +24,6 @@ /* * @test * @library /test/lib - * @library /testlibrary/asm * @summary Test that type annotations are retained after a retransform * @requires vm.jvmti * @modules java.base/jdk.internal.misc @@ -46,6 +45,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.lang.classfile.ClassBuilder; +import java.lang.classfile.ClassElement; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassTransform; +import java.lang.classfile.FieldModel; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; @@ -55,17 +59,10 @@ import java.lang.reflect.AnnotatedType; import java.lang.reflect.AnnotatedWildcardType; import java.lang.reflect.Executable; -import java.lang.reflect.TypeVariable; import java.security.ProtectionDomain; -import java.util.Arrays; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import java.util.Map; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.FieldVisitor; -import static org.objectweb.asm.Opcodes.ASM7; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE_USE) @@ -86,53 +83,27 @@ public byte[] asm(ClassLoader loader, String className, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { - ClassWriter cw = new ClassWriter(0); - ClassVisitor cv = new ReAddDummyFieldsClassVisitor(ASM7, cw) { }; - ClassReader cr = new ClassReader(classfileBuffer); - cr.accept(cv, 0); - return cw.toByteArray(); - } - - public class ReAddDummyFieldsClassVisitor extends ClassVisitor { - - LinkedList fields = new LinkedList<>(); - - public ReAddDummyFieldsClassVisitor(int api, ClassVisitor cv) { - super(api, cv); - } - - @Override public FieldVisitor visitField(int access, String name, - String desc, String signature, Object value) { - if (name.startsWith("dummy")) { - // Remove dummy field - fields.addLast(new F(access, name, desc, signature, value)); - return null; + // Shuffle constant pool + ClassFile context = ClassFile.of(ClassFile.ConstantPoolSharingOption.NEW_POOL); + return context.transformClass(context.parse(classfileBuffer), new ClassTransform() { + final List dummyFields = new ArrayList<>(); + + @Override + public void accept(ClassBuilder builder, ClassElement element) { + if (element instanceof FieldModel field && field.fieldName().stringValue().startsWith("dummy")) { + // Hold on to the associated constant pool entries too + dummyFields.addLast(field); + } else { + builder.with(element); + } } - return cv.visitField(access, name, desc, signature, value); - } - @Override public void visitEnd() { - F f; - while ((f = fields.pollFirst()) != null) { - // Re-add dummy fields - cv.visitField(f.access, f.name, f.desc, f.signature, f.value); + @Override + public void atEnd(ClassBuilder builder) { + // Add the associated constant pool entries to the end of the CP + dummyFields.forEach(builder); } - } - - private class F { - private int access; - private String name; - private String desc; - private String signature; - private Object value; - F(int access, String name, String desc, String signature, Object value) { - this.access = access; - this.name = name; - this.desc = desc; - this.signature = signature; - this.value = value; - } - } + }); } @Override public byte[] transform(ClassLoader loader, String className, diff --git a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineGenericSignatureTest.java b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineGenericSignatureTest.java index f220a93f187..923253e8051 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineGenericSignatureTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineGenericSignatureTest.java @@ -35,6 +35,11 @@ import java.io.File; import java.io.FileOutputStream; +import java.lang.classfile.ClassBuilder; +import java.lang.classfile.ClassElement; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassTransform; +import java.lang.classfile.attribute.SourceFileAttribute; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -141,27 +146,28 @@ private void printDisassembled(String description, Class cls, byte[] bytes) thro private byte[] getNewClassBytes() { byte[] bytecode = InMemoryJavaCompiler.compile(GenericSignatureTarget.class.getName(), newTargetClassSource); - ClassWriter cw = new ClassWriter(0); - ClassReader cr = new ClassReader(bytecode); - cr.accept(new ClassVisitor(Opcodes.ASM7, cw) { + ClassFile context = ClassFile.of(); + return context.transformClass(context.parse(bytecode), new ClassTransform() { private boolean sourceSet = false; @Override - public void visitSource(String source, String debug) { - sourceSet = true; - log("Changing source: \"" + source + "\" -> \"" + sourceFileNameNew + "\""); - super.visitSource(sourceFileNameNew, debug); + public void accept(ClassBuilder builder, ClassElement element) { + if (element instanceof SourceFileAttribute src) { + sourceSet = true; + log("Changing source: \"" + src.sourceFile() + "\" -> \"" + sourceFileNameNew + "\""); + builder.with(SourceFileAttribute.of(sourceFileNameNew)); + } else { + builder.with(element); + } } @Override - public void visitEnd() { + public void atEnd(ClassBuilder builder) { if (!sourceSet) { log("Set source: \"" + sourceFileNameNew + "\""); - super.visitSource(sourceFileNameNew, null); + builder.with(SourceFileAttribute.of(sourceFileNameNew)); } - super.visitEnd(); } - }, 0); - return cw.toByteArray(); + }); } private void runTest() throws Throwable { diff --git a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineObject.java b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineObject.java index de90c15b5a5..d3349282410 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineObject.java +++ b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineObject.java @@ -27,7 +27,6 @@ * @summary Ensure Object natives stay registered after redefinition * @requires vm.jvmti * @library /test/lib - * @library /testlibrary/asm * @modules java.base/jdk.internal.misc * java.compiler * java.instrument @@ -41,19 +40,15 @@ import java.io.FileNotFoundException; import java.io.PrintWriter; import java.lang.RuntimeException; +import java.lang.classfile.ClassBuilder; +import java.lang.classfile.ClassElement; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassFileVersion; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; import java.lang.instrument.UnmodifiableClassException; import java.security.ProtectionDomain; -import java.util.Arrays; - -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; - -import static org.objectweb.asm.Opcodes.ASM6; -import static org.objectweb.asm.Opcodes.V1_8; public class RedefineObject { @@ -69,31 +64,14 @@ public byte[] asm(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { - ClassWriter cw = new ClassWriter(0); - // Force an older ASM to force a bytecode update - ClassVisitor cv = new DummyClassVisitor(ASM6, cw) { }; - ClassReader cr = new ClassReader(classfileBuffer); - cr.accept(cv, 0); - byte[] bytes = cw.toByteArray(); - return bytes; - } - - public class DummyClassVisitor extends ClassVisitor { - - public DummyClassVisitor(int api, ClassVisitor cv) { - super(api, cv); - } - - public void visit( - final int version, - final int access, - final String name, - final String signature, - final String superName, - final String[] interfaces) { - // Artificially lower to JDK 8 version to force a redefine - cv.visit(V1_8, access, name, signature, superName, interfaces); - } + return ClassFile.of().transformClass(ClassFile.of().parse(classfileBuffer), (classBuilder, classElement) -> { + if (classElement instanceof ClassFileVersion cfv) { + // Force a redefine with different class file versions + classBuilder.with(ClassFileVersion.of(cfv.majorVersion() - 1, 0)); + } else { + classBuilder.with(classElement); + } + }); } @Override public byte[] transform(ClassLoader loader, String className, diff --git a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineRetransform/RedefineRetransform.java b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineRetransform/RedefineRetransform.java index a09d1dc318e..167b546ac9d 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineRetransform/RedefineRetransform.java +++ b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineRetransform/RedefineRetransform.java @@ -27,7 +27,6 @@ * @bug 7124710 * * @requires vm.jvmti - * @library /testlibrary/asm * @library /test/lib * * @comment main/othervm/native -Xlog:redefine*=trace -agentlib:RedefineRetransform RedefineRetransform @@ -42,13 +41,17 @@ import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; - -import org.objectweb.asm.AnnotationVisitor; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; +import java.lang.classfile.Annotation; +import java.lang.classfile.AnnotationElement; +import java.lang.classfile.AnnotationValue; +import java.lang.classfile.Attributes; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.classfile.ClassTransform; +import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute; +import java.lang.constant.ClassDesc; +import java.util.List; +import java.util.NoSuchElementException; /* * The test verifies that after interleaved RedefineClasses/RetransformClasses calls @@ -81,67 +84,32 @@ public TestClass() { } // Class bytes for initial TestClass (ClassVersion == 0). private static byte[] initialClassBytes; - private static class VersionScanner extends ClassVisitor { - private Integer detectedVersion; - private Integer versionToSet; - // to get version - public VersionScanner() { - super(Opcodes.ASM7); - } - // to set version - public VersionScanner(int verToSet, ClassVisitor classVisitor) { - super(Opcodes.ASM7, classVisitor); - versionToSet = verToSet; - } - - public int detectedVersion() { - if (detectedVersion == null) { - throw new RuntimeException("Version not detected"); - } - return detectedVersion; - } - - @Override - public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { - //log("visitAnnotation: descr = '" + descriptor + "', visible = " + visible); - if (Type.getDescriptor(ClassVersion.class).equals(descriptor)) { - return new AnnotationVisitor(Opcodes.ASM7, super.visitAnnotation(descriptor, visible)) { - @Override - public void visit(String name, Object value) { - //log("visit: name = '" + name + "', value = " + value - // + " (" + (value == null ? "N/A" : value.getClass()) + ")"); - if ("value".equals(name) && value instanceof Integer intValue) { - detectedVersion = intValue; - if (versionToSet != null) { - //log("replace with " + versionToSet); - value = versionToSet; - } - } - super.visit(name, value); - } - }; - } - return super.visitAnnotation(descriptor, visible); - } - } + private static final ClassDesc CD_ClassVersion = ClassVersion.class.describeConstable().orElseThrow(); // Generates TestClass class bytes with the specified ClassVersion value. private static byte[] getClassBytes(int ver) { if (ver < 0) { return null; } - ClassWriter cw = new ClassWriter(0); - ClassReader cr = new ClassReader(initialClassBytes); - cr.accept(new VersionScanner(ver, cw), 0); - return cw.toByteArray(); + return ClassFile.of().transformClass(ClassFile.of().parse(initialClassBytes), + // overwrites previously passed RVAA + ClassTransform.endHandler(classBuilder -> classBuilder.with(RuntimeVisibleAnnotationsAttribute + .of(Annotation.of(CD_ClassVersion, AnnotationElement.ofInt("value", ver)))))); } // Extracts ClassVersion values from the provided class bytes. private static int getClassBytesVersion(byte[] classBytes) { - ClassReader cr = new ClassReader(classBytes); - VersionScanner scanner = new VersionScanner(); - cr.accept(scanner, 0); - return scanner.detectedVersion(); + ClassModel classModel = ClassFile.of().parse(classBytes); + RuntimeVisibleAnnotationsAttribute rvaa = classModel.findAttribute(Attributes.runtimeVisibleAnnotations()).orElseThrow(); + List classVersionElementValuePairs = rvaa.annotations().stream() + .filter(anno -> anno.className().isFieldType(CD_ClassVersion)) + .findFirst().orElseThrow().elements(); + if (classVersionElementValuePairs.size() != 1) + throw new NoSuchElementException(); + AnnotationElement elementValuePair = classVersionElementValuePairs.getFirst(); + if (!elementValuePair.name().equalsString("value") || !(elementValuePair.value() instanceof AnnotationValue.OfInt intVal)) + throw new NoSuchElementException(); + return intVal.intValue(); } static void init() { diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest/ThreadStateTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest/ThreadStateTest.java index 4c8b9bb030f..c6a7debed1a 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest/ThreadStateTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest/ThreadStateTest.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 @@ -27,15 +27,17 @@ * @summary Exercise JvmtiThreadState creation concurrently with terminating vthreads * @requires vm.continuations * @modules java.base/java.lang:+open + * @library /test/lib * @run main/othervm/native -agentlib:ThreadStateTest ThreadStateTest */ -import java.util.concurrent.*; import java.util.Arrays; import java.util.ArrayList; import java.util.List; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; +import jdk.test.lib.thread.VThreadScheduler; public class ThreadStateTest { static final int VTHREAD_COUNT = 64; @@ -59,7 +61,7 @@ private void runTest() throws Exception { while (tryCount-- > 0) { ExecutorService scheduler = Executors.newFixedThreadPool(8); - ThreadFactory factory = virtualThreadBuilder(scheduler).factory(); + ThreadFactory factory = VThreadScheduler.virtualThreadBuilder(scheduler).factory(); List virtualThreads = new ArrayList<>(); for (int i = 0; i < VTHREAD_COUNT; i++) { @@ -98,22 +100,4 @@ public static void main(String[] args) throws Exception { ThreadStateTest obj = new ThreadStateTest(); obj.runTest(); } - - private static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) { - Thread.Builder.OfVirtual builder = Thread.ofVirtual(); - try { - Class clazz = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder"); - Constructor ctor = clazz.getDeclaredConstructor(Executor.class); - ctor.setAccessible(true); - return (Thread.Builder.OfVirtual) ctor.newInstance(scheduler); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - if (cause instanceof RuntimeException re) { - throw re; - } - throw new RuntimeException(e); - } catch (Exception e) { - throw new RuntimeException(e); - } - } } diff --git a/test/hotspot/jtreg/serviceability/sa/LingeredAppWithVirtualThread.java b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithVirtualThread.java new file mode 100644 index 00000000000..ca98506e133 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithVirtualThread.java @@ -0,0 +1,87 @@ + +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, NTT DATA + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.invoke.MethodHandle; +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SymbolLookup; +import java.lang.foreign.ValueLayout; +import java.util.concurrent.CountDownLatch; + +import jdk.test.lib.apps.LingeredApp; + +public class LingeredAppWithVirtualThread extends LingeredApp implements Runnable { + + private static final String THREAD_NAME = "target thread"; + + private static final MethodHandle hndSleep; + + private static final int sleepArg; + + private static final CountDownLatch signal = new CountDownLatch(1); + + static { + MemorySegment func; + if (System.getProperty("os.name").startsWith("Windows")) { + func = SymbolLookup.libraryLookup("Kernel32", Arena.global()) + .findOrThrow("Sleep"); + sleepArg = 3600_000; // 1h in milliseconds + } else { + func = Linker.nativeLinker() + .defaultLookup() + .findOrThrow("sleep"); + sleepArg = 3600; // 1h in seconds + } + + var desc = FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT); + hndSleep = Linker.nativeLinker().downcallHandle(func, desc); + } + + @Override + public void run() { + Thread.yield(); + signal.countDown(); + try { + hndSleep.invoke(sleepArg); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + public static void main(String[] args) { + try { + Thread.ofVirtual() + .name(THREAD_NAME) + .start(new LingeredAppWithVirtualThread()); + + signal.await(); + LingeredApp.main(args); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackWithVirtualThread.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackWithVirtualThread.java new file mode 100644 index 00000000000..6d7921c7ed8 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackWithVirtualThread.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, NTT DATA + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jdk.test.lib.JDKToolLauncher; +import jdk.test.lib.SA.SATestUtils; +import jdk.test.lib.Utils; +import jdk.test.lib.apps.LingeredApp; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @bug 8369505 + * @requires vm.hasSA + * @library /test/lib + * @run driver TestJhsdbJstackWithVirtualThread + */ +public class TestJhsdbJstackWithVirtualThread { + + private static void runJstack(LingeredApp app) throws Exception { + JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb"); + launcher.addVMArgs(Utils.getFilteredTestJavaOpts("-showversion")); + launcher.addToolArg("jstack"); + launcher.addToolArg("--pid"); + launcher.addToolArg(Long.toString(app.getPid())); + + ProcessBuilder pb = SATestUtils.createProcessBuilder(launcher); + Process jhsdb = pb.start(); + OutputAnalyzer out = new OutputAnalyzer(jhsdb); + + jhsdb.waitFor(); + + System.out.println(out.getStdout()); + System.err.println(out.getStderr()); + + out.stderrShouldBeEmptyIgnoreDeprecatedWarnings(); + out.shouldNotContain("must have non-zero frame size"); + } + + public static void main(String... args) throws Exception { + SATestUtils.skipIfCannotAttach(); // throws SkippedException if attach not expected to work. + LingeredApp app = null; + + try { + app = new LingeredAppWithVirtualThread(); + LingeredApp.startApp(app); + System.out.println("Started LingeredApp with pid " + app.getPid()); + runJstack(app); + System.out.println("Test Completed"); + } catch (Throwable e) { + e.printStackTrace(); + throw e; + } finally { + LingeredApp.stopApp(app); + } + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/generators/tests/TestGenerators.java b/test/hotspot/jtreg/testlibrary_tests/generators/tests/TestGenerators.java index 8ad0c17ba98..f949f99c035 100644 --- a/test/hotspot/jtreg/testlibrary_tests/generators/tests/TestGenerators.java +++ b/test/hotspot/jtreg/testlibrary_tests/generators/tests/TestGenerators.java @@ -391,13 +391,13 @@ static void testEmptyGenerators() { Asserts.assertThrows(EmptyGeneratorException.class, () -> G.uniformDoubles(1, 0)); Asserts.assertNotNull(G.uniformDoubles(0, 1)); - Asserts.assertNotNull(G.uniformDoubles(0, 0)); + Asserts.assertThrows(EmptyGeneratorException.class, () -> G.uniformDoubles(0, 0)); Asserts.assertThrows(EmptyGeneratorException.class, () -> G.uniformDoubles(0, 1).restricted(1.1d, 2.4d)); Asserts.assertNotNull(G.uniformDoubles(0, 1).restricted(0.9d, 2.4d)); Asserts.assertThrows(EmptyGeneratorException.class, () -> G.uniformFloats(1, 0)); Asserts.assertNotNull(G.uniformFloats(0, 1)); - Asserts.assertNotNull(G.uniformFloats(0, 0)); + Asserts.assertThrows(EmptyGeneratorException.class, () -> G.uniformFloats(0, 0)); Asserts.assertThrows(EmptyGeneratorException.class, () -> G.uniformFloats(0, 1).restricted(1.1f, 2.4f)); Asserts.assertNotNull(G.uniformFloats(0, 1).restricted(0.9f, 2.4f)); @@ -592,8 +592,13 @@ static void testFuzzy() { var floatBoundGen = G.uniformFloats(); for (int j = 0; j < 500; j++) { - float a = floatBoundGen.next(), b = floatBoundGen.next(); - float lo = Math.min(a, b), hi = Math.max(a, b); + float lo = 1, hi = 0; + // Failure of a single round is very rare, repeated failure even rarer. + while (lo >= hi) { + float a = floatBoundGen.next(), b = floatBoundGen.next(); + lo = Math.min(a, b); + hi = Math.max(a, b); + } var gb = G.uniformFloats(lo, hi); for (int i = 0; i < 10_000; i++) { float x = gb.next(); @@ -604,8 +609,13 @@ static void testFuzzy() { var doubleBoundGen = G.uniformDoubles(); for (int j = 0; j < 500; j++) { - double a = doubleBoundGen.next(), b = doubleBoundGen.next(); - double lo = Math.min(a, b), hi = Math.max(a, b); + double lo = 1, hi = 0; + // Failure of a single round is very rare, repeated failure even rarer. + while (lo >= hi) { + double a = doubleBoundGen.next(), b = doubleBoundGen.next(); + lo = Math.min(a, b); + hi = Math.max(a, b); + } var gb = G.uniformDoubles(lo, hi); for (int i = 0; i < 10_000; i++) { double x = gb.next(); diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java index 496fcbddb0f..46813bbff78 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java @@ -23,15 +23,22 @@ package ir_framework.tests; -import java.util.Set; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.Field; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import compiler.lib.ir_framework.*; -import compiler.lib.ir_framework.shared.TestRunException; import compiler.lib.ir_framework.shared.TestFormatException; +import compiler.lib.ir_framework.shared.TestRunException; import jdk.test.lib.Asserts; /* * @test + * @bug 8365262 8369232 * @requires vm.debug == true & vm.compMode != "Xint" & vm.compiler2.enabled & vm.flagless * @summary Test cross product scenarios with the framework. * @library /test/lib /testlibrary_tests / @@ -39,29 +46,20 @@ */ public class TestScenariosCrossProduct { - static void hasNFailures(String s, int count) { - if (!s.matches("The following scenarios have failed: (#[0-9](, )?){" + count + "}. Please check stderr for more information.")) { - throw new RuntimeException("Expected " + count + " failures in \"" + s + "\""); - } - } public static void main(String[] args) { - // Test argument handling - try { - TestFramework t = new TestFramework(); - t.addCrossProductScenarios((Set[]) null); - Asserts.fail("Should have thrown exception"); - } catch (TestFormatException e) {} - try { - TestFramework t = new TestFramework(); - t.addCrossProductScenarios(Set.of("foo", "bar"), null); - Asserts.fail("Should have thrown exception"); - } catch (TestFormatException e) {} + expectFormatFailure((Set[]) null); + expectFormatFailure(Set.of("foo", "bar"), null); + try { TestFramework t = new TestFramework(); t.addCrossProductScenarios(Set.of("blub"), Set.of("foo", null)); - Asserts.fail("Should have thrown exception"); - } catch (NullPointerException e) {} // Set.of prevents null elements + shouldHaveThrown(); + } catch (NullPointerException _) { + // Expected: Set.of prevents null elements + } + + try { TestFramework t = new TestFramework(); t.addCrossProductScenarios(); @@ -70,95 +68,291 @@ public static void main(String[] args) { } // Single set should test all flags in the set by themselves. - try { - TestFramework t1 = new TestFramework(); - t1.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=51", - "-XX:TLABRefillWasteFraction=53", - "-XX:TLABRefillWasteFraction=64")); - t1.start(); - Asserts.fail("Should have thrown exception"); - } catch (TestRunException e) { - hasNFailures(e.getMessage(), 3); - } + new TestCase() + .inputFlags(Set.of( + Set.of("-XX:TLABRefillWasteFraction=51", + "-XX:TLABRefillWasteFraction=53", + "-XX:TLABRefillWasteFraction=64") + ) + ) + .expectedScenariosWithFlags(Set.of( + Set.of("-XX:TLABRefillWasteFraction=51"), + Set.of("-XX:TLABRefillWasteFraction=53"), + Set.of("-XX:TLABRefillWasteFraction=64") + )) + .run(); // The cross product of a set with one element and a set with three elements is three sets. - try { - TestFramework t2 = new TestFramework(); - t2.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=53"), - Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2", "-XX:+UseNewCode3")); - t2.start(); - Asserts.fail("Should have thrown exception"); - } catch (TestRunException e) { - hasNFailures(e.getMessage(), 3); - } + new TestCase() + .inputFlags(Set.of( + Set.of("-XX:TLABRefillWasteFraction=53"), + Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2", "-XX:+UseNewCode3") + ) + ) + .expectedScenariosWithFlags(Set.of( + Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode"), + Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode2"), + Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode3") + )) + .run(); + // The cross product of two sets with two elements is four sets. - try { - TestFramework t3 = new TestFramework(); - t3.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=53", "-XX:TLABRefillWasteFraction=64"), - Set.of("-XX:+UseNewCode", "-XX:-UseNewCode")); - t3.start(); - Asserts.fail("Should have thrown exception"); - } catch (TestRunException e) { - hasNFailures(e.getMessage(), 4); - } + new TestCase() + .inputFlags(Set.of( + Set.of("-XX:TLABRefillWasteFraction=53", "-XX:TLABRefillWasteFraction=64"), + Set.of("-XX:+UseNewCode", "-XX:-UseNewCode") + ) + ) + .expectedScenariosWithFlags(Set.of( + Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode"), + Set.of("-XX:TLABRefillWasteFraction=53", "-XX:-UseNewCode"), + Set.of("-XX:TLABRefillWasteFraction=64", "-XX:+UseNewCode"), + Set.of("-XX:TLABRefillWasteFraction=64", "-XX:-UseNewCode") + )) + .run(); + // Test with a pair of flags. + new TestCase() + .inputFlags(Set.of( + Set.of("-XX:TLABRefillWasteFraction=50 -XX:+UseNewCode", "-XX:TLABRefillWasteFraction=40"), + Set.of("-XX:+UseNewCode2") + ) + ) + .expectedScenariosWithFlags(Set.of( + Set.of("-XX:TLABRefillWasteFraction=50", "-XX:+UseNewCode", "-XX:+UseNewCode2"), + Set.of("-XX:TLABRefillWasteFraction=40", "-XX:+UseNewCode2") + )) + .run(); + + // Test with an empty string, resulting in 6 scenarios. + new TestCase() + .inputFlags(Set.of( + Set.of("", "-XX:TLABRefillWasteFraction=51", "-XX:TLABRefillWasteFraction=53"), + Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2") + ) + ) + .expectedScenariosWithFlags(Set.of( + Set.of("-XX:+UseNewCode"), + Set.of("-XX:+UseNewCode2"), + Set.of("-XX:TLABRefillWasteFraction=51", "-XX:+UseNewCode"), + Set.of("-XX:TLABRefillWasteFraction=51", "-XX:+UseNewCode2"), + Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode"), + Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode2") + )) + .run(); + + // Test with 3 input sets which equals to 2x2x2 = 8 scenarios. + new TestCase() + .inputFlags(Set.of( + Set.of("-XX:TLABRefillWasteFraction=51", + "-XX:TLABRefillWasteFraction=53"), + Set.of("-XX:+UseNewCode", + "-XX:-UseNewCode"), + Set.of("-XX:+UseNewCode2", + "-XX:-UseNewCode2") + ) + ) + .expectedScenariosWithFlags(Set.of( + Set.of("-XX:TLABRefillWasteFraction=51", "-XX:+UseNewCode", "-XX:+UseNewCode2"), + Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode", "-XX:+UseNewCode2"), + Set.of("-XX:TLABRefillWasteFraction=51", "-XX:-UseNewCode", "-XX:+UseNewCode2"), + Set.of("-XX:TLABRefillWasteFraction=53", "-XX:-UseNewCode", "-XX:+UseNewCode2"), + Set.of("-XX:TLABRefillWasteFraction=51", "-XX:+UseNewCode", "-XX:-UseNewCode2"), + Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode", "-XX:-UseNewCode2"), + Set.of("-XX:TLABRefillWasteFraction=51", "-XX:-UseNewCode", "-XX:-UseNewCode2"), + Set.of("-XX:TLABRefillWasteFraction=53", "-XX:-UseNewCode", "-XX:-UseNewCode2") + )) + .run(); + + TestFramework testFramework = new TestFramework(); + testFramework.addScenarios(new Scenario(0, "-XX:TLABRefillWasteFraction=50", "-XX:+UseNewCode")); + testFramework.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=51", "-XX:TLABRefillWasteFraction=53"), + Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2")); try { - TestFramework t4 = new TestFramework(); - t4.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=50 -XX:+UseNewCode", "-XX:TLABRefillWasteFraction=40"), - Set.of("-XX:+UseNewCode2")); - t4.start(); - Asserts.fail("Should have thrown exception"); - } catch (TestRunException e) { - hasNFailures(e.getMessage(), 1); + testFramework.addScenarios(new Scenario(4, "-XX:+UseNewCode3")); // fails because index 4 is already used + shouldHaveThrown(); + } catch (TestFormatException _) { + // Expected. } + testFramework.addScenarios(new Scenario(5, "-XX:+UseNewCode3")); + + new TestCase() + .expectedScenariosWithFlags(Set.of( + Set.of("-XX:TLABRefillWasteFraction=50", "-XX:+UseNewCode"), + Set.of("-XX:TLABRefillWasteFraction=51", "-XX:+UseNewCode"), + Set.of("-XX:TLABRefillWasteFraction=51", "-XX:+UseNewCode2"), + Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode"), + Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode2"), + Set.of("-XX:+UseNewCode3") + )) + .runWithPreAddedScenarios(testFramework); + + runEndToEndTest(); + } - // Test with an empty string. All 6 scenarios fail because 64 is the default value for TLABRefillWasteFraction. + private static void expectFormatFailure(Set... flagSets) { + TestFramework testFramework = new TestFramework(); try { - TestFramework t5 = new TestFramework(); - t5.addCrossProductScenarios(Set.of("", "-XX:TLABRefillWasteFraction=51", "-XX:TLABRefillWasteFraction=53"), - Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2")); - t5.start(); - Asserts.fail("Should have thrown exception"); - } catch (TestRunException e) { - hasNFailures(e.getMessage(), 6); + testFramework.addCrossProductScenarios(flagSets); + shouldHaveThrown(); + } catch (TestFormatException _) { + // Expected. } + } - try { - TestFramework t6 = new TestFramework(); - t6.addScenarios(new Scenario(0, "-XX:TLABRefillWasteFraction=50", "-XX:+UseNewCode")); // failPair - t6.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=51", "-XX:TLABRefillWasteFraction=53"), - Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2")); - try { - t6.addScenarios(new Scenario(4, "-XX:+UseNewCode3")); // fails because index 4 is already used - Asserts.fail("Should have thrown exception"); - } catch (TestFormatException e) {} - t6.addScenarios(new Scenario(5, "-XX:+UseNewCode3")); // fail default - t6.start(); - Asserts.fail("Should have thrown exception"); - } catch (TestRunException e) { - hasNFailures(e.getMessage(), 6); + private static void shouldHaveThrown() { + Asserts.fail("Should have thrown exception"); + } + + static class TestCase { + private Set> inputFlags; + private Set> expectedScenariosWithFlags; + + public TestCase inputFlags(Set> inputFlags) { + this.inputFlags = inputFlags; + return this; + } + + public TestCase expectedScenariosWithFlags(Set> expectedScenariosWithFlags) { + this.expectedScenariosWithFlags = expectedScenariosWithFlags; + return this; + } + + public void run() { + TestFramework testFramework = new TestFramework(); + testFramework.addCrossProductScenarios(inputFlags.toArray(new Set[0])); + runWithPreAddedScenarios(testFramework); + } + + public void runWithPreAddedScenarios(TestFramework testFramework) { + List scenariosFromCrossProduct = getScenarios(testFramework); + assertScenarioCount(expectedScenariosWithFlags.size(), scenariosFromCrossProduct); + assertScenariosWithFlags(scenariosFromCrossProduct, expectedScenariosWithFlags); + assertSameResultWhenManuallyAdding(scenariosFromCrossProduct, expectedScenariosWithFlags); + } + + private static void assertScenarioCount(int expectedCount, List scenarios) { + Asserts.assertEQ(expectedCount, scenarios.size(), "Scenario count is off"); + } + + /** + * Check that the added scenarios to the IR framework with TestFramework.addCrossProductScenarios() + * (i.e. 'scenariosFromCrossProduct') match the expected flag combos (i.e. 'expectedScenariosWithFlags'). + */ + private static void assertScenariosWithFlags(List scenariosFromCrossProduct, + Set> expectedScenariosWithFlags) { + for (Set expectedScenarioFlags : expectedScenariosWithFlags) { + if (scenariosFromCrossProduct.stream() + .map(Scenario::getFlags) + .map(Set::copyOf) + .anyMatch(flags -> flags.equals(expectedScenarioFlags))) { + continue; + } + System.err.println("Scenarios from cross product:"); + for (Scenario s : scenariosFromCrossProduct) { + System.err.println(Arrays.toString(s.getFlags().toArray())); + } + throw new RuntimeException("Could not find a scenario with the provided flags: " + Arrays.toString(expectedScenarioFlags.toArray())); + } + } + + /** + * Add scenarios for the provided flag sets in 'expectedScenariosWithFlags' by using TestFramework.addScenarios(). + * We should end up with the same scenarios as if we added them with TestFramework.addCrossProductScenarios(). + * This is verified by this method by comparing the flags of the scenarios, ignoring scenario indices. + */ + private static void assertSameResultWhenManuallyAdding(List scenariosFromCrossProduct, + Set> expectedScenariosWithFlags) { + List expectedScenarios = getScenariosWithFlags(expectedScenariosWithFlags); + List fetchedScenarios = addScenariosAndFetchFromFramework(expectedScenarios); + assertSameScenarios(scenariosFromCrossProduct, fetchedScenarios); + } + + private static List getScenariosWithFlags(Set> expectedScenariosWithFlags) { + List expecedScenarioList = new ArrayList<>(); + int index = -1; // Use some different indices - should not matter what we choose. + for (Set expectedScenarioFlags : expectedScenariosWithFlags) { + expecedScenarioList.add(new Scenario(index--, expectedScenarioFlags.toArray(new String[0]))); + } + return expecedScenarioList; + } + + private static List addScenariosAndFetchFromFramework(List expecedScenarioList) { + TestFramework testFramework = new TestFramework(); + testFramework.addScenarios(expecedScenarioList.toArray(new Scenario[0])); + return getScenarios(testFramework); + } + + private static void assertSameScenarios(List scenariosFromCrossProduct, + List expectedScenarios) { + assertScenariosWithFlags(scenariosFromCrossProduct, fetchFlags(expectedScenarios)); + } + + private static Set> fetchFlags(List scenarios) { + return scenarios.stream() + .map(scenario -> new HashSet<>(scenario.getFlags())) + .collect(Collectors.toSet()); } } - @Test - @IR(applyIf = {"TLABRefillWasteFraction", "64"}, counts = {IRNode.CALL, "1"}) - public void failDefault() { + private static List getScenarios(TestFramework testFramework) { + Field field; + try { + field = TestFramework.class.getDeclaredField("scenarios"); + field.setAccessible(true); + return (List)field.get(testFramework); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } } - @Test - @IR(applyIf = {"TLABRefillWasteFraction", "51"}, counts = {IRNode.CALL, "1"}) - public void fail1() { + /** + * Also run a simple end-to-end test to sanity check the API method. We capture the stderr to fetch the + * scenario flags. + */ + private static void runEndToEndTest() { + TestFramework testFramework = new TestFramework(); + + // Capture stderr + PrintStream originalErr = System.err; + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PrintStream printStream = new PrintStream(outputStream); + System.setErr(printStream); + + try { + testFramework + .addCrossProductScenarios(Set.of("-XX:+UseNewCode", "-XX:-UseNewCode"), + Set.of("-XX:+UseNewCode2", "-XX:-UseNewCode2")) + .addFlags() + .start(); + shouldHaveThrown(); + } catch (TestRunException e) { + // Expected. + System.setErr(originalErr); + Asserts.assertTrue(e.getMessage().contains("The following scenarios have failed: #0, #1, #2, #3.")); + String stdErr = outputStream.toString(); + Asserts.assertTrue(stdErr.contains("Scenario flags: [-XX:+UseNewCode, -XX:+UseNewCode2]")); + Asserts.assertTrue(stdErr.contains("Scenario flags: [-XX:-UseNewCode, -XX:-UseNewCode2]")); + Asserts.assertTrue(stdErr.contains("Scenario flags: [-XX:+UseNewCode, -XX:-UseNewCode2]")); + Asserts.assertTrue(stdErr.contains("Scenario flags: [-XX:-UseNewCode, -XX:+UseNewCode2]")); + Asserts.assertEQ(4, scenarioCount(stdErr)); + } } - @Test - @IR(applyIf = {"TLABRefillWasteFraction", "53"}, counts = {IRNode.CALL, "1"}) - public void fail2() { + public static int scenarioCount(String stdErr) { + Pattern pattern = Pattern.compile("Scenario flags"); + Matcher matcher = pattern.matcher(stdErr); + int count = 0; + while (matcher.find()) { + count++; + } + return count; } @Test - @IR(applyIfAnd = {"TLABRefillWasteFraction", "50", "UseNewCode", "true"}, counts = {IRNode.CALL, "1"}) - public void failPair() { + public void endToEndTest() { + throw new RuntimeException("executed test"); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java index 6e11a705054..c21d2492fc7 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java @@ -70,29 +70,52 @@ public static String generate(CompileFramework comp) { var withConstantsTemplate = Template.make("expression", (Expression expression) -> { // Create a token: fill the expression with a fixed set of constants. // We then use the same token with the same constants, once compiled and once not compiled. + // + // Some expressions can throw Exceptions. We have to catch them. In such a case, we return + // the Exception instead of the value from the expression, and compare the Exceptions. + // + // Some Expressions do not have a deterministic result. For example, different NaN or + // precision results from some operators. We only compare the results if we know that the + // result is deterministically the same. TemplateToken expressionToken = expression.asToken(expression.argumentTypes.stream().map(t -> t.con()).toList()); return body( let("returnType", expression.returnType), """ @Test public static void $primitiveConTest() { - #returnType v0 = ${primitiveConTest}_compiled(); - #returnType v1 = ${primitiveConTest}_reference(); - Verify.checkEQ(v0, v1); + Object v0 = ${primitiveConTest}_compiled(); + Object v1 = ${primitiveConTest}_reference(); + """, + expression.info.isResultDeterministic ? "Verify.checkEQ(v0, v1);\n" : "", + """ } @DontInline - public static #returnType ${primitiveConTest}_compiled() { + public static Object ${primitiveConTest}_compiled() { + try { """, - "return ", expressionToken, ";\n", + "return ", expressionToken, ";\n", + expression.info.exceptions.stream().map(exception -> + "} catch (" + exception + " e) { return e;\n" + ).toList(), """ + } finally { + // Just so that javac is happy if there are no exceptions to catch. + } } @DontCompile - public static #returnType ${primitiveConTest}_reference() { + public static Object ${primitiveConTest}_reference() { + try { """, - "return ", expressionToken, ";\n", + "return ", expressionToken, ";\n", + expression.info.exceptions.stream().map(exception -> + "} catch (" + exception + " e) { return e;\n" + ).toList(), """ + } finally { + // Just so that javac is happy if there are no exceptions to catch. + } } """ ); diff --git a/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/GenClassPoolJar.java b/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/GenClassPoolJar.java index cea00fc2efc..511db8b8ed1 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/GenClassPoolJar.java +++ b/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/GenClassPoolJar.java @@ -26,6 +26,9 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassTransform; +import java.lang.constant.ClassDesc; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; import java.nio.file.Files; @@ -42,11 +45,6 @@ import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Opcodes; - /** * Class that imitates shell script to produce jar file with many similar * classes inside. @@ -261,28 +259,9 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOEx * @return new class file to write into class */ byte[] morphClass(byte[] classToMorph, String newName) { - ClassReader cr = new ClassReader(classToMorph); - ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS); - ClassVisitor cv = new ClassRenamer(cw, newName); - cr.accept(cv, 0); - return cw.toByteArray(); + var context = ClassFile.of(); + return context.transformClass(context.parse(classToMorph), + ClassDesc.ofInternalName(newName), + ClassTransform.ACCEPT_ALL); } - - /** - * Visitor to rename class. - */ - static class ClassRenamer extends ClassVisitor implements Opcodes { - private final String newName; - - public ClassRenamer(ClassVisitor cv, String newName) { - super(ASM4, cv); - this.newName = newName; - } - - @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - cv.visit(version, access, newName, signature, superName, interfaces); - } - - } } diff --git a/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_inMemoryCompilation_keep_cl/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_inMemoryCompilation_keep_cl/TestDescription.java index 19b2a940788..5337aa73d2e 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_inMemoryCompilation_keep_cl/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_inMemoryCompilation_keep_cl/TestDescription.java @@ -30,7 +30,6 @@ * VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, javac] * * @modules java.base/jdk.internal.misc - * @library /testlibrary/asm * @library /vmTestbase * /test/lib * diff --git a/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_inMemoryCompilation_keep_class/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_inMemoryCompilation_keep_class/TestDescription.java index b84411234a5..75d45dda2fe 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_inMemoryCompilation_keep_class/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_inMemoryCompilation_keep_class/TestDescription.java @@ -30,7 +30,6 @@ * VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, javac] * * @modules java.base/jdk.internal.misc - * @library /testlibrary/asm * @library /vmTestbase * /test/lib * diff --git a/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_inMemoryCompilation_keep_obj/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_inMemoryCompilation_keep_obj/TestDescription.java index f0cc45744c4..aceade65a66 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_inMemoryCompilation_keep_obj/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_inMemoryCompilation_keep_obj/TestDescription.java @@ -30,7 +30,6 @@ * VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, javac] * * @modules java.base/jdk.internal.misc - * @library /testlibrary/asm * @library /vmTestbase * /test/lib * diff --git a/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_keep_cl/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_keep_cl/TestDescription.java index 48f1680897f..a2547a7556f 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_keep_cl/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_keep_cl/TestDescription.java @@ -30,7 +30,6 @@ * VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, javac] * * @modules java.base/jdk.internal.misc - * @library /testlibrary/asm * @library /vmTestbase * /test/lib * diff --git a/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_keep_class/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_keep_class/TestDescription.java index 68263db73ac..d822f65034d 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_keep_class/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_keep_class/TestDescription.java @@ -30,7 +30,6 @@ * VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, javac] * * @modules java.base/jdk.internal.misc - * @library /testlibrary/asm * @library /vmTestbase * /test/lib * diff --git a/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_keep_obj/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_keep_obj/TestDescription.java index 4ee02a649d3..e875b964f0f 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_keep_obj/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/tests/unloading_keepRef_rootClass_keep_obj/TestDescription.java @@ -30,7 +30,6 @@ * VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, javac] * * @modules java.base/jdk.internal.misc - * @library /testlibrary/asm * @library /vmTestbase * /test/lib * diff --git a/test/hotspot/jtreg/vmTestbase/gc/vector/CircularListLow/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/vector/CircularListLow/TestDescription.java index 252e3a2d40f..68fe6779e99 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 bd094045abf..8ae86af035d 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 3d57bfb6ea9..e660dac4fd5 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/hotspot/jtreg/vmTestbase/nsk/jvmti/GetClassFields/getclfld007.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetClassFields/getclfld007.java index 6d79e479b97..300966c04dd 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetClassFields/getclfld007.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetClassFields/getclfld007.java @@ -25,14 +25,12 @@ import java.io.PrintStream; import java.io.InputStream; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.classfile.FieldModel; import java.util.List; import java.util.ArrayList; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.FieldVisitor; -import org.objectweb.asm.Opcodes; - public class getclfld007 { @@ -79,44 +77,29 @@ public static int run(String args[], PrintStream out) { static void check(Class cls) throws Exception { - FieldExplorer explorer = new FieldExplorer(cls); - List fields = explorer.get(); + List fields = getFields(cls); check(cls, fields.toArray(new String[0])); } - // helper class to get list of the class fields - // in the order they appear in the class file - static class FieldExplorer extends ClassVisitor { - private final Class cls; - private List fieldNameAndSig = new ArrayList<>(); - private FieldExplorer(Class cls) { - super(Opcodes.ASM7); - this.cls = cls; - } - - @Override - public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { - System.out.println(" field '" + name + "', type = " + descriptor); - fieldNameAndSig.add(name); - fieldNameAndSig.add(descriptor); - return super.visitField(access, name, descriptor, signature, value); - } - - private InputStream getClassBytes() throws Exception { - String clsName = cls.getName(); - String clsPath = clsName.replace('.', '/') + ".class"; - return cls.getClassLoader().getResourceAsStream(clsPath); - } + private static InputStream getClassBytes(Class cls) throws Exception { + String clsName = cls.getName(); + String clsPath = clsName.replace('.', '/') + ".class"; + return cls.getClassLoader().getResourceAsStream(clsPath); + } - // each field is represented by 2 Strings in the list: name and type descriptor - public List get() throws Exception { - System.out.println("Class " + cls.getName()); - try (InputStream classBytes = getClassBytes()) { - ClassReader classReader = new ClassReader(classBytes); - classReader.accept(this, 0); + // get list of the class fields in the order they appear in the class file + // each field is represented by 2 Strings in the list: name and type descriptor + public static List getFields(Class cls) throws Exception { + System.out.println("Class " + cls.getName()); + List fieldNameAndSig = new ArrayList<>(); + try (InputStream classBytes = getClassBytes(cls)) { + ClassModel classModel = ClassFile.of().parse(classBytes.readAllBytes()); + for (FieldModel field : classModel.fields()) { + fieldNameAndSig.add(field.fieldName().stringValue()); + fieldNameAndSig.add(field.fieldType().stringValue()); } - return fieldNameAndSig; } + return fieldNameAndSig; } static class InnerClass1 { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetCurrentThreadCpuTime/curthrcputime001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetCurrentThreadCpuTime/curthrcputime001/TestDescription.java index 0b7e1d2a89e..ba09ff735d8 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetCurrentThreadCpuTime/curthrcputime001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetCurrentThreadCpuTime/curthrcputime001/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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,7 @@ * COMMENTS * Fixed the 4968019, 5006885 bugs. * + * @requires test.thread.factory == null * @library /vmTestbase * /test/lib * @build nsk.jvmti.GetCurrentThreadCpuTime.curthrcputime001 diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetThreadCpuTime/thrcputime001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetThreadCpuTime/thrcputime001/TestDescription.java index cf0fe3e5d2d..b935639e183 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetThreadCpuTime/thrcputime001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetThreadCpuTime/thrcputime001/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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,7 @@ * COMMENTS * Fixed the 4968019, 5006885 bugs. * + * @requires test.thread.factory == null * @library /vmTestbase * /test/lib * @build nsk.jvmti.GetThreadCpuTime.thrcputime001 diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t001.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t001.java index 4c7905809d9..eb202c87ef3 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2018, 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 @@ -123,6 +123,8 @@ public int runIt(String argv[], PrintStream out) { /* =================================================================== */ class tc03t001Thread extends Thread { + // The thread name prefix is used to find thread from jvmti agent. + final static String threadNamePrefix = "Debuggee Thread"; Object lock1; Object lock2; @@ -130,7 +132,7 @@ class tc03t001Thread extends Thread { int lock2Counter = 0; public tc03t001Thread(Object o1, Object o2) { - super("Debuggee Thread " + o1 + o2); + super(threadNamePrefix + " " + o1 + o2); lock1 = o1; lock2 = o2; } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t001/tc03t001.cpp b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t001/tc03t001.cpp index ff7d346d237..9ea61a27bc6 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t001/tc03t001.cpp +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t001/tc03t001.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2024, 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 @@ -41,10 +41,13 @@ typedef struct { static jlong timeout = 0; /* test objects */ -static threadDesc *threadList = nullptr; -static jint threads_count = 0; +static threadDesc *debuggee_threads = nullptr; +static jint debuggee_threads_cnt = 0; static int numberOfDeadlocks = 0; +static const char* THREAD_NAME_PREFIX = "Debugee Thread"; +static const size_t THREAD_NAME_PREFIX_LEN = strlen(THREAD_NAME_PREFIX); + /* ========================================================================== */ static int printDeadlock(jvmtiEnv* jvmti, JNIEnv* jni, int dThread) { @@ -56,9 +59,9 @@ static int printDeadlock(jvmtiEnv* jvmti, JNIEnv* jni, int dThread) { NSK_DISPLAY1("Found deadlock #%d:\n", numberOfDeadlocks); for (pThread = dThread;;pThread = cThread) { - NSK_DISPLAY1(" \"%s\":\n", threadList[pThread].name); + NSK_DISPLAY1(" \"%s\":\n", debuggee_threads[pThread].name); if (!NSK_JVMTI_VERIFY( - jvmti->GetCurrentContendedMonitor(threadList[pThread].thread, &monitor))) + jvmti->GetCurrentContendedMonitor(debuggee_threads[pThread].thread, &monitor))) return NSK_FALSE; if (monitor != nullptr) { if (!NSK_JNI_VERIFY(jni, (klass = jni->GetObjectClass(monitor)) != nullptr)) @@ -74,8 +77,8 @@ static int printDeadlock(jvmtiEnv* jvmti, JNIEnv* jni, int dThread) { return NSK_FALSE; if (usageInfo.owner == nullptr) break; - for (cThread = 0; cThread < threads_count; cThread++) { - if (jni->IsSameObject(threadList[cThread].thread, usageInfo.owner)) + for (cThread = 0; cThread < debuggee_threads_cnt; cThread++) { + if (jni->IsSameObject(debuggee_threads[cThread].thread, usageInfo.owner)) break; } if (usageInfo.waiters != nullptr) { @@ -84,10 +87,10 @@ static int printDeadlock(jvmtiEnv* jvmti, JNIEnv* jni, int dThread) { if (usageInfo.notify_waiters != nullptr) { jvmti->Deallocate((unsigned char*)usageInfo.notify_waiters); } - if (!NSK_VERIFY(cThread != threads_count)) + if (!NSK_VERIFY(cThread != debuggee_threads_cnt)) return NSK_FALSE; NSK_DISPLAY1(" which is held by \"%s\"\n", - threadList[cThread].name); + debuggee_threads[cThread].name); if (cThread == dThread) break; } @@ -103,8 +106,9 @@ static int findDeadlockThreads(jvmtiEnv* jvmti, JNIEnv* jni) { int tDfn = 0, gDfn = 0; int pThread, cThread; int i; + int threads_count = 0; - NSK_DISPLAY0("Create threadList\n"); + NSK_DISPLAY0("Create debuggee_threads\n"); /* get all live threads */ if (!NSK_JVMTI_VERIFY(jvmti->GetAllThreads(&threads_count, &threads))) @@ -114,7 +118,7 @@ static int findDeadlockThreads(jvmtiEnv* jvmti, JNIEnv* jni) { return NSK_FALSE; if (!NSK_JVMTI_VERIFY( - jvmti->Allocate(threads_count*sizeof(threadDesc), (unsigned char**)&threadList))) + jvmti->Allocate(threads_count*sizeof(threadDesc), (unsigned char**)&debuggee_threads))) return NSK_FALSE; for (i = 0; i < threads_count; i++) { @@ -127,22 +131,30 @@ static int findDeadlockThreads(jvmtiEnv* jvmti, JNIEnv* jni) { NSK_DISPLAY3(" thread #%d (%s): %p\n", i, info.name, threads[i]); - threadList[i].thread = threads[i]; - threadList[i].dfn = -1; - threadList[i].name = info.name; + if (!strncmp(info.name, THREAD_NAME_PREFIX, THREAD_NAME_PREFIX_LEN)) { + NSK_DISPLAY1("Skipping thread %s\n", info.name); + if (!NSK_JVMTI_VERIFY(jvmti->Deallocate((unsigned char*)info.name))) + return NSK_FALSE; + continue; + } + + debuggee_threads[debuggee_threads_cnt].thread = threads[i]; + debuggee_threads[debuggee_threads_cnt].dfn = -1; + debuggee_threads[debuggee_threads_cnt].name = info.name; + debuggee_threads_cnt++; } /* deallocate thread list */ if (!NSK_JVMTI_VERIFY(jvmti->Deallocate((unsigned char*)threads))) return NSK_FALSE; - for (i = 0; i < threads_count; i++) { - if (threadList[i].dfn < 0) { + for (i = 0; i < debuggee_threads_cnt; i++) { + if (debuggee_threads[i].dfn < 0) { tDfn = gDfn; - threadList[i].dfn = gDfn++; + debuggee_threads[i].dfn = gDfn++; for (pThread = i;;pThread = cThread) { if (!NSK_JVMTI_VERIFY( - jvmti->GetCurrentContendedMonitor(threadList[pThread].thread, &monitor))) + jvmti->GetCurrentContendedMonitor(debuggee_threads[pThread].thread, &monitor))) return NSK_FALSE; if (monitor == nullptr) break; @@ -150,8 +162,8 @@ static int findDeadlockThreads(jvmtiEnv* jvmti, JNIEnv* jni) { return NSK_FALSE; if (usageInfo.owner == nullptr) break; - for (cThread = 0; cThread < threads_count; cThread++) { - if (jni->IsSameObject(threadList[cThread].thread, usageInfo.owner)) + for (cThread = 0; cThread < debuggee_threads_cnt; cThread++) { + if (jni->IsSameObject(debuggee_threads[cThread].thread, usageInfo.owner)) break; } if (usageInfo.waiters != nullptr) { @@ -160,10 +172,10 @@ static int findDeadlockThreads(jvmtiEnv* jvmti, JNIEnv* jni) { if (usageInfo.notify_waiters != nullptr) { jvmti->Deallocate((unsigned char*)usageInfo.notify_waiters); } - if (!NSK_VERIFY(cThread != threads_count)) + if (!NSK_VERIFY(cThread != debuggee_threads_cnt)) return NSK_FALSE; - if (threadList[cThread].dfn < 0) { - threadList[cThread].dfn = gDfn++; + if (debuggee_threads[cThread].dfn < 0) { + debuggee_threads[cThread].dfn = gDfn++; } else if (cThread == pThread) { break; } else { @@ -179,9 +191,9 @@ static int findDeadlockThreads(jvmtiEnv* jvmti, JNIEnv* jni) { } /* deallocate thread names */ - for (i = 0; i < threads_count; i++) { - if (threadList[i].name != nullptr) { - if (!NSK_JVMTI_VERIFY(jvmti->Deallocate((unsigned char*)threadList[i].name))) + for (i = 0; i < debuggee_threads_cnt; i++) { + if (debuggee_threads[i].name != nullptr) { + if (!NSK_JVMTI_VERIFY(jvmti->Deallocate((unsigned char*)debuggee_threads[i].name))) return NSK_FALSE; } } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t002.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t002.java index f4ca83c0b54..852b915acd7 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2018, 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 @@ -104,7 +104,8 @@ public int runIt(String argv[], PrintStream out) { /* =================================================================== */ class tc03t002Thread extends Thread { - + // The thread name prefix is used to find thread from jvmti agent. + final static String threadNamePrefix = "Debuggee Thread"; static Wicket startingBarrier = new Wicket(3); static Wicket lockingBarrier = new Wicket(3); Wicket waitingBarrier = new Wicket(); @@ -112,7 +113,7 @@ class tc03t002Thread extends Thread { Object lock2; public tc03t002Thread(Object o1, Object o2) { - super("Debuggee Thread " + o1 + o2); + super(threadNamePrefix + " " + o1 + o2); lock1 = o1; lock2 = o2; } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t002/tc03t002.cpp b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t002/tc03t002.cpp index 11c74e3a9e2..5d18d6c23af 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t002/tc03t002.cpp +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/contention/TC03/tc03t002/tc03t002.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2024, 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 @@ -41,10 +41,13 @@ typedef struct { static jlong timeout = 0; /* test objects */ -static threadDesc *threadList = nullptr; -static jint threads_count = 0; +static threadDesc *debuggee_threads = nullptr; +static jint debuggee_threads_cnt = 0; static int numberOfDeadlocks = 0; +static const char* THREAD_NAME_PREFIX = "Debugee Thread"; +static const size_t THREAD_NAME_PREFIX_LEN = strlen(THREAD_NAME_PREFIX); + /* ========================================================================== */ static int printDeadlock(jvmtiEnv* jvmti, JNIEnv* jni, int dThread) { @@ -56,9 +59,9 @@ static int printDeadlock(jvmtiEnv* jvmti, JNIEnv* jni, int dThread) { NSK_DISPLAY1("Found deadlock #%d:\n", numberOfDeadlocks); for (pThread = dThread;;pThread = cThread) { - NSK_DISPLAY1(" \"%s\":\n", threadList[pThread].name); + NSK_DISPLAY1(" \"%s\":\n", debuggee_threads[pThread].name); if (!NSK_JVMTI_VERIFY( - jvmti->GetCurrentContendedMonitor(threadList[pThread].thread, &monitor))) + jvmti->GetCurrentContendedMonitor(debuggee_threads[pThread].thread, &monitor))) return NSK_FALSE; if (monitor != nullptr) { if (!NSK_JNI_VERIFY(jni, (klass = jni->GetObjectClass(monitor)) != nullptr)) @@ -74,8 +77,8 @@ static int printDeadlock(jvmtiEnv* jvmti, JNIEnv* jni, int dThread) { return NSK_FALSE; if (usageInfo.owner == nullptr) break; - for (cThread = 0; cThread < threads_count; cThread++) { - if (jni->IsSameObject(threadList[cThread].thread, usageInfo.owner)) + for (cThread = 0; cThread < debuggee_threads_cnt; cThread++) { + if (jni->IsSameObject(debuggee_threads[cThread].thread, usageInfo.owner)) break; } if (usageInfo.waiters != nullptr) { @@ -84,10 +87,10 @@ static int printDeadlock(jvmtiEnv* jvmti, JNIEnv* jni, int dThread) { if (usageInfo.notify_waiters != nullptr) { jvmti->Deallocate((unsigned char*)usageInfo.notify_waiters); } - if (!NSK_VERIFY(cThread != threads_count)) + if (!NSK_VERIFY(cThread != debuggee_threads_cnt)) return NSK_FALSE; NSK_DISPLAY1(" which is held by \"%s\"\n", - threadList[cThread].name); + debuggee_threads[cThread].name); if (cThread == dThread) break; } @@ -103,8 +106,9 @@ static int findDeadlockThreads(jvmtiEnv* jvmti, JNIEnv* jni) { int tDfn = 0, gDfn = 0; int pThread, cThread; int i; + int threads_count = 0; - NSK_DISPLAY0("Create threadList\n"); + NSK_DISPLAY0("Create debuggee_threads\n"); /* get all live threads */ if (!NSK_JVMTI_VERIFY(jvmti->GetAllThreads(&threads_count, &threads))) @@ -114,7 +118,7 @@ static int findDeadlockThreads(jvmtiEnv* jvmti, JNIEnv* jni) { return NSK_FALSE; if (!NSK_JVMTI_VERIFY( - jvmti->Allocate(threads_count*sizeof(threadDesc), (unsigned char**)&threadList))) + jvmti->Allocate(threads_count*sizeof(threadDesc), (unsigned char**)&debuggee_threads))) return NSK_FALSE; for (i = 0; i < threads_count; i++) { @@ -127,22 +131,31 @@ static int findDeadlockThreads(jvmtiEnv* jvmti, JNIEnv* jni) { NSK_DISPLAY3(" thread #%d (%s): %p\n", i, info.name, threads[i]); - threadList[i].thread = threads[i]; - threadList[i].dfn = -1; - threadList[i].name = info.name; + if (!strncmp(info.name, THREAD_NAME_PREFIX, THREAD_NAME_PREFIX_LEN)) { + NSK_DISPLAY1("Skipping thread %s\n", info.name); + if (!NSK_JVMTI_VERIFY(jvmti->Deallocate((unsigned char*)info.name))) + return NSK_FALSE; + continue; + } + + debuggee_threads[debuggee_threads_cnt].thread = threads[i]; + debuggee_threads[debuggee_threads_cnt].dfn = -1; + debuggee_threads[debuggee_threads_cnt].name = info.name; + debuggee_threads_cnt++; } /* deallocate thread list */ if (!NSK_JVMTI_VERIFY(jvmti->Deallocate((unsigned char*)threads))) return NSK_FALSE; - for (i = 0; i < threads_count; i++) { - if (threadList[i].dfn < 0) { + for (i = 0; i < debuggee_threads_cnt; i++) { + + if (debuggee_threads[i].dfn < 0) { tDfn = gDfn; - threadList[i].dfn = gDfn++; + debuggee_threads[i].dfn = gDfn++; for (pThread = i;;pThread = cThread) { if (!NSK_JVMTI_VERIFY( - jvmti->GetCurrentContendedMonitor(threadList[pThread].thread, &monitor))) + jvmti->GetCurrentContendedMonitor(debuggee_threads[pThread].thread, &monitor))) return NSK_FALSE; if (monitor == nullptr) break; @@ -150,8 +163,8 @@ static int findDeadlockThreads(jvmtiEnv* jvmti, JNIEnv* jni) { return NSK_FALSE; if (usageInfo.owner == nullptr) break; - for (cThread = 0; cThread < threads_count; cThread++) { - if (jni->IsSameObject(threadList[cThread].thread, usageInfo.owner)) + for (cThread = 0; cThread < debuggee_threads_cnt; cThread++) { + if (jni->IsSameObject(debuggee_threads[cThread].thread, usageInfo.owner)) break; } if (usageInfo.waiters != nullptr) { @@ -160,10 +173,10 @@ static int findDeadlockThreads(jvmtiEnv* jvmti, JNIEnv* jni) { if (usageInfo.notify_waiters != nullptr) { jvmti->Deallocate((unsigned char*)usageInfo.notify_waiters); } - if (!NSK_VERIFY(cThread != threads_count)) + if (!NSK_VERIFY(cThread != debuggee_threads_cnt)) return NSK_FALSE; - if (threadList[cThread].dfn < 0) { - threadList[cThread].dfn = gDfn++; + if (debuggee_threads[cThread].dfn < 0) { + debuggee_threads[cThread].dfn = gDfn++; } else if (cThread == pThread) { break; } else { @@ -179,9 +192,9 @@ static int findDeadlockThreads(jvmtiEnv* jvmti, JNIEnv* jni) { } /* deallocate thread names */ - for (i = 0; i < threads_count; i++) { - if (threadList[i].name != nullptr) { - if (!NSK_JVMTI_VERIFY(jvmti->Deallocate((unsigned char*)threadList[i].name))) + for (i = 0; i < debuggee_threads_cnt; i++) { + if (debuggee_threads[i].name != nullptr) { + if (!NSK_JVMTI_VERIFY(jvmti->Deallocate((unsigned char*)debuggee_threads[i].name))) return NSK_FALSE; } } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java b/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java index 5ea01fb3af4..e3b6693657c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, 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 @@ -28,7 +28,7 @@ package nsk.share; -import java.lang.ref.Cleaner; +import java.lang.ref.PhantomReference; import java.util.*; import nsk.share.gc.gp.*; import nsk.share.test.ExecutionController; @@ -77,19 +77,9 @@ public class ClassUnloader { public static final String INTERNAL_CLASS_LOADER_NAME = "nsk.share.CustomClassLoader"; /** - * Whole amount of time in milliseconds to wait for class loader to be reclaimed. + * Phantom reference to the class loader. */ - private static final int WAIT_TIMEOUT = 15000; - - /** - * Sleep time in milliseconds for the loop waiting for the class loader to be reclaimed. - */ - private static final int WAIT_DELTA = 1000; - - /** - * Has class loader been reclaimed or not. - */ - volatile boolean is_reclaimed = false; + private PhantomReference customClassLoaderPhantomRef = null; /** * Current class loader used for loading classes. @@ -101,6 +91,14 @@ public class ClassUnloader { */ private Vector> classObjects = new Vector>(); + /** + * Has class loader been reclaimed or not. + */ + private boolean isClassLoaderReclaimed() { + return customClassLoaderPhantomRef != null + && customClassLoaderPhantomRef.refersTo(null); + } + /** * Class object of the first class been loaded with current class loader. * To get the rest loaded classes use getLoadedClass(int). @@ -138,8 +136,7 @@ public CustomClassLoader createClassLoader() { customClassLoader = new CustomClassLoader(); classObjects.removeAllElements(); - // Register a Cleaner to inform us when the class loader has been reclaimed. - Cleaner.create().register(customClassLoader, () -> { is_reclaimed = true; } ); + customClassLoaderPhantomRef = new PhantomReference<>(customClassLoader, null); return customClassLoader; } @@ -154,8 +151,7 @@ public void setClassLoader(CustomClassLoader customClassLoader) { this.customClassLoader = customClassLoader; classObjects.removeAllElements(); - // Register a Cleaner to inform us when the class loader has been reclaimed. - Cleaner.create().register(customClassLoader, () -> { is_reclaimed = true; } ); + customClassLoaderPhantomRef = new PhantomReference<>(customClassLoader, null); } /** @@ -244,32 +240,15 @@ public void loadClass(String className, String classDir) throws ClassNotFoundExc */ public boolean unloadClass(ExecutionController stresser) { - is_reclaimed = false; - // free references to class and class loader to be able for collecting by GC - long waitTimeout = (customClassLoader == null) ? 0 : WAIT_TIMEOUT; classObjects.removeAllElements(); customClassLoader = null; // force class unloading by eating memory pool eatMemory(stresser); - // give GC chance to run and wait for receiving reclaim notification - long timeToFinish = System.currentTimeMillis() + waitTimeout; - while (!is_reclaimed && System.currentTimeMillis() < timeToFinish) { - if (!stresser.continueExecution()) { - return false; - } - try { - // suspend thread for a while - Thread.sleep(WAIT_DELTA); - } catch (InterruptedException e) { - throw new Failure("Unexpected InterruptedException while class unloading: " + e); - } - } - // force GC to unload marked class loader and its classes - if (is_reclaimed) { + if (isClassLoaderReclaimed()) { Runtime.getRuntime().gc(); return true; } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/AbstractDebuggeeTest.java b/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/AbstractDebuggeeTest.java index 0668297d211..74516fd5a69 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/AbstractDebuggeeTest.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/AbstractDebuggeeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -149,19 +149,11 @@ public void loadTestClass(String className) { } } - public static final int MAX_UNLOAD_ATTEMPS = 5; - public void unloadTestClass(String className, boolean expectedUnloadingResult) { ClassUnloader classUnloader = loadedClasses.get(className); - int unloadAttemps = 0; - if (classUnloader != null) { - boolean wasUnloaded = false; - - while (!wasUnloaded && (unloadAttemps++ < MAX_UNLOAD_ATTEMPS)) { - wasUnloaded = classUnloader.unloadClass(); - } + boolean wasUnloaded = classUnloader.unloadClass(); if (wasUnloaded) loadedClasses.remove(className); @@ -183,13 +175,6 @@ public void unloadTestClass(String className, boolean expectedUnloadingResult) { } } - static public void sleep1sec() { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - } - } - private StateTestThread stateTestThread; public static final String COMMAND_QUIT = "quit"; @@ -362,9 +347,6 @@ public void forceGC() { eatMemory(); } - public void voidValueMethod() { - } - public void unexpectedException(Throwable t) { setSuccess(false); t.printStackTrace(log.getOutStream()); diff --git a/test/jdk/ProblemList-Virtual.txt b/test/jdk/ProblemList-Virtual.txt index d4994a9375d..280a25c9c16 100644 --- a/test/jdk/ProblemList-Virtual.txt +++ b/test/jdk/ProblemList-Virtual.txt @@ -30,8 +30,6 @@ com/sun/jdi/EATests.java#id0 8264699 generic- com/sun/jdi/ExceptionEvents.java 8278470 generic-all com/sun/jdi/RedefineCrossStart.java 8278470 generic-all -javax/management/remote/mandatory/loading/MissingClassTest.java 8145413 windows-x64 - java/lang/ScopedValue/StressStackOverflow.java#default 8309646 generic-all java/lang/ScopedValue/StressStackOverflow.java#no-TieredCompilation 8309646 generic-all java/lang/ScopedValue/StressStackOverflow.java#TieredStopAtLevel1 8309646 generic-all @@ -40,28 +38,7 @@ javax/management/remote/mandatory/connection/DeadLockTest.java 8309069 windows-x javax/management/remote/mandatory/connection/ConnectionTest.java 8308352 windows-x64 -########## -## Tests incompatible with virtual test thread factory. -## There is no goal to run all test with virtual test thread factory. -## So any test might be added as incompatible, the bug id is not required. - -# Incorrect stack/threadgroup/exception expectations for main thread -java/lang/StackWalker/DumpStackTest.java 0000000 generic-all -java/lang/StackWalker/StackWalkTest.java 0000000 generic-all -java/lang/StackWalker/CallerFromMain.java 0000000 generic-all -java/lang/Thread/MainThreadTest.java 0000000 generic-all -java/lang/Thread/UncaughtExceptionsTest.java 0000000 generic-all -java/lang/invoke/condy/CondyNestedResolutionTest.java 0000000 generic-all -java/lang/ref/OOMEInReferenceHandler.java 0000000 generic-all -java/util/concurrent/locks/Lock/OOMEInAQS.java 0000000 generic-all -jdk/internal/vm/Continuation/Scoped.java 0000000 generic-all - -# The problems with permissions -jdk/jfr/startupargs/TestDumpOnExit.java 0000000 generic-all -java/util/Properties/StoreReproducibilityTest.java 0000000 generic-all -javax/management/ImplementationVersion/ImplVersionTest.java 0000000 generic-all -javax/management/remote/mandatory/version/ImplVersionTest.java 0000000 generic-all # Valhalla -java/lang/Thread/virtual/stress/PingPong.java 8314996 macosx-all +java/lang/Thread/virtual/stress/PingPong.java 8342977 generic-all java/lang/Thread/virtual/stress/Skynet.java 8342977 generic-all diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 3e6a1cf8038..4ebe10f5c8f 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -684,7 +684,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 @@ -763,6 +762,7 @@ jdk/incubator/vector/LoadJsvmlTest.java 8305390 windows- jdk/jfr/event/compiler/TestCodeSweeper.java 8338127 generic-all jdk/jfr/event/oldobject/TestShenandoah.java 8342951 generic-all jdk/jfr/event/runtime/TestResidentSetSizeEvent.java 8309846 aix-ppc64 +jdk/jfr/jvm/TestWaste.java 8369949 generic-all ############################################################################ 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 00000000000..f098be4fdbd --- /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/com/sun/jdi/valhalla/FieldWatchpointsTest.java b/test/jdk/com/sun/jdi/valhalla/FieldWatchpointsTest.java index 2cacbda7b63..60f3676a342 100644 --- a/test/jdk/com/sun/jdi/valhalla/FieldWatchpointsTest.java +++ b/test/jdk/com/sun/jdi/valhalla/FieldWatchpointsTest.java @@ -45,7 +45,7 @@ import java.util.ArrayList; import java.util.List; -class FieldWatchpointsTarg { +value class FieldWatchpointsTarg { static value class Value { int v; Value() { @@ -60,12 +60,17 @@ public int getValue() { } static Value staticField = new Value(1); + // flattened field + Value instanceField = new Value(2); public static void main(String[] args) { System.out.println(">>Targ.main"); - Value obj = new Value(2); // modify - System.out.println("obj value = " + obj.v); // access - System.out.println("staticField value = " + staticField.v); // access + // modify FieldWatchpointsTarg.instanceField and FieldWatchpointsTarg.instanceField.v + FieldWatchpointsTarg targ = new FieldWatchpointsTarg(); + // access FieldWatchpointsTarg.instanceField and FieldWatchpointsTarg.instanceField.v + System.out.println("obj value = " + targ.instanceField.v); + // access FieldWatchpointsTarg.staticField and FieldWatchpointsTarg.staticField.v + System.out.println("staticField value = " + staticField.v); System.out.println("< arrays) throws Exception { - for (ArrayReference arr1: arrays) { - for (ArrayReference arr2: arrays) { - if (!arraysEquals(arr1, arr2)) { - System.out.println("Arrays are different (1):" - + "\n - " + arrayToString(arr1) - + "\n - " + arrayToString(arr2)); - throw new RuntimeException("Arrays are different"); - } + // Compare 1st and 2nd, 2nd and 3rd, etc. + for (int i = 1; i < arrays.size(); i++) { // start from 1 + ArrayReference arr1 = arrays.get(i - 1); + ArrayReference arr2 = arrays.get(i); + if (!arraysEquals(arr1, arr2)) { + System.out.println("Arrays are different (" + (i - 1) + " and " + i + "):" + + "\n - " + arrayToString(arr1) + + "\n - " + arrayToString(arr2)); + throw new RuntimeException("Arrays are different"); } } } diff --git a/test/jdk/com/sun/jdi/valhalla/ValueClassTypeTest.java b/test/jdk/com/sun/jdi/valhalla/ValueClassTypeTest.java index 9a8ccbc8d4a..26b2b27b7e6 100644 --- a/test/jdk/com/sun/jdi/valhalla/ValueClassTypeTest.java +++ b/test/jdk/com/sun/jdi/valhalla/ValueClassTypeTest.java @@ -98,31 +98,22 @@ protected void runTests() throws Exception { ObjectReference value1 = (ObjectReference)testClass.getValue(testField); ClassType valueClass = (ClassType)value1.type(); - Field valueField = valueClass.fieldByName("staticField"); - ObjectReference value2 = (ObjectReference)valueClass.getValue(valueField); Method valueCtor = valueClass.concreteMethodByName("", "(I)V"); ObjectReference newValue1 = valueClass.newInstance(mainThread, valueCtor, List.of(vm().mirrorOf(10)), 0); - ObjectReference newValue2 = valueClass.newInstance(mainThread, valueCtor, List.of(vm().mirrorOf(11)), 0); // sanity check for enableCollection/disableCollection newValue1.disableCollection(); - newValue2.disableCollection(); testClass.setValue(testField, newValue1); - valueClass.setValue(valueField, newValue2); ObjectReference updatedValue1 = (ObjectReference)testClass.getValue(testField); - ObjectReference updatedValue2 = (ObjectReference)valueClass.getValue(valueField); assertEqual(updatedValue1, newValue1); assertNotEqual(value1, newValue1); - assertEqual(updatedValue2, newValue2); - assertNotEqual(value2, newValue2); // sanity check for enableCollection/disableCollection newValue1.enableCollection(); - newValue2.enableCollection(); } finally { // Resume the target until end listenUntilVMDisconnect(); diff --git a/test/jdk/java/awt/PrintJob/GetGraphicsTest.java b/test/jdk/java/awt/PrintJob/GetGraphicsTest.java index 61abe37b66b..2fe82e57c5a 100644 --- a/test/jdk/java/awt/PrintJob/GetGraphicsTest.java +++ b/test/jdk/java/awt/PrintJob/GetGraphicsTest.java @@ -23,7 +23,7 @@ /* @test - @bug 50510568367702 + @bug 5051056 8367702 @key headful printer @summary PrintJob.getGraphics() should return null after PrintJob.end() is called. @run main GetGraphicsTest diff --git a/test/jdk/java/awt/Toolkit/ScreenInsetsTest/ScreenInsetsTest.java b/test/jdk/java/awt/Toolkit/ScreenInsetsTest/ScreenInsetsTest.java index 2cd05b08fc5..207954c521b 100644 --- a/test/jdk/java/awt/Toolkit/ScreenInsetsTest/ScreenInsetsTest.java +++ b/test/jdk/java/awt/Toolkit/ScreenInsetsTest/ScreenInsetsTest.java @@ -24,7 +24,7 @@ /* * @test * @key headful - * @bug 8020443 6899304 4737732 + * @bug 8020443 6899304 4737732 8357390 * @summary Tests that Toolkit.getScreenInsets() returns correct insets * @library /test/lib * @build jdk.test.lib.Platform @@ -44,7 +44,7 @@ public class ScreenInsetsTest { private static final int SIZE = 100; // Allow a margin tolerance of 1 pixel due to scaling - private static final int MARGIN_TOLERANCE = 1; + private static final int MARGIN_TOLERANCE = 2; public static void main(String[] args) throws InterruptedException { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 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 00000000000..bb8d7a0ab88 --- /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/font/NumericShaper/NSEqualsTest.java b/test/jdk/java/awt/font/NumericShaper/NSEqualsTest.java new file mode 100644 index 00000000000..6af73b7efa2 --- /dev/null +++ b/test/jdk/java/awt/font/NumericShaper/NSEqualsTest.java @@ -0,0 +1,84 @@ +/* + * 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 8365077 + * @summary confirm that an instance which is created with Enum ranges is + * equal to another instance which is created with equivalent traditional + * ranges, and that in such a case the hashCodes are also equal. + */ + +import java.awt.font.NumericShaper; +import java.awt.font.NumericShaper.Range; +import static java.awt.font.NumericShaper.Range.*; +import java.util.EnumSet; + +public class NSEqualsTest { + + public static void main(String[] args) { + + for (Range r1 : Range.values()) { + test(r1); + for (Range r2 : Range.values()) { + test(r1, r2); + } + } + } + + static void test(Range r) { + if (r.ordinal() > MONGOLIAN.ordinal()) { + return; + } + int o = 1 << r.ordinal(); + NumericShaper nsr = NumericShaper.getContextualShaper(EnumSet.of(r)); + NumericShaper nso = NumericShaper.getContextualShaper(o); + printAndCompare(nsr, nso); + } + + static void test(Range r1, Range r2) { + if (r1.ordinal() > MONGOLIAN.ordinal() || r2.ordinal() > MONGOLIAN.ordinal()) { + return; + } + int o1 = 1 << r1.ordinal(); + int o2 = 1 << r2.ordinal(); + + NumericShaper nsr = NumericShaper.getContextualShaper(EnumSet.of(r1, r2)); + NumericShaper nso = NumericShaper.getContextualShaper(o1 | o2); + printAndCompare(nsr, nso); + } + + static void printAndCompare(NumericShaper nsr, NumericShaper nso) { + System.err.println(nsr); + System.err.println(nso); + System.err.println(nsr.hashCode() + " vs " + nso.hashCode() + + " equal: " + nsr.equals(nso)); + if (!nsr.equals(nso)) { + throw new RuntimeException("Expected equal"); + } + if (nsr.hashCode() != nso.hashCode()) { + throw new RuntimeException("Different hash codes:"); + } + } +} + diff --git a/test/jdk/java/io/ByteArrayOutputStream/WriteToReleasesCarrier.java b/test/jdk/java/io/ByteArrayOutputStream/WriteToReleasesCarrier.java index c0607fd9494..a0c13f24c7c 100644 --- a/test/jdk/java/io/ByteArrayOutputStream/WriteToReleasesCarrier.java +++ b/test/jdk/java/io/ByteArrayOutputStream/WriteToReleasesCarrier.java @@ -1,5 +1,5 @@ /* - * 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 @@ -27,6 +27,7 @@ * @summary Test ByteArrayOutputStream.writeTo releases carrier thread * @requires vm.continuations * @modules java.base/java.lang:+open + * @library /test/lib * @run main WriteToReleasesCarrier */ @@ -34,14 +35,14 @@ import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; -import java.lang.reflect.Constructor; import java.util.Arrays; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.LockSupport; +import jdk.test.lib.thread.VThreadScheduler; public class WriteToReleasesCarrier { public static void main(String[] args) throws Exception { @@ -53,7 +54,7 @@ public static void main(String[] args) throws Exception { var target = new ParkingOutputStream(); try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { - Thread.Builder builder = virtualThreadBuilder(scheduler); + Thread.Builder builder = VThreadScheduler.virtualThreadBuilder(scheduler); var started = new CountDownLatch(1); var vthread1 = builder.start(() -> { started.countDown(); @@ -118,14 +119,4 @@ byte[] toByteArray() { return baos.toByteArray(); } } - - /** - * Returns a builder to create virtual threads that use the given scheduler. - */ - static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) throws Exception { - Class clazz = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder"); - Constructor ctor = clazz.getDeclaredConstructor(Executor.class); - ctor.setAccessible(true); - return (Thread.Builder.OfVirtual) ctor.newInstance(scheduler); - } } diff --git a/test/jdk/java/io/Serializable/cloneArray/CloneArray.java b/test/jdk/java/io/Serializable/cloneArray/CloneArray.java index a8de4407912..0a427e29da5 100644 --- a/test/jdk/java/io/Serializable/cloneArray/CloneArray.java +++ b/test/jdk/java/io/Serializable/cloneArray/CloneArray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2019, 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 @@ -25,7 +25,6 @@ /* @test * @bug 6990094 * @summary Verify ObjectInputStream.cloneArray works on many kinds of arrays - * @author Stuart Marks, Joseph D. Darcy */ import java.io.ByteArrayInputStream; diff --git a/test/jdk/java/lang/Byte/Decode.java b/test/jdk/java/lang/Byte/Decode.java index b4ef798cb7e..590c35989f7 100644 --- a/test/jdk/java/lang/Byte/Decode.java +++ b/test/jdk/java/lang/Byte/Decode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2007, 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 @@ -25,8 +25,6 @@ * @test * @bug 4242173 5017980 6576055 * @summary Test Byte.decode method - * @author madbot - * @author Joseph D. Darcy */ /** diff --git a/test/jdk/java/lang/Class/IsAnnotationType.java b/test/jdk/java/lang/Class/IsAnnotationType.java index 2189e1c5715..e007201d95c 100644 --- a/test/jdk/java/lang/Class/IsAnnotationType.java +++ b/test/jdk/java/lang/Class/IsAnnotationType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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,7 +25,6 @@ * @test * @bug 4891872 4988155 * @summary Check isAnnotation() method - * @author Joseph D. Darcy */ import java.lang.annotation.*; diff --git a/test/jdk/java/lang/Class/IsEnum.java b/test/jdk/java/lang/Class/IsEnum.java index fc0b0f7632b..3d2d9103ddb 100644 --- a/test/jdk/java/lang/Class/IsEnum.java +++ b/test/jdk/java/lang/Class/IsEnum.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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,7 +25,6 @@ * @test * @bug 4891872 4989735 4990789 5020490 * @summary Check isEnum() method - * @author Joseph D. Darcy */ import java.lang.annotation.*; diff --git a/test/jdk/java/lang/Class/IsSynthetic.java b/test/jdk/java/lang/Class/IsSynthetic.java index d594f6303e1..1775b6bc11d 100644 --- a/test/jdk/java/lang/Class/IsSynthetic.java +++ b/test/jdk/java/lang/Class/IsSynthetic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 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 @@ -25,7 +25,6 @@ * @test * @bug 5012133 * @summary Check Class.isSynthetic method - * @author Joseph D. Darcy */ import java.lang.reflect.*; diff --git a/test/jdk/java/lang/Class/getEnclosingConstructor/EnclosingConstructorTests.java b/test/jdk/java/lang/Class/getEnclosingConstructor/EnclosingConstructorTests.java index ca128021e0c..c08f33af1e6 100644 --- a/test/jdk/java/lang/Class/getEnclosingConstructor/EnclosingConstructorTests.java +++ b/test/jdk/java/lang/Class/getEnclosingConstructor/EnclosingConstructorTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 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 @@ -25,7 +25,6 @@ * @test * @bug 4962341 6832557 * @summary Check getEnclosingMethod method - * @author Joseph D. Darcy */ import java.lang.reflect.Constructor; diff --git a/test/jdk/java/lang/Class/getEnclosingMethod/EnclosingMethodTests.java b/test/jdk/java/lang/Class/getEnclosingMethod/EnclosingMethodTests.java index 3a2a0910fc7..e890bb75d1e 100644 --- a/test/jdk/java/lang/Class/getEnclosingMethod/EnclosingMethodTests.java +++ b/test/jdk/java/lang/Class/getEnclosingMethod/EnclosingMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 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 @@ -25,7 +25,6 @@ * @test * @bug 4962341 * @summary Check getEnclosingMethod method - * @author Joseph D. Darcy */ import java.lang.reflect.Method; diff --git a/test/jdk/java/lang/Double/BitwiseConversion.java b/test/jdk/java/lang/Double/BitwiseConversion.java index 381a7e9aed6..06e15d95575 100644 --- a/test/jdk/java/lang/Double/BitwiseConversion.java +++ b/test/jdk/java/lang/Double/BitwiseConversion.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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,7 +28,6 @@ * @library ../Math * @build DoubleConsts * @run main BitwiseConversion - * @author Joseph D. Darcy */ import static java.lang.Double.*; diff --git a/test/jdk/java/lang/Double/Constants.java b/test/jdk/java/lang/Double/Constants.java index 676630e12b9..e7aa5cbceb9 100644 --- a/test/jdk/java/lang/Double/Constants.java +++ b/test/jdk/java/lang/Double/Constants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2005, 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 @@ -26,7 +26,6 @@ * @compile Constants.java * @bug 4397405 4826652 * @summary Testing constant-ness of Double.{MIN_VALUE, MAX_VALUE}, etc. - * @author Joseph D. Darcy */ public class Constants { diff --git a/test/jdk/java/lang/Double/Extrema.java b/test/jdk/java/lang/Double/Extrema.java index fef17100e0a..db9a10fb279 100644 --- a/test/jdk/java/lang/Double/Extrema.java +++ b/test/jdk/java/lang/Double/Extrema.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2005, 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,7 +25,6 @@ * @test * @bug 4408489 4826652 * @summary Testing values of Double.{MIN_VALUE, MIN_NORMAL, MAX_VALUE} - * @author Joseph D. Darcy */ public class Extrema { diff --git a/test/jdk/java/lang/Double/NaNInfinityParsing.java b/test/jdk/java/lang/Double/NaNInfinityParsing.java index 846dcecd3b9..8b5d0e4888f 100644 --- a/test/jdk/java/lang/Double/NaNInfinityParsing.java +++ b/test/jdk/java/lang/Double/NaNInfinityParsing.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,7 +25,6 @@ * @test * @bug 4428772 * @summary Testing recognition of "NaN" and "Infinity" strings - * @author Joseph D. Darcy */ diff --git a/test/jdk/java/lang/Double/ParseHexFloatingPoint.java b/test/jdk/java/lang/Double/ParseHexFloatingPoint.java index a26a8b7a756..60fa13df75b 100644 --- a/test/jdk/java/lang/Double/ParseHexFloatingPoint.java +++ b/test/jdk/java/lang/Double/ParseHexFloatingPoint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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,7 +28,6 @@ * @run main ParseHexFloatingPoint * @bug 4826774 8078672 * @summary Numerical tests for hexadecimal inputs to parse{Double, Float} (use -Dseed=X to set PRNG seed) - * @author Joseph D. Darcy * @key randomness */ diff --git a/test/jdk/java/lang/Double/ToHexString.java b/test/jdk/java/lang/Double/ToHexString.java index a9f07bba508..912835b7aeb 100644 --- a/test/jdk/java/lang/Double/ToHexString.java +++ b/test/jdk/java/lang/Double/ToHexString.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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,7 +28,6 @@ * @library ../Math * @build DoubleConsts * @run main ToHexString - * @author Joseph D. Darcy */ import java.util.regex.*; diff --git a/test/jdk/java/lang/Float/BitwiseConversion.java b/test/jdk/java/lang/Float/BitwiseConversion.java index 973e00e7008..9bb4a34a55b 100644 --- a/test/jdk/java/lang/Float/BitwiseConversion.java +++ b/test/jdk/java/lang/Float/BitwiseConversion.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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,7 +28,6 @@ * @library ../Math * @build FloatConsts * @run main BitwiseConversion - * @author Joseph D. Darcy */ import static java.lang.Float.*; diff --git a/test/jdk/java/lang/Float/Constants.java b/test/jdk/java/lang/Float/Constants.java index b6ad85ca0c2..47463fc5916 100644 --- a/test/jdk/java/lang/Float/Constants.java +++ b/test/jdk/java/lang/Float/Constants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2005, 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 @@ -26,7 +26,6 @@ * @compile Constants.java * @bug 4397405 4826652 * @summary Testing constant-ness of Float.{MIN_VALUE, MAX_VALUE}, etc. - * @author Joseph D. Darcy */ public class Constants { diff --git a/test/jdk/java/lang/Float/Extrema.java b/test/jdk/java/lang/Float/Extrema.java index 869f22072e7..46447bfcb2f 100644 --- a/test/jdk/java/lang/Float/Extrema.java +++ b/test/jdk/java/lang/Float/Extrema.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2005, 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,7 +25,6 @@ * @test * @bug 4408489 4826652 * @summary Testing values of Float.{MIN_VALUE, MIN_NORMAL, MAX_VALUE} - * @author Joseph D. Darcy */ public class Extrema { diff --git a/test/jdk/java/lang/Float/NaNInfinityParsing.java b/test/jdk/java/lang/Float/NaNInfinityParsing.java index 38a402a9775..6df42b376a5 100644 --- a/test/jdk/java/lang/Float/NaNInfinityParsing.java +++ b/test/jdk/java/lang/Float/NaNInfinityParsing.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,7 +25,6 @@ * @test * @bug 4428772 * @summary Testing recognition of "NaN" and "Infinity" strings - * @author Joseph D. Darcy */ diff --git a/test/jdk/java/lang/Integer/Decode.java b/test/jdk/java/lang/Integer/Decode.java index 42493221fe4..b5f881a0e9f 100644 --- a/test/jdk/java/lang/Integer/Decode.java +++ b/test/jdk/java/lang/Integer/Decode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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,6 @@ * @test * @bug 4136371 5017980 6576055 * @summary Test Integer.decode method - * @author madbot - * @author Joseph D. Darcy */ /** diff --git a/test/jdk/java/lang/Integer/ParsingTest.java b/test/jdk/java/lang/Integer/ParsingTest.java index f5f64f70c84..24e1dd6f3aa 100644 --- a/test/jdk/java/lang/Integer/ParsingTest.java +++ b/test/jdk/java/lang/Integer/ParsingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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,7 +25,6 @@ * @test * @bug 5017980 6576055 8041972 8055251 * @summary Test parsing methods - * @author Joseph D. Darcy */ import java.lang.IndexOutOfBoundsException; diff --git a/test/jdk/java/lang/Integer/Unsigned.java b/test/jdk/java/lang/Integer/Unsigned.java index 6c3aecc70c1..911f3f8fe8f 100644 --- a/test/jdk/java/lang/Integer/Unsigned.java +++ b/test/jdk/java/lang/Integer/Unsigned.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 @@ -25,7 +25,6 @@ * @test * @bug 4504839 4215269 6322074 * @summary Basic tests for unsigned operations. - * @author Joseph D. Darcy */ public class Unsigned { public static void main(String... args) { diff --git a/test/jdk/java/lang/Long/Decode.java b/test/jdk/java/lang/Long/Decode.java index d47f976e6fc..fc2762b1bc2 100644 --- a/test/jdk/java/lang/Long/Decode.java +++ b/test/jdk/java/lang/Long/Decode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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,6 @@ * @test * @bug 4136371 5017980 6576055 * @summary Test Long.decode method - * @author madbot - * @author Joseph D. Darcy */ import java.math.BigInteger; diff --git a/test/jdk/java/lang/Long/ParsingTest.java b/test/jdk/java/lang/Long/ParsingTest.java index cbf83415d36..23d007dfd26 100644 --- a/test/jdk/java/lang/Long/ParsingTest.java +++ b/test/jdk/java/lang/Long/ParsingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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,7 +25,6 @@ * @test * @bug 5017980 6576055 8041972 8055251 * @summary Test parsing methods - * @author Joseph D. Darcy */ /** diff --git a/test/jdk/java/lang/Long/Unsigned.java b/test/jdk/java/lang/Long/Unsigned.java index f6eeed534b8..aba66c72a92 100644 --- a/test/jdk/java/lang/Long/Unsigned.java +++ b/test/jdk/java/lang/Long/Unsigned.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2020, 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 @@ -25,7 +25,6 @@ * @test * @bug 4504839 4215269 6322074 8030814 * @summary Basic tests for unsigned operations - * @author Joseph D. Darcy */ import java.math.*; diff --git a/test/jdk/java/lang/Short/Decode.java b/test/jdk/java/lang/Short/Decode.java index 8f76751b589..d63e911b710 100644 --- a/test/jdk/java/lang/Short/Decode.java +++ b/test/jdk/java/lang/Short/Decode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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,6 @@ * @test * @bug 4136371 5017980 6576055 * @summary Test Short.decode method - * @author madbot - * @author Joseph D. Darcy */ /** diff --git a/test/jdk/java/lang/StackWalker/CallerFromMain.java b/test/jdk/java/lang/StackWalker/CallerFromMain.java index 86e7cfb7043..81456b02c36 100644 --- a/test/jdk/java/lang/StackWalker/CallerFromMain.java +++ b/test/jdk/java/lang/StackWalker/CallerFromMain.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,8 +24,9 @@ /* * @test * @bug 8140450 - * @library /test/lib * @summary Test if the getCallerClass method returns empty optional + * @requires test.thread.factory == null + * @library /test/lib * @run main CallerFromMain exec */ diff --git a/test/jdk/java/lang/StackWalker/DumpStackTest.java b/test/jdk/java/lang/StackWalker/DumpStackTest.java index c196b1c4ad3..1365e39ccfa 100644 --- a/test/jdk/java/lang/StackWalker/DumpStackTest.java +++ b/test/jdk/java/lang/StackWalker/DumpStackTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @summary Verify outputs of Thread.dumpStack() and Throwable.printStackTrace(). * This test should also been run against jdk9 successfully except of * VM option MemberNameInStackFrame. + * @requires test.thread.factory == null * @run main/othervm DumpStackTest */ diff --git a/test/jdk/java/lang/StackWalker/StackWalkTest.java b/test/jdk/java/lang/StackWalker/StackWalkTest.java index 80e934d477c..8848f323cab 100644 --- a/test/jdk/java/lang/StackWalker/StackWalkTest.java +++ b/test/jdk/java/lang/StackWalker/StackWalkTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,7 @@ * @test * @bug 8140450 * @summary Stack Walk Test (use -Dseed=X to set PRNG seed) + * @requires test.thread.factory == null * @library /test/lib * @build jdk.test.lib.RandomFactory * @compile StackRecorderUtil.java diff --git a/test/jdk/java/lang/Thread/MainThreadTest.java b/test/jdk/java/lang/Thread/MainThreadTest.java index 2482a8aa6d2..7129170ad4e 100644 --- a/test/jdk/java/lang/Thread/MainThreadTest.java +++ b/test/jdk/java/lang/Thread/MainThreadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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,6 +25,7 @@ * @test * @bug 4533087 * @summary Test to see if the main thread is in its thread group + * @requires test.thread.factory == null */ public class MainThreadTest { diff --git a/test/jdk/java/lang/Thread/UncaughtExceptionsTest.java b/test/jdk/java/lang/Thread/UncaughtExceptionsTest.java index 915d1cb6b76..1bb810d372f 100644 --- a/test/jdk/java/lang/Thread/UncaughtExceptionsTest.java +++ b/test/jdk/java/lang/Thread/UncaughtExceptionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2024, 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 @@ -36,6 +36,7 @@ * @bug 4833089 4992454 * @summary Check for proper handling of uncaught exceptions * @author Martin Buchholz + * @requires test.thread.factory == null * @library /test/lib * @build jdk.test.lib.process.* * @run junit UncaughtExceptionsTest diff --git a/test/jdk/java/lang/Throwable/SuppressedExceptions.java b/test/jdk/java/lang/Throwable/SuppressedExceptions.java index f6fe09df4ef..157a4ffa99b 100644 --- a/test/jdk/java/lang/Throwable/SuppressedExceptions.java +++ b/test/jdk/java/lang/Throwable/SuppressedExceptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013, 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 @@ -28,7 +28,6 @@ * @test * @bug 6911258 6962571 6963622 6991528 7005628 8012044 * @summary Basic tests of suppressed exceptions - * @author Joseph D. Darcy */ public class SuppressedExceptions { diff --git a/test/jdk/java/lang/annotation/Missing/MissingTest.java b/test/jdk/java/lang/annotation/Missing/MissingTest.java index d8a1c44e7ee..10e3b436d12 100644 --- a/test/jdk/java/lang/annotation/Missing/MissingTest.java +++ b/test/jdk/java/lang/annotation/Missing/MissingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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,7 +25,6 @@ * @test * @bug 6322301 5041778 * @summary Verify when missing annotation classes cause exceptions - * @author Joseph D. Darcy * @compile MissingTest.java A.java B.java C.java D.java Marker.java Missing.java MissingWrapper.java MissingDefault.java * @clean Missing * @run main MissingTest diff --git a/test/jdk/java/lang/annotation/TestIncompleteAnnotationExceptionNPE.java b/test/jdk/java/lang/annotation/TestIncompleteAnnotationExceptionNPE.java index 9e9e7a7a60a..390f0fc96bd 100644 --- a/test/jdk/java/lang/annotation/TestIncompleteAnnotationExceptionNPE.java +++ b/test/jdk/java/lang/annotation/TestIncompleteAnnotationExceptionNPE.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 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,7 +25,6 @@ * @test * @bug 7021922 * @summary Test null handling of IncompleteAnnotationException constructor - * @author Joseph D. Darcy */ import java.lang.annotation.*; diff --git a/test/jdk/java/lang/module/customfs/ModulesInCustomFileSystem.java b/test/jdk/java/lang/module/customfs/ModulesInCustomFileSystem.java index 801f2e5fca6..e023a3a5839 100644 --- a/test/jdk/java/lang/module/customfs/ModulesInCustomFileSystem.java +++ b/test/jdk/java/lang/module/customfs/ModulesInCustomFileSystem.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, 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 @@ -28,7 +28,7 @@ * @library /test/lib * @build ModulesInCustomFileSystem m1/* m2/* * jdk.test.lib.util.JarUtils - * @run testng/othervm ModulesInCustomFileSystem + * @run testng/othervm -Djava.io.tmpdir=. ModulesInCustomFileSystem * @summary Test ModuleFinder to find modules in a custom file system */ diff --git a/test/jdk/java/lang/ref/OOMEInReferenceHandler.java b/test/jdk/java/lang/ref/OOMEInReferenceHandler.java index 2f80fa6178a..9f0a4ea378e 100644 --- a/test/jdk/java/lang/ref/OOMEInReferenceHandler.java +++ b/test/jdk/java/lang/ref/OOMEInReferenceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, 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 @@ -25,6 +25,7 @@ * @test * @bug 7038914 8016341 * @summary Verify that the reference handler does not die after an OOME allocating the InterruptedException object + * @requires test.thread.factory == null * @run main/othervm -XX:-UseGCOverheadLimit -Xmx24M -XX:-UseTLAB OOMEInReferenceHandler * @author peter.levart@gmail.com * @key intermittent diff --git a/test/jdk/java/lang/reflect/AnnotatedElement/TestAnnotatedElementDefaults.java b/test/jdk/java/lang/reflect/AnnotatedElement/TestAnnotatedElementDefaults.java index 97e9a79e14b..34c4f840241 100644 --- a/test/jdk/java/lang/reflect/AnnotatedElement/TestAnnotatedElementDefaults.java +++ b/test/jdk/java/lang/reflect/AnnotatedElement/TestAnnotatedElementDefaults.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 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 @@ -25,7 +25,6 @@ * @test * @bug 8005294 * @summary Check behavior of default methods of AnnotatedElement - * @author Joseph D. Darcy */ import java.lang.annotation.*; diff --git a/test/jdk/java/lang/reflect/Constructor/GenericStringTest.java b/test/jdk/java/lang/reflect/Constructor/GenericStringTest.java index 56a781fce18..a8c6c671d86 100644 --- a/test/jdk/java/lang/reflect/Constructor/GenericStringTest.java +++ b/test/jdk/java/lang/reflect/Constructor/GenericStringTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2018, 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 @@ -25,7 +25,6 @@ * @test * @bug 5033583 6316717 6470106 8161500 8162539 6304578 * @summary Check toGenericString() and toString() methods - * @author Joseph D. Darcy */ import java.lang.reflect.*; diff --git a/test/jdk/java/lang/reflect/Constructor/TestParameterAnnotations.java b/test/jdk/java/lang/reflect/Constructor/TestParameterAnnotations.java index 5773825a8e2..7512d36cb42 100644 --- a/test/jdk/java/lang/reflect/Constructor/TestParameterAnnotations.java +++ b/test/jdk/java/lang/reflect/Constructor/TestParameterAnnotations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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,7 +25,6 @@ * @test * @bug 6332964 * @summary Verify getParameterAnnotations doesn't throw spurious errors - * @author Joseph D. Darcy */ import java.lang.reflect.*; diff --git a/test/jdk/java/lang/reflect/DefaultAccessibility.java b/test/jdk/java/lang/reflect/DefaultAccessibility.java index 6dce3e249c2..69f8946e617 100644 --- a/test/jdk/java/lang/reflect/DefaultAccessibility.java +++ b/test/jdk/java/lang/reflect/DefaultAccessibility.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 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 @@ -25,7 +25,6 @@ * @test * @bug 6648344 * @summary Test that default accessibility is false - * @author Joseph D. Darcy */ import java.lang.reflect.*; diff --git a/test/jdk/java/lang/reflect/Field/GenericStringTest.java b/test/jdk/java/lang/reflect/Field/GenericStringTest.java index 65fbbc43381..5b5c321a3e1 100644 --- a/test/jdk/java/lang/reflect/Field/GenericStringTest.java +++ b/test/jdk/java/lang/reflect/Field/GenericStringTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2016, 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 @@ -25,7 +25,6 @@ * @test * @bug 5033583 8161500 * @summary Check toGenericString() method - * @author Joseph D. Darcy */ import java.lang.reflect.*; diff --git a/test/jdk/java/lang/reflect/Generics/HashCodeTest.java b/test/jdk/java/lang/reflect/Generics/HashCodeTest.java index 53fbad758d0..00e1494d04a 100644 --- a/test/jdk/java/lang/reflect/Generics/HashCodeTest.java +++ b/test/jdk/java/lang/reflect/Generics/HashCodeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 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 @@ -25,7 +25,6 @@ * @test * @bug 5097856 * @summary Computing hashCode of objects modeling generics shouldn't blow stack - * @author Joseph D. Darcy */ import java.util.*; diff --git a/test/jdk/java/lang/reflect/Generics/Probe.java b/test/jdk/java/lang/reflect/Generics/Probe.java index cbe94aef7a4..e14ed54f863 100644 --- a/test/jdk/java/lang/reflect/Generics/Probe.java +++ b/test/jdk/java/lang/reflect/Generics/Probe.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2013, 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 @@ -25,7 +25,6 @@ * @test * @bug 5003916 6704655 6873951 6476261 8004928 * @summary Testing parsing of signatures attributes of nested classes - * @author Joseph D. Darcy */ import java.lang.reflect.*; diff --git a/test/jdk/java/lang/reflect/Generics/StringsAndBounds.java b/test/jdk/java/lang/reflect/Generics/StringsAndBounds.java index 6d7756b2163..5c44207bcc5 100644 --- a/test/jdk/java/lang/reflect/Generics/StringsAndBounds.java +++ b/test/jdk/java/lang/reflect/Generics/StringsAndBounds.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 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 @@ -25,7 +25,6 @@ * @test * @bug 5015676 4987888 4997464 * @summary Testing upper bounds and availability of toString methods - * @author Joseph D. Darcy */ import java.lang.reflect.*; diff --git a/test/jdk/java/lang/reflect/Generics/TestParameterizedType.java b/test/jdk/java/lang/reflect/Generics/TestParameterizedType.java index 6bb3aa47aef..99fc3db0c97 100644 --- a/test/jdk/java/lang/reflect/Generics/TestParameterizedType.java +++ b/test/jdk/java/lang/reflect/Generics/TestParameterizedType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 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 @@ -25,7 +25,6 @@ * @test * @bug 5061485 * @summary Test sematics of ParameterizedType.equals - * @author Joseph D. Darcy */ import java.util.*; diff --git a/test/jdk/java/lang/reflect/Generics/exceptionCauseTest.java b/test/jdk/java/lang/reflect/Generics/exceptionCauseTest.java index dc8397aded6..485e8d3c51e 100644 --- a/test/jdk/java/lang/reflect/Generics/exceptionCauseTest.java +++ b/test/jdk/java/lang/reflect/Generics/exceptionCauseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 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 @@ -25,7 +25,6 @@ * @test * @bug 4981727 * @summary - * @author Joseph D. Darcy */ import java.io.PrintStream; diff --git a/test/jdk/java/lang/reflect/Generics/getAnnotationTest.java b/test/jdk/java/lang/reflect/Generics/getAnnotationTest.java index 0e1317e8d2d..766543863d0 100644 --- a/test/jdk/java/lang/reflect/Generics/getAnnotationTest.java +++ b/test/jdk/java/lang/reflect/Generics/getAnnotationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 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 @@ -25,7 +25,6 @@ * @test * @bug 4979440 * @summary Test for signature parsing corner case - * @author Joseph D. Darcy */ import java.lang.reflect.*; diff --git a/test/jdk/java/lang/reflect/Method/GenericStringTest.java b/test/jdk/java/lang/reflect/Method/GenericStringTest.java index c1c8e141f79..e7fe873d3cc 100644 --- a/test/jdk/java/lang/reflect/Method/GenericStringTest.java +++ b/test/jdk/java/lang/reflect/Method/GenericStringTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2021, 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 @@ -25,7 +25,6 @@ * @test * @bug 5033583 6316717 6470106 8004979 8161500 8162539 6304578 * @summary Check toGenericString() and toString() methods - * @author Joseph D. Darcy */ import java.lang.reflect.*; diff --git a/test/jdk/java/lang/reflect/Method/IsDefaultTest.java b/test/jdk/java/lang/reflect/Method/IsDefaultTest.java index c48ebaf9e96..0c983a8b0ba 100644 --- a/test/jdk/java/lang/reflect/Method/IsDefaultTest.java +++ b/test/jdk/java/lang/reflect/Method/IsDefaultTest.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 @@ -25,7 +25,6 @@ * @test * @bug 8005042 * @summary Check behavior of Method.isDefault - * @author Joseph D. Darcy */ import java.lang.reflect.*; diff --git a/test/jdk/java/lang/reflect/Method/defaultMethodModeling/DefaultMethodModeling.java b/test/jdk/java/lang/reflect/Method/defaultMethodModeling/DefaultMethodModeling.java index 30eca409cea..b91f73ee845 100644 --- a/test/jdk/java/lang/reflect/Method/defaultMethodModeling/DefaultMethodModeling.java +++ b/test/jdk/java/lang/reflect/Method/defaultMethodModeling/DefaultMethodModeling.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 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 @@ -25,7 +25,6 @@ * @test * @bug 8011590 * @summary Check modeling of default methods - * @author Joseph D. Darcy */ import java.util.Objects; diff --git a/test/jdk/java/lang/reflect/TypeVariable/TestAnnotatedElement.java b/test/jdk/java/lang/reflect/TypeVariable/TestAnnotatedElement.java index 94f8d459cf0..9788eab3060 100644 --- a/test/jdk/java/lang/reflect/TypeVariable/TestAnnotatedElement.java +++ b/test/jdk/java/lang/reflect/TypeVariable/TestAnnotatedElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 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,7 +25,6 @@ * @test * @bug 7086192 * @summary Verify functionality of AnnotatedElement methods on type variables - * @author Joseph D. Darcy */ import java.lang.reflect.*; diff --git a/test/jdk/java/math/BigDecimal/AddTests.java b/test/jdk/java/math/BigDecimal/AddTests.java index 8045e280435..d6f57366d2a 100644 --- a/test/jdk/java/math/BigDecimal/AddTests.java +++ b/test/jdk/java/math/BigDecimal/AddTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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,7 +25,6 @@ * @test * @bug 6362557 8200698 * @summary Some tests of add(BigDecimal, mc) - * @author Joseph D. Darcy */ import java.math.*; diff --git a/test/jdk/java/math/BigDecimal/CompareToTests.java b/test/jdk/java/math/BigDecimal/CompareToTests.java index baefcc5b499..b6ae31bca80 100644 --- a/test/jdk/java/math/BigDecimal/CompareToTests.java +++ b/test/jdk/java/math/BigDecimal/CompareToTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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,7 +25,6 @@ * @test * @bug 6473768 * @summary Tests of BigDecimal.compareTo - * @author Joseph D. Darcy */ import java.math.*; import static java.math.BigDecimal.*; diff --git a/test/jdk/java/math/BigDecimal/DivideTests.java b/test/jdk/java/math/BigDecimal/DivideTests.java index fe0fea73ff6..140271dc000 100644 --- a/test/jdk/java/math/BigDecimal/DivideTests.java +++ b/test/jdk/java/math/BigDecimal/DivideTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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,7 +25,6 @@ * @test * @bug 4851776 4907265 6177836 6876282 8066842 * @summary Some tests for the divide methods. - * @author Joseph D. Darcy */ import java.math.*; diff --git a/test/jdk/java/math/BigDecimal/IntegralDivisionTests.java b/test/jdk/java/math/BigDecimal/IntegralDivisionTests.java index 9fab5bb28ae..7df63b6329f 100644 --- a/test/jdk/java/math/BigDecimal/IntegralDivisionTests.java +++ b/test/jdk/java/math/BigDecimal/IntegralDivisionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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,7 +24,6 @@ * @test * @bug 4904082 4917089 6337226 6378503 * @summary Tests that integral division and related methods return the proper result and scale. - * @author Joseph D. Darcy */ import java.math.*; public class IntegralDivisionTests { diff --git a/test/jdk/java/math/BigDecimal/NegateTests.java b/test/jdk/java/math/BigDecimal/NegateTests.java index 5b570325e90..fc4ce82e286 100644 --- a/test/jdk/java/math/BigDecimal/NegateTests.java +++ b/test/jdk/java/math/BigDecimal/NegateTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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,7 +25,6 @@ * @test * @bug 6325535 * @summary Test for the rounding behavior of negate(MathContext) - * @author Joseph D. Darcy */ import java.math.*; diff --git a/test/jdk/java/math/BigDecimal/PowTests.java b/test/jdk/java/math/BigDecimal/PowTests.java index 49fc74c8791..a552e981c2e 100644 --- a/test/jdk/java/math/BigDecimal/PowTests.java +++ b/test/jdk/java/math/BigDecimal/PowTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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,7 +25,6 @@ * @test * @bug 4916097 * @summary Some exponent over/undeflow tests for the pow method - * @author Joseph D. Darcy */ import java.math.*; diff --git a/test/jdk/java/math/BigDecimal/PrecisionTests.java b/test/jdk/java/math/BigDecimal/PrecisionTests.java index 43df43e16bc..88d9843c724 100644 --- a/test/jdk/java/math/BigDecimal/PrecisionTests.java +++ b/test/jdk/java/math/BigDecimal/PrecisionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 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 @@ -25,7 +25,6 @@ * @test * @bug 1234567 * @summary Test that precision() is computed properly. - * @author Joseph D. Darcy */ import java.math.*; diff --git a/test/jdk/java/math/BigDecimal/RoundingTests.java b/test/jdk/java/math/BigDecimal/RoundingTests.java index 95d579e791a..6a12124fd62 100644 --- a/test/jdk/java/math/BigDecimal/RoundingTests.java +++ b/test/jdk/java/math/BigDecimal/RoundingTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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,7 +25,6 @@ * @test * @bug 6334849 * @summary Tests of dropping digits near the scale threshold - * @author Joseph D. Darcy */ import java.math.*; public class RoundingTests { diff --git a/test/jdk/java/math/BigDecimal/ScaleByPowerOfTenTests.java b/test/jdk/java/math/BigDecimal/ScaleByPowerOfTenTests.java index 638fce9ed54..93131b77be2 100644 --- a/test/jdk/java/math/BigDecimal/ScaleByPowerOfTenTests.java +++ b/test/jdk/java/math/BigDecimal/ScaleByPowerOfTenTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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,7 +25,6 @@ * @test * @bug 4899722 * @summary Basic tests of scaleByPowerOfTen - * @author Joseph D. Darcy */ import java.math.*; diff --git a/test/jdk/java/math/BigDecimal/StrippingZerosTest.java b/test/jdk/java/math/BigDecimal/StrippingZerosTest.java index 083b4eabbf1..c79c26c0ce5 100644 --- a/test/jdk/java/math/BigDecimal/StrippingZerosTest.java +++ b/test/jdk/java/math/BigDecimal/StrippingZerosTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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,7 +27,6 @@ * @summary A few tests of stripTrailingZeros * @run main StrippingZerosTest * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+EliminateAutoBox -XX:AutoBoxCacheMax=20000 StrippingZerosTest - * @author Joseph D. Darcy */ import java.math.*; diff --git a/test/jdk/java/math/BigDecimal/ToPlainStringTests.java b/test/jdk/java/math/BigDecimal/ToPlainStringTests.java index 0a1f617c0e5..28d5cdcd89d 100644 --- a/test/jdk/java/math/BigDecimal/ToPlainStringTests.java +++ b/test/jdk/java/math/BigDecimal/ToPlainStringTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2023, 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 @@ -27,7 +27,6 @@ * @summary Basic tests of toPlainString method * @run main ToPlainStringTests * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+EliminateAutoBox -XX:AutoBoxCacheMax=20000 ToPlainStringTests - * @author Joseph D. Darcy */ import java.math.*; diff --git a/test/jdk/java/math/BigDecimal/ZeroScalingTests.java b/test/jdk/java/math/BigDecimal/ZeroScalingTests.java index 05a59150b13..908487d68ef 100644 --- a/test/jdk/java/math/BigDecimal/ZeroScalingTests.java +++ b/test/jdk/java/math/BigDecimal/ZeroScalingTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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,7 +27,6 @@ * @summary Tests that the scale of zero is propagated properly and has the * proper effect and that setting the scale to zero does not mutate the * BigDecimal. - * @author Joseph D. Darcy */ import java.math.*; diff --git a/test/jdk/java/math/BigInteger/CompareToTests.java b/test/jdk/java/math/BigInteger/CompareToTests.java index 4e549fa2a53..3d9ff40ce56 100644 --- a/test/jdk/java/math/BigInteger/CompareToTests.java +++ b/test/jdk/java/math/BigInteger/CompareToTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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,7 +25,6 @@ * @test * @bug 6473768 * @summary Tests of BigInteger.compareTo - * @author Joseph D. Darcy */ import java.math.*; import static java.math.BigInteger.*; diff --git a/test/jdk/java/math/BigInteger/ExtremeShiftingTests.java b/test/jdk/java/math/BigInteger/ExtremeShiftingTests.java index 853b88668a2..a173d1cf6dd 100644 --- a/test/jdk/java/math/BigInteger/ExtremeShiftingTests.java +++ b/test/jdk/java/math/BigInteger/ExtremeShiftingTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2013, 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 @@ -27,7 +27,6 @@ * @summary Tests of shiftLeft and shiftRight on Integer.MIN_VALUE * @requires os.maxMemory >= 1g * @run main/othervm -Xmx512m ExtremeShiftingTests - * @author Joseph D. Darcy */ import java.math.BigInteger; import static java.math.BigInteger.*; diff --git a/test/jdk/java/math/BigInteger/OperatorNpeTests.java b/test/jdk/java/math/BigInteger/OperatorNpeTests.java index 2985ae00c8e..593e15445d5 100644 --- a/test/jdk/java/math/BigInteger/OperatorNpeTests.java +++ b/test/jdk/java/math/BigInteger/OperatorNpeTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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,7 +25,6 @@ * @test * @bug 6365176 * @summary Get NullPointerExceptions when expected - * @author Joseph D. Darcy */ import java.math.*; diff --git a/test/jdk/java/math/BigInteger/StringConstructor.java b/test/jdk/java/math/BigInteger/StringConstructor.java index c8fd9f83c6b..aa95fd4e346 100644 --- a/test/jdk/java/math/BigInteger/StringConstructor.java +++ b/test/jdk/java/math/BigInteger/StringConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2007, 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,7 +25,6 @@ * @test * @bug 4489146 5017980 * @summary tests String constructors of BigInteger - * @author Joseph D. Darcy */ import java.math.*; diff --git a/test/jdk/java/math/BigInteger/TestValueExact.java b/test/jdk/java/math/BigInteger/TestValueExact.java index 63ee1583527..fcd934653be 100644 --- a/test/jdk/java/math/BigInteger/TestValueExact.java +++ b/test/jdk/java/math/BigInteger/TestValueExact.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 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,7 +25,6 @@ * @test * @bug 6371401 * @summary Tests of fooValueExact methods - * @author Joseph D. Darcy */ import java.math.BigInteger; diff --git a/test/jdk/java/math/RoundingMode/RoundingModeTests.java b/test/jdk/java/math/RoundingMode/RoundingModeTests.java index 87ecad945b6..6edccb9277d 100644 --- a/test/jdk/java/math/RoundingMode/RoundingModeTests.java +++ b/test/jdk/java/math/RoundingMode/RoundingModeTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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,7 +25,6 @@ * @test * @bug 4851776 4891522 4905335 * @summary Basic tests for the RoundingMode class. - * @author Joseph D. Darcy */ import java.math.RoundingMode; diff --git a/test/jdk/java/net/httpclient/BufferSize1Test.java b/test/jdk/java/net/httpclient/BufferSize1Test.java new file mode 100644 index 00000000000..842dc06a630 --- /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 00000000000..caef0a58a6d --- /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/ContentLengthHeaderTest.java b/test/jdk/java/net/httpclient/ContentLengthHeaderTest.java index f302de4ee48..d7c77d0690a 100644 --- a/test/jdk/java/net/httpclient/ContentLengthHeaderTest.java +++ b/test/jdk/java/net/httpclient/ContentLengthHeaderTest.java @@ -29,8 +29,9 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.common.HttpServerAdapters - * @bug 8283544 + * @bug 8283544 8358942 * @run testng/othervm + * -Djdk.httpclient.allowRestrictedHeaders=content-length * -Djdk.internal.httpclient.debug=true * ContentLengthHeaderTest */ @@ -95,8 +96,8 @@ public void setup() throws IOException, URISyntaxException, InterruptedException testContentLengthServerH2.addHandler(new NoContentLengthHandler(), NO_BODY_PATH); testContentLengthServerH3.addHandler(new NoContentLengthHandler(), NO_BODY_PATH); testContentLengthServerH1.addHandler(new ContentLengthHandler(), BODY_PATH); - testContentLengthServerH2.addHandler(new OptionalContentLengthHandler(), BODY_PATH); - testContentLengthServerH3.addHandler(new OptionalContentLengthHandler(), BODY_PATH); + testContentLengthServerH2.addHandler(new ContentLengthHandler(), BODY_PATH); + testContentLengthServerH3.addHandler(new ContentLengthHandler(), BODY_PATH); testContentLengthURIH1 = URIBuilder.newBuilder() .scheme("http") .loopback() @@ -163,6 +164,13 @@ Object[][] bodies() { }; } + @DataProvider(name = "h1body") + Object[][] h1body() { + return new Object[][]{ + {HTTP_1_1, URI.create(testContentLengthURIH1 + BODY_PATH)} + }; + } + @DataProvider(name = "nobodies") Object[][] nobodies() { return new Object[][]{ @@ -186,6 +194,35 @@ public void getWithNoBody(Version version, URI uri) throws IOException, Interrup assertEquals(resp.version(), version); } + @Test(dataProvider = "nobodies") + // A GET request with empty request body should have no Content-length header + public void getWithEmptyBody(Version version, URI uri) throws IOException, InterruptedException { + testLog.println(version + " Checking GET with no request body"); + HttpRequest req = HttpRequest.newBuilder() + .version(version) + .method("GET", HttpRequest.BodyPublishers.noBody()) + .uri(uri) + .build(); + HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); + assertEquals(resp.statusCode(), 200, resp.body()); + assertEquals(resp.version(), version); + } + + @Test(dataProvider = "bodies") + // A GET request with empty request body and explicitly added Content-length header + public void getWithZeroContentLength(Version version, URI uri) throws IOException, InterruptedException { + testLog.println(version + " Checking GET with no request body"); + HttpRequest req = HttpRequest.newBuilder() + .version(version) + .method("GET", HttpRequest.BodyPublishers.noBody()) + .header("Content-length", "0") + .uri(uri) + .build(); + HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); + assertEquals(resp.statusCode(), 200, resp.body()); + assertEquals(resp.version(), version); + } + @Test(dataProvider = "bodies") // A GET request with a request body should have a Content-length header // in HTTP/1.1 @@ -215,6 +252,20 @@ public void deleteWithNoBody(Version version, URI uri) throws IOException, Inter assertEquals(resp.version(), version); } + @Test(dataProvider = "nobodies") + // A DELETE request with empty request body should have no Content-length header + public void deleteWithEmptyBody(Version version, URI uri) throws IOException, InterruptedException { + testLog.println(version + " Checking DELETE with no request body"); + HttpRequest req = HttpRequest.newBuilder() + .version(version) + .method("DELETE", HttpRequest.BodyPublishers.noBody()) + .uri(uri) + .build(); + HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); + assertEquals(resp.statusCode(), 200, resp.body()); + assertEquals(resp.version(), version); + } + @Test(dataProvider = "bodies") // A DELETE request with a request body should have a Content-length header // in HTTP/1.1 @@ -244,6 +295,20 @@ public void headWithNoBody(Version version, URI uri) throws IOException, Interru assertEquals(resp.version(), version); } + @Test(dataProvider = "nobodies") + // A HEAD request with empty request body should have no Content-length header + public void headWithEmptyBody(Version version, URI uri) throws IOException, InterruptedException { + testLog.println(version + " Checking HEAD with no request body"); + HttpRequest req = HttpRequest.newBuilder() + .version(version) + .method("HEAD", HttpRequest.BodyPublishers.noBody()) + .uri(uri) + .build(); + HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); + assertEquals(resp.statusCode(), 200, resp.body()); + assertEquals(resp.version(), version); + } + @Test(dataProvider = "bodies") // A HEAD request with a request body should have a Content-length header // in HTTP/1.1 @@ -261,6 +326,66 @@ public void headWithBody(Version version, URI uri) throws IOException, Interrupt assertEquals(resp.version(), version); } + @Test(dataProvider = "h1body") + // A POST request with empty request body should have a Content-length header + // in HTTP/1.1 + public void postWithEmptyBody(Version version, URI uri) throws IOException, InterruptedException { + testLog.println(version + " Checking POST with request body"); + HttpRequest req = HttpRequest.newBuilder() + .version(version) + .method("POST", HttpRequest.BodyPublishers.noBody()) + .uri(uri) + .build(); + HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); + assertEquals(resp.statusCode(), 200, resp.body()); + assertEquals(resp.version(), version); + } + + @Test(dataProvider = "bodies") + // A POST request with a request body should have a Content-length header + // in HTTP/1.1 + public void postWithBody(Version version, URI uri) throws IOException, InterruptedException { + testLog.println(version + " Checking POST with request body"); + HttpRequest req = HttpRequest.newBuilder() + .version(version) + .POST(HttpRequest.BodyPublishers.ofString("POST Body")) + .uri(uri) + .build(); + HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); + assertEquals(resp.statusCode(), 200, resp.body()); + assertEquals(resp.version(), version); + } + + @Test(dataProvider = "h1body") + // A PUT request with empty request body should have a Content-length header + // in HTTP/1.1 + public void putWithEmptyBody(Version version, URI uri) throws IOException, InterruptedException { + testLog.println(version + " Checking PUT with request body"); + HttpRequest req = HttpRequest.newBuilder() + .version(version) + .method("PUT", HttpRequest.BodyPublishers.noBody()) + .uri(uri) + .build(); + HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); + assertEquals(resp.statusCode(), 200, resp.body()); + assertEquals(resp.version(), version); + } + + @Test(dataProvider = "bodies") + // A PUT request with a request body should have a Content-length header + // in HTTP/1.1 + public void putWithBody(Version version, URI uri) throws IOException, InterruptedException { + testLog.println(version + " Checking PUT with request body"); + HttpRequest req = HttpRequest.newBuilder() + .version(version) + .PUT(HttpRequest.BodyPublishers.ofString("PUT Body")) + .uri(uri) + .build(); + HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); + assertEquals(resp.statusCode(), 200, resp.body()); + assertEquals(resp.version(), version); + } + public static void handleResponse(long expected, HttpTestExchange ex, String body, int rCode) throws IOException { try (InputStream is = ex.getRequestBody()) { byte[] reqBody = is.readAllBytes(); @@ -324,27 +449,4 @@ public void handle(HttpTestExchange exchange) throws IOException { } } } - - /** - * A handler used for cases where the presence of a Content-Length - * header is optional. If present, its value must match the number of - * bytes sent in the request body. - */ - static class OptionalContentLengthHandler implements HttpTestHandler { - - @Override - public void handle(HttpTestExchange exchange) throws IOException { - testLog.println("OptionalContentLengthHandler: Received Headers " - + exchange.getRequestHeaders().entrySet() + - " from " + exchange.getRequestMethod() + " request."); - Optional contentLength = exchange.getRequestHeaders().firstValue("Content-Length"); - - // Check Content-length header was set - if (contentLength.isPresent()) { - handleResponse(Long.parseLong(contentLength.get()), exchange, "Request completed", 200); - } else { - handleResponse(-1, exchange, "Request completed, no content length", 200); - } - } - } } diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java index 9973272b435..19f7369125d 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 7109d310d51..e38d18dc943 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 65242394162..2a1514fae8a 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 d67db44e396..c83f1bc1f5d 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 e67934b8a19..7ac63072737 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 ee3950dec06..2057e8e8939 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/java/util/Locale/LocaleEnhanceTest.java b/test/jdk/java/util/Locale/LocaleEnhanceTest.java index 7ab8db4c9e2..8bcbe20d197 100644 --- a/test/jdk/java/util/Locale/LocaleEnhanceTest.java +++ b/test/jdk/java/util/Locale/LocaleEnhanceTest.java @@ -31,9 +31,8 @@ import java.io.ObjectOutputStream; import java.net.URISyntaxException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.text.DecimalFormatSymbols; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import java.util.IllformedLocaleException; import java.util.List; @@ -47,26 +46,24 @@ import org.junit.jupiter.params.provider.NullSource; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -/** +/* * @test * @bug 6875847 6992272 7002320 7015500 7023613 7032820 7033504 7004603 * 7044019 8008577 8176853 8255086 8263202 8287868 8174269 8369452 + * 8369590 * @summary test API changes to Locale * @modules jdk.localedata - * @compile LocaleEnhanceTest.java * @run junit/othervm -esa LocaleEnhanceTest */ public class LocaleEnhanceTest { - public LocaleEnhanceTest() { - } - - /// - /// Generic sanity tests - /// - /** A canonical language code. */ private static final String l = "en"; @@ -79,6 +76,10 @@ public LocaleEnhanceTest() { /** A canonical variant code. */ private static final String v = "NewYork"; + /// + /// Generic sanity tests + /// + /** * Ensure that Builder builds locales that have the expected * tag and java6 ID. Note the odd cases for the ID. @@ -124,12 +125,12 @@ public void testCreateLocaleCanonicalValid() { .setRegion(idc) .setVariant(idv) .build(); - assertEquals(msg + "language", idl, l.getLanguage()); - assertEquals(msg + "script", ids, l.getScript()); - assertEquals(msg + "country", idc, l.getCountry()); - assertEquals(msg + "variant", idv, l.getVariant()); - assertEquals(msg + "tag", tag, l.toLanguageTag()); - assertEquals(msg + "id", id, l.toString()); + assertEquals(idl, l.getLanguage(), msg + "language"); + assertEquals(ids, l.getScript(), msg + "script"); + assertEquals(idc, l.getCountry(), msg + "country"); + assertEquals(idv, l.getVariant(), msg + "variant"); + assertEquals(tag, l.toLanguageTag(), msg + "tag"); + assertEquals(id, l.toString(), msg + "id"); } catch (IllegalArgumentException e) { fail(msg + e.getMessage()); @@ -181,13 +182,13 @@ public void testCreateLocaleMultipleVariants() { .setVariant(idv) .build(); - assertEquals(msg + " language", idl, l.getLanguage()); - assertEquals(msg + " script", ids, l.getScript()); - assertEquals(msg + " country", idc, l.getCountry()); - assertEquals(msg + " variant", idv, l.getVariant()); + assertEquals(idl, l.getLanguage(), msg + " language"); + assertEquals(ids, l.getScript(), msg + " script"); + assertEquals(idc, l.getCountry(), msg + " country"); + assertEquals(idv, l.getVariant(), msg + " variant"); - assertEquals(msg + "tag", tag, l.toLanguageTag()); - assertEquals(msg + "id", id, l.toString()); + assertEquals(tag, l.toLanguageTag(), msg + "tag"); + assertEquals(id, l.toString(), msg + "id"); } catch (IllegalArgumentException e) { fail(msg + e.getMessage()); @@ -235,7 +236,7 @@ public void testCreateLocaleCanonicalInvalidSeparator() { for (int i = 0; i < invalids.length; ++i) { String id = invalids[i]; Locale l = Locale.forLanguageTag(id); - assertEquals(id, "und", l.toLanguageTag()); + assertEquals("und", l.toLanguageTag(), id); } } @@ -255,14 +256,14 @@ public void testCurrentLocales() { // except no_NO_NY Locale tagResult = Locale.forLanguageTag(tag); if (!target.getVariant().equals("NY")) { - assertEquals("tagResult", target, tagResult); + assertEquals(target, tagResult, "tagResult"); } // the builder also recreates the original locale, // except ja_JP_JP, th_TH_TH and no_NO_NY Locale builderResult = builder.setLocale(target).build(); if (target.getVariant().length() != 2) { - assertEquals("builderResult", target, builderResult); + assertEquals(target, builderResult, "builderResult"); } } } @@ -275,11 +276,11 @@ public void testIcuLocales() throws Exception { BufferedReader br = new BufferedReader( new InputStreamReader( LocaleEnhanceTest.class.getResourceAsStream("icuLocales.txt"), - "UTF-8")); + StandardCharsets.UTF_8)); String id = null; while (null != (id = br.readLine())) { Locale result = Locale.forLanguageTag(id); - assertEquals("ulocale", id, result.toLanguageTag()); + assertEquals(id, result.toLanguageTag(), "ulocale"); } } @@ -291,163 +292,151 @@ public void testIcuLocales() throws Exception { public void testConstructor() { // all the old weirdness still holds, no new weirdness String[][] tests = { - // language to lower case, region to upper, variant unchanged - // short - { "X", "y", "z", "x", "Y" }, - // long - { "xXxXxXxXxXxX", "yYyYyYyYyYyYyYyY", "zZzZzZzZzZzZzZzZ", - "xxxxxxxxxxxx", "YYYYYYYYYYYYYYYY" }, - // mapped language ids - { "he", "IL", "", "he" }, - { "iw", "IL", "", "he" }, - { "yi", "DE", "", "yi" }, - { "ji", "DE", "", "yi" }, - { "id", "ID", "", "id" }, - { "in", "ID", "", "id" }, - // special variants - { "ja", "JP", "JP" }, - { "th", "TH", "TH" }, - { "no", "NO", "NY" }, - { "no", "NO", "NY" }, - // no canonicalization of 3-letter language codes - { "eng", "US", "" } + // language to lower case, region to upper, variant unchanged + // short + {"X", "y", "z", "x", "Y"}, + // long + {"xXxXxXxXxXxX", "yYyYyYyYyYyYyYyY", "zZzZzZzZzZzZzZzZ", + "xxxxxxxxxxxx", "YYYYYYYYYYYYYYYY"}, + // mapped language ids + {"he", "IL", "", "he"}, + {"iw", "IL", "", "he"}, + {"yi", "DE", "", "yi"}, + {"ji", "DE", "", "yi"}, + {"id", "ID", "", "id"}, + {"in", "ID", "", "id"}, + // special variants + {"ja", "JP", "JP"}, + {"th", "TH", "TH"}, + {"no", "NO", "NY"}, + {"no", "NO", "NY"}, + // no canonicalization of 3-letter language codes + {"eng", "US", ""} }; - for (int i = 0; i < tests.length; ++ i) { + for (int i = 0; i < tests.length; ++i) { String[] test = tests[i]; String id = String.valueOf(i); Locale locale = Locale.of(test[0], test[1], test[2]); - assertEquals(id + " lang", test.length > 3 ? test[3] : test[0], locale.getLanguage()); - assertEquals(id + " region", test.length > 4 ? test[4] : test[1], locale.getCountry()); - assertEquals(id + " variant", test.length > 5 ? test[5] : test[2], locale.getVariant()); + assertEquals(test.length > 3 ? test[3] : test[0], locale.getLanguage(), id + " lang"); + assertEquals(test.length > 4 ? test[4] : test[1], locale.getCountry(), id + " region"); + assertEquals(test.length > 5 ? test[5] : test[2], locale.getVariant(), id + " variant"); } } /// - /// Locale API tests. + /// Locale API Tests /// @Test public void testGetScript() { // forLanguageTag normalizes case Locale locale = Locale.forLanguageTag("und-latn"); - assertEquals("forLanguageTag", "Latn", locale.getScript()); + assertEquals("Latn", locale.getScript(), "forLanguageTag"); // Builder normalizes case locale = new Builder().setScript("LATN").build(); - assertEquals("builder", "Latn", locale.getScript()); + assertEquals("Latn", locale.getScript(), "builder"); // empty string is returned, not null, if there is no script locale = Locale.forLanguageTag("und"); - assertEquals("script is empty string", "", locale.getScript()); + assertEquals("", locale.getScript(), "script is empty string"); } @Test public void testGetExtension() { // forLanguageTag does NOT normalize to hyphen Locale locale = Locale.forLanguageTag("und-a-some_ex-tension"); - assertEquals("some_ex-tension", null, locale.getExtension('a')); + assertNull(locale.getExtension('a'), "some_ex-tension"); // regular extension locale = new Builder().setExtension('a', "some-ex-tension").build(); - assertEquals("builder", "some-ex-tension", locale.getExtension('a')); + assertEquals("some-ex-tension", locale.getExtension('a'), "builder"); // returns null if extension is not present - assertEquals("empty b", null, locale.getExtension('b')); + assertNull(locale.getExtension('b'), "empty b"); // throws exception if extension tag is illegal - new ExpectIAE() { public void call() { Locale.forLanguageTag("").getExtension('\uD800'); }}; + assertThrows(IllegalArgumentException.class, () -> Locale.forLanguageTag("").getExtension('\uD800')); // 'x' is not an extension, it's a private use tag, but it's accessed through this API locale = Locale.forLanguageTag("x-y-z-blork"); - assertEquals("x", "y-z-blork", locale.getExtension('x')); + assertEquals("y-z-blork", locale.getExtension('x'), "x"); } @Test public void testGetExtensionKeys() { Locale locale = Locale.forLanguageTag("und-a-xx-yy-b-zz-ww"); Set result = locale.getExtensionKeys(); - assertEquals("result size", 2, result.size()); - assertTrue("'a','b'", result.contains('a') && result.contains('b')); + assertEquals(2, result.size(), "result size"); + assertTrue(result.contains('a') && result.contains('b'), "'a','b'"); // result is not mutable - try { - result.add('x'); - fail("expected exception on add to extension key set"); - } - catch (UnsupportedOperationException e) { - // ok - } + assertThrows(UnsupportedOperationException.class, () -> result.add('x')); // returns empty set if no extensions locale = Locale.forLanguageTag("und"); - assertTrue("empty result", locale.getExtensionKeys().isEmpty()); + assertTrue(locale.getExtensionKeys().isEmpty(), "empty result"); } @Test public void testGetUnicodeLocaleAttributes() { Locale locale = Locale.forLanguageTag("en-US-u-abc-def"); Set attributes = locale.getUnicodeLocaleAttributes(); - assertEquals("number of attributes", 2, attributes.size()); - assertTrue("attribute abc", attributes.contains("abc")); - assertTrue("attribute def", attributes.contains("def")); + assertEquals(2, attributes.size(), "number of attributes"); + assertTrue(attributes.contains("abc"), "attribute abc"); + assertTrue(attributes.contains("def"), "attribute def"); locale = Locale.forLanguageTag("en-US-u-ca-gregory"); attributes = locale.getUnicodeLocaleAttributes(); - assertTrue("empty attributes", attributes.isEmpty()); + assertTrue(attributes.isEmpty(), "empty attributes"); } @Test public void testGetUnicodeLocaleType() { Locale locale = Locale.forLanguageTag("und-u-co-japanese-nu-thai"); - assertEquals("collation", "japanese", locale.getUnicodeLocaleType("co")); - assertEquals("numbers", "thai", locale.getUnicodeLocaleType("nu")); + assertEquals("japanese", locale.getUnicodeLocaleType("co"), "collation"); + assertEquals("thai", locale.getUnicodeLocaleType("nu"), "numbers"); // Unicode locale extension key is case insensitive - assertEquals("key case", "japanese", locale.getUnicodeLocaleType("Co")); + assertEquals("japanese", locale.getUnicodeLocaleType("Co"), "key case"); // if keyword is not present, returns null - assertEquals("locale keyword not present", null, locale.getUnicodeLocaleType("xx")); + assertNull(locale.getUnicodeLocaleType("xx"), "locale keyword not present"); // if no locale extension is set, returns null locale = Locale.forLanguageTag("und"); - assertEquals("locale extension not present", null, locale.getUnicodeLocaleType("co")); + assertNull(locale.getUnicodeLocaleType("co"), "locale extension not present"); // typeless keyword locale = Locale.forLanguageTag("und-u-kn"); - assertEquals("typeless keyword", "", locale.getUnicodeLocaleType("kn")); + assertEquals("", locale.getUnicodeLocaleType("kn"), "typeless keyword"); // invalid keys throw exception - new ExpectIAE() { public void call() { Locale.forLanguageTag("").getUnicodeLocaleType("q"); }}; - new ExpectIAE() { public void call() { Locale.forLanguageTag("").getUnicodeLocaleType("abcdefghi"); }}; + assertThrows(IllegalArgumentException.class, () -> Locale.forLanguageTag("").getUnicodeLocaleType("q")); + assertThrows(IllegalArgumentException.class, () -> Locale.forLanguageTag("").getUnicodeLocaleType("abcdefghi")); // null argument throws exception - new ExpectNPE() { public void call() { Locale.forLanguageTag("").getUnicodeLocaleType(null); }}; + assertThrows(NullPointerException.class, () -> Locale.forLanguageTag("").getUnicodeLocaleType(null)); } @Test public void testGetUnicodeLocaleKeys() { Locale locale = Locale.forLanguageTag("und-u-co-japanese-nu-thai"); Set result = locale.getUnicodeLocaleKeys(); - assertEquals("two keys", 2, result.size()); - assertTrue("co and nu", result.contains("co") && result.contains("nu")); + assertEquals(2, result.size(), "two keys"); + assertTrue(result.contains("co") && result.contains("nu"), "co and nu"); // result is not modifiable - try { - result.add("frobozz"); - fail("expected exception when add to locale key set"); - } - catch (UnsupportedOperationException e) { - // ok - } + assertThrows(UnsupportedOperationException.class, () -> result.add("frobozz")); } @Test public void testPrivateUseExtension() { Locale locale = Locale.forLanguageTag("x-y-x-blork-"); - assertEquals("blork", "y-x-blork", locale.getExtension(Locale.PRIVATE_USE_EXTENSION)); + assertEquals("y-x-blork", locale.getExtension(Locale.PRIVATE_USE_EXTENSION), "blork"); locale = Locale.forLanguageTag("und"); - assertEquals("no privateuse", null, locale.getExtension(Locale.PRIVATE_USE_EXTENSION)); + assertNull(locale.getExtension(Locale.PRIVATE_USE_EXTENSION), "no privateuse"); } @Test @@ -455,63 +444,63 @@ public void testToLanguageTag() { // lots of normalization to test here // test locales created using the constructor String[][] tests = { - // empty locale canonicalizes to 'und' - { "", "", "", "und" }, - // variant alone is not a valid Locale, but has a valid language tag - { "", "", "NewYork", "und-NewYork" }, - // standard valid locales - { "", "Us", "", "und-US" }, - { "", "US", "NewYork", "und-US-NewYork" }, - { "EN", "", "", "en" }, - { "EN", "", "NewYork", "en-NewYork" }, - { "EN", "US", "", "en-US" }, - { "EN", "US", "NewYork", "en-US-NewYork" }, - // underscore in variant will be emitted as multiple variant subtags - { "en", "US", "Newer_Yorker", "en-US-Newer-Yorker" }, - // invalid variant subtags are appended as private use - { "en", "US", "new_yorker", "en-US-x-lvariant-new-yorker" }, - // the first invalid variant subtags and following variant subtags are appended as private use - { "en", "US", "Windows_XP_Home", "en-US-Windows-x-lvariant-XP-Home" }, - // too long variant and following variant subtags disappear - { "en", "US", "WindowsVista_SP2", "en-US" }, - // invalid region subtag disappears - { "en", "USA", "", "en" }, - // invalid language tag disappears - { "e", "US", "", "und-US" }, - // three-letter language tags are not canonicalized - { "Eng", "", "", "eng" }, - // legacy languages canonicalize to modern equivalents - { "he", "IL", "", "he-IL" }, - { "iw", "IL", "", "he-IL" }, - { "yi", "DE", "", "yi-DE" }, - { "ji", "DE", "", "yi-DE" }, - { "id", "ID", "", "id-ID" }, - { "in", "ID", "", "id-ID" }, - // special values are converted on output - { "ja", "JP", "JP", "ja-JP-u-ca-japanese-x-lvariant-JP" }, - { "th", "TH", "TH", "th-TH-u-nu-thai-x-lvariant-TH" }, - { "no", "NO", "NY", "nn-NO" } + // empty locale canonicalizes to 'und' + {"", "", "", "und"}, + // variant alone is not a valid Locale, but has a valid language tag + {"", "", "NewYork", "und-NewYork"}, + // standard valid locales + {"", "Us", "", "und-US"}, + {"", "US", "NewYork", "und-US-NewYork"}, + {"EN", "", "", "en"}, + {"EN", "", "NewYork", "en-NewYork"}, + {"EN", "US", "", "en-US"}, + {"EN", "US", "NewYork", "en-US-NewYork"}, + // underscore in variant will be emitted as multiple variant subtags + {"en", "US", "Newer_Yorker", "en-US-Newer-Yorker"}, + // invalid variant subtags are appended as private use + {"en", "US", "new_yorker", "en-US-x-lvariant-new-yorker"}, + // the first invalid variant subtags and following variant subtags are appended as private use + {"en", "US", "Windows_XP_Home", "en-US-Windows-x-lvariant-XP-Home"}, + // too long variant and following variant subtags disappear + {"en", "US", "WindowsVista_SP2", "en-US"}, + // invalid region subtag disappears + {"en", "USA", "", "en"}, + // invalid language tag disappears + {"e", "US", "", "und-US"}, + // three-letter language tags are not canonicalized + {"Eng", "", "", "eng"}, + // legacy languages canonicalize to modern equivalents + {"he", "IL", "", "he-IL"}, + {"iw", "IL", "", "he-IL"}, + {"yi", "DE", "", "yi-DE"}, + {"ji", "DE", "", "yi-DE"}, + {"id", "ID", "", "id-ID"}, + {"in", "ID", "", "id-ID"}, + // special values are converted on output + {"ja", "JP", "JP", "ja-JP-u-ca-japanese-x-lvariant-JP"}, + {"th", "TH", "TH", "th-TH-u-nu-thai-x-lvariant-TH"}, + {"no", "NO", "NY", "nn-NO"} }; for (int i = 0; i < tests.length; ++i) { String[] test = tests[i]; Locale locale = Locale.of(test[0], test[1], test[2]); - assertEquals("case " + i, test[3], locale.toLanguageTag()); + assertEquals(test[3], locale.toLanguageTag(), "case " + i); } // test locales created from forLanguageTag String[][] tests1 = { - // case is normalized during the round trip - { "EN-us", "en-US" }, - { "en-Latn-US", "en-Latn-US" }, - // reordering Unicode locale extensions - { "de-u-co-phonebk-ca-gregory", "de-u-ca-gregory-co-phonebk" }, - // private use only language tag is preserved (no extra "und") - { "x-elmer", "x-elmer" }, - { "x-lvariant-JP", "x-lvariant-JP" }, + // case is normalized during the round trip + {"EN-us", "en-US"}, + {"en-Latn-US", "en-Latn-US"}, + // reordering Unicode locale extensions + {"de-u-co-phonebk-ca-gregory", "de-u-ca-gregory-co-phonebk"}, + // private use only language tag is preserved (no extra "und") + {"x-elmer", "x-elmer"}, + {"x-lvariant-JP", "x-lvariant-JP"}, }; for (String[] test : tests1) { Locale locale = Locale.forLanguageTag(test[0]); - assertEquals("case " + test[0], test[1], locale.toLanguageTag()); + assertEquals(test[1], locale.toLanguageTag(), "case " + test[0]); } } @@ -524,102 +513,101 @@ public void testForLanguageTag() { // sample private use tags) come from 4646bis Feb 29, 2009. String[][] tests = { - // private use tags only - { "x-abc", "x-abc" }, - { "x-a-b-c", "x-a-b-c" }, - { "x-a-12345678", "x-a-12345678" }, - - // legacy language tags with preferred mappings - { "i-ami", "ami" }, - { "i-bnn", "bnn" }, - { "i-hak", "hak" }, - { "i-klingon", "tlh" }, - { "i-lux", "lb" }, // two-letter tag - { "i-navajo", "nv" }, // two-letter tag - { "i-pwn", "pwn" }, - { "i-tao", "tao" }, - { "i-tay", "tay" }, - { "i-tsu", "tsu" }, - { "art-lojban", "jbo" }, - { "no-bok", "nb" }, - { "no-nyn", "nn" }, - { "sgn-BE-FR", "sfb" }, - { "sgn-BE-NL", "vgt" }, - { "sgn-CH-DE", "sgg" }, - { "zh-guoyu", "cmn" }, - { "zh-hakka", "hak" }, - { "zh-min-nan", "nan" }, - { "zh-xiang", "hsn" }, - - // irregular legacy language tags, no preferred mappings, drop illegal fields - // from end. If no subtag is mappable, fallback to 'und' - { "i-default", "en-x-i-default" }, - { "i-enochian", "x-i-enochian" }, - { "i-mingo", "see-x-i-mingo" }, - { "en-GB-oed", "en-GB-x-oed" }, - { "zh-min", "nan-x-zh-min" }, - { "cel-gaulish", "xtg-x-cel-gaulish" }, + // private use tags only + {"x-abc", "x-abc"}, + {"x-a-b-c", "x-a-b-c"}, + {"x-a-12345678", "x-a-12345678"}, + + // legacy language tags with preferred mappings + {"i-ami", "ami"}, + {"i-bnn", "bnn"}, + {"i-hak", "hak"}, + {"i-klingon", "tlh"}, + {"i-lux", "lb"}, // two-letter tag + {"i-navajo", "nv"}, // two-letter tag + {"i-pwn", "pwn"}, + {"i-tao", "tao"}, + {"i-tay", "tay"}, + {"i-tsu", "tsu"}, + {"art-lojban", "jbo"}, + {"no-bok", "nb"}, + {"no-nyn", "nn"}, + {"sgn-BE-FR", "sfb"}, + {"sgn-BE-NL", "vgt"}, + {"sgn-CH-DE", "sgg"}, + {"zh-guoyu", "cmn"}, + {"zh-hakka", "hak"}, + {"zh-min-nan", "nan"}, + {"zh-xiang", "hsn"}, + + // irregular legacy language tags, no preferred mappings, drop illegal fields + // from end. If no subtag is mappable, fallback to 'und' + {"i-default", "en-x-i-default"}, + {"i-enochian", "x-i-enochian"}, + {"i-mingo", "see-x-i-mingo"}, + {"en-GB-oed", "en-GB-x-oed"}, + {"zh-min", "nan-x-zh-min"}, + {"cel-gaulish", "xtg-x-cel-gaulish"}, }; for (int i = 0; i < tests.length; ++i) { String[] test = tests[i]; Locale locale = Locale.forLanguageTag(test[0]); - assertEquals("legacy language tag case " + i, test[1], locale.toLanguageTag()); + assertEquals(test[1], locale.toLanguageTag(), "legacy language tag case " + i); } // forLanguageTag ignores everything past the first place it encounters // a syntax error - tests = new String[][] { - { "valid", - "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y-12345678-z", - "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y-12345678-z" }, - { "segment of private use tag too long", - "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y-123456789-z", - "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y" }, - { "segment of private use tag is empty", - "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y--12345678-z", - "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y" }, - { "first segment of private use tag is empty", - "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x--y-12345678-z", - "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def" }, - { "illegal extension tag", - "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-\uD800-y-12345678-z", - "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def" }, - { "locale subtag with no value", - "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-x-y-12345678-z", - "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-x-y-12345678-z" }, - { "locale key subtag invalid", - "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-123456789-def-x-y-12345678-z", - "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc" }, - // locale key subtag invalid in earlier position, all following subtags - // dropped (and so the locale extension dropped as well) - { "locale key subtag invalid in earlier position", - "en-US-Newer-Yorker-a-bb-cc-dd-u-123456789-abc-bb-def-x-y-12345678-z", - "en-US-Newer-Yorker-a-bb-cc-dd" }, + tests = new String[][]{ + {"valid", + "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y-12345678-z", + "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y-12345678-z"}, + {"segment of private use tag too long", + "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y-123456789-z", + "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y"}, + {"segment of private use tag is empty", + "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y--12345678-z", + "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y"}, + {"first segment of private use tag is empty", + "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x--y-12345678-z", + "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def"}, + {"illegal extension tag", + "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-\uD800-y-12345678-z", + "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def"}, + {"locale subtag with no value", + "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-x-y-12345678-z", + "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-x-y-12345678-z"}, + {"locale key subtag invalid", + "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-123456789-def-x-y-12345678-z", + "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc"}, + // locale key subtag invalid in earlier position, all following subtags + // dropped (and so the locale extension dropped as well) + {"locale key subtag invalid in earlier position", + "en-US-Newer-Yorker-a-bb-cc-dd-u-123456789-abc-bb-def-x-y-12345678-z", + "en-US-Newer-Yorker-a-bb-cc-dd"}, }; for (int i = 0; i < tests.length; ++i) { String[] test = tests[i]; String msg = "syntax error case " + i + " " + test[0]; try { Locale locale = Locale.forLanguageTag(test[1]); - assertEquals(msg, test[2], locale.toLanguageTag()); - } - catch (IllegalArgumentException e) { + assertEquals(test[2], locale.toLanguageTag(), msg); + } catch (IllegalArgumentException e) { fail(msg + " caught exception: " + e); } } // duplicated extension are just ignored Locale locale = Locale.forLanguageTag("und-d-aa-00-bb-01-D-AA-10-cc-11-c-1234"); - assertEquals("extension", "aa-00-bb-01", locale.getExtension('d')); - assertEquals("extension c", "1234", locale.getExtension('c')); + assertEquals("aa-00-bb-01", locale.getExtension('d'), "extension"); + assertEquals("1234", locale.getExtension('c'), "extension c"); locale = Locale.forLanguageTag("und-U-ca-gregory-u-ca-japanese"); - assertEquals("Unicode extension", "ca-gregory", locale.getExtension(Locale.UNICODE_LOCALE_EXTENSION)); + assertEquals("ca-gregory", locale.getExtension(Locale.UNICODE_LOCALE_EXTENSION), "Unicode extension"); // redundant Unicode locale keys in an extension are ignored locale = Locale.forLanguageTag("und-u-aa-000-bb-001-bB-002-cc-003-c-1234"); - assertEquals("Unicode keywords", "aa-000-bb-001-cc-003", locale.getExtension(Locale.UNICODE_LOCALE_EXTENSION)); - assertEquals("Duplicated Unicode locake key followed by an extension", "1234", locale.getExtension('c')); + assertEquals("aa-000-bb-001-cc-003", locale.getExtension(Locale.UNICODE_LOCALE_EXTENSION), "Unicode keywords"); + assertEquals("1234", locale.getExtension('c'), "Duplicated Unicode locake key followed by an extension"); } @Test @@ -630,12 +618,12 @@ public void testGetDisplayScript() { Locale oldLocale = Locale.getDefault(); Locale.setDefault(Locale.US); - assertEquals("latn US", "Latin", latnLocale.getDisplayScript()); - assertEquals("hans US", "Simplified", hansLocale.getDisplayScript()); + assertEquals("Latin", latnLocale.getDisplayScript(), "latn US"); + assertEquals("Simplified", hansLocale.getDisplayScript(), "hans US"); Locale.setDefault(Locale.GERMANY); - assertEquals("latn DE", "Lateinisch", latnLocale.getDisplayScript()); - assertEquals("hans DE", "Vereinfacht", hansLocale.getDisplayScript()); + assertEquals("Lateinisch", latnLocale.getDisplayScript(), "latn DE"); + assertEquals("Vereinfacht", hansLocale.getDisplayScript(), "hans DE"); Locale.setDefault(oldLocale); } @@ -645,11 +633,11 @@ public void testGetDisplayScriptWithLocale() { Locale latnLocale = Locale.forLanguageTag("und-latn"); Locale hansLocale = Locale.forLanguageTag("und-hans"); - assertEquals("latn US", "Latin", latnLocale.getDisplayScript(Locale.US)); - assertEquals("hans US", "Simplified", hansLocale.getDisplayScript(Locale.US)); + assertEquals("Latin", latnLocale.getDisplayScript(Locale.US), "latn US"); + assertEquals("Simplified", hansLocale.getDisplayScript(Locale.US), "hans US"); - assertEquals("latn DE", "Lateinisch", latnLocale.getDisplayScript(Locale.GERMANY)); - assertEquals("hans DE", "Vereinfacht", hansLocale.getDisplayScript(Locale.GERMANY)); + assertEquals("Lateinisch", latnLocale.getDisplayScript(Locale.GERMANY), "latn DE"); + assertEquals("Vereinfacht", hansLocale.getDisplayScript(Locale.GERMANY), "hans DE"); } @Test @@ -695,10 +683,10 @@ public void testGetDisplayName() { for (int i = 0; i < testLocales.length; i++) { Locale loc = testLocales[i]; - assertEquals("English display name for " + loc.toLanguageTag(), - displayNameEnglish[i], loc.getDisplayName(Locale.ENGLISH)); - assertEquals("Simplified Chinese display name for " + loc.toLanguageTag(), - displayNameSimplifiedChinese[i], loc.getDisplayName(Locale.CHINA)); + assertEquals(displayNameEnglish[i], loc.getDisplayName(Locale.ENGLISH), + "English display name for " + loc.toLanguageTag()); + assertEquals(displayNameSimplifiedChinese[i], loc.getDisplayName(Locale.CHINA), + "Simplified Chinese display name for " + loc.toLanguageTag()); } } @@ -716,37 +704,33 @@ public void testBuilderSetLocale() { Locale locale = Locale.forLanguageTag(languageTag); Locale result = lenientBuilder - .setLocale(locale) - .build(); - assertEquals("long tag", target, result.toLanguageTag()); - assertEquals("long tag", locale, result); + .setLocale(locale) + .build(); + assertEquals(target, result.toLanguageTag(), "long tag"); + assertEquals(locale, result, "long tag"); // null is illegal - new BuilderNPE("locale") { - public void call() { b.setLocale(null); } - }; + assertThrows(NullPointerException.class, () -> builder.setLocale(null), + "Setting null locale should throw NPE"); // builder canonicalizes the three legacy locales: // ja_JP_JP, th_TH_TH, no_NY_NO. locale = builder.setLocale(Locale.of("ja", "JP", "JP")).build(); - assertEquals("ja_JP_JP languagetag", "ja-JP-u-ca-japanese", locale.toLanguageTag()); - assertEquals("ja_JP_JP variant", "", locale.getVariant()); + assertEquals("ja-JP-u-ca-japanese", locale.toLanguageTag(), "ja_JP_JP languagetag"); + assertEquals("", locale.getVariant(), "ja_JP_JP variant"); locale = builder.setLocale(Locale.of("th", "TH", "TH")).build(); - assertEquals("th_TH_TH languagetag", "th-TH-u-nu-thai", locale.toLanguageTag()); - assertEquals("th_TH_TH variant", "", locale.getVariant()); + assertEquals("th-TH-u-nu-thai", locale.toLanguageTag(), "th_TH_TH languagetag"); + assertEquals("", locale.getVariant(), "th_TH_TH variant"); locale = builder.setLocale(Locale.of("no", "NO", "NY")).build(); - assertEquals("no_NO_NY languagetag", "nn-NO", locale.toLanguageTag()); - assertEquals("no_NO_NY language", "nn", locale.getLanguage()); - assertEquals("no_NO_NY variant", "", locale.getVariant()); + assertEquals("nn-NO", locale.toLanguageTag(), "no_NO_NY languagetag"); + assertEquals("nn", locale.getLanguage(), "no_NO_NY language"); + assertEquals("", locale.getVariant(), "no_NO_NY variant"); // non-canonical, non-legacy locales are invalid - new BuilderILE("123_4567_89") { - public void call() { - b.setLocale(Locale.of("123", "4567", "89")); - } - }; + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLocale(Locale.of("123", "4567", "89")), "123_4567_89"); } @Test @@ -755,16 +739,20 @@ public void testBuilderSetLanguageTag() { String target = "en-Latn-US-NewYork-a-xx-b-yy-x-1-2-3"; Builder builder = new Builder(); String result = builder - .setLanguageTag(source) - .build() - .toLanguageTag(); - assertEquals("language", target, result); - - // redundant extensions cause a failure - new BuilderILE() { public void call() { b.setLanguageTag("und-a-xx-yy-b-ww-A-00-11-c-vv"); }}; - - // redundant Unicode locale extension keys within an Unicode locale extension cause a failure - new BuilderILE() { public void call() { b.setLanguageTag("und-u-nu-thai-NU-chinese-xx-1234"); }}; + .setLanguageTag(source) + .build() + .toLanguageTag(); + assertEquals(target, result, "language"); + + // redundant extensions are ignored + assertEquals("und-a-xx-yy-b-ww-c-vv", + new Builder().setLanguageTag("und-a-xx-yy-b-ww-A-00-11-c-vv").build().toLanguageTag()); + // redundant Unicode locale extension keys are ignored + assertEquals("und-u-cu-usd-nu-thai-xx-1234", + new Builder().setLanguageTag("und-u-nu-thai-cu-usd-NU-chinese-xx-1234").build().toLanguageTag()); + // redundant Unicode locale extension attributes are ignored + assertEquals("und-u-bar-foo", + new Builder().setLanguageTag("und-u-foo-bar-FOO").build().toLanguageTag()); } // Test the values that should clear the builder @@ -776,9 +764,9 @@ public void testBuilderSetLanguageTagClear(String tag) { var bldr = new Builder(); bldr.setLanguageTag("en-US"); assertDoesNotThrow(() -> bldr.setLanguageTag(tag)); - assertEquals("Setting a %s language tag did not clear the builder" - .formatted(tag == null ? "null" : "empty"), - empty.build(), bldr.build()); + assertEquals(empty.build(), bldr.build(), + "Setting a %s language tag did not clear the builder" + .formatted(tag == null ? "null" : "empty")); } @Test @@ -789,18 +777,18 @@ public void testBuilderSetLanguage() { String defaulted = ""; Builder builder = new Builder(); String result = builder - .setLanguage(source) - .build() - .getLanguage(); - assertEquals("en", target, result); + .setLanguage(source) + .build() + .getLanguage(); + assertEquals(target, result, "en"); // setting with empty resets result = builder - .setLanguage(target) - .setLanguage("") - .build() - .getLanguage(); - assertEquals("empty", defaulted, result); + .setLanguage(target) + .setLanguage("") + .build() + .getLanguage(); + assertEquals(defaulted, result, "empty"); // setting with null resets too result = builder @@ -808,23 +796,25 @@ public void testBuilderSetLanguage() { .setLanguage(null) .build() .getLanguage(); - assertEquals("null", defaulted, result); + assertEquals(defaulted, result, "null"); // language codes must be 2-8 alpha // for forwards compatibility, 4-alpha and 5-8 alpha (registered) // languages are accepted syntax - new BuilderILE("q", "abcdefghi", "13") { public void call() { b.setLanguage(arg); }}; + for (String arg : List.of("q", "abcdefghi", "13")) { + assertThrows(IllformedLocaleException.class, () -> new Builder().setLanguage(arg)); + } // language code validation is NOT performed, any 2-8-alpha passes - assertNotNull("2alpha", builder.setLanguage("zz").build()); - assertNotNull("8alpha", builder.setLanguage("abcdefgh").build()); + assertNotNull(builder.setLanguage("zz").build(), "2alpha"); + assertNotNull(builder.setLanguage("abcdefgh").build(), "8alpha"); // three-letter language codes are NOT canonicalized to two-letter result = builder - .setLanguage("eng") - .build() - .getLanguage(); - assertEquals("eng", "eng", result); + .setLanguage("eng") + .build() + .getLanguage(); + assertEquals("eng", result, "eng"); } @Test @@ -835,18 +825,18 @@ public void testBuilderSetScript() { String defaulted = ""; Builder builder = new Builder(); String result = builder - .setScript(source) - .build() - .getScript(); - assertEquals("script", target, result); + .setScript(source) + .build() + .getScript(); + assertEquals(target, result, "script"); // setting with empty resets result = builder - .setScript(target) - .setScript("") - .build() - .getScript(); - assertEquals("empty", defaulted, result); + .setScript(target) + .setScript("") + .build() + .getScript(); + assertEquals(defaulted, result, "empty"); // settting with null also resets result = builder @@ -854,14 +844,17 @@ public void testBuilderSetScript() { .setScript(null) .build() .getScript(); - assertEquals("null", defaulted, result); + assertEquals(defaulted, result, "null"); // ill-formed script codes throw IAE // must be 4alpha - new BuilderILE("abc", "abcde", "l3tn") { public void call() { b.setScript(arg); }}; + for (String arg : List.of("abc", "abcde", "l3tn")) { + assertThrows(IllformedLocaleException.class, () -> new Builder().setScript(arg)); + } + // script code validation is NOT performed, any 4-alpha passes - assertEquals("4alpha", "Wxyz", builder.setScript("wxyz").build().getScript()); + assertEquals("Wxyz", builder.setScript("wxyz").build().getScript(), "4alpha"); } @Test @@ -872,18 +865,18 @@ public void testBuilderSetRegion() { String defaulted = ""; Builder builder = new Builder(); String result = builder - .setRegion(source) - .build() - .getCountry(); - assertEquals("us", target, result); + .setRegion(source) + .build() + .getCountry(); + assertEquals(target, result, "us"); // setting with empty resets result = builder - .setRegion(target) - .setRegion("") - .build() - .getCountry(); - assertEquals("empty", defaulted, result); + .setRegion(target) + .setRegion("") + .build() + .getCountry(); + assertEquals(defaulted, result, "empty"); // setting with null also resets result = builder @@ -891,15 +884,17 @@ public void testBuilderSetRegion() { .setRegion(null) .build() .getCountry(); - assertEquals("null", defaulted, result); + assertEquals(defaulted, result, "null"); // ill-formed region codes throw IAE // 2 alpha or 3 numeric - new BuilderILE("q", "abc", "12", "1234", "a3", "12a") { public void call() { b.setRegion(arg); }}; + for (String arg : List.of("q", "abc", "12", "1234", "a3", "12a")) { + assertThrows(IllformedLocaleException.class, () -> new Builder().setRegion(arg)); + } // region code validation is NOT performed, any 2-alpha or 3-digit passes - assertEquals("2alpha", "ZZ", builder.setRegion("ZZ").build().getCountry()); - assertEquals("3digit", "000", builder.setRegion("000").build().getCountry()); + assertEquals("ZZ", builder.setRegion("ZZ").build().getCountry(), "2alpha"); + assertEquals("000", builder.setRegion("000").build().getCountry(), "3digit"); } @Test @@ -910,31 +905,31 @@ public void testBuilderSetVariant() { String defaulted = ""; Builder builder = new Builder(); String result = builder - .setVariant(source) - .build() - .getVariant(); - assertEquals("NewYork", target, result); + .setVariant(source) + .build() + .getVariant(); + assertEquals(target, result, "NewYork"); result = builder - .setVariant("NeWeR_YoRkEr") - .build() - .toLanguageTag(); - assertEquals("newer yorker", "und-NeWeR-YoRkEr", result); + .setVariant("NeWeR_YoRkEr") + .build() + .toLanguageTag(); + assertEquals("und-NeWeR-YoRkEr", result, "newer yorker"); // subtags of variant are NOT reordered result = builder - .setVariant("zzzzz_yyyyy_xxxxx") - .build() - .getVariant(); - assertEquals("zyx", "zzzzz_yyyyy_xxxxx", result); + .setVariant("zzzzz_yyyyy_xxxxx") + .build() + .getVariant(); + assertEquals("zzzzz_yyyyy_xxxxx", result, "zyx"); // setting to empty resets result = builder - .setVariant(target) - .setVariant("") - .build() - .getVariant(); - assertEquals("empty", defaulted, result); + .setVariant(target) + .setVariant("") + .build() + .getVariant(); + assertEquals(defaulted, result, "empty"); // setting to null also resets result = builder @@ -942,17 +937,21 @@ public void testBuilderSetVariant() { .setVariant(null) .build() .getVariant(); - assertEquals("null", defaulted, result); + assertEquals(defaulted, result, "null"); // ill-formed variants throw IAE // digit followed by 3-7 characters, or alpha followed by 4-8 characters. - new BuilderILE("abcd", "abcdefghi", "1ab", "1abcdefgh") { public void call() { b.setVariant(arg); }}; + for (String arg : List.of("abcd", "abcdefghi", "1ab", "1abcdefgh")) { + assertThrows(IllformedLocaleException.class, () -> new Builder().setVariant(arg)); + } + // 4 characters is ok as long as the first is a digit - assertEquals("digit+3alpha", "1abc", builder.setVariant("1abc").build().getVariant()); + assertEquals("1abc", builder.setVariant("1abc").build().getVariant(), "digit+3alpha"); // all subfields must conform - new BuilderILE("abcde-fg") { public void call() { b.setVariant(arg); }}; + assertThrows(IllformedLocaleException.class, () -> new Builder().setVariant("abcde-fg")); + } @Test @@ -963,18 +962,18 @@ public void testBuilderSetExtension() { String target = "ab-abcdefgh-12-12345678"; Builder builder = new Builder(); String result = builder - .setExtension(sourceKey, sourceValue) - .build() - .getExtension(sourceKey); - assertEquals("extension", target, result); + .setExtension(sourceKey, sourceValue) + .build() + .getExtension(sourceKey); + assertEquals(target, result, "extension"); // setting with empty resets result = builder - .setExtension(sourceKey, sourceValue) - .setExtension(sourceKey, "") - .build() - .getExtension(sourceKey); - assertEquals("empty", null, result); + .setExtension(sourceKey, sourceValue) + .setExtension(sourceKey, "") + .build() + .getExtension(sourceKey); + assertNull(result, "empty"); // setting with null also resets result = builder @@ -982,100 +981,120 @@ public void testBuilderSetExtension() { .setExtension(sourceKey, null) .build() .getExtension(sourceKey); - assertEquals("null", null, result); + assertNull(result, "null"); // ill-formed extension keys throw IAE // must be in [0-9a-ZA-Z] - new BuilderILE("$") { public void call() { b.setExtension('$', sourceValue); }}; + assertThrows(IllformedLocaleException.class, + () -> new Builder().setExtension('$', sourceValue)); + // each segment of value must be 2-8 alphanum - new BuilderILE("ab-cd-123456789") { public void call() { b.setExtension(sourceKey, arg); }}; + assertThrows(IllformedLocaleException.class, + () -> new Builder().setExtension(sourceKey, "ab-cd-123456789")); + // no multiple hyphens. - new BuilderILE("ab--cd") { public void call() { b.setExtension(sourceKey, arg); }}; + assertThrows(IllformedLocaleException.class, + () -> new Builder().setExtension(sourceKey, "ab--cd")); + // locale extension key has special handling Locale locale = builder - .setExtension('u', "co-japanese") - .build(); - assertEquals("locale extension", "japanese", locale.getUnicodeLocaleType("co")); + .setExtension('u', "co-japanese") + .build(); + assertEquals("japanese", locale.getUnicodeLocaleType("co"), "locale extension"); // locale extension has same behavior with set locale keyword Locale locale2 = builder - .setUnicodeLocaleKeyword("co", "japanese") - .build(); - assertEquals("locales with extension", locale, locale2); + .setUnicodeLocaleKeyword("co", "japanese") + .build(); + assertEquals(locale, locale2, "locales with extension"); // setting locale extension overrides all previous calls to setLocaleKeyword Locale locale3 = builder - .setExtension('u', "xxx-nu-thai") - .build(); - assertEquals("remove co", null, locale3.getUnicodeLocaleType("co")); - assertEquals("override thai", "thai", locale3.getUnicodeLocaleType("nu")); - assertEquals("override attribute", 1, locale3.getUnicodeLocaleAttributes().size()); + .setExtension('u', "xxx-nu-thai") + .build(); + assertNull(locale3.getUnicodeLocaleType("co"), "remove co"); + assertEquals("thai", locale3.getUnicodeLocaleType("nu"), "override thai"); + assertEquals(1, locale3.getUnicodeLocaleAttributes().size(), "override attribute"); // setting locale keyword extends values already set by the locale extension Locale locale4 = builder - .setUnicodeLocaleKeyword("co", "japanese") - .build(); - assertEquals("extend", "japanese", locale4.getUnicodeLocaleType("co")); - assertEquals("extend", "thai", locale4.getUnicodeLocaleType("nu")); + .setUnicodeLocaleKeyword("co", "japanese") + .build(); + assertEquals("japanese", locale4.getUnicodeLocaleType("co"), "extend"); + assertEquals("thai", locale4.getUnicodeLocaleType("nu"), "extend"); // locale extension subtags are reordered result = builder - .clear() - .setExtension('u', "456-123-zz-123-yy-456-xx-789") - .build() - .toLanguageTag(); - assertEquals("reorder", "und-u-123-456-xx-789-yy-456-zz-123", result); + .clear() + .setExtension('u', "456-123-zz-123-yy-456-xx-789") + .build() + .toLanguageTag(); + assertEquals("und-u-123-456-xx-789-yy-456-zz-123", result, "reorder"); // multiple keyword types result = builder - .clear() - .setExtension('u', "nu-thai-foobar") - .build() - .getUnicodeLocaleType("nu"); - assertEquals("multiple types", "thai-foobar", result); + .clear() + .setExtension('u', "nu-thai-foobar") + .build() + .getUnicodeLocaleType("nu"); + assertEquals("thai-foobar", result, "multiple types"); // redundant locale extensions are ignored result = builder - .clear() - .setExtension('u', "nu-thai-NU-chinese-xx-1234") - .build() - .toLanguageTag(); - assertEquals("duplicate keys", "und-u-nu-thai-xx-1234", result); + .clear() + .setExtension('u', "nu-thai-NU-chinese-xx-1234") + .build() + .toLanguageTag(); + assertEquals("und-u-nu-thai-xx-1234", result, "duplicate keys"); + + // redundant locale attributes are ignored + result = builder + .clear() + .setExtension('u', "posix-posix") + .build() + .toLanguageTag(); + assertEquals("und-u-posix", result, "duplicate attributes"); } @Test public void testBuilderAddUnicodeLocaleAttribute() { Builder builder = new Builder(); Locale locale = builder - .addUnicodeLocaleAttribute("def") - .addUnicodeLocaleAttribute("abc") - .build(); + .addUnicodeLocaleAttribute("def") + .addUnicodeLocaleAttribute("abc") + .build(); Set uattrs = locale.getUnicodeLocaleAttributes(); - assertEquals("number of attributes", 2, uattrs.size()); - assertTrue("attribute abc", uattrs.contains("abc")); - assertTrue("attribute def", uattrs.contains("def")); + assertEquals(2, uattrs.size(), "number of attributes"); + assertTrue(uattrs.contains("abc"), "attribute abc"); + assertTrue(uattrs.contains("def"), "attribute def"); // remove attribute locale = builder.removeUnicodeLocaleAttribute("xxx") - .build(); + .build(); - assertEquals("remove bogus", 2, uattrs.size()); + uattrs = locale.getUnicodeLocaleAttributes(); + assertEquals(2, uattrs.size(), "remove bogus"); // add duplicate locale = builder.addUnicodeLocaleAttribute("abc") - .build(); - assertEquals("add duplicate", 2, uattrs.size()); + .build(); + uattrs = locale.getUnicodeLocaleAttributes(); + assertEquals(2, uattrs.size(), "add duplicate"); // null attribute throws NPE - new BuilderNPE("null attribute") { public void call() { b.addUnicodeLocaleAttribute(null); }}; - new BuilderNPE("null attribute removal") { public void call() { b.removeUnicodeLocaleAttribute(null); }}; + assertThrows(NullPointerException.class, + () -> new Builder().addUnicodeLocaleAttribute(null), "null attribute"); + + assertThrows(NullPointerException.class, + () -> new Builder().removeUnicodeLocaleAttribute(null), "null attribute removal"); // illformed attribute throws IllformedLocaleException - new BuilderILE("invalid attribute") { public void call() { b.addUnicodeLocaleAttribute("ca"); }}; + assertThrows(IllformedLocaleException.class, + () -> new Builder().addUnicodeLocaleAttribute("ca"), "invalid attribute"); } @Test @@ -1083,43 +1102,51 @@ public void testBuildersetUnicodeLocaleKeyword() { // Note: most behavior is tested in testBuilderSetExtension Builder builder = new Builder(); Locale locale = builder - .setUnicodeLocaleKeyword("co", "japanese") - .setUnicodeLocaleKeyword("nu", "thai") - .build(); - assertEquals("co", "japanese", locale.getUnicodeLocaleType("co")); - assertEquals("nu", "thai", locale.getUnicodeLocaleType("nu")); - assertEquals("keys", 2, locale.getUnicodeLocaleKeys().size()); + .setUnicodeLocaleKeyword("co", "japanese") + .setUnicodeLocaleKeyword("nu", "thai") + .build(); + assertEquals("japanese", locale.getUnicodeLocaleType("co"), "co"); + assertEquals("thai", locale.getUnicodeLocaleType("nu"), "nu"); + assertEquals(2, locale.getUnicodeLocaleKeys().size(), "keys"); // can clear a keyword by setting to null, others remain String result = builder - .setUnicodeLocaleKeyword("co", null) - .build() - .toLanguageTag(); - assertEquals("empty co", "und-u-nu-thai", result); + .setUnicodeLocaleKeyword("co", null) + .build() + .toLanguageTag(); + assertEquals("und-u-nu-thai", result, "empty co"); // locale keyword extension goes when all keywords are gone result = builder - .setUnicodeLocaleKeyword("nu", null) - .build() - .toLanguageTag(); - assertEquals("empty nu", "und", result); + .setUnicodeLocaleKeyword("nu", null) + .build() + .toLanguageTag(); + assertEquals("und", result, "empty nu"); // locale keywords are ordered independent of order of addition result = builder - .setUnicodeLocaleKeyword("zz", "012") - .setUnicodeLocaleKeyword("aa", "345") - .build() - .toLanguageTag(); - assertEquals("reordered", "und-u-aa-345-zz-012", result); + .setUnicodeLocaleKeyword("zz", "012") + .setUnicodeLocaleKeyword("aa", "345") + .build() + .toLanguageTag(); + assertEquals("und-u-aa-345-zz-012", result, "reordered"); // null keyword throws NPE - new BuilderNPE("keyword") { public void call() { b.setUnicodeLocaleKeyword(null, "thai"); }}; + assertThrows(NullPointerException.class, + () -> new Builder().setUnicodeLocaleKeyword(null, "thai"), "keyword"); + // well-formed keywords are two alphanum - new BuilderILE("a", "abc") { public void call() { b.setUnicodeLocaleKeyword(arg, "value"); }}; + for (String arg : List.of("a", "abc")) { + assertThrows(IllformedLocaleException.class, + () -> new Builder().setUnicodeLocaleKeyword(arg, "value")); + } // well-formed values are 3-8 alphanum - new BuilderILE("ab", "abcdefghi") { public void call() { b.setUnicodeLocaleKeyword("ab", arg); }}; + for (String arg : List.of("ab", "abcdefghi")) { + assertThrows(IllformedLocaleException.class, + () -> new Builder().setUnicodeLocaleKeyword("ab", arg)); + } } @Test @@ -1129,13 +1156,15 @@ public void testBuilderPrivateUseExtension() { String target = "c-b-a"; Builder builder = new Builder(); String result = builder - .setExtension(Locale.PRIVATE_USE_EXTENSION, source) - .build() - .getExtension(Locale.PRIVATE_USE_EXTENSION); - assertEquals("abc", target, result); + .setExtension(Locale.PRIVATE_USE_EXTENSION, source) + .build() + .getExtension(Locale.PRIVATE_USE_EXTENSION); + assertEquals(target, result, "abc"); // multiple hyphens are ill-formed - new BuilderILE("a--b") { public void call() { b.setExtension(Locale.PRIVATE_USE_EXTENSION, arg); }}; + assertThrows(IllformedLocaleException.class, + () -> new Builder().setExtension(Locale.PRIVATE_USE_EXTENSION, "a--b"), + "multiple-hyphens should throw IAE"); } @Test @@ -1144,11 +1173,11 @@ public void testBuilderClear() { Builder builder = new Builder(); Locale locale = Locale.forLanguageTag(monster); String result = builder - .setLocale(locale) - .clear() - .build() - .toLanguageTag(); - assertEquals("clear", "und", result); + .setLocale(locale) + .clear() + .build() + .toLanguageTag(); + assertEquals("und", result, "clear"); } @Test @@ -1164,33 +1193,33 @@ public void testBuilderBuild() { @Test public void testSerialize() { final Locale[] testLocales = { - Locale.ROOT, - Locale.ENGLISH, - Locale.US, - Locale.of("en", "US", "Win"), - Locale.of("en", "US", "Win_XP"), - Locale.JAPAN, - Locale.of("ja", "JP", "JP"), - Locale.of("th", "TH"), - Locale.of("th", "TH", "TH"), - Locale.of("no", "NO"), - Locale.of("nb", "NO"), - Locale.of("nn", "NO"), - Locale.of("no", "NO", "NY"), - Locale.of("nn", "NO", "NY"), - Locale.of("he", "IL"), - Locale.of("he", "IL", "var"), - Locale.of("Language", "Country", "Variant"), - Locale.of("", "US"), - Locale.of("", "", "Java"), - Locale.forLanguageTag("en-Latn-US"), - Locale.forLanguageTag("zh-Hans"), - Locale.forLanguageTag("zh-Hant-TW"), - Locale.forLanguageTag("ja-JP-u-ca-japanese"), - Locale.forLanguageTag("und-Hant"), - Locale.forLanguageTag("und-a-123-456"), - Locale.forLanguageTag("en-x-java"), - Locale.forLanguageTag("th-TH-u-ca-buddist-nu-thai-x-lvariant-TH"), + Locale.ROOT, + Locale.ENGLISH, + Locale.US, + Locale.of("en", "US", "Win"), + Locale.of("en", "US", "Win_XP"), + Locale.JAPAN, + Locale.of("ja", "JP", "JP"), + Locale.of("th", "TH"), + Locale.of("th", "TH", "TH"), + Locale.of("no", "NO"), + Locale.of("nb", "NO"), + Locale.of("nn", "NO"), + Locale.of("no", "NO", "NY"), + Locale.of("nn", "NO", "NY"), + Locale.of("he", "IL"), + Locale.of("he", "IL", "var"), + Locale.of("Language", "Country", "Variant"), + Locale.of("", "US"), + Locale.of("", "", "Java"), + Locale.forLanguageTag("en-Latn-US"), + Locale.forLanguageTag("zh-Hans"), + Locale.forLanguageTag("zh-Hant-TW"), + Locale.forLanguageTag("ja-JP-u-ca-japanese"), + Locale.forLanguageTag("und-Hant"), + Locale.forLanguageTag("und-a-123-456"), + Locale.forLanguageTag("en-x-java"), + Locale.forLanguageTag("th-TH-u-ca-buddist-nu-thai-x-lvariant-TH"), }; for (Locale locale : testLocales) { @@ -1205,7 +1234,7 @@ public void testSerialize() { ObjectInputStream ois = new ObjectInputStream(bis); Object o = ois.readObject(); - assertEquals("roundtrip " + locale, locale, o); + assertEquals(locale, o, "roundtrip " + locale); } catch (Exception e) { fail(locale + " encountered exception:" + e.getLocalizedMessage()); } @@ -1231,11 +1260,9 @@ public void testDeserialize6() { } if (dataDir == null) { - fail("'dataDir' is null. serialized.data.dir Property value is "+dataDirName); - return; + fail("'dataDir' is null. serialized.data.dir Property value is " + dataDirName); } else if (!dataDir.isDirectory()) { - fail("'dataDir' is not a directory. dataDir: "+dataDir.toString()); - return; + fail("'dataDir' is not a directory. dataDir: " + dataDir.toString()); } File[] files = dataDir.listFiles(); @@ -1261,10 +1288,9 @@ public void testDeserialize6() { // deserialize try (FileInputStream fis = new FileInputStream(testfile); - ObjectInputStream ois = new ObjectInputStream(fis)) - { + ObjectInputStream ois = new ObjectInputStream(fis)) { Object o = ois.readObject(); - assertEquals("Deserialize Java 6 Locale " + locale, o, locale); + assertEquals(o, locale, "Deserialize Java 6 Locale " + locale); } catch (Exception e) { fail("Exception while reading " + testfile.getAbsolutePath() + " - " + e.getMessage()); } @@ -1284,15 +1310,15 @@ public void testBug7002320() { // extension "nu-thai". // String[][] testdata = { - {"ja-JP-x-lvariant-JP", "ja-JP-u-ca-japanese-x-lvariant-JP"}, // special case 1 - {"ja-JP-x-lvariant-JP-XXX"}, - {"ja-JP-u-ca-japanese-x-lvariant-JP"}, - {"ja-JP-u-ca-gregory-x-lvariant-JP"}, - {"ja-JP-u-cu-jpy-x-lvariant-JP"}, - {"ja-x-lvariant-JP"}, - {"th-TH-x-lvariant-TH", "th-TH-u-nu-thai-x-lvariant-TH"}, // special case 2 - {"th-TH-u-nu-thai-x-lvariant-TH"}, - {"en-US-x-lvariant-JP"}, + {"ja-JP-x-lvariant-JP", "ja-JP-u-ca-japanese-x-lvariant-JP"}, // special case 1 + {"ja-JP-x-lvariant-JP-XXX"}, + {"ja-JP-u-ca-japanese-x-lvariant-JP"}, + {"ja-JP-u-ca-gregory-x-lvariant-JP"}, + {"ja-JP-u-cu-jpy-x-lvariant-JP"}, + {"ja-x-lvariant-JP"}, + {"th-TH-x-lvariant-TH", "th-TH-u-nu-thai-x-lvariant-TH"}, // special case 2 + {"th-TH-u-nu-thai-x-lvariant-TH"}, + {"en-US-x-lvariant-JP"}, }; Builder bldr = new Builder(); @@ -1304,22 +1330,22 @@ public void testBug7002320() { // forLanguageTag Locale loc = Locale.forLanguageTag(in); String out = loc.toLanguageTag(); - assertEquals("Language tag roundtrip by forLanguageTag with input: " + in, expected, out); + assertEquals(expected, out, "Language tag roundtrip by forLanguageTag with input: " + in); // setLanguageTag bldr.clear(); bldr.setLanguageTag(in); loc = bldr.build(); out = loc.toLanguageTag(); - assertEquals("Language tag roundtrip by Builder.setLanguageTag with input: " + in, expected, out); + assertEquals(expected, out, "Language tag roundtrip by Builder.setLanguageTag with input: " + in); } } @Test public void testBug7023613() { String[][] testdata = { - {"en-Latn", "en__#Latn"}, - {"en-u-ca-japanese", "en__#u-ca-japanese"}, + {"en-Latn", "en__#Latn"}, + {"en-u-ca-japanese", "en__#u-ca-japanese"}, }; for (String[] data : testdata) { @@ -1328,7 +1354,7 @@ public void testBug7023613() { Locale loc = Locale.forLanguageTag(in); String out = loc.toString(); - assertEquals("Empty country field with non-empty script/extension with input: " + in, expected, out); + assertEquals(expected, out, "Empty country field with non-empty script/extension with input: " + in); } } @@ -1342,7 +1368,7 @@ public void testBug7033504() { checkCalendar(Locale.of("ja", "JP", "JP"), "java.util.JapaneseImperialCalendar"); checkCalendar(Locale.of("ja", "jp", "JP"), "java.util.JapaneseImperialCalendar"); checkCalendar(Locale.forLanguageTag("en-u-ca-japanese"), - "java.util.JapaneseImperialCalendar"); + "java.util.JapaneseImperialCalendar"); checkDigit(Locale.of("th", "TH", "th"), '0'); checkDigit(Locale.of("th", "th", "th"), '0'); @@ -1353,151 +1379,12 @@ public void testBug7033504() { private void checkCalendar(Locale loc, String expected) { Calendar cal = Calendar.getInstance(loc); - assertEquals("Wrong calendar", expected, cal.getClass().getName()); + assertEquals(expected, cal.getClass().getName(), "Wrong calendar"); } private void checkDigit(Locale loc, Character expected) { DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(loc); Character zero = dfs.getZeroDigit(); - assertEquals("Wrong digit zero char", expected, zero); - } - - /// - /// utility asserts - /// - - private void assertTrue(String msg, boolean v) { - if (!v) { - fail(msg + ": expected true"); - } - } - - private void assertFalse(String msg, boolean v) { - if (v) { - fail(msg + ": expected false"); - } - } - - private void assertEquals(String msg, Object e, Object v) { - if (e == null ? v != null : !e.equals(v)) { - if (e != null) { - e = "'" + e + "'"; - } - if (v != null) { - v = "'" + v + "'"; - } - fail(msg + ": expected " + e + " but got " + v); - } - } - - private void assertNotEquals(String msg, Object e, Object v) { - if (e == null ? v == null : e.equals(v)) { - if (e != null) { - e = "'" + e + "'"; - } - fail(msg + ": expected not equal " + e); - } - } - - private void assertNull(String msg, Object o) { - if (o != null) { - fail(msg + ": expected null but got '" + o + "'"); - } - } - - private void assertNotNull(String msg, Object o) { - if (o == null) { - fail(msg + ": expected non null"); - } - } - - // not currently used, might get rid of exceptions from the API - private abstract class ExceptionTest { - private final Class exceptionClass; - - ExceptionTest(Class exceptionClass) { - this.exceptionClass = exceptionClass; - } - - public void run() { - String failMsg = null; - try { - call(); - failMsg = "expected " + exceptionClass.getName() + " but no exception thrown."; - } - catch (Exception e) { - if (!exceptionClass.isAssignableFrom(e.getClass())) { - failMsg = "expected " + exceptionClass.getName() + " but caught " + e; - } - } - if (failMsg != null) { - String msg = message(); - msg = msg == null ? "" : msg + " "; - fail(msg + failMsg); - } - } - - public String message() { - return null; - } - - public abstract void call(); - } - - private abstract class ExpectNPE extends ExceptionTest { - ExpectNPE() { - super(NullPointerException.class); - run(); - } - } - - private abstract class BuilderNPE extends ExceptionTest { - protected final String msg; - protected final Builder b = new Builder(); - - BuilderNPE(String msg) { - super(NullPointerException.class); - - this.msg = msg; - - run(); - } - - public String message() { - return msg; - } - } - - private abstract class ExpectIAE extends ExceptionTest { - ExpectIAE() { - super(IllegalArgumentException.class); - run(); - } - } - - private abstract class BuilderILE extends ExceptionTest { - protected final String[] args; - protected final Builder b = new Builder(); - - protected String arg; // mutates during call - - BuilderILE(String... args) { - super(IllformedLocaleException.class); - - this.args = args; - - run(); - } - - public void run() { - for (String arg : args) { - this.arg = arg; - super.run(); - } - } - - public String message() { - return "arg: '" + arg + "'"; - } + assertEquals(expected, zero, "Wrong digit zero char"); } } diff --git a/test/jdk/java/util/concurrent/locks/Lock/OOMEInAQS.java b/test/jdk/java/util/concurrent/locks/Lock/OOMEInAQS.java index aee6c3617c8..0ed76f4b885 100644 --- a/test/jdk/java/util/concurrent/locks/Lock/OOMEInAQS.java +++ b/test/jdk/java/util/concurrent/locks/Lock/OOMEInAQS.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 @@ -39,6 +39,7 @@ * @bug 8066859 * @summary Check that AQS-based locks, conditions, and CountDownLatches do not fail when encountering OOME * @requires vm.gc.G1 + * @requires test.thread.factory == null * @requires !(vm.graal.enabled & vm.compMode == "Xcomp") * @run main/othervm -XX:+UseG1GC -XX:-UseGCOverheadLimit -Xmx48M -XX:-UseTLAB OOMEInAQS */ diff --git a/test/jdk/javax/net/ssl/ServerName/SSLSocketSNISensitive.java b/test/jdk/javax/net/ssl/ServerName/SSLSocketSNISensitive.java index 45593b9129e..fd1569b4eea 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 ca5742f37b2..0b030c71459 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 f426cce33e3..bcdfa270394 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 c6bf74bd533..808d137223e 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/JColorChooser/Test4234761.java b/test/jdk/javax/swing/JColorChooser/Test4234761.java index c2b2d9ed7b9..fb55ca37feb 100644 --- a/test/jdk/javax/swing/JColorChooser/Test4234761.java +++ b/test/jdk/javax/swing/JColorChooser/Test4234761.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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,10 +23,12 @@ /* * @test - * @key headful * @bug 4234761 + * @key headful * @summary RGB values sholdn't be changed in transition to HSB tab - * @author Oleg Mokhovikov + * @library /test/lib + * @build jtreg.SkippedException + * @run main Test4234761 */ import java.awt.Color; @@ -35,11 +37,17 @@ import javax.swing.JColorChooser; import javax.swing.JDialog; import javax.swing.JTabbedPane; +import javax.swing.UIManager; + +import jtreg.SkippedException; public class Test4234761 implements PropertyChangeListener { private static final Color COLOR = new Color(51, 51, 51); public static void main(String[] args) { + if (UIManager.getLookAndFeel().getName().contains("GTK")) { + throw new SkippedException("Test skipped for GTK"); + } JColorChooser chooser = new JColorChooser(COLOR); JDialog dialog = Test4177735.show(chooser); diff --git a/test/jdk/javax/swing/JSpinner/8223788/JSpinnerButtonFocusTest.java b/test/jdk/javax/swing/JSpinner/8223788/JSpinnerButtonFocusTest.java index 4060042ca4f..994a03959b2 100644 --- a/test/jdk/javax/swing/JSpinner/8223788/JSpinnerButtonFocusTest.java +++ b/test/jdk/javax/swing/JSpinner/8223788/JSpinnerButtonFocusTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -63,7 +63,7 @@ public static void main(String args[]) throws Exception { robot.setAutoDelay(50); SwingUtilities.invokeAndWait(() -> { - frame = new JFrame(); + frame = new JFrame("JSpinnerButtonFocusTest"); spinner1 = new JSpinner(); spinner2 = new JSpinner(); @@ -72,6 +72,15 @@ public static void main(String args[]) throws Exception { frame.getContentPane().add(spinner2, BorderLayout.SOUTH); editor1 = ((DefaultEditor)spinner1.getEditor()); + editor1.getTextField().addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + super.focusGained(e); + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + latch1.countDown(); + } + }); editor1.setFocusable(false); spinner1.setFocusable(false); @@ -84,26 +93,18 @@ public static void main(String args[]) throws Exception { frame.setFocusTraversalPolicyProvider(true); frame.setAlwaysOnTop(true); - frame.pack(); + frame.setSize(100, 100); + frame.setLocationRelativeTo(null); frame.setVisible(true); }); robot.waitForIdle(); - - editor1.getTextField().addFocusListener(new FocusAdapter() { - @Override - public void focusGained(FocusEvent e) { - super.focusGained(e); - robot.keyPress(KeyEvent.VK_TAB); - robot.keyRelease(KeyEvent.VK_TAB); - latch1.countDown(); - } - }); + robot.delay(1000); SwingUtilities.invokeAndWait(() -> { editor1.getTextField().requestFocusInWindow(); }); - if (!latch1.await(15, TimeUnit.MINUTES)) { + if (!latch1.await(1, TimeUnit.MINUTES)) { throw new RuntimeException(LF.getClassName() + ": Timeout waiting for editor1 to gain focus."); } diff --git a/test/jdk/javax/swing/JTabbedPane/4624207/bug4624207.java b/test/jdk/javax/swing/JTabbedPane/4624207/bug4624207.java index 10de2ab221a..4d2fdcf030c 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/jdk/internal/jimage/ImageLocationTest.java b/test/jdk/jdk/internal/jimage/ImageLocationTest.java new file mode 100644 index 00000000000..66a76328ae9 --- /dev/null +++ b/test/jdk/jdk/internal/jimage/ImageLocationTest.java @@ -0,0 +1,136 @@ +/* + * 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.jimage.ImageLocation; +import jdk.internal.jimage.ModuleReference; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * @test + * @summary Tests for ImageLocation. + * @modules java.base/jdk.internal.jimage + * @run junit/othervm -esa -DDISABLE_PREVIEW_PATCHING=false ImageLocationTest + */ +public class ImageLocationTest { + + @ParameterizedTest + @ValueSource(strings = { + "/modules/modfoo/com", + "/modules/modfoo/com/foo/Foo.class"}) + public void getFlags_resourceNames(String name) { + String previewName = previewName(name); + + int noPreviewFlags = + ImageLocation.getFlags(name, Set.of(name)::contains); + assertEquals(0, noPreviewFlags); + assertFalse(ImageLocation.hasPreviewVersion(noPreviewFlags)); + assertFalse(ImageLocation.isPreviewOnly(noPreviewFlags)); + + int withPreviewFlags = + ImageLocation.getFlags(name, Set.of(name, previewName)::contains); + assertTrue(ImageLocation.hasPreviewVersion(withPreviewFlags)); + assertFalse(ImageLocation.isPreviewOnly(withPreviewFlags)); + + int previewOnlyFlags = ImageLocation.getFlags(previewName, Set.of(previewName)::contains); + assertFalse(ImageLocation.hasPreviewVersion(previewOnlyFlags)); + assertTrue(ImageLocation.isPreviewOnly(previewOnlyFlags)); + } + + @ParameterizedTest + @ValueSource(strings = { + "/modules", + "/packages", + "/modules/modfoo", + "/modules/modfoo/META-INF", + "/modules/modfoo/META-INF/module-info.class"}) + public void getFlags_zero(String name) { + assertEquals(0, ImageLocation.getFlags(name, Set.of(name)::contains)); + } + + @Test + public void getFlags_packageFlags() { + assertThrows( + IllegalArgumentException.class, + () -> ImageLocation.getFlags("/packages/pkgname", p -> true)); + } + + @Test + public void getPackageFlags_noPreview() { + List refs = List.of( + ModuleReference.forPackage("modfoo", false), + ModuleReference.forEmptyPackage("modbar", false), + ModuleReference.forEmptyPackage("modbaz", false)); + int noPreviewFlags = ImageLocation.getPackageFlags(refs); + assertEquals(0, noPreviewFlags); + assertFalse(ImageLocation.hasPreviewVersion(noPreviewFlags)); + assertFalse(ImageLocation.isPreviewOnly(noPreviewFlags)); + } + + @Test + public void getPackageFlags_withPreview() { + List refs = List.of( + ModuleReference.forPackage("modfoo", true), + ModuleReference.forEmptyPackage("modbar", false), + ModuleReference.forEmptyPackage("modbaz", true)); + int withPreviewFlags = ImageLocation.getPackageFlags(refs); + assertTrue(ImageLocation.hasPreviewVersion(withPreviewFlags)); + assertFalse(ImageLocation.isPreviewOnly(withPreviewFlags)); + } + + @Test + public void getPackageFlags_previewOnly() { + List refs = List.of( + ModuleReference.forPackage("modfoo", true), + ModuleReference.forEmptyPackage("modbar", true), + ModuleReference.forEmptyPackage("modbaz", true)); + int previewOnlyFlags = ImageLocation.getPackageFlags(refs); + // Note the asymmetry between this and the getFlags() case. Unlike + // module resources, there is no concept of a separate package directory + // existing in the preview namespace, so a single entry serves both + // purposes, and hasPreviewVersion() and isPreviewOnly() can both be set. + assertTrue(ImageLocation.hasPreviewVersion(previewOnlyFlags)); + assertTrue(ImageLocation.isPreviewOnly(previewOnlyFlags)); + } + + private static final Pattern MODULES_PATH = Pattern.compile("/modules/([^/]+)/(.+)"); + + private static String previewName(String name) { + var m = MODULES_PATH.matcher(name); + if (m.matches() && !m.group(2).startsWith("/META-INF/preview/")) { + return "/modules/" + m.group(1) + "/META-INF/preview/" + m.group(2); + } + throw new IllegalStateException("Invalid modules name: " + name); + } +} diff --git a/test/jdk/jdk/internal/jimage/ImageReaderTest.java b/test/jdk/jdk/internal/jimage/ImageReaderTest.java index de52ed1503d..e835f68f23a 100644 --- a/test/jdk/jdk/internal/jimage/ImageReaderTest.java +++ b/test/jdk/jdk/internal/jimage/ImageReaderTest.java @@ -23,6 +23,7 @@ import jdk.internal.jimage.ImageReader; import jdk.internal.jimage.ImageReader.Node; +import jdk.internal.jimage.PreviewMode; import jdk.test.lib.compiler.InMemoryJavaCompiler; import jdk.test.lib.util.JarBuilder; import jdk.tools.jlink.internal.LinkableRuntimeImage; @@ -43,6 +44,7 @@ import java.util.Set; import java.util.stream.Collectors; +import static java.util.stream.Collectors.toSet; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -63,22 +65,33 @@ * @library /test/jdk/tools/lib * /test/lib * @build tests.* - * @run junit/othervm ImageReaderTest + * @run junit/othervm -esa -DDISABLE_PREVIEW_PATCHING=false ImageReaderTest */ /// Using PER_CLASS lifecycle means the (expensive) image file is only build once. /// There is no mutable test instance state to worry about. @TestInstance(PER_CLASS) public class ImageReaderTest { - + // The '@' prefix marks the entry as a preview entry which will be placed in + // the '/modules//META-INF/preview/...' namespace. private static final Map> IMAGE_ENTRIES = Map.of( "modfoo", Arrays.asList( - "com.foo.Alpha", - "com.foo.Beta", - "com.foo.bar.Gamma"), + "com.foo.HasPreviewVersion", + "com.foo.NormalFoo", + "com.foo.bar.NormalBar", + // Replaces original class in preview mode. + "@com.foo.HasPreviewVersion", + // New class in existing package in preview mode. + "@com.foo.bar.IsPreviewOnly"), "modbar", Arrays.asList( "com.bar.One", - "com.bar.Two")); + "com.bar.Two", + // Two new packages in preview mode (new symbolic links). + "@com.bar.preview.stuff.Foo", + "@com.bar.preview.stuff.Bar"), + "modgus", Arrays.asList( + // A second module with a preview-only empty package (preview). + "@com.bar.preview.other.Gus")); private final Path image = buildJImage(IMAGE_ENTRIES); @ParameterizedTest @@ -91,7 +104,7 @@ public class ImageReaderTest { "/modules/modfoo/com/foo", "/modules/modfoo/com/foo/bar"}) public void testModuleDirectories_expected(String name) throws IOException { - try (ImageReader reader = ImageReader.open(image)) { + try (ImageReader reader = ImageReader.open(image, PreviewMode.DISABLED)) { assertDir(reader, name); } } @@ -106,32 +119,32 @@ public void testModuleDirectories_expected(String name) throws IOException { "/modules/modfoo//com", "/modules/modfoo/com/"}) public void testModuleNodes_absent(String name) throws IOException { - try (ImageReader reader = ImageReader.open(image)) { + try (ImageReader reader = ImageReader.open(image, PreviewMode.DISABLED)) { assertAbsent(reader, name); } } @Test public void testModuleResources() throws IOException { - try (ImageReader reader = ImageReader.open(image)) { - assertNode(reader, "/modules/modfoo/com/foo/Alpha.class"); + try (ImageReader reader = ImageReader.open(image, PreviewMode.DISABLED)) { + assertNode(reader, "/modules/modfoo/com/foo/HasPreviewVersion.class"); assertNode(reader, "/modules/modbar/com/bar/One.class"); ImageClassLoader loader = new ImageClassLoader(reader, IMAGE_ENTRIES.keySet()); - assertEquals("Class: com.foo.Alpha", loader.loadAndGetToString("modfoo", "com.foo.Alpha")); - assertEquals("Class: com.foo.Beta", loader.loadAndGetToString("modfoo", "com.foo.Beta")); - assertEquals("Class: com.foo.bar.Gamma", loader.loadAndGetToString("modfoo", "com.foo.bar.Gamma")); + assertEquals("Class: com.foo.HasPreviewVersion", loader.loadAndGetToString("modfoo", "com.foo.HasPreviewVersion")); + assertEquals("Class: com.foo.NormalFoo", loader.loadAndGetToString("modfoo", "com.foo.NormalFoo")); + assertEquals("Class: com.foo.bar.NormalBar", loader.loadAndGetToString("modfoo", "com.foo.bar.NormalBar")); assertEquals("Class: com.bar.One", loader.loadAndGetToString("modbar", "com.bar.One")); } } @ParameterizedTest @CsvSource(delimiter = ':', value = { - "modfoo:com/foo/Alpha.class", + "modfoo:com/foo/HasPreviewVersion.class", "modbar:com/bar/One.class", }) public void testResource_present(String modName, String resPath) throws IOException { - try (ImageReader reader = ImageReader.open(image)) { + try (ImageReader reader = ImageReader.open(image, PreviewMode.DISABLED)) { assertNotNull(reader.findResourceNode(modName, resPath)); assertTrue(reader.containsResource(modName, resPath)); @@ -147,18 +160,18 @@ public void testResource_present(String modName, String resPath) throws IOExcept "modfoo:/com/bar/One.class", // Resource in wrong module. "modfoo:com/bar/One.class", - "modbar:com/foo/Alpha.class", + "modbar:com/foo/HasPreviewVersion.class", // Directories are not returned. "modfoo:com/foo", "modbar:com/bar", // JImage entries exist for these, but they are not resources. - "modules:modfoo/com/foo/Alpha.class", + "modules:modfoo/com/foo/HasPreviewVersion.class", "packages:com.foo/modfoo", // Empty module names/paths do not find resources. - "'':modfoo/com/foo/Alpha.class", + "'':modfoo/com/foo/HasPreviewVersion.class", "modfoo:''"}) public void testResource_absent(String modName, String resPath) throws IOException { - try (ImageReader reader = ImageReader.open(image)) { + try (ImageReader reader = ImageReader.open(image, PreviewMode.DISABLED)) { assertNull(reader.findResourceNode(modName, resPath)); assertFalse(reader.containsResource(modName, resPath)); @@ -175,10 +188,10 @@ public void testResource_absent(String modName, String resPath) throws IOExcepti // Don't permit module names to contain paths. "modfoo/com/bar:One.class", "modfoo/com:bar/One.class", - "modules/modfoo/com:foo/Alpha.class", + "modules/modfoo/com:foo/HasPreviewVersion.class", }) public void testResource_invalid(String modName, String resPath) throws IOException { - try (ImageReader reader = ImageReader.open(image)) { + try (ImageReader reader = ImageReader.open(image, PreviewMode.DISABLED)) { assertThrows(IllegalArgumentException.class, () -> reader.containsResource(modName, resPath)); assertThrows(IllegalArgumentException.class, () -> reader.findResourceNode(modName, resPath)); } @@ -186,9 +199,9 @@ public void testResource_invalid(String modName, String resPath) throws IOExcept @Test public void testPackageDirectories() throws IOException { - try (ImageReader reader = ImageReader.open(image)) { + try (ImageReader reader = ImageReader.open(image, PreviewMode.DISABLED)) { Node root = assertDir(reader, "/packages"); - Set pkgNames = root.getChildNames().collect(Collectors.toSet()); + Set pkgNames = root.getChildNames().collect(toSet()); assertTrue(pkgNames.contains("/packages/com")); assertTrue(pkgNames.contains("/packages/com.foo")); assertTrue(pkgNames.contains("/packages/com.bar")); @@ -203,7 +216,7 @@ public void testPackageDirectories() throws IOException { @Test public void testPackageLinks() throws IOException { - try (ImageReader reader = ImageReader.open(image)) { + try (ImageReader reader = ImageReader.open(image, PreviewMode.DISABLED)) { Node moduleFoo = assertDir(reader, "/modules/modfoo"); Node moduleBar = assertDir(reader, "/modules/modbar"); assertSame(assertLink(reader, "/packages/com.foo/modfoo").resolveLink(), moduleFoo); @@ -211,6 +224,123 @@ public void testPackageLinks() throws IOException { } } + @Test + public void testPreviewResources_disabled() throws IOException { + try (ImageReader reader = ImageReader.open(image, PreviewMode.DISABLED)) { + ImageClassLoader loader = new ImageClassLoader(reader, IMAGE_ENTRIES.keySet()); + + // No preview classes visible. + assertEquals("Class: com.foo.HasPreviewVersion", loader.loadAndGetToString("modfoo", "com.foo.HasPreviewVersion")); + assertEquals("Class: com.foo.NormalFoo", loader.loadAndGetToString("modfoo", "com.foo.NormalFoo")); + assertEquals("Class: com.foo.bar.NormalBar", loader.loadAndGetToString("modfoo", "com.foo.bar.NormalBar")); + + // NormalBar exists but IsPreviewOnly doesn't. + assertResource(reader, "modfoo", "com/foo/bar/NormalBar.class"); + assertAbsent(reader, "/modules/modfoo/com/foo/bar/IsPreviewOnly.class"); + assertDirContents(reader, "/modules/modfoo/com/foo", "HasPreviewVersion.class", "NormalFoo.class", "bar"); + assertDirContents(reader, "/modules/modfoo/com/foo/bar", "NormalBar.class"); + } + } + + @Test + public void testPreviewResources_enabled() throws IOException { + try (ImageReader reader = ImageReader.open(image, PreviewMode.ENABLED)) { + ImageClassLoader loader = new ImageClassLoader(reader, IMAGE_ENTRIES.keySet()); + + // Preview version of classes either overwrite existing entries or are added to directories. + assertEquals("Preview: com.foo.HasPreviewVersion", loader.loadAndGetToString("modfoo", "com.foo.HasPreviewVersion")); + assertEquals("Class: com.foo.NormalFoo", loader.loadAndGetToString("modfoo", "com.foo.NormalFoo")); + assertEquals("Class: com.foo.bar.NormalBar", loader.loadAndGetToString("modfoo", "com.foo.bar.NormalBar")); + assertEquals("Preview: com.foo.bar.IsPreviewOnly", loader.loadAndGetToString("modfoo", "com.foo.bar.IsPreviewOnly")); + + // Both NormalBar and IsPreviewOnly exist (direct lookup and as child nodes). + assertResource(reader, "modfoo", "com/foo/bar/NormalBar.class"); + assertResource(reader, "modfoo", "com/foo/bar/IsPreviewOnly.class"); + assertDirContents(reader, "/modules/modfoo/com/foo", "HasPreviewVersion.class", "NormalFoo.class", "bar"); + assertDirContents(reader, "/modules/modfoo/com/foo/bar", "NormalBar.class", "IsPreviewOnly.class"); + } + } + + @Test + public void testPreviewOnlyPackages_disabled() throws IOException { + try (ImageReader reader = ImageReader.open(image, PreviewMode.DISABLED)) { + ImageClassLoader loader = new ImageClassLoader(reader, IMAGE_ENTRIES.keySet()); + + // No 'preview' package or anything inside it. + assertDirContents(reader, "/modules/modbar/com/bar", "One.class", "Two.class"); + assertAbsent(reader, "/modules/modbar/com/bar/preview"); + assertAbsent(reader, "/modules/modbar/com/bar/preview/stuff/Foo.class"); + + // And no package link. + assertAbsent(reader, "/packages/com.bar.preview"); + } + } + + @Test + public void testPreviewOnlyPackages_enabled() throws IOException { + try (ImageReader reader = ImageReader.open(image, PreviewMode.ENABLED)) { + ImageClassLoader loader = new ImageClassLoader(reader, IMAGE_ENTRIES.keySet()); + + // In preview mode 'preview' package exists with preview only content. + assertDirContents(reader, "/modules/modbar/com/bar", "One.class", "Two.class", "preview"); + assertDirContents(reader, "/modules/modbar/com/bar/preview/stuff", "Foo.class", "Bar.class"); + assertResource(reader, "modbar", "com/bar/preview/stuff/Foo.class"); + + // And package links exists. + assertDirContents(reader, "/packages/com.bar.preview", "modbar", "modgus"); + } + } + + @Test + public void testPreviewModeLinks_disabled() throws IOException { + try (ImageReader reader = ImageReader.open(image, PreviewMode.DISABLED)) { + assertDirContents(reader, "/packages/com.bar", "modbar"); + // Missing symbolic link and directory when not in preview mode. + assertAbsent(reader, "/packages/com.bar.preview"); + assertAbsent(reader, "/packages/com.bar.preview.stuff"); + assertAbsent(reader, "/modules/modbar/com/bar/preview"); + assertAbsent(reader, "/modules/modbar/com/bar/preview/stuff"); + } + } + + @Test + public void testPreviewModeLinks_enabled() throws IOException { + try (ImageReader reader = ImageReader.open(image, PreviewMode.ENABLED)) { + // In preview mode there is a new preview-only module visible. + assertDirContents(reader, "/packages/com.bar", "modbar", "modgus"); + // And additional packages are present. + assertDirContents(reader, "/packages/com.bar.preview", "modbar", "modgus"); + assertDirContents(reader, "/packages/com.bar.preview.stuff", "modbar"); + assertDirContents(reader, "/packages/com.bar.preview.other", "modgus"); + // And the preview-only content appears as we expect. + assertDirContents(reader, "/modules/modbar/com/bar", "One.class", "Two.class", "preview"); + assertDirContents(reader, "/modules/modbar/com/bar/preview", "stuff"); + assertDirContents(reader, "/modules/modbar/com/bar/preview/stuff", "Foo.class", "Bar.class"); + // In both modules in which it was added. + assertDirContents(reader, "/modules/modgus/com/bar", "preview"); + assertDirContents(reader, "/modules/modgus/com/bar/preview", "other"); + assertDirContents(reader, "/modules/modgus/com/bar/preview/other", "Gus.class"); + } + } + + @ParameterizedTest + @ValueSource(booleans = {false, true}) + public void testPreviewEntriesAlwaysHidden(boolean previewMode) throws IOException { + try (ImageReader reader = ImageReader.open(image, previewMode ? PreviewMode.ENABLED : PreviewMode.DISABLED)) { + // The META-INF directory exists, but does not contain the preview directory. + Node dir = assertDir(reader, "/modules/modfoo/META-INF"); + assertEquals(0, dir.getChildNames().filter(n -> n.endsWith("/preview")).count()); + // Neither the preview directory, nor anything in it, can be looked-up directly. + assertAbsent(reader, "/modules/modfoo/META-INF/preview"); + assertAbsent(reader, "/modules/modfoo/META-INF/preview/com/foo"); + // HasPreviewVersion.class is a preview class in the test data, and thus appears in + // two places in the jimage). Ensure the preview version is always hidden. + String alphaPath = "com/foo/HasPreviewVersion.class"; + assertNode(reader, "/modules/modfoo/" + alphaPath); + assertAbsent(reader, "/modules/modfoo/META-INF/preview/" + alphaPath); + } + } + private static ImageReader.Node assertNode(ImageReader reader, String name) throws IOException { ImageReader.Node node = reader.findNode(name); assertNotNull(node, "Could not find node: " + name); @@ -223,9 +353,30 @@ private static ImageReader.Node assertDir(ImageReader reader, String name) throw return dir; } + private static void assertDirContents(ImageReader reader, String name, String... expectedChildNames) throws IOException { + Node dir = assertDir(reader, name); + Set localChildNames = dir.getChildNames() + .peek(s -> assertTrue(s.startsWith(name + "/"))) + .map(s -> s.substring(name.length() + 1)) + .collect(toSet()); + assertEquals( + Set.of(expectedChildNames), + localChildNames, + String.format("Unexpected child names in directory '%s'", name)); + } + + private static void assertResource(ImageReader reader, String modName, String resPath) throws IOException { + assertTrue(reader.containsResource(modName, resPath), "Resource should exist: " + modName + "/" + resPath); + Node resNode = reader.findResourceNode(modName, resPath); + assertTrue(resNode.isResource(), "Node should be a resource: " + resNode.getName()); + String nodeName = "/modules/" + modName + "/" + resPath; + assertEquals(nodeName, resNode.getName()); + assertSame(resNode, reader.findNode(nodeName)); + } + private static ImageReader.Node assertLink(ImageReader reader, String name) throws IOException { ImageReader.Node link = assertNode(reader, name); - assertTrue(link.isLink(), "Node was not a symbolic link: " + name); + assertTrue(link.isLink(), "Node should be a symbolic link: " + link.getName()); return link; } @@ -250,20 +401,23 @@ public static Path buildJImage(Map> entries) { jar.addEntry("module-info.class", InMemoryJavaCompiler.compile("module-info", moduleInfo)); classes.forEach(fqn -> { + boolean isPreviewEntry = fqn.startsWith("@"); + if (isPreviewEntry) { + fqn = fqn.substring(1); + } int lastDot = fqn.lastIndexOf('.'); String pkg = fqn.substring(0, lastDot); String cls = fqn.substring(lastDot + 1); - - String path = fqn.replace('.', '/') + ".class"; String source = String.format( """ package %s; public class %s { public String toString() { - return "Class: %s"; + return "%s: %s"; } } - """, pkg, cls, fqn); + """, pkg, cls, isPreviewEntry ? "Preview" : "Class", fqn); + String path = (isPreviewEntry ? "META-INF/preview/" : "") + fqn.replace('.', '/') + ".class"; jar.addEntry(path, InMemoryJavaCompiler.compile(fqn, source)); }); try { diff --git a/test/jdk/jdk/internal/jimage/JImageReadTest.java b/test/jdk/jdk/internal/jimage/JImageReadTest.java index 35fb2adb687..f61eef5140e 100644 --- a/test/jdk/jdk/internal/jimage/JImageReadTest.java +++ b/test/jdk/jdk/internal/jimage/JImageReadTest.java @@ -42,6 +42,7 @@ import jdk.internal.jimage.ImageReader; import jdk.internal.jimage.ImageLocation; +import jdk.internal.jimage.PreviewMode; import org.testng.annotations.DataProvider; import org.testng.annotations.Optional; import org.testng.annotations.Parameters; @@ -58,9 +59,9 @@ public class JImageReadTest { static String javaHome = System.getProperty("java.home"); static Path imageFile = Paths.get(javaHome, "lib", "modules"); - @DataProvider(name="classes") + @DataProvider(name = "classes") static Object[][] loadClasses() { - return new Object[][] { + return new Object[][]{ {"java.base", "java/lang/String.class"}, {"java.base", "java/lang/Object.class"}, {"java.base", "sun/reflect/generics/tree/TypeArgument.class"}, @@ -78,7 +79,7 @@ static Object[][] loadClasses() { * @param className the classname * @throws Exception is thrown if there is a test error */ - @Test(dataProvider="classes") + @Test(dataProvider = "classes") public static void test1_ReadClasses(String moduleName, String className) throws Exception { final int classMagic = 0xCAFEBABE; @@ -103,8 +104,8 @@ public static void test1_ReadClasses(String moduleName, String className) throws if (moduleName.contains("NOSUCH") || className.contains("NOSUCH")) { Assert.assertTrue(location == null, "location found for non-existing module: " - + moduleName - + ", or class: " + className); + + moduleName + + ", or class: " + className); return; // no more to test for non-existing class } else { Assert.assertTrue(location != null, "location not found: " + className); @@ -230,7 +231,7 @@ static void test3_verifyNames() { Arrays.sort(names); Arrays.sort(nativeNames); String[] combined = Arrays.copyOf(names, nativeNames.length + names.length); - System.arraycopy(nativeNames,0, combined, names.length, nativeNames.length); + System.arraycopy(nativeNames, 0, combined, names.length, nativeNames.length); Arrays.sort(combined); int missing = 0; for (int i = 0; i < combined.length; i++) { @@ -337,16 +338,16 @@ static void test4_nameTooLong() throws IOException { @Test static void test5_imageReaderEndianness() throws IOException { // Will be opened with native byte order. - try (ImageReader nativeReader = ImageReader.open(imageFile)) { + try (ImageReader nativeReader = ImageReader.open(imageFile, PreviewMode.DISABLED)) { // Just ensure something works as expected. Assert.assertNotNull(nativeReader.findNode("/")); - } catch (IOException expected) { + } catch (IOException unexpected) { Assert.fail("Reader should be openable with native byte order."); } // Reader should not be openable with the wrong byte order. ByteOrder otherOrder = ByteOrder.nativeOrder() == BIG_ENDIAN ? LITTLE_ENDIAN : BIG_ENDIAN; - Assert.assertThrows(IOException.class, () -> ImageReader.open(imageFile, otherOrder)); + Assert.assertThrows(IOException.class, () -> ImageReader.open(imageFile, otherOrder, PreviewMode.DISABLED)); } // main method to run standalone from jtreg @@ -354,7 +355,7 @@ static void test5_imageReaderEndianness() throws IOException { @Parameters({"x"}) @SuppressWarnings("raw_types") public static void main(@Optional String[] args) { - Class[] testclass = { JImageReadTest.class}; + Class[] testclass = {JImageReadTest.class}; TestNG testng = new TestNG(); testng.setTestClasses(testclass); testng.run(); diff --git a/test/jdk/jdk/internal/jimage/ModuleReferenceTest.java b/test/jdk/jdk/internal/jimage/ModuleReferenceTest.java new file mode 100644 index 00000000000..996a86ee0c8 --- /dev/null +++ b/test/jdk/jdk/internal/jimage/ModuleReferenceTest.java @@ -0,0 +1,238 @@ +/* + * 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.jimage.ModuleReference; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.nio.IntBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; + +import static jdk.internal.jimage.ModuleReference.forEmptyPackageIn; +import static jdk.internal.jimage.ModuleReference.forPackageIn; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * @test + * @summary Tests for ModuleReference. + * @modules java.base/jdk.internal.jimage + * @run junit/othervm -esa ModuleReferenceTest + */ +public final class ModuleReferenceTest { + // Copied (not referenced) for testing. + private static final int FLAGS_HAS_PREVIEW_VERSION = 0x1; + private static final int FLAGS_HAS_NORMAL_VERSION = 0x2; + private static final int FLAGS_HAS_CONTENT = 0x4; + + @ParameterizedTest + @ValueSource(booleans = {false, true}) + public void emptyRefs(boolean isPreview) { + ModuleReference ref = forEmptyPackageIn("module", isPreview); + + assertEquals("module", ref.name()); + assertFalse(ref.hasResources()); + assertEquals(isPreview, ref.hasPreviewVersion()); + assertEquals(isPreview, ref.isPreviewOnly()); + } + + @ParameterizedTest + @ValueSource(booleans = {false, true}) + public void resourceRefs(boolean isPreview) { + ModuleReference ref = forPackageIn("module", isPreview); + + assertEquals("module", ref.name()); + assertTrue(ref.hasResources()); + assertEquals(isPreview, ref.hasPreviewVersion()); + assertEquals(isPreview, ref.isPreviewOnly()); + } + + @ParameterizedTest + @ValueSource(booleans = {false, true}) + public void mergedRefs(boolean isPreview) { + ModuleReference emptyRef = forEmptyPackageIn("module", true); + ModuleReference resourceRef = forPackageIn("module", isPreview); + ModuleReference merged = emptyRef.merge(resourceRef); + + // Merging preserves whether there's content. + assertTrue(merged.hasResources()); + // And clears the preview-only status unless it was set in both. + assertEquals(isPreview, merged.isPreviewOnly()); + } + + @Test + public void writeBuffer() { + List refs = Arrays.asList( + forEmptyPackageIn("alpha", true), + forEmptyPackageIn("beta", false).merge(forEmptyPackageIn("beta", true)), + forPackageIn("gamma", false), + forEmptyPackageIn("zeta", false)); + IntBuffer buffer = IntBuffer.allocate(2 * refs.size()); + ModuleReference.write(refs, buffer, fakeEncoder()); + assertArrayEquals( + new int[]{ + FLAGS_HAS_PREVIEW_VERSION, 100, + FLAGS_HAS_NORMAL_VERSION | FLAGS_HAS_PREVIEW_VERSION, 101, + FLAGS_HAS_NORMAL_VERSION | FLAGS_HAS_CONTENT, 102, + FLAGS_HAS_NORMAL_VERSION, 103}, + buffer.array()); + } + + @Test + public void writeBuffer_emptyList() { + IntBuffer buffer = IntBuffer.allocate(0); + var err = assertThrows( + IllegalArgumentException.class, + () -> ModuleReference.write(List.of(), buffer, null)); + assertTrue(err.getMessage().contains("non-empty")); + } + + @Test + public void writeBuffer_badCapacity() { + List refs = Arrays.asList( + forPackageIn("first", false), + forEmptyPackageIn("alpha", false)); + IntBuffer buffer = IntBuffer.allocate(10); + var err = assertThrows( + IllegalArgumentException.class, + () -> ModuleReference.write(refs, buffer, null)); + assertTrue(err.getMessage().contains("buffer capacity")); + } + + @Test + public void writeBuffer_multiplePackagesWithResources() { + // Only one module reference (at most) can have resources. + List refs = Arrays.asList( + forPackageIn("alpha", false), + forPackageIn("beta", false)); + IntBuffer buffer = IntBuffer.allocate(2 * refs.size()); + var err = assertThrows( + IllegalArgumentException.class, + () -> ModuleReference.write(refs, buffer, null)); + assertTrue(err.getMessage().contains("resources")); + } + + @Test + public void writeBuffer_badOrdering() { + // Badly ordered because preview references should come first. + List refs = Arrays.asList( + forEmptyPackageIn("alpha", false), + forEmptyPackageIn("beta", true)); + IntBuffer buffer = IntBuffer.allocate(2 * refs.size()); + var err = assertThrows( + IllegalArgumentException.class, + () -> ModuleReference.write(refs, buffer, null)); + assertTrue(err.getMessage().contains("strictly ordered")); + } + + @Test + public void writeBuffer_duplicateRef() { + // Technically distinct, and correctly sorted, but with duplicate names. + List refs = Arrays.asList( + forEmptyPackageIn("duplicate", true), + forEmptyPackageIn("duplicate", false)); + IntBuffer buffer = IntBuffer.allocate(2 * refs.size()); + var err = assertThrows( + IllegalArgumentException.class, + () -> ModuleReference.write(refs, buffer, null)); + assertTrue(err.getMessage().contains("unique")); + } + + @Test + public void readNameOffsets() { + // Preview versions must be first (important for early exit). + IntBuffer buffer = IntBuffer.wrap(new int[]{ + FLAGS_HAS_NORMAL_VERSION | FLAGS_HAS_PREVIEW_VERSION, 100, + FLAGS_HAS_PREVIEW_VERSION, 101, + FLAGS_HAS_NORMAL_VERSION | FLAGS_HAS_CONTENT, 102, + FLAGS_HAS_NORMAL_VERSION, 103}); + + List normalOffsets = asList(ModuleReference.readNameOffsets(buffer, true, false)); + List previewOffsets = asList(ModuleReference.readNameOffsets(buffer, false, true)); + List allOffsets = asList(ModuleReference.readNameOffsets(buffer, true, true)); + + assertEquals(List.of(100, 102, 103), normalOffsets); + assertEquals(List.of(100, 101), previewOffsets); + assertEquals(List.of(100, 101, 102, 103), allOffsets); + } + + @Test + public void readNameOffsets_badBufferSize() { + var err = assertThrows( + IllegalArgumentException.class, + () -> ModuleReference.readNameOffsets(IntBuffer.allocate(3), true, false)); + assertTrue(err.getMessage().contains("buffer size")); + } + + @Test + public void readNameOffsets_badFlags() { + IntBuffer buffer = IntBuffer.wrap(new int[]{FLAGS_HAS_CONTENT, 100}); + var err = assertThrows( + IllegalArgumentException.class, + () -> ModuleReference.readNameOffsets(buffer, false, false)); + assertTrue(err.getMessage().contains("flags")); + } + + @Test + public void sortOrder_previewFirst() { + List refs = Arrays.asList( + forEmptyPackageIn("normal.beta", false), + forPackageIn("preview.beta", true), + forEmptyPackageIn("preview.alpha", true), + forEmptyPackageIn("normal.alpha", false)); + refs.sort(Comparator.naturalOrder()); + // Non-empty first with remaining sorted by name. + assertEquals( + List.of("preview.alpha", "preview.beta", "normal.alpha", "normal.beta"), + refs.stream().map(ModuleReference::name).toList()); + } + + private static List asList(Iterator src) { + List list = new ArrayList<>(); + src.forEachRemaining(list::add); + return list; + } + + // Encodes strings sequentially starting from index 100. + private static Function fakeEncoder() { + List cache = new ArrayList<>(); + return s -> { + int i = cache.indexOf(s); + if (i == -1) { + cache.add(s); + return 100 + (cache.size() - 1); + } else { + return 100 + i; + } + }; + } +} diff --git a/test/jdk/jdk/internal/misc/TerminatingThreadLocal/TestTerminatingThreadLocal.java b/test/jdk/jdk/internal/misc/TerminatingThreadLocal/TestTerminatingThreadLocal.java index cf46c5b1d22..bbf2e99b950 100644 --- a/test/jdk/jdk/internal/misc/TerminatingThreadLocal/TestTerminatingThreadLocal.java +++ b/test/jdk/jdk/internal/misc/TerminatingThreadLocal/TestTerminatingThreadLocal.java @@ -23,9 +23,7 @@ import jdk.internal.misc.TerminatingThreadLocal; -import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -37,6 +35,8 @@ import java.util.function.Function; import java.util.stream.Stream; +import jdk.test.lib.thread.VThreadScheduler; + import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import static org.testng.Assert.*; @@ -46,6 +46,7 @@ * @bug 8202788 8291897 8357637 * @summary TerminatingThreadLocal unit test * @modules java.base/java.lang:+open java.base/jdk.internal.misc + * @library /test/lib * @requires vm.continuations * @run testng/othervm TestTerminatingThreadLocal */ @@ -139,7 +140,7 @@ protected T initialValue() { // capture carrier Thread carrier = pool.submit(Thread::currentThread).get(); - ThreadFactory factory = virtualThreadBuilder(pool) + ThreadFactory factory = VThreadScheduler.virtualThreadBuilder(pool) .name("ttl-test-virtual-", 0) .factory(); try (var executor = Executors.newThreadPerTaskExecutor(factory)) { @@ -202,24 +203,4 @@ protected void threadTerminated(String value) { assertEquals(terminatedValues, List.of(ttlValue)); } - - /** - * Returns a builder to create virtual threads that use the given scheduler. - */ - static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) { - try { - Class clazz = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder"); - Constructor ctor = clazz.getDeclaredConstructor(Executor.class); - ctor.setAccessible(true); - return (Thread.Builder.OfVirtual) ctor.newInstance(scheduler); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - if (cause instanceof RuntimeException re) { - throw re; - } - throw new RuntimeException(e); - } catch (Exception e) { - throw new RuntimeException(e); - } - } } diff --git a/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java b/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java index 8069965e8d8..c27a1c7480f 100644 --- a/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java +++ b/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -70,7 +70,7 @@ private static void testMemoryFailCount() { // We need swap to execute this test or will SEGV if (memAndSwapLimit <= memLimit) { - System.out.println("No swap memory limits, test case skipped"); + System.out.println("No swap memory limits. Ignoring test!"); } else { long count = Metrics.systemMetrics().getMemoryFailCount(); diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java index 7cbab5c3b86..2afb5ed93b1 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 jdk.test.lib.containers.docker.DockerRunOptions; import jdk.test.lib.containers.docker.DockerTestUtils; import jdk.test.lib.process.OutputAnalyzer; +import jtreg.SkippedException; /* * @test @@ -112,18 +113,22 @@ private static void testMemoryFailCount(String value) throws Exception { // Check whether swapping really works for this test // On some systems there is no swap space enabled. And running - // 'java -Xms{mem-limit} -Xmx{mem-limit} -version' would fail due to swap space size being 0. + // 'java -Xms{mem-limit} -Xmx{mem-limit} -XX:+AlwaysPreTouch -version' + // would fail due to swap space size being 0. Note that when swap is + // properly enabled on the system the container gets the same amount + // of swap as is configured for memory. Thus, 2x{mem-limit} is the actual + // memory and swap bound for this pre-test. DockerRunOptions preOpts = new DockerRunOptions(imageName, "/jdk/bin/java", "-version"); preOpts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") .addDockerOpts("--memory=" + value) + .addJavaOpts("-XX:+AlwaysPreTouch") .addJavaOpts("-Xms" + value) .addJavaOpts("-Xmx" + value); OutputAnalyzer oa = DockerTestUtils.dockerRunJava(preOpts); String output = oa.getOutput(); if (!output.contains("version")) { - System.out.println("Swapping doesn't work for this test."); - return; + throw new SkippedException("Swapping doesn't work for this test."); } DockerRunOptions opts = @@ -137,8 +142,7 @@ private static void testMemoryFailCount(String value) throws Exception { oa = DockerTestUtils.dockerRunJava(opts); output = oa.getOutput(); if (output.contains("Ignoring test")) { - System.out.println("Ignored by the tester"); - return; + throw new SkippedException("Ignored by the tester"); } oa.shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); } diff --git a/test/jdk/jdk/internal/vm/Continuation/Scoped.java b/test/jdk/jdk/internal/vm/Continuation/Scoped.java index 908267792b8..448c2ab71a5 100644 --- a/test/jdk/jdk/internal/vm/Continuation/Scoped.java +++ b/test/jdk/jdk/internal/vm/Continuation/Scoped.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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,6 +25,7 @@ * @test * @summary Nested continuations test * @requires vm.continuations + * @requires test.thread.factory == null * @modules java.base/jdk.internal.vm * @build java.base/java.lang.StackWalkerHelper * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames -Xint Scoped diff --git a/test/jdk/sun/java2d/marlin/Bug8341381.java b/test/jdk/sun/java2d/marlin/Bug8341381.java new file mode 100644 index 00000000000..b469ac49313 --- /dev/null +++ b/test/jdk/sun/java2d/marlin/Bug8341381.java @@ -0,0 +1,601 @@ +/* + * 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.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Locale; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import javax.imageio.ImageIO; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.Timer; + +import static java.lang.System.out; + +/** + * @test + * @bug 8341381 + * @summary fix cubic offsetting issue (numerical accuracy) + * @run main/othervm/timeout=20 Bug8341381 + * @modules java.desktop/sun.java2d.marlin + */ +public final class Bug8341381 { + + static final boolean SHOW_GUI = false; + + static final boolean CHECK_PIXELS = true; + static final boolean TRACE_ALL = false; + static final boolean TRACE_CHECK_PIXELS = false; + + static final boolean SAVE_IMAGE = false; + + static final boolean INTENSIVE = false; + + static final double DPI = 96; + static final float STROKE_WIDTH = 15f; + + // delay is 1 frame at 60hz + static final int DELAY = 16; + // off-screen test step (1.0 by default) + static final double STEP = (INTENSIVE) ? 1.0 / 117 : 1.0; + + // stats: + static int N_TEST = 0; + static int N_FAIL = 0; + + static final AtomicBoolean isMarlin = new AtomicBoolean(); + static final CountDownLatch latch = new CountDownLatch(1); + + public static void main(final String[] args) { + Locale.setDefault(Locale.US); + + // FIRST: Get Marlin runtime state from its log: + + // initialize j.u.l Logger: + final Logger log = Logger.getLogger("sun.java2d.marlin"); + log.addHandler(new Handler() { + @Override + public void publish(LogRecord record) { + final String msg = record.getMessage(); + if (msg != null) { + // last space to avoid matching other settings: + if (msg.startsWith("sun.java2d.renderer ")) { + isMarlin.set(msg.contains("DMarlinRenderingEngine")); + } + } + + final Throwable th = record.getThrown(); + // detect any Throwable: + if (th != null) { + out.println("Test failed:\n" + record.getMessage()); + th.printStackTrace(out); + throw new RuntimeException("Test failed: ", th); + } + } + + @Override + public void flush() { + } + + @Override + public void close() throws SecurityException { + } + }); + + out.println("Bug8341381: start"); + final long startTime = System.currentTimeMillis(); + + // enable Marlin logging & internal checks: + System.setProperty("sun.java2d.renderer.log", "true"); + System.setProperty("sun.java2d.renderer.useLogger", "true"); + + try { + startTest(); + + out.println("WAITING ..."); + latch.await(15, TimeUnit.SECONDS); // 2s typically + + if (isMarlin.get()) { + out.println("Marlin renderer used at runtime."); + } else { + throw new RuntimeException("Marlin renderer NOT used at runtime !"); + } + + // show test report: + out.println("TESTS: " + N_TEST + " FAILS: " + N_FAIL); + + if (N_FAIL > 0) { + throw new RuntimeException("Bug8341381: " + N_FAIL + " / " + N_TEST + " test(s) failed !"); + } + + } catch (InterruptedException ie) { + throw new RuntimeException(ie); + } catch (InvocationTargetException ite) { + throw new RuntimeException(ite); + } finally { + final double elapsed = (System.currentTimeMillis() - startTime) / 1000.0; + out.println("Bug8341381: end (" + elapsed + " s)"); + } + } + + private static void startTest() throws InterruptedException, InvocationTargetException { + if (SHOW_GUI) { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + final JFrame viewer = new JFrame(); + viewer.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + viewer.setContentPane(new CanvasPanel(viewer)); + viewer.pack(); + viewer.setVisible(true); + } + }); + return; + } else { + out.println("STEP: " + STEP); + new Thread(new Runnable() { + @Override + public void run() { + final Context ctx = new Context(); + final Dimension initialDim = ctx.bugDisplay.getSize(DPI); + + double w = initialDim.width; + double h = initialDim.height; + do { + ctx.shouldScale(w, h); + ctx.paintImage(); + + // resize component: + w -= STEP; + h -= STEP; + + } while (ctx.iterate()); + } + }).start(); + } + } + + static final class Context { + + final BugDisplay bugDisplay = new BugDisplay(); + double width = 0.0, height = 0.0; + + BufferedImage bimg = null; + + boolean shouldScale(final double w, final double h) { + if ((w != width) || (h != height) || !bugDisplay.isScaled) { + width = w; + height = h; + bugDisplay.scale(width, height); + N_TEST++; + return true; + } + return false; + } + + void paintImage() { + final int w = bugDisplay.canvasWidth; + final int h = bugDisplay.canvasHeight; + + if ((bimg == null) || (w > bimg.getWidth()) || (h > bimg.getHeight())) { + bimg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE); + } + final Graphics gi = bimg.getGraphics(); + try { + bugDisplay.paint(gi); + } finally { + gi.dispose(); + } + if (!bugDisplay.checkImage(bimg)) { + N_FAIL++; + } + } + + boolean iterate() { + if ((bugDisplay.canvasWidth > 10) || (bugDisplay.canvasHeight > 10)) { + // continue: + return true; + } + out.println("Stop"); + latch.countDown(); + return false; + } + } + + static final class CanvasPanel extends JPanel { + private static final long serialVersionUID = 1L; + + private final Context ctx = new Context(); + private boolean resized = false; + private Timer timer = null; + + public CanvasPanel(final JFrame frame) { + timer = new Timer(DELAY, e -> { + if (resized) { + resized = false; + + if (ctx.iterate()) { + // resize component: + setSize((int) Math.round(ctx.width - 1), (int) Math.round(ctx.height - 1)); + } else { + timer.stop(); + if (frame != null) { + frame.setVisible(false); + } + } + } + }); + timer.setCoalesce(true); + timer.setRepeats(true); + timer.start(); + } + + @Override + public void paint(final Graphics g) { + final Dimension dim = getSize(); + if (ctx.shouldScale(dim.width, dim.height)) { + this.resized = true; + } + super.paint(g); + + // paint on buffered image: + if (CHECK_PIXELS) { + final int w = ctx.bugDisplay.canvasWidth; + final int h = ctx.bugDisplay.canvasHeight; + if (this.resized) { + ctx.paintImage(); + } + g.drawImage(ctx.bimg.getSubimage(0, 0, w, h), 0, 0, null); + } else { + ctx.bugDisplay.paint(g); + } + } + + @Override + public Dimension getPreferredSize() { + return ctx.bugDisplay.getSize(DPI); + } + } + + static final class BugDisplay { + + boolean isScaled = false; + int canvasWidth; + int canvasHeight; + + private final static java.util.List curves1 = Arrays.asList( + new CubicCurve2D.Double(2191.0, 7621.0, 2191.0, 7619.0, 2191.0, 7618.0, 2191.0, 7617.0), + new CubicCurve2D.Double(2191.0, 7617.0, 2191.0, 7617.0, 2191.0, 7616.0, 2191.0, 7615.0), + new CubicCurve2D.Double(2198.0, 7602.0, 2200.0, 7599.0, 2203.0, 7595.0, 2205.0, 7590.0), + new CubicCurve2D.Double(2205.0, 7590.0, 2212.0, 7580.0, 2220.0, 7571.0, 2228.0, 7563.0), + new CubicCurve2D.Double(2228.0, 7563.0, 2233.0, 7557.0, 2239.0, 7551.0, 2245.0, 7546.0), + new CubicCurve2D.Double(2245.0, 7546.0, 2252.0, 7540.0, 2260.0, 7534.0, 2267.0, 7528.0), + new CubicCurve2D.Double(2267.0, 7528.0, 2271.0, 7526.0, 2275.0, 7524.0, 2279.0, 7521.0), + new CubicCurve2D.Double(2279.0, 7521.0, 2279.0, 7520.0, 2280.0, 7520.0, 2281.0, 7519.0) + ); + private final static java.util.List curves2 = Arrays.asList( + new CubicCurve2D.Double(2281.0, 7519.0, 2282.0, 7518.0, 2282.0, 7517.0, 2283.0, 7516.0), + new CubicCurve2D.Double(2283.0, 7516.0, 2284.0, 7515.0, 2284.0, 7515.0, 2285.0, 7514.0), + new CubicCurve2D.Double(2291.0, 7496.0, 2292.0, 7495.0, 2292.0, 7494.0, 2291.0, 7493.0), + new CubicCurve2D.Double(2291.0, 7493.0, 2290.0, 7492.0, 2290.0, 7492.0, 2289.0, 7492.0), + new CubicCurve2D.Double(2289.0, 7492.0, 2288.0, 7491.0, 2286.0, 7492.0, 2285.0, 7492.0), + new CubicCurve2D.Double(2262.0, 7496.0, 2260.0, 7497.0, 2259.0, 7497.0, 2257.0, 7498.0), + new CubicCurve2D.Double(2257.0, 7498.0, 2254.0, 7498.0, 2251.0, 7499.0, 2248.0, 7501.0), + new CubicCurve2D.Double(2248.0, 7501.0, 2247.0, 7501.0, 2245.0, 7502.0, 2244.0, 7503.0), + new CubicCurve2D.Double(2207.0, 7523.0, 2203.0, 7525.0, 2199.0, 7528.0, 2195.0, 7530.0), + new CubicCurve2D.Double(2195.0, 7530.0, 2191.0, 7534.0, 2186.0, 7538.0, 2182.0, 7541.0) + ); + private final static java.util.List curves3 = Arrays.asList( + new CubicCurve2D.Double(2182.0, 7541.0, 2178.0, 7544.0, 2174.0, 7547.0, 2170.0, 7551.0), + new CubicCurve2D.Double(2170.0, 7551.0, 2164.0, 7556.0, 2158.0, 7563.0, 2152.0, 7569.0), + new CubicCurve2D.Double(2152.0, 7569.0, 2148.0, 7573.0, 2145.0, 7577.0, 2141.0, 7582.0), + new CubicCurve2D.Double(2141.0, 7582.0, 2138.0, 7588.0, 2134.0, 7595.0, 2132.0, 7602.0), + new CubicCurve2D.Double(2132.0, 7602.0, 2132.0, 7605.0, 2131.0, 7608.0, 2131.0, 7617.0), + new CubicCurve2D.Double(2131.0, 7617.0, 2131.0, 7620.0, 2131.0, 7622.0, 2131.0, 7624.0), + new CubicCurve2D.Double(2131.0, 7624.0, 2131.0, 7630.0, 2132.0, 7636.0, 2135.0, 7641.0), + new CubicCurve2D.Double(2135.0, 7641.0, 2136.0, 7644.0, 2137.0, 7647.0, 2139.0, 7650.0), + new CubicCurve2D.Double(2139.0, 7650.0, 2143.0, 7658.0, 2149.0, 7664.0, 2155.0, 7670.0), + new CubicCurve2D.Double(2155.0, 7670.0, 2160.0, 7676.0, 2165.0, 7681.0, 2171.0, 7686.0) + ); + private final static java.util.List curves4 = Arrays.asList( + new CubicCurve2D.Double(2171.0, 7686.0, 2174.0, 7689.0, 2177.0, 7692.0, 2180.0, 7694.0), + new CubicCurve2D.Double(2180.0, 7694.0, 2185.0, 7698.0, 2191.0, 7702.0, 2196.0, 7706.0), + new CubicCurve2D.Double(2196.0, 7706.0, 2199.0, 7708.0, 2203.0, 7711.0, 2207.0, 7713.0), + new CubicCurve2D.Double(2244.0, 7734.0, 2245.0, 7734.0, 2247.0, 7735.0, 2248.0, 7736.0), + new CubicCurve2D.Double(2248.0, 7736.0, 2251.0, 7738.0, 2254.0, 7739.0, 2257.0, 7739.0), + new CubicCurve2D.Double(2257.0, 7739.0, 2259.0, 7739.0, 2260.0, 7739.0, 2262.0, 7740.0), + new CubicCurve2D.Double(2285.0, 7745.0, 2286.0, 7745.0, 2288.0, 7745.0, 2289.0, 7745.0), + new CubicCurve2D.Double(2289.0, 7745.0, 2290.0, 7745.0, 2290.0, 7744.0, 2291.0, 7743.0), + new CubicCurve2D.Double(2291.0, 7743.0, 2292.0, 7742.0, 2292.0, 7741.0, 2291.0, 7740.0), + new CubicCurve2D.Double(2285.0, 7722.0, 2284.0, 7721.0, 2284.0, 7721.0, 2283.0, 7720.0), + new CubicCurve2D.Double(2283.0, 7720.0, 2282.0, 7719.0, 2282.0, 7719.0, 2281.0, 7718.0), + new CubicCurve2D.Double(2281.0, 7718.0, 2280.0, 7717.0, 2279.0, 7716.0, 2279.0, 7716.0), + new CubicCurve2D.Double(2279.0, 7716.0, 2275.0, 7712.0, 2271.0, 7710.0, 2267.0, 7708.0), + new CubicCurve2D.Double(2267.0, 7708.0, 2260.0, 7702.0, 2252.0, 7697.0, 2245.0, 7691.0), + new CubicCurve2D.Double(2245.0, 7691.0, 2239.0, 7685.0, 2233.0, 7679.0, 2228.0, 7673.0), + new CubicCurve2D.Double(2228.0, 7673.0, 2220.0, 7665.0, 2212.0, 7656.0, 2205.0, 7646.0), + new CubicCurve2D.Double(2205.0, 7646.0, 2203.0, 7641.0, 2200.0, 7637.0, 2198.0, 7634.0) + ); + + private final static Point2D.Double[] extent = {new Point2D.Double(0.0, 0.0), new Point2D.Double(7777.0, 10005.0)}; + + private final static Stroke STROKE = new BasicStroke(STROKE_WIDTH); + private final static Stroke STROKE_DASHED = new BasicStroke(STROKE_WIDTH, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, + 10.0f, new float[] {100f, 0f}, 0.0f); + + // members: + private final java.util.List allCurves = new ArrayList<>(); + private final Rectangle2D bboxAllCurves = new Rectangle2D.Double(); + + BugDisplay() { + allCurves.addAll(curves1); + allCurves.addAll(curves2); + allCurves.addAll(curves3); + allCurves.addAll(curves4); + + // initialize bounding box: + double x1 = Double.POSITIVE_INFINITY; + double y1 = Double.POSITIVE_INFINITY; + double x2 = Double.NEGATIVE_INFINITY; + double y2 = Double.NEGATIVE_INFINITY; + + for (final CubicCurve2D c : allCurves) { + final Rectangle2D r = c.getBounds2D(); + if (r.getMinX() < x1) { + x1 = r.getMinX(); + } + if (r.getMinY() < y1) { + y1 = r.getMinY(); + } + if (r.getMaxX() > x2) { + x2 = r.getMaxX(); + } + if (r.getMaxY() > y2) { + y2 = r.getMaxY(); + } + } + // add margin of 10%: + final double m = 1.1 * STROKE_WIDTH; + bboxAllCurves.setFrameFromDiagonal(x1 - m, y1 - m, x2 + m, y2 + m); + } + + public void paint(final Graphics g) { + final Graphics2D g2d = (Graphics2D) g; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + g2d.setColor(Color.WHITE); + g2d.fillRect(0, 0, this.canvasWidth, this.canvasHeight); + + // ------ scale + final AffineTransform tx_orig = g2d.getTransform(); + final AffineTransform tx = getDrawTransform(); + g2d.transform(tx); + + // draw bbox: + if (!CHECK_PIXELS) { + g2d.setColor(Color.RED); + g2d.setStroke(STROKE); + g2d.draw(bboxAllCurves); + } + // draw curves: + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); + g2d.setColor(Color.BLACK); + + // dasher + stroker: + g2d.setStroke(STROKE_DASHED); + this.allCurves.forEach(g2d::draw); + + // reset + g2d.setTransform(tx_orig); + } + + private AffineTransform getDrawTransform() { + // ------ scale + double minX = extent[0].x, maxX = extent[1].x; + double minY = extent[0].y, maxY = extent[1].y; + + // we're scaling and respecting the proportions, check which scale to use + double sx = this.canvasWidth / Math.abs(maxX - minX); + double sy = this.canvasHeight / Math.abs(maxY - minY); + double s = Math.min(sx, sy); + + double m00, m11, m02, m12; + if (minX < maxX) { + m00 = s; + m02 = -s * minX; + } else { + // inverted X axis + m00 = -s; + m02 = this.canvasWidth + s * maxX; + } + if (minY < maxY) { + m11 = s; + m12 = -s * minY; + } else { + // inverted Y axis + m11 = -s; + m12 = this.canvasHeight + s * maxY; + } + + // scale to the available view port + AffineTransform scaleTransform = new AffineTransform(m00, 0, 0, m11, m02, m12); + + // invert the Y axis since (0, 0) is at top left for AWT + AffineTransform invertY = new AffineTransform(1, 0, 0, -1, 0, this.canvasHeight); + invertY.concatenate(scaleTransform); + + return invertY; + } + + public Dimension getSize(double dpi) { + double metricScalingFactor = 0.02539999969303608; + // 1 inch = 25,4 millimeter + final double factor = dpi * metricScalingFactor / 25.4; + + int width = (int) Math.ceil(Math.abs(extent[1].x - extent[0].x) * factor); + int height = (int) Math.ceil(Math.abs(extent[1].y - extent[0].y) * factor); + + return new Dimension(width, height); + } + + public void scale(double w, double h) { + double extentWidth = Math.abs(extent[1].x - extent[0].x); + double extentHeight = Math.abs(extent[1].y - extent[0].y); + + double fx = w / extentWidth; + if (fx * extentHeight > h) { + fx = h / extentHeight; + } + this.canvasWidth = (int) Math.round(fx * extentWidth); + this.canvasHeight = (int) Math.round(fx * extentHeight); + + // out.println("canvas scaled (" + canvasWidth + " x " + canvasHeight + ")"); + + this.isScaled = true; + } + + protected boolean checkImage(BufferedImage image) { + final AffineTransform tx = getDrawTransform(); + + final Point2D pMin = new Point2D.Double(bboxAllCurves.getMinX(), bboxAllCurves.getMinY()); + final Point2D pMax = new Point2D.Double(bboxAllCurves.getMaxX(), bboxAllCurves.getMaxY()); + + final Point2D tMin = tx.transform(pMin, null); + final Point2D tMax = tx.transform(pMax, null); + + int xMin = (int) tMin.getX(); + int xMax = (int) tMax.getX(); + if (xMin > xMax) { + int t = xMin; + xMin = xMax; + xMax = t; + } + + int yMin = (int) tMin.getY(); + int yMax = (int) tMax.getY(); + if (yMin > yMax) { + int t = yMin; + yMin = yMax; + yMax = t; + } + // add pixel margin (AA): + xMin -= 3; + xMax += 4; + yMin -= 3; + yMax += 4; + + if (xMin < 0 || xMax > image.getWidth() + || yMin < 0 || yMax > image.getHeight()) { + return true; + } + + // out.println("Checking rectangle: " + tMin + " to " + tMax); + // out.println("X min: " + xMin + " - max: " + xMax); + // out.println("Y min: " + yMin + " - max: " + yMax); + + final Raster raster = image.getData(); + final int expected = Color.WHITE.getRGB(); + int nBadPixels = 0; + + // horizontal lines: + for (int x = xMin; x <= xMax; x++) { + if (!checkPixel(raster, x, yMin, expected)) { + nBadPixels++; + } + if (!checkPixel(raster, x, yMax, expected)) { + nBadPixels++; + } + } + + // vertical lines: + for (int y = yMin; y <= yMax; y++) { + if (!checkPixel(raster, xMin, y, expected)) { + nBadPixels++; + } + if (!checkPixel(raster, xMax, y, expected)) { + nBadPixels++; + } + } + + if (nBadPixels != 0) { + out.println("(" + canvasWidth + " x " + canvasHeight + ") BAD pixels = " + nBadPixels); + + if (SAVE_IMAGE) { + try { + final File file = new File("Bug8341381-" + canvasWidth + "-" + canvasHeight + ".png"); + + out.println("Writing file: " + file.getAbsolutePath()); + ImageIO.write(image.getSubimage(0, 0, canvasWidth, canvasHeight), "PNG", file); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + return false; + } else if (TRACE_ALL) { + out.println("(" + canvasWidth + " x " + canvasHeight + ") OK"); + } + return true; + } + + private final static int[] TMP_RGB = new int[1]; + + private static boolean checkPixel(final Raster raster, + final int x, final int y, + final int expected) { + + final int[] rgb = (int[]) raster.getDataElements(x, y, TMP_RGB); + + if (rgb[0] != expected) { + if (TRACE_CHECK_PIXELS) { + out.println("bad pixel at (" + x + ", " + y + ") = " + rgb[0] + + " expected = " + expected); + } + return false; + } + return true; + } + } +} diff --git a/test/jdk/sun/security/provider/certpath/DisabledAlgorithms/CPValidatorTrustAnchor.java b/test/jdk/sun/security/provider/certpath/DisabledAlgorithms/CPValidatorTrustAnchor.java index 63eaa12b00c..d74d712d8d7 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 be9e42ca544..7f8408f35f0 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 698bbdf88b1..26d5c69e219 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 7f9573a8eeb..051c940b3b0 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 6a67364360f..f1e5415e2c3 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 ff91e3dff81..be3da70f851 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 733ab9fa522..0b542997a19 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 911f53f5120..a10a18971d2 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/jimage/ImageReaderDuplicateChildNodesTest.java b/test/jdk/tools/jimage/ImageReaderDuplicateChildNodesTest.java index 3eed3bf972c..cfa387d6f8b 100644 --- a/test/jdk/tools/jimage/ImageReaderDuplicateChildNodesTest.java +++ b/test/jdk/tools/jimage/ImageReaderDuplicateChildNodesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, 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 @@ -22,6 +22,7 @@ */ import jdk.internal.jimage.ImageReader; +import jdk.internal.jimage.PreviewMode; import java.nio.file.Files; import java.nio.file.Path; @@ -54,7 +55,7 @@ public static void main(final String[] args) throws Exception { System.out.println("Running test against image " + imagePath); final String integersParentResource = "/modules/java.base/java/lang"; final String integerResource = integersParentResource + "/Integer.class"; - try (final ImageReader reader = ImageReader.open(imagePath)) { + try (final ImageReader reader = ImageReader.open(imagePath, PreviewMode.DISABLED)) { // find the child node/resource first final ImageReader.Node integerNode = reader.findNode(integerResource); if (integerNode == null) { diff --git a/test/jdk/tools/jlink/whitebox/ImageResourcesTreeTestDriver.java b/test/jdk/tools/jlink/whitebox/ImageResourcesTreeTestDriver.java new file mode 100644 index 00000000000..225cc637c39 --- /dev/null +++ b/test/jdk/tools/jlink/whitebox/ImageResourcesTreeTestDriver.java @@ -0,0 +1,31 @@ +/* + * 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 + * @summary Whitebox tests for ImageResourcesTree. + * @modules jdk.jlink/jdk.tools.jlink.internal + * @build jdk.jlink/jdk.tools.jlink.internal.ImageResourcesTreeTest + * @run junit/othervm -ea -esa jdk.jlink/jdk.tools.jlink.internal.ImageResourcesTreeTest + */ +public class ImageResourcesTreeTestDriver {} diff --git a/test/jdk/tools/jlink/whitebox/TEST.properties b/test/jdk/tools/jlink/whitebox/TEST.properties new file mode 100644 index 00000000000..35196da49bf --- /dev/null +++ b/test/jdk/tools/jlink/whitebox/TEST.properties @@ -0,0 +1,3 @@ +modules = \ + jdk.jlink/jdk.tools.jlink.internal +bootclasspath.dirs=. diff --git a/test/jdk/tools/jlink/whitebox/jdk.jlink/jdk/tools/jlink/internal/ImageResourcesTreeTest.java b/test/jdk/tools/jlink/whitebox/jdk.jlink/jdk/tools/jlink/internal/ImageResourcesTreeTest.java new file mode 100644 index 00000000000..37999136985 --- /dev/null +++ b/test/jdk/tools/jlink/whitebox/jdk.jlink/jdk/tools/jlink/internal/ImageResourcesTreeTest.java @@ -0,0 +1,261 @@ +/* + * 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.tools.jlink.internal; + +import jdk.internal.jimage.ModuleReference; +import jdk.tools.jlink.internal.ImageResourcesTree.Node; +import jdk.tools.jlink.internal.ImageResourcesTree.PackageNode; +import jdk.tools.jlink.internal.ImageResourcesTree.ResourceNode; +import jdk.tools.jlink.internal.ImageResourcesTree.Tree; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ImageResourcesTreeTest { + + private static final String MODULES_PREFIX = "/modules/"; + private static final String PACKAGES_PREFIX = "/packages/"; + + @Test + public void directoryNodes() { + List paths = List.of( + "/java.base/java/util/SomeClass.class", + "/java.base/java/util/SomeOtherClass.class", + "/java.base/java/util/resource.txt", + "/java.logging/java/util/logging/LoggingClass.class", + "/java.logging/java/util/logging/OtherLoggingClass.class"); + + Tree tree = new Tree(paths); + Map nodes = tree.getMap(); + + // All paths from the root (but not the root itself). + assertTrue(nodes.containsKey("/modules")); + assertTrue(nodes.containsKey("/modules/java.base")); + assertTrue(nodes.containsKey("/modules/java.base/java")); + assertTrue(nodes.containsKey("/modules/java.base/java/util")); + assertFalse(nodes.containsKey("/")); + + // Check for mismatched modules. + assertTrue(nodes.containsKey("/modules/java.logging/java/util/logging")); + assertFalse(nodes.containsKey("/modules/java.base/java/util/logging")); + + Set dirPaths = nodes.keySet().stream() + .filter(p -> p.startsWith(MODULES_PREFIX)) + .collect(Collectors.toSet()); + for (String path : dirPaths) { + Node dir = nodes.get(path); + assertFalse(dir instanceof ResourceNode, "Unexpected resource: " + dir); + assertEquals(path, dir.getPath()); + assertTrue(path.endsWith("/" + dir.getName()), "Unexpected directory name: " + dir); + } + } + + @Test + public void resourceNodes() { + List paths = List.of( + "/java.base/java/util/SomeClass.class", + "/java.base/java/util/SomeOtherClass.class", + "/java.base/java/util/resource.txt", + "/java.logging/java/util/logging/LoggingClass.class", + "/java.logging/java/util/logging/OtherLoggingClass.class"); + + Tree tree = new Tree(paths); + // This map *does not* contain the resources, only the "directory" nodes. + Map nodes = tree.getMap(); + + assertContainsResources( + nodes.get("/modules/java.base/java/util"), + "SomeClass.class", "SomeOtherClass.class", "resource.txt"); + + assertContainsResources( + nodes.get("/modules/java.logging/java/util/logging"), + "LoggingClass.class", "OtherLoggingClass.class"); + } + + @Test + public void expectedPackages() { + // Paths are only to resources. Packages are inferred. + List paths = List.of( + "/java.base/java/util/SomeClass.class", + "/java.logging/java/util/logging/SomeClass.class"); + + Tree tree = new Tree(paths); + Map nodes = tree.getMap(); + Node packages = nodes.get("/packages"); + List pkgNames = nodes.keySet().stream() + .filter(p -> p.startsWith(PACKAGES_PREFIX)) + .map(p -> p.substring(PACKAGES_PREFIX.length())) + .sorted() + .toList(); + + assertEquals(List.of("java", "java.util", "java.util.logging"), pkgNames); + for (String pkgName : pkgNames) { + PackageNode pkgNode = assertInstanceOf(PackageNode.class, packages.getChildren(pkgName)); + assertSame(nodes.get(PACKAGES_PREFIX + pkgNode.getName()), pkgNode); + } + } + + @Test + public void expectedPackageEntries() { + List paths = List.of( + "/java.base/java/util/SomeClass.class", + "/java.logging/java/util/logging/SomeClass.class"); + + Tree tree = new Tree(paths); + Map nodes = tree.getMap(); + PackageNode pkgUtil = getPackageNode(nodes, "java.util"); + List modRefs = pkgUtil.getModuleReferences(); + assertEquals(2, modRefs.size()); + + List modNames = modRefs.stream().map(ModuleReference::name).toList(); + assertEquals(List.of("java.base", "java.logging"), modNames); + + // Ordered by name. + assertNonEmptyRef(modRefs.get(0), "java.base"); + assertEmptyRef(modRefs.get(1), "java.logging"); + } + + @Test + public void expectedPackageEntries_withPreviewResources() { + List paths = List.of( + "/java.base/java/util/SomeClass.class", + "/java.base/java/util/OtherClass.class", + "/java.base/META-INF/preview/java/util/OtherClass.class", + "/java.logging/java/util/logging/SomeClass.class"); + + Tree tree = new Tree(paths); + Map nodes = tree.getMap(); + PackageNode pkgUtil = getPackageNode(nodes, "java.util"); + List modRefs = pkgUtil.getModuleReferences(); + + ModuleReference baseRef = modRefs.get(0); + assertNonEmptyRef(baseRef, "java.base"); + assertTrue(baseRef.hasPreviewVersion()); + } + + @Test + public void expectedPackageEntries_withPreviewOnlyPackages() { + List paths = List.of( + "/java.base/java/util/SomeClass.class", + "/java.base/META-INF/preview/java/util/preview/only/PreviewClass.class"); + + Tree tree = new Tree(paths); + Map nodes = tree.getMap(); + + // Preview only package (with content). + PackageNode nonEmptyPkg = getPackageNode(nodes, "java.util.preview.only"); + ModuleReference nonEmptyRef = nonEmptyPkg.getModuleReferences().getFirst(); + assertNonEmptyPreviewOnlyRef(nonEmptyRef, "java.base"); + + // Preview only packages can be empty. + PackageNode emptyPkg = getPackageNode(nodes, "java.util.preview"); + ModuleReference emptyRef = emptyPkg.getModuleReferences().getFirst(); + assertEmptyPreviewOnlyRef(emptyRef, "java.base"); + } + + @Test + public void expectedPackageOrder_sharedPackage() { + // Resource in many modules define the same package (java.shared), but + // this only has content in one module (java.content). + // Order of test data is shuffled to show reordering in entry list. + // "java.moduleN" would sort before after "java.previewN" if it were + // only sorted by name, but preview entries come first. + // Expect: preview{1..3) -> content -> module{1..3} + List paths = List.of( + // Module with content in "java.shared". + "/java.content/java/shared/MainPackageClass.class", + // Other resources (in other modules) which implicitly define "java.shared". + "/java.module3/java/shared/three/SomeClass.class", + "/java.module2/java/shared/two/SomeClass.class", + "/java.module1/java/shared/one/SomeClass.class", + // Preview resources in other modules which implicitly define "java.shared". + "/java.preview3/META-INF/preview/java/shared/baz/SomeClass.class", + "/java.preview2/META-INF/preview/java/shared/bar/SomeClass.class", + "/java.preview1/META-INF/preview/java/shared/foo/SomeClass.class"); + + Tree tree = new Tree(paths); + Map nodes = tree.getMap(); + + PackageNode sharedPkg = getPackageNode(nodes, "java.shared"); + List refs = sharedPkg.getModuleReferences(); + + // Preview packages first, by name. + int n = 1; + for (ModuleReference ref : refs.subList(0, 3)) { + assertEmptyPreviewOnlyRef(ref, "java.preview" + (n++)); + } + // The content package (simply due to its name). + assertNonEmptyRef(refs.get(3), "java.content"); + // And the non-preview empty packages after. + n = 1; + for (ModuleReference ref : refs.subList(4, 7)) { + assertEmptyRef(ref, "java.module" + (n++)); + } + } + + static PackageNode getPackageNode(Map nodes, String pkgName) { + return assertInstanceOf(PackageNode.class, nodes.get(PACKAGES_PREFIX + pkgName)); + } + + static void assertContainsResources(Node dirNode, String... resourceNames) { + for (String name : resourceNames) { + Node node = assertInstanceOf(ResourceNode.class, dirNode.getChildren(name)); + assertEquals(name, node.getName()); + assertEquals(dirNode.getPath() + "/" + name, node.getPath()); + } + } + + static void assertNonEmptyRef(ModuleReference ref, String modName) { + assertEquals(modName, ref.name(), "Unexpected module name: " + ref); + assertTrue(ref.hasResources(), "Expected non-empty reference: " + ref); + assertFalse(ref.isPreviewOnly(), "Expected not preview-only: " + ref); + } + + static void assertEmptyRef(ModuleReference ref, String modName) { + assertEquals(modName, ref.name(), "Unexpected module name: " + ref); + assertFalse(ref.hasResources(), "Expected empty reference: " + ref); + assertFalse(ref.isPreviewOnly(), "Expected not preview-only: " + ref); + } + + static void assertNonEmptyPreviewOnlyRef(ModuleReference ref, String modName) { + assertEquals(modName, ref.name(), "Unexpected module name: " + ref); + assertTrue(ref.hasResources(), "Expected empty reference: " + ref); + assertTrue(ref.isPreviewOnly(), "Expected preview-only: " + ref); + } + + static void assertEmptyPreviewOnlyRef(ModuleReference ref, String modName) { + assertEquals(modName, ref.name(), "Unexpected module name: " + ref); + assertFalse(ref.hasResources(), "Expected empty reference: " + ref); + assertTrue(ref.isPreviewOnly(), "Expected preview-only: " + ref); + } +} diff --git a/test/jdk/tools/jpackage/apps/PrintEnv.java b/test/jdk/tools/jpackage/apps/PrintEnv.java index 64a243a0abc..4aed3c41422 100644 --- a/test/jdk/tools/jpackage/apps/PrintEnv.java +++ b/test/jdk/tools/jpackage/apps/PrintEnv.java @@ -69,6 +69,7 @@ private static List printArgs(String[] args) { lines.add(ModuleFinder.ofSystem().findAll().stream() .map(ModuleReference::descriptor) .map(ModuleDescriptor::name) + .sorted() .collect(Collectors.joining(","))); } else if (arg.equals(PRINT_WORK_DIR)) { lines.add("$CD=" + Path.of("").toAbsolutePath()); diff --git a/test/jdk/tools/jpackage/clean_stashed_files.sh b/test/jdk/tools/jpackage/clean_stashed_files.sh index 28ad42048ac..db393877b19 100644 --- a/test/jdk/tools/jpackage/clean_stashed_files.sh +++ b/test/jdk/tools/jpackage/clean_stashed_files.sh @@ -120,8 +120,7 @@ macDmgFilterScpt() { # - Trim random absolute temp path # - Replace "/dmg-workdir/" (new) with "/images/" (old) find "$stash_dir" -name '*.scpt' -type f | xargs -I {} sed $sed_inplace_option \ - -e 's|"/.*/jdk.jpackage[0-9]\{1,\}/|"/jdk.jpackage/|' \ - -e 's|"file:///.*/jdk.jpackage[0-9]\{1,\}/|"file:///jdk.jpackage/|' \ + -e 's|/jdk.jpackage[0-9]\{1,\}/|/jdk.jpackage/|' \ -e 's|/dmg-workdir/|/images/|' \ '{}' } diff --git a/test/jdk/tools/jpackage/clean_test_output.sh b/test/jdk/tools/jpackage/clean_test_output.sh index e472d780ded..200d6add299 100644 --- a/test/jdk/tools/jpackage/clean_test_output.sh +++ b/test/jdk/tools/jpackage/clean_test_output.sh @@ -56,6 +56,9 @@ filterFile () { # Strip variable part of temporary directory name `jdk.jpackage5060841750457404688` -e 's|\([\/]\)jdk\.jpackage[0-9]\{1,\}\b|\1jdk.jpackage|g' + # Strip variable part of temporary directory name `jdk.jpackage.test217379316521032539` + -e 's|\([\/]\)jdk\.jpackage\.test[0-9]\{1,\}\b|\1jdk.jpackage.test|g' + # Convert PID value `[PID: 131561]` -e 's/\[PID: [0-9]\{1,\}\]/[PID: ]/' @@ -76,6 +79,41 @@ filterFile () { # Convert variable part of stack trace entry `at jdk.jpackage.test.JPackageCommand.execute(JPackageCommand.java:863)` -e 's/^\(.*\b\.java:\)[0-9]\{1,\}\()\r\{0,1\}\)$/\1N\2/' + + # Whipe out entire output of /usr/bin/hdiutil command. + # It is of little to no interest and contains too many variable parts to deal with individually. + -e '/^Running \/usr\/bin\/hdiutil/,/^Returned:/{ + //,/^Output:/!d + }' + + # Zip stack traces. + -e $'/^\tat /{ + :a + g + N + s/.*\\n// + /^\tat /ba + s/\\(^\t... \\)[0-9]\\{1,\\}\\( more\\)/\\1N\\2/ + s/\(.*\)/\tat \\n\\1/ + P + D + }' + + # Convert PID value in `taskkill /F /PID 5640` + -e 's|taskkill /F /PID [0-9]\{1,\}|taskkill /F /PID |' + + # Convert PID value in `The process with PID 5640 has been terminated` + -e 's|\(The process with PID \)[0-9]\{1,\}\( has been terminated\)|\1\2|' + + # Convert timeout value in `Check timeout value 57182ms is positive` + -e 's|\(Check timeout value \)[0-9]\{1,\}\(ms is positive\)|\1\2|' + + # Convert variable part of /usr/bin/osascript output `jdk.jpackage/config/SigningRuntimeImagePackageTest-dmg-setup.scpt:455:497: execution error: Finder got an error: Can’t set 1 to icon view. (-10006)` + -e 's|\(-dmg-setup.scpt:\)[0-9]\{1,\}:[0-9]\{1,\}\(: execution error: \)|\1\2|' + + # Use the same name for all exceptions. + -e 's|[^ ]\{1,\}\.[^ ]\{1,\}\Exception:|:|g' + -e 's|[^ ]\{1,\}\.[^ ]\{1,\}\ExceptionBox:|:|g' ) sed $sed_inplace_option "$1" "${expressions[@]}" 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 e16b175ab8a..f88d1f81a34 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 00000000000..28b55f98fe2 --- /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 00000000000..0310d276e21 --- /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 da94db30925..4cf89fca3cc 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 50222d89ceb..66da89fc3f9 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 7ab3b824aa4..0701421e999 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 e630659bdb1..2321e4e852e 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 00000000000..ba3131a7680 --- /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/FileAssociations.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/FileAssociations.java index ebdbb474006..576e294874a 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/FileAssociations.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/FileAssociations.java @@ -27,12 +27,11 @@ import java.nio.file.Path; 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.stream.Stream; +import java.util.TreeMap; import jdk.jpackage.internal.util.PathUtils; @@ -44,7 +43,7 @@ public FileAssociations(String faSuffixName) { } private void createFile() { - Map entries = new HashMap<>(Map.of( + Map entries = new TreeMap<>(Map.of( "extension", suffixName, "mime-type", getMime() )); 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 69ea4ecfaa0..6c7b6a25255 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 8984450f54b..b3729093ad2 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 d62575d2fef..c69c29af53a 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 6285d9d93a0..79652a9828e 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java @@ -24,8 +24,8 @@ package jdk.jpackage.test; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; +import java.util.Optional; public final class LauncherIconVerifier { public LauncherIconVerifier() { @@ -38,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; @@ -71,13 +85,13 @@ 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 { TKit.assertFileExists(iconPath); if (!verifyFileInAppImageOnly) { - TKit.assertTrue(-1 == Files.mismatch(expectedIcon, iconPath), + TKit.assertSameFileContent(expectedIcon, iconPath, String.format( "Check icon file [%s] of %s launcher is a copy of source icon file [%s]", iconPath, label, expectedIcon)); 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 87dc203daa1..55cb38f21cf 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 caec0e315c4..9776ab5c4c8 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java @@ -22,6 +22,12 @@ */ 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; import java.lang.reflect.InvocationTargetException; @@ -32,17 +38,17 @@ import java.util.Arrays; import java.util.Collection; 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.TreeMap; +import java.util.TreeSet; import java.util.function.Function; 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; @@ -161,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") @@ -323,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 { @@ -372,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); @@ -389,8 +392,7 @@ static void addBundleDesktopIntegrationVerifier(PackageTest test, boolean integr Map> scriptlets = getScriptlets(cmd); if (integrated) { - Set requiredScriptlets = Stream.of(Scriptlet.values()).sorted().collect( - Collectors.toSet()); + var requiredScriptlets = Stream.of(Scriptlet.values()).sorted().toList(); TKit.assertTrue(scriptlets.keySet().containsAll( requiredScriptlets), String.format( "Check all required scriptlets %s found in the package. Package scriptlets: %s", @@ -452,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) { @@ -488,13 +508,26 @@ private static void verifyDesktopFile(JPackageCommand cmd, Optional mandatoryKeys = new HashSet<>(Set.of("Name", "Comment", + final Set mandatoryKeys = new TreeSet<>(Set.of("Name", "Comment", "Exec", "Icon", "Terminal", "Type", "Categories")); mandatoryKeys.removeAll(data.keySet()); TKit.assertTrue(mandatoryKeys.isEmpty(), String.format( "Check for missing %s keys in the file", mandatoryKeys)); - for (var e : Map.of("Type", "Application", "Terminal", "false").entrySet()) { + final String launcherDescription; + if (cmd.name().equals(launcherName) || predefinedAppImage.isPresent()) { + launcherDescription = Optional.ofNullable(cmd.getArgumentValue("--description")).orElseGet(cmd::name); + } else { + launcherDescription = getAdditionalLauncherProperties(cmd, launcherName).findProperty("description").or(() -> { + 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)); @@ -710,7 +743,7 @@ private static Map> getScriptlets( private static Map> getDebScriptlets( JPackageCommand cmd, Set scriptlets) { - Map> result = new HashMap<>(); + Map> result = new TreeMap<>(); TKit.withTempDirectory("dpkg-control-files", tempDir -> { // Extract control Debian package files into temporary directory Executor.of("dpkg", "-e") @@ -732,7 +765,7 @@ private static Map> getRpmScriptlets( List output = Executor.of("rpm", "-qp", "--scripts", cmd.outputBundle().toString()).executeAndGetOutput(); - Map> result = new HashMap<>(); + Map> result = new TreeMap<>(); List curScriptletBody = null; for (String str : output) { Matcher m = Scriptlet.RPM_HEADER_PATTERN.matcher(str); @@ -766,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) { @@ -846,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); } @@ -887,7 +920,8 @@ Optional findQuotedValue(String property) { private static final Pattern XDG_CMD_ICON_SIZE_PATTERN = Pattern.compile("\\s--size\\s+(\\d+)\\b"); // Values grabbed from https://linux.die.net/man/1/xdg-icon-resource - private static final Set XDG_CMD_VALID_ICON_SIZES = Set.of(16, 22, 32, 48, 64, 128); + private static final Set XDG_CMD_VALID_ICON_SIZES = unmodifiableSortedSet( + new TreeSet<>(List.of(16, 22, 32, 48, 64, 128))); private static final Method getServiceUnitFileName = initGetServiceUnitFileName(); } 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 d01536e327d..3900851f810 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 af9f57c4f7f..7d2bb908edb 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 ae27e292bf6..81d31ed7267 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 e7f06b0d608..fa8fe166f5a 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 00000000000..f35e255951e --- /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 84453038cd2..3226811fe36 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 b3f188bb371..a19b3697a81 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java @@ -25,10 +25,10 @@ import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; import static java.util.stream.Collectors.toSet; +import static jdk.jpackage.internal.util.function.ThrowingBiFunction.toBiFunction; 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; @@ -38,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; @@ -109,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()) { @@ -119,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); } } @@ -141,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); + }); }); }); } @@ -217,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()); } @@ -256,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); } } @@ -308,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); } } @@ -575,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()); @@ -653,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) { @@ -667,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), @@ -678,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)); @@ -689,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), @@ -700,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)); @@ -712,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( @@ -724,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))) { @@ -737,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)); @@ -747,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)); } @@ -764,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(); @@ -776,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(); @@ -797,6 +833,35 @@ public static void assertPathExists(Path path, boolean exists) { } } + public static void assertMismatchFileContent(Path a, Path b) { + assertFilesMismatch(a, b, true, Optional.empty()); + } + + public static void assertMismatchFileContent(Path a, Path b, String msg) { + assertFilesMismatch(a, b, true, Optional.of(msg)); + } + + public static void assertSameFileContent(Path a, Path b) { + assertFilesMismatch(a, b, false, Optional.empty()); + } + + public static void assertSameFileContent(Path a, Path b, String msg) { + assertFilesMismatch(a, b, false, Optional.of(msg)); + } + + public static void assertFilesMismatch(Path a, Path b, boolean expectMismatch, Optional msg) { + var mismatch = toBiFunction(Files::mismatch).apply(a, b) != -1; + if (expectMismatch) { + assertTrue(mismatch, msg.orElseGet(() -> { + return String.format("Check the content of [%s] and [%s] files mismatch", a, b); + })); + } else { + assertTrue(!mismatch, msg.orElseGet(() -> { + return String.format("Check the content of [%s] and [%s] files is the same", a, b); + })); + } + } + public static void assertDirectoryNotEmpty(Path path) { assertDirectoryExists(path, Optional.of(false)); } @@ -853,7 +918,7 @@ public static void assertReadableFileExists(Path path) { } public static void assertUnexpected(String msg) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); error(concatMessages("Unexpected", msg)); } @@ -879,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()) { @@ -906,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()) { @@ -951,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)); @@ -1177,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) { @@ -1194,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) { @@ -1226,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)); } @@ -1262,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() { @@ -1319,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 227c73bc68e..4009fe2f687 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 36ae81b7db4..80c8b133790 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 00000000000..471a7cb55a9 --- /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 00000000000..c91b178cb10 --- /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 4d3b33bcd6b..8d373cb2b86 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/CustomInfoPListTest.java b/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java index f8e606d77e8..d91f4e3504b 100644 --- a/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java +++ b/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java @@ -21,6 +21,7 @@ * questions. */ +import static java.util.Collections.unmodifiableSortedSet; import static java.util.Map.entry; import static jdk.jpackage.internal.util.PListWriter.writeDict; import static jdk.jpackage.internal.util.PListWriter.writePList; @@ -40,6 +41,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.TreeSet; import java.util.function.BiConsumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -142,12 +144,12 @@ public record TestConfig(Set customPLists) { if (customPLists.isEmpty()) { throw new IllegalArgumentException(); } + customPLists = unmodifiableSortedSet(new TreeSet<>(customPLists)); } @Override public String toString() { return customPLists.stream() - .sorted(Comparator.comparing(CustomPListType::role)) .map(CustomPListType::toString) .collect(Collectors.joining("+")); } @@ -155,12 +157,12 @@ public String toString() { JPackageCommand init(JPackageCommand cmd) throws IOException { if (customPLists.contains(CustomPListType.APP_WITH_FA)) { final Path propFile = TKit.createTempFile("fa.properties"); - var map = Map.ofEntries( + final var props = List.of( entry("mime-type", "application/x-jpackage-foo"), entry("extension", "foo"), entry("description", "bar") ); - TKit.createPropertiesFile(propFile, map); + TKit.createPropertiesFile(propFile, props); cmd.setArgumentValue("--file-associations", propFile); } diff --git a/test/jdk/tools/jpackage/macosx/EntitlementsTest.java b/test/jdk/tools/jpackage/macosx/EntitlementsTest.java new file mode 100644 index 00000000000..aa5879e0c61 --- /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/MacFileAssociationsTest.java b/test/jdk/tools/jpackage/macosx/MacFileAssociationsTest.java index 3277b56ab46..ab7a23ec89a 100644 --- a/test/jdk/tools/jpackage/macosx/MacFileAssociationsTest.java +++ b/test/jdk/tools/jpackage/macosx/MacFileAssociationsTest.java @@ -32,6 +32,7 @@ import java.nio.file.Path; import java.util.Comparator; +import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Stream; @@ -61,7 +62,7 @@ public class MacFileAssociationsTest { @Test public static void test() throws Exception { final Path propFile = TKit.createTempFile("fa.properties"); - Map map = Map.ofEntries( + final var props = List.of( entry("mime-type", "application/x-jpackage-foo"), entry("extension", "foo"), entry("description", "bar"), @@ -73,7 +74,7 @@ public static void test() throws Exception { entry("mac.UISupportsDocumentBrowser", "false"), entry("mac.NSExportableTypes", "public.png, public.jpg"), entry("mac.UTTypeConformsTo", "public.image, public.data")); - TKit.createPropertiesFile(propFile, map); + TKit.createPropertiesFile(propFile, props); final var cmd = JPackageCommand.helloAppImage().setFakeRuntime(); cmd.addArguments("--file-associations", propFile); diff --git a/test/jdk/tools/jpackage/macosx/MacSignTest.java b/test/jdk/tools/jpackage/macosx/MacSignTest.java index b6f3f91ff58..f5f8b3825cc 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 e93e659408f..37ba8a6c299 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 94199b31434..906734e6a9c 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 d25d9a7fa81..6db1cb2faab 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 2c2f5c3bb0f..b1e9155dacb 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 3522d8d43e5..16cf616cfd3 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 b137824a910..efcaadc3fa8 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 5484245f111..1e38f9b0c29 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 9c50c6ffc98..f000e79227e 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 a7bfbf376ed..21f475cbd78 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/AppImageFillOrderTest.java b/test/jdk/tools/jpackage/share/AppImageFillOrderTest.java new file mode 100644 index 00000000000..75c0ddfc16f --- /dev/null +++ b/test/jdk/tools/jpackage/share/AppImageFillOrderTest.java @@ -0,0 +1,359 @@ +/* + * 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 java.util.stream.Collectors.toMap; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.TreeMap; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Stream; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.ParameterSupplier; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.AppImageFile; +import jdk.jpackage.test.ApplicationLayout; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.TKit; + +/* + * @test + * @summary test order in which jpackage fills app image + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.* + * @compile -Xlint:all -Werror AppImageFillOrderTest.java + * @run main/othervm/timeout=1440 -Xmx512m + * jdk.jpackage.test.Main + * --jpt-run=AppImageFillOrderTest + */ + +/** + * Test order in which overlapping items are added to the app image. jpackage + * defaults should go first to let user-provided content override them. + * + *

    + * Custom content comes from: + *

      + *
    • input directory (--input) + *
    • app content (--app-content) + *
        + */ +public class AppImageFillOrderTest { + + @Test + @ParameterSupplier + public void test(AppImageOverlay overlays[]) { + test(createJPackage().setFakeRuntime(), overlays); + } + + /** + * Test they can override a file in the runtime. + * @param jlink + */ + @Test + @Parameter("true") + @Parameter("false") + public void testRuntime(boolean jlink) { + var cmd = createJPackage(); + if (jlink) { + cmd.ignoreDefaultRuntime(true); + } else { + // Configure fake runtime and create it. + cmd.setFakeRuntime().executePrerequisiteActions(); + + var runtimeDir = Path.of(cmd.getArgumentValue("--runtime-image")); + if (!runtimeDir.toAbsolutePath().normalize().startsWith(TKit.workDir().toAbsolutePath().normalize())) { + throw new IllegalStateException(String.format( + "Fake runtime [%s] created outside of the test work directory [%s]", + runtimeDir, TKit.workDir())); + } + + TKit.createTextFile(runtimeDir.resolve(RUNTIME_RELEASE_FILE), List.of("Foo release")); + } + + test(cmd, AppImageAppContentOverlay.APP_CONTENT_RUNTIME_RELEASE_FILE); + } + + /** + * Test they can not override .jpackage.xml file. + * @throws IOException + */ + @Test + public void testAppImageFile() throws IOException { + + var cmd = createJPackage().setFakeRuntime(); + + var outputBundle = cmd.outputBundle(); + + buildOverlay(cmd, TKit.createTempDirectory("app-content"), AppImageFile.getPathInAppImage(outputBundle)) + .textContent("This is not a valid XML content") + .configureCmdOptions().createOverlayFile(); + + // Run jpackage and verify it created valid .jpackage.xml file ignoring the overlay. + cmd.executeAndAssertImageCreated(); + + TKit.trace(String.format("Parse [%s] file...", AppImageFile.getPathInAppImage(outputBundle))); + AppImageFile.load(outputBundle); + } + + private static void test(JPackageCommand cmd, AppImageOverlay... overlays) { + if (overlays.length == 0) { + throw new IllegalArgumentException(); + } + + final var outputDir = Path.of(cmd.getArgumentValue("--dest")); + final var noOverlaysOutputDir = Path.of(outputDir.toString() + "-no-overlay"); + cmd.setArgumentValue("--dest", noOverlaysOutputDir); + + // Run the command without overlays with redirected output directory. + cmd.execute(); + + final Optional appContentRoot; + if (Stream.of(overlays).anyMatch(AppImageAppContentOverlay.class::isInstance)) { + appContentRoot = Optional.of(TKit.createTempDirectory("app-content")); + } else { + appContentRoot = Optional.empty(); + } + + // Apply overlays to the command. + var fileCopies = Stream.of(overlays).map(overlay -> { + switch (overlay) { + case AppImageDefaultOverlay v -> { + return v.addOverlay(cmd); + } + case AppImageAppContentOverlay v -> { + return v.addOverlay(cmd, appContentRoot.orElseThrow()); + } + } + }).flatMap(Collection::stream).collect(toMap(FileCopy::out, x -> x, (a, b) -> { + return b; + }, TreeMap::new)).values().stream().toList(); + + // Collect paths in the app image that will be affected by overlays. + var noOverlayOutputPaths = fileCopies.stream().map(FileCopy::out).toList(); + + fileCopies = fileCopies.stream().map(v -> { + return new FileCopy(v.in(), outputDir.resolve(noOverlaysOutputDir.relativize(v.out()))); + }).toList(); + + // Restore the original output directory for the command and execute it. + cmd.setArgumentValue("--dest", outputDir).execute(); + + for (var i = 0; i != fileCopies.size(); i++) { + var noOverlayPath = noOverlayOutputPaths.get(i); + var fc = fileCopies.get(i); + TKit.assertSameFileContent(fc.in(), fc.out()); + TKit.assertMismatchFileContent(noOverlayPath, fc.out()); + } + } + + public static Collection test() { + return Stream.of( + + // Overwrite main launcher .cfg file from the input dir. + List.of(AppImageDefaultOverlay.INPUT_MAIN_LAUNCHER_CFG), + + // Overwrite main launcher .cfg file from the app content dir. + List.of(AppImageAppContentOverlay.APP_CONTENT_MAIN_LAUNCHER_CFG), + + // Overwrite main launcher .cfg file from the input dir and from the app content dir. + // The one from app content should win. + List.of( + AppImageDefaultOverlay.INPUT_MAIN_LAUNCHER_CFG, + AppImageAppContentOverlay.APP_CONTENT_MAIN_LAUNCHER_CFG + ), + + // Overwrite main jar from the app content dir. + List.of(AppImageAppContentOverlay.APP_CONTENT_MAIN_JAR) + ).map(args -> { + return args.toArray(AppImageOverlay[]::new); + }).map(args -> { + return new Object[] {args}; + }).toList(); + } + + + public sealed interface AppImageOverlay { + } + + + private enum AppImageDefaultOverlay implements AppImageOverlay { + INPUT_MAIN_LAUNCHER_CFG(AppImageFillOrderTest::replaceMainLauncherCfgFile), + ; + + AppImageDefaultOverlay(Function func) { + Objects.requireNonNull(func); + this.func = cmd -> { + return List.of(func.apply(cmd)); + }; + } + + Collection addOverlay(JPackageCommand cmd) { + return func.apply(cmd); + } + + private final Function> func; + } + + + private enum AppImageAppContentOverlay implements AppImageOverlay { + // Replace the standard main launcher .cfg file with the custom one from the app content. + APP_CONTENT_MAIN_LAUNCHER_CFG((cmd, appContentRoot) -> { + return buildOverlay(cmd, appContentRoot, cmd.appLauncherCfgPath(null)) + .textContent("!Olleh") + .configureCmdOptions().createOverlayFile(); + }), + + // Replace the jar file that jpackage will pick up from the input directory with the custom one. + APP_CONTENT_MAIN_JAR((cmd, appContentRoot) -> { + return buildOverlay(cmd, appContentRoot, cmd.appLayout().appDirectory().resolve(cmd.getArgumentValue("--main-jar"))) + .textContent("Surprise!") + .configureCmdOptions().createOverlayFile(); + }), + + // Replace "release" file in the runtime directory. + APP_CONTENT_RUNTIME_RELEASE_FILE((cmd, appContentRoot) -> { + return buildOverlay(cmd, appContentRoot, cmd.appLayout().runtimeHomeDirectory().resolve("release")) + .textContent("blob") + .configureCmdOptions().createOverlayFile(); + }), + ; + + AppImageAppContentOverlay(BiFunction func) { + Objects.requireNonNull(func); + this.func = (cmd, appContentRoot) -> { + return List.of(func.apply(cmd, appContentRoot)); + }; + } + + Collection addOverlay(JPackageCommand cmd, Path appContentRoot) { + return func.apply(cmd, appContentRoot); + } + + private final BiFunction> func; + } + + + private record FileCopy(Path in, Path out) { + FileCopy { + Objects.requireNonNull(in); + Objects.requireNonNull(out); + } + } + + + private static FileCopy replaceMainLauncherCfgFile(JPackageCommand cmd) { + // Replace the standard main launcher .cfg file with the custom one from the input dir. + final var outputFile = cmd.appLauncherCfgPath(null); + + final var inputDir = Path.of(cmd.getArgumentValue("--input")); + + final var file = inputDir.resolve(outputFile.getFileName()); + + TKit.createTextFile(file, List.of("Hello!")); + + return new FileCopy(file, outputFile); + } + + private static AppContentOverlayFileBuilder buildOverlay(JPackageCommand cmd, Path appContentRoot, Path outputFile) { + return new AppContentOverlayFileBuilder(cmd, appContentRoot, outputFile); + } + + + private static final class AppContentOverlayFileBuilder { + + AppContentOverlayFileBuilder(JPackageCommand cmd, Path appContentRoot, Path outputFile) { + if (outputFile.isAbsolute()) { + throw new IllegalArgumentException(); + } + + if (!outputFile.startsWith(cmd.outputBundle())) { + throw new IllegalArgumentException(); + } + + this.cmd = Objects.requireNonNull(cmd); + this.outputFile = Objects.requireNonNull(outputFile); + this.appContentRoot = Objects.requireNonNull(appContentRoot); + } + + FileCopy createOverlayFile() { + final var file = appContentRoot.resolve(pathInAppContentDirectory()); + + try { + Files.createDirectories(file.getParent()); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + fileContentInitializer.accept(file); + + return new FileCopy(file, outputFile); + } + + AppContentOverlayFileBuilder configureCmdOptions() { + cmd.addArguments("--app-content", appContentRoot.resolve(pathInAppContentDirectory().getName(0))); + return this; + } + + AppContentOverlayFileBuilder content(Consumer v) { + fileContentInitializer = v; + return this; + } + + AppContentOverlayFileBuilder textContent(String... lines) { + return content(path -> { + TKit.createTextFile(path, List.of(lines)); + }); + } + + private Path pathInAppContentDirectory() { + return APP_IMAGE_LAYOUT.resolveAt(cmd.outputBundle()).contentDirectory().relativize(outputFile); + } + + private Consumer fileContentInitializer; + private final JPackageCommand cmd; + private final Path outputFile; + private final Path appContentRoot; + } + + + private static JPackageCommand createJPackage() { + // With short name. + var cmd = JPackageCommand.helloAppImage().setArgumentValue("--name", "Foo"); + + // Clean leftovers in the input dir from the previous test run if any. + TKit.deleteDirectoryContentsRecursive(cmd.inputDir()); + + return cmd; + } + + private static final ApplicationLayout APP_IMAGE_LAYOUT = ApplicationLayout.platformAppImage(); + private static final Path RUNTIME_RELEASE_FILE = Path.of("release"); +} diff --git a/test/jdk/tools/jpackage/share/AppImagePackageTest.java b/test/jdk/tools/jpackage/share/AppImagePackageTest.java index 34a418c6f9e..aacb76b122b 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/FileAssociationsTest.java b/test/jdk/tools/jpackage/share/FileAssociationsTest.java index 2257c5dbc65..cd4a0491532 100644 --- a/test/jdk/tools/jpackage/share/FileAssociationsTest.java +++ b/test/jdk/tools/jpackage/share/FileAssociationsTest.java @@ -21,10 +21,13 @@ * questions. */ +import static java.util.Map.entry; import static jdk.jpackage.test.JPackageStringBundle.MAIN; import java.nio.file.Path; +import java.util.List; import java.util.Map; +import java.util.TreeMap; import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.FileAssociations; @@ -114,9 +117,9 @@ public static void testNoMime() { final Path propFile = TKit.workDir().resolve("fa.properties"); initPackageTest().addRunOnceInitializer(() -> { - TKit.createPropertiesFile(propFile, Map.of( - "extension", "foo", - "description", "bar" + TKit.createPropertiesFile(propFile, List.of( + entry("extension", "foo"), + entry("description", "bar") )); }).addInitializer(cmd -> { cmd.addArguments("--file-associations", propFile); @@ -131,10 +134,10 @@ public static void testTooManyMimes() { final Path propFile = TKit.workDir().resolve("fa.properties"); initPackageTest().addRunOnceInitializer(() -> { - TKit.createPropertiesFile(propFile, Map.of( - "mime-type", "application/x-jpackage-foo, application/x-jpackage-bar", - "extension", "foo", - "description", "bar" + TKit.createPropertiesFile(propFile, List.of( + entry("mime-type", "application/x-jpackage-foo, application/x-jpackage-bar"), + entry("extension", "foo"), + entry("description", "bar") )); }).addInitializer(cmd -> { cmd.addArguments("--file-associations", propFile); diff --git a/test/jdk/tools/jpackage/share/IconTest.java b/test/jdk/tools/jpackage/share/IconTest.java index a2a9e67bd91..03726e524dc 100644 --- a/test/jdk/tools/jpackage/share/IconTest.java +++ b/test/jdk/tools/jpackage/share/IconTest.java @@ -21,10 +21,10 @@ * questions. */ +import static jdk.jpackage.test.AdditionalLauncher.getAdditionalLauncherProperties; + import java.io.IOException; -import java.util.stream.Stream; -import java.util.stream.Collectors; -import java.util.function.Consumer; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; @@ -32,19 +32,26 @@ 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 jdk.jpackage.test.TKit; -import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.LauncherIconVerifier; -import jdk.jpackage.test.PackageTest; -import jdk.jpackage.test.Executor; -import jdk.jpackage.test.LinuxHelper; -import jdk.jpackage.test.AdditionalLauncher; -import jdk.jpackage.internal.util.function.ThrowingConsumer; +import java.util.TreeMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; import jdk.jpackage.internal.util.function.ThrowingBiConsumer; +import jdk.jpackage.internal.util.function.ThrowingConsumer; +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; +import jdk.jpackage.test.TKit; /* * @test @@ -92,18 +99,18 @@ public IconTest(BundleType bundleType, IconType mainLauncherIconType, IconType additionalLauncherIconType, String[] extraJPackageArgs) { this.appImage = (bundleType == BundleType.AppImage); this.extraJPackageArgs = extraJPackageArgs; - config = Map.of( + config = new TreeMap<>(Map.of( Launcher.Main, mainLauncherIconType, - Launcher.Additional, additionalLauncherIconType); + Launcher.Additional, additionalLauncherIconType)); } public IconTest(BundleType bundleType, IconType mainLauncherIconType, IconType additionalLauncherIconType) { this.appImage = (bundleType == BundleType.AppImage); this.extraJPackageArgs = new String[0]; - config = Map.of( + config = new TreeMap<>(Map.of( Launcher.Main, mainLauncherIconType, - Launcher.Additional, additionalLauncherIconType); + Launcher.Additional, additionalLauncherIconType)); } public IconTest(BundleType bundleType, IconType mainLauncherIconType) { @@ -158,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); @@ -188,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); @@ -279,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: @@ -362,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"); + }); + + cmd.addArguments("--resource-dir", resourceDir); - String dstIconFileName = Optional.ofNullable(launcherName).orElseGet( - () -> cmd.name()) + TKit.ICON_SUFFIX; + String dstIconFileName = Optional.ofNullable(launcherName).orElseGet(cmd::name) + TKit.ICON_SUFFIX; TKit.trace(String.format("Resource file: [%s] <- [%s]", resourceDir.resolve(dstIconFileName), iconPath)); @@ -392,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 f7c597d2ed3..d36731c2960 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 c9e3c8508aa..1c6bfd51b62 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 f66f774b227..caa129713b4 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 7f04ee2bd2e..909ee06b01a 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/jdk/tools/launcher/ChangeDataModel.java b/test/jdk/tools/launcher/ChangeDataModel.java index e6bc281ad62..14945045435 100644 --- a/test/jdk/tools/launcher/ChangeDataModel.java +++ b/test/jdk/tools/launcher/ChangeDataModel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, 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 @@ -27,7 +27,6 @@ * @compile -XDignore.symbol.file ChangeDataModel.java * @run main ChangeDataModel * @summary Verify -d32, -d64 and -J prefixed data-model options are rejected on all platforms - * @author Joseph D. Darcy, ksrini */ import java.util.Arrays; diff --git a/test/jdk/tools/launcher/I18NTest.java b/test/jdk/tools/launcher/I18NTest.java index aa1ce24e798..83d90f56327 100644 --- a/test/jdk/tools/launcher/I18NTest.java +++ b/test/jdk/tools/launcher/I18NTest.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 @@ -27,7 +27,6 @@ * @compile -XDignore.symbol.file I18NTest.java * @run main I18NTest * @summary Test to see if class files with non-ASCII characters can be run - * @author Joseph D. Darcy, Kumar Srinivasan */ diff --git a/test/jdk/tools/launcher/UnresolvedExceptions.java b/test/jdk/tools/launcher/UnresolvedExceptions.java index ce14f405aed..6faa306c34c 100644 --- a/test/jdk/tools/launcher/UnresolvedExceptions.java +++ b/test/jdk/tools/launcher/UnresolvedExceptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -29,7 +29,6 @@ * @compile -XDignore.symbol.file UnresolvedExceptions.java * @run main UnresolvedExceptions * @summary Verifying jvm won't segv if exception not available - * @author Joseph D. Darcy, ksrini */ import java.io.File; diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index 1577df1d822..47b48a5d810 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -149,6 +149,7 @@ public Map call() { vmGC(map); // vm.gc.X = true/false vmGCforCDS(map); // may set vm.gc vmOptFinalFlags(map); + vmOptFinalIntxFlags(map); dump(map.map); log("Leaving call()"); @@ -390,6 +391,26 @@ protected void vmOptFinalFlags(SafeMap map) { vmOptFinalFlag(map, "UseVectorizedMismatchIntrinsic"); } + /** + * Selected final flag of type intx. + * + * @param map - property-value pairs + * @param flagName - flag name + */ + private void vmOptFinalIntxFlag(SafeMap map, String flagName) { + map.put("vm.opt.final." + flagName, + () -> String.valueOf(WB.getIntxVMFlag(flagName))); + } + + /** + * Selected sets of final flags of type intx. + * + * @param map - property-value pairs + */ + protected void vmOptFinalIntxFlags(SafeMap map) { + vmOptFinalIntxFlag(map, "MaxVectorSize"); + } + /** * @return "true" if VM has a serviceability agent. */ diff --git a/test/langtools/jdk/javadoc/tool/api/basic/APITest.java b/test/langtools/jdk/javadoc/tool/api/basic/APITest.java index e4e3c6baa87..71908f34e99 100644 --- a/test/langtools/jdk/javadoc/tool/api/basic/APITest.java +++ b/test/langtools/jdk/javadoc/tool/api/basic/APITest.java @@ -215,6 +215,7 @@ protected void error(String msg) { "resource-files/stylesheet.css", "resource-files/sun.svg", "resource-files/x.svg", + "resource-files/sort-a-z.svg", "resource-files/fonts/dejavu.css", "resource-files/fonts/DejaVuLGCSans-Bold.woff", "resource-files/fonts/DejaVuLGCSans-Bold.woff2", @@ -265,4 +266,3 @@ protected void error(String msg) { && !s.equals("system-properties.html")) .collect(Collectors.toSet()); } - diff --git a/test/lib/jdk/test/lib/security/CertificateBuilder.java b/test/lib/jdk/test/lib/security/CertificateBuilder.java index e5044d46b0f..d35a21e7ab5 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 00000000000..91d26601383 --- /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) {} +} diff --git a/test/micro/org/openjdk/bench/jdk/internal/jrtfs/ImageReaderBenchmark.java b/test/micro/org/openjdk/bench/jdk/internal/jrtfs/ImageReaderBenchmark.java index 4f97e12171f..f2e30bea2a1 100644 --- a/test/micro/org/openjdk/bench/jdk/internal/jrtfs/ImageReaderBenchmark.java +++ b/test/micro/org/openjdk/bench/jdk/internal/jrtfs/ImageReaderBenchmark.java @@ -24,6 +24,7 @@ import jdk.internal.jimage.ImageReader; import jdk.internal.jimage.ImageReader.Node; +import jdk.internal.jimage.PreviewMode; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -31,6 +32,7 @@ import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; @@ -39,15 +41,20 @@ import org.openjdk.jmh.infra.Blackhole; import java.io.IOException; -import java.nio.ByteOrder; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.mapping; +import static java.util.stream.Collectors.toList; /// Benchmarks for ImageReader. See individual benchmarks for details on what they /// measure, and their potential applicability for real world conclusions. @@ -67,16 +74,14 @@ public class ImageReaderBenchmark { } /// NOT annotated with `@State` since it needs to potentially be used as a - /// per-benchmark or a per-iteration state object. The subclasses provide + /// per-trial or a per-iteration state object. The subclasses provide /// any lifetime annotations that are needed. static class BaseState { protected Path copiedImageFile; - protected ByteOrder byteOrder; long count = 0; public void setUp() throws IOException { copiedImageFile = Files.createTempFile("copied_jimage", ""); - byteOrder = ByteOrder.nativeOrder(); Files.copy(SYSTEM_IMAGE_FILE, copiedImageFile, REPLACE_EXISTING); } @@ -86,14 +91,18 @@ public void tearDown() throws IOException { } } + /// A {@link Level#Trial per-trial} state which provides an image reader, + /// suitable for {@link Mode#AverageTime average time} benchmarks. @State(Scope.Benchmark) - public static class WarmStartWithImageReader extends BaseState { + public static class WarmStart extends BaseState { + @Param({"DISABLED", "ENABLED"}) + PreviewMode previewMode; ImageReader reader; @Setup(Level.Trial) public void setUp() throws IOException { super.setUp(); - reader = ImageReader.open(copiedImageFile, byteOrder); + reader = ImageReader.open(copiedImageFile, previewMode); } @TearDown(Level.Trial) @@ -102,8 +111,23 @@ public void tearDown() throws IOException { } } + @State(Scope.Benchmark) + public static class WarmStartWithCachedNodes extends WarmStart { + @Setup(Level.Trial) + public void setUp() throws IOException { + super.setUp(); + countAllNodes(reader, reader.findNode("/")); + } + } + + /// A {@link Level#Iteration per-iteration} state suitable for + /// {@link Mode#SingleShotTime single shot} benchmarks. Unlike + /// {@link WarmStart}, this state does not provide a reader instance. @State(Scope.Benchmark) public static class ColdStart extends BaseState { + @Param({"DISABLED", "ENABLED"}) + PreviewMode previewMode; + @Setup(Level.Iteration) public void setUp() throws IOException { super.setUp(); @@ -116,13 +140,13 @@ public void tearDown() throws IOException { } @State(Scope.Benchmark) - public static class ColdStartWithImageReader extends BaseState { + public static class ColdStartWithImageReader extends ColdStart { ImageReader reader; @Setup(Level.Iteration) public void setup() throws IOException { super.setUp(); - reader = ImageReader.open(copiedImageFile, byteOrder); + reader = ImageReader.open(copiedImageFile, previewMode); } @TearDown(Level.Iteration) @@ -137,10 +161,49 @@ public void tearDown() throws IOException { /// so this benchmark should be fast and very stable. @Benchmark @BenchmarkMode(Mode.AverageTime) - public void warmCache_CountAllNodes(WarmStartWithImageReader state) throws IOException { + public void warmStart_CountAllNodes(WarmStartWithCachedNodes state) throws IOException { state.count = countAllNodes(state.reader, state.reader.findNode("/")); } + /// Benchmarks {@link ImageReader#containsResource(String, String)} when no + /// nodes have been cached in the {@link ImageReader}. In non-preview mode, + /// this should be identical to the case where nodes are cached (because the + /// cache isn't used) but in preview mode, the cache will be tested for + /// preview resources, and thus differ depending on whether nodes are present. + /// + /// This doesn't need to be a cold start because it never modifies the nodes + /// cache. + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void warmStart_ContainsResource_emptyNodeCache(WarmStart state) throws IOException { + state.count = countContainsResource(state.reader, ClassList.pathMap()); + } + + /// As above, but the nodes cache has been filled, giving preview mode a + /// different code path. + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void warmStart_ContainsResource_fullNodeCache(WarmStartWithCachedNodes state) throws IOException { + state.count = countContainsResource(state.reader, ClassList.pathMap()); + } + + /// As {@link #warmStart_ContainsResource_emptyNodeCache}, but tests + /// {@link ImageReader#findResourceNode(String, String)}. + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void warmStart_FindResourceNode_emptyNodeCache(WarmStart state) throws IOException { + state.count = countFindResourceNode(state.reader, ClassList.pathMap()); + } + + /// As {@link #warmStart_ContainsResource_fullNodeCache}, but tests + /// {@link ImageReader#findResourceNode(String, String)}. + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void warmStart_FindResourceNode_fullNodeCache(WarmStartWithCachedNodes state) throws IOException { + state.count = countFindResourceNode(state.reader, ClassList.pathMap()); + } + + /// Benchmarks counting of all nodes in the system image from a "cold start". This /// visits all nodes in depth-first order and counts them. /// @@ -149,12 +212,12 @@ public void warmCache_CountAllNodes(WarmStartWithImageReader state) throws IOExc @Benchmark @BenchmarkMode(Mode.SingleShotTime) public void coldStart_InitAndCount(ColdStart state) throws IOException { - try (var reader = ImageReader.open(state.copiedImageFile, state.byteOrder)) { + try (var reader = ImageReader.open(state.copiedImageFile, state.previewMode)) { state.count = countAllNodes(reader, reader.findNode("/")); } } - /// As above, but includes the time to initialize the `ImageReader`. + /// As above, but excludes the time to initialize the `ImageReader`. @Benchmark @BenchmarkMode(Mode.SingleShotTime) public void coldStart_CountOnly(ColdStartWithImageReader state) throws IOException { @@ -173,8 +236,8 @@ public void coldStart_CountOnly(ColdStartWithImageReader state) throws IOExcepti @BenchmarkMode(Mode.SingleShotTime) public void coldStart_LoadJavacInitClasses(Blackhole bh, ColdStart state) throws IOException { int errors = 0; - try (var reader = ImageReader.open(state.copiedImageFile, state.byteOrder)) { - for (String path : INIT_CLASSES) { + try (var reader = ImageReader.open(state.copiedImageFile, state.previewMode)) { + for (String path : ClassList.names()) { // Path determination isn't perfect so there can be a few "misses" in here. // Report the count of bad paths as the "result", which should be < 20 or so. Node node = reader.findNode(path); @@ -185,9 +248,9 @@ public void coldStart_LoadJavacInitClasses(Blackhole bh, ColdStart state) throws } } } - state.count = INIT_CLASSES.size(); + state.count = ClassList.count(); // Allow up to 2% missing classes before complaining. - if ((100 * errors) / INIT_CLASSES.size() >= 2) { + if ((100 * errors) / ClassList.count() >= 2) { reportMissingClassesAndFail(state, errors); } } @@ -206,12 +269,39 @@ static long countAllNodes(ImageReader reader, Node node) { return count; } + static long countContainsResource(ImageReader reader, Map> modToPaths) + throws IOException { + long count = 0; + for (Map.Entry> e : modToPaths.entrySet()) { + String mod = e.getKey(); + for (String path : e.getValue()) { + if (reader.containsResource(mod, path)) { + count++; + } + } + } + return count; + } + + static long countFindResourceNode(ImageReader reader, Map> modToPaths) throws IOException { + long count = 0; + for (Map.Entry> e : modToPaths.entrySet()) { + String mod = e.getKey(); + for (String path : e.getValue()) { + if (reader.findResourceNode(mod, path) != null) { + count++; + } + } + } + return count; + } + // Run if the INIT_CLASSES list below is sufficiently out-of-date. // DO NOT run this before the benchmark, as it will cache all the nodes! private static void reportMissingClassesAndFail(ColdStart state, int errors) throws IOException { List missing = new ArrayList<>(errors); - try (var reader = ImageReader.open(state.copiedImageFile, state.byteOrder)) { - for (String path : INIT_CLASSES) { + try (var reader = ImageReader.open(state.copiedImageFile, state.previewMode)) { + for (String path : ClassList.names()) { if (reader.findNode(path) == null) { missing.add(path); } @@ -222,858 +312,891 @@ private static void reportMissingClassesAndFail(ColdStart state, int errors) thr "Too many missing classes (%d of %d) in the hardcoded benchmark list.\n" + "Please regenerate it according to instructions in the source code.\n" + "Missing classes:\n\t%s", - errors, INIT_CLASSES.size(), String.join("\n\t", missing))); + errors, ClassList.count(), String.join("\n\t", missing))); } - // Note: This list is inherently a little fragile and may end up being more - // trouble than it's worth to maintain. If it turns out that it needs to be - // regenerated often when this benchmark is run, then a new approach should - // be considered, such as: - // * Limit the list of classes to non-internal ones. - // * Calculate the list dynamically based on the running JVM. - // - // Created by running "java -verbose:class", throwing away anonymous inner - // classes and anything without a reliable name, and grouping by the stated - // source. It's not perfect, but it's representative. - // - // /bin/java -verbose:class HelloWorld 2>&1 \ - // | fgrep '[class,load]' | cut -d' ' -f2 \ - // | tr '.' '/' \ - // | egrep -v '\$[0-9$]' \ - // | fgrep -v 'HelloWorld' \ - // | fgrep -v '/META-INF/preview/' \ - // | while read f ; do echo "${f}.class" ; done \ - // > initclasses.txt - // - // Output: - // java/lang/Object.class - // java/io/Serializable.class - // ... - // - // jimage list /images/jdk/lib/modules \ - // | awk '/^Module: */ { MOD=$2 }; /^ */ { print "/modules/"MOD"/"$1 }' \ - // > fullpaths.txt - // - // Output: - // ... - // /modules/java.base/java/lang/Object.class - // /modules/java.base/java/lang/OutOfMemoryError.class - // ... - // - // while read c ; do grep "/$c" fullpaths.txt ; done < initclasses.txt \ - // | while read c ; do printf ' "%s",\n' "$c" ; done \ - // > initpaths.txt - // - // Output: - private static final Set INIT_CLASSES = Set.of( - "/modules/java.base/java/lang/Object.class", - "/modules/java.base/java/io/Serializable.class", - "/modules/java.base/java/lang/Comparable.class", - "/modules/java.base/java/lang/CharSequence.class", - "/modules/java.base/java/lang/constant/Constable.class", - "/modules/java.base/java/lang/constant/ConstantDesc.class", - "/modules/java.base/java/lang/String.class", - "/modules/java.base/java/lang/reflect/AnnotatedElement.class", - "/modules/java.base/java/lang/reflect/GenericDeclaration.class", - "/modules/java.base/java/lang/reflect/Type.class", - "/modules/java.base/java/lang/invoke/TypeDescriptor.class", - "/modules/java.base/java/lang/invoke/TypeDescriptor$OfField.class", - "/modules/java.base/java/lang/Class.class", - "/modules/java.base/java/lang/Cloneable.class", - "/modules/java.base/java/lang/ClassLoader.class", - "/modules/java.base/java/lang/System.class", - "/modules/java.base/java/lang/Throwable.class", - "/modules/java.base/java/lang/Error.class", - "/modules/java.base/java/lang/Exception.class", - "/modules/java.base/java/lang/RuntimeException.class", - "/modules/java.base/java/security/ProtectionDomain.class", - "/modules/java.base/java/security/SecureClassLoader.class", - "/modules/java.base/java/lang/ReflectiveOperationException.class", - "/modules/java.base/java/lang/ClassNotFoundException.class", - "/modules/java.base/java/lang/Record.class", - "/modules/java.base/java/lang/LinkageError.class", - "/modules/java.base/java/lang/NoClassDefFoundError.class", - "/modules/java.base/java/lang/ClassCastException.class", - "/modules/java.base/java/lang/ArrayStoreException.class", - "/modules/java.base/java/lang/VirtualMachineError.class", - "/modules/java.base/java/lang/InternalError.class", - "/modules/java.base/java/lang/OutOfMemoryError.class", - "/modules/java.base/java/lang/StackOverflowError.class", - "/modules/java.base/java/lang/IllegalMonitorStateException.class", - "/modules/java.base/java/lang/ref/Reference.class", - "/modules/java.base/java/lang/IllegalCallerException.class", - "/modules/java.base/java/lang/ref/SoftReference.class", - "/modules/java.base/java/lang/ref/WeakReference.class", - "/modules/java.base/java/lang/ref/FinalReference.class", - "/modules/java.base/java/lang/ref/PhantomReference.class", - "/modules/java.base/java/lang/ref/Finalizer.class", - "/modules/java.base/java/lang/Runnable.class", - "/modules/java.base/java/lang/Thread.class", - "/modules/java.base/java/lang/Thread$FieldHolder.class", - "/modules/java.base/java/lang/Thread$Constants.class", - "/modules/java.base/java/lang/Thread$UncaughtExceptionHandler.class", - "/modules/java.base/java/lang/ThreadGroup.class", - "/modules/java.base/java/lang/BaseVirtualThread.class", - "/modules/java.base/java/lang/VirtualThread.class", - "/modules/java.base/java/lang/ThreadBuilders$BoundVirtualThread.class", - "/modules/java.base/java/util/Map.class", - "/modules/java.base/java/util/Dictionary.class", - "/modules/java.base/java/util/Hashtable.class", - "/modules/java.base/java/util/Properties.class", - "/modules/java.base/java/lang/Module.class", - "/modules/java.base/java/lang/reflect/AccessibleObject.class", - "/modules/java.base/java/lang/reflect/Member.class", - "/modules/java.base/java/lang/reflect/Field.class", - "/modules/java.base/java/lang/reflect/Parameter.class", - "/modules/java.base/java/lang/reflect/Executable.class", - "/modules/java.base/java/lang/reflect/Method.class", - "/modules/java.base/java/lang/reflect/Constructor.class", - "/modules/java.base/jdk/internal/vm/ContinuationScope.class", - "/modules/java.base/jdk/internal/vm/Continuation.class", - "/modules/java.base/jdk/internal/vm/StackChunk.class", - "/modules/java.base/jdk/internal/reflect/MethodAccessor.class", - "/modules/java.base/jdk/internal/reflect/MethodAccessorImpl.class", - "/modules/java.base/jdk/internal/reflect/ConstantPool.class", - "/modules/java.base/java/lang/annotation/Annotation.class", - "/modules/java.base/jdk/internal/reflect/CallerSensitive.class", - "/modules/java.base/jdk/internal/reflect/ConstructorAccessor.class", - "/modules/java.base/jdk/internal/reflect/ConstructorAccessorImpl.class", - "/modules/java.base/jdk/internal/reflect/DirectConstructorHandleAccessor$NativeAccessor.class", - "/modules/java.base/java/lang/invoke/MethodHandle.class", - "/modules/java.base/java/lang/invoke/DirectMethodHandle.class", - "/modules/java.base/java/lang/invoke/VarHandle.class", - "/modules/java.base/java/lang/invoke/MemberName.class", - "/modules/java.base/java/lang/invoke/ResolvedMethodName.class", - "/modules/java.base/java/lang/invoke/MethodHandleNatives.class", - "/modules/java.base/java/lang/invoke/LambdaForm.class", - "/modules/java.base/java/lang/invoke/TypeDescriptor$OfMethod.class", - "/modules/java.base/java/lang/invoke/MethodType.class", - "/modules/java.base/java/lang/BootstrapMethodError.class", - "/modules/java.base/java/lang/invoke/CallSite.class", - "/modules/java.base/jdk/internal/foreign/abi/NativeEntryPoint.class", - "/modules/java.base/jdk/internal/foreign/abi/ABIDescriptor.class", - "/modules/java.base/jdk/internal/foreign/abi/VMStorage.class", - "/modules/java.base/jdk/internal/foreign/abi/UpcallLinker$CallRegs.class", - "/modules/java.base/java/lang/invoke/ConstantCallSite.class", - "/modules/java.base/java/lang/invoke/MutableCallSite.class", - "/modules/java.base/java/lang/invoke/VolatileCallSite.class", - "/modules/java.base/java/lang/AssertionStatusDirectives.class", - "/modules/java.base/java/lang/Appendable.class", - "/modules/java.base/java/lang/AbstractStringBuilder.class", - "/modules/java.base/java/lang/StringBuffer.class", - "/modules/java.base/java/lang/StringBuilder.class", - "/modules/java.base/jdk/internal/misc/UnsafeConstants.class", - "/modules/java.base/jdk/internal/misc/Unsafe.class", - "/modules/java.base/jdk/internal/module/Modules.class", - "/modules/java.base/java/lang/AutoCloseable.class", - "/modules/java.base/java/io/Closeable.class", - "/modules/java.base/java/io/InputStream.class", - "/modules/java.base/java/io/ByteArrayInputStream.class", - "/modules/java.base/java/net/URL.class", - "/modules/java.base/java/lang/Enum.class", - "/modules/java.base/java/util/jar/Manifest.class", - "/modules/java.base/jdk/internal/loader/BuiltinClassLoader.class", - "/modules/java.base/jdk/internal/loader/ClassLoaders.class", - "/modules/java.base/jdk/internal/loader/ClassLoaders$AppClassLoader.class", - "/modules/java.base/jdk/internal/loader/ClassLoaders$PlatformClassLoader.class", - "/modules/java.base/java/security/CodeSource.class", - "/modules/java.base/java/util/concurrent/ConcurrentMap.class", - "/modules/java.base/java/util/AbstractMap.class", - "/modules/java.base/java/util/concurrent/ConcurrentHashMap.class", - "/modules/java.base/java/lang/Iterable.class", - "/modules/java.base/java/util/Collection.class", - "/modules/java.base/java/util/SequencedCollection.class", - "/modules/java.base/java/util/List.class", - "/modules/java.base/java/util/RandomAccess.class", - "/modules/java.base/java/util/AbstractCollection.class", - "/modules/java.base/java/util/AbstractList.class", - "/modules/java.base/java/util/ArrayList.class", - "/modules/java.base/java/lang/StackTraceElement.class", - "/modules/java.base/java/nio/Buffer.class", - "/modules/java.base/java/lang/StackWalker.class", - "/modules/java.base/java/lang/StackStreamFactory$AbstractStackWalker.class", - "/modules/java.base/java/lang/StackWalker$StackFrame.class", - "/modules/java.base/java/lang/ClassFrameInfo.class", - "/modules/java.base/java/lang/StackFrameInfo.class", - "/modules/java.base/java/lang/LiveStackFrame.class", - "/modules/java.base/java/lang/LiveStackFrameInfo.class", - "/modules/java.base/java/util/concurrent/locks/AbstractOwnableSynchronizer.class", - "/modules/java.base/java/lang/Boolean.class", - "/modules/java.base/java/lang/Character.class", - "/modules/java.base/java/lang/Number.class", - "/modules/java.base/java/lang/Float.class", - "/modules/java.base/java/lang/Double.class", - "/modules/java.base/java/lang/Byte.class", - "/modules/java.base/java/lang/Short.class", - "/modules/java.base/java/lang/Integer.class", - "/modules/java.base/java/lang/Long.class", - "/modules/java.base/java/lang/Void.class", - "/modules/java.base/java/util/Iterator.class", - "/modules/java.base/java/lang/reflect/RecordComponent.class", - "/modules/java.base/jdk/internal/vm/vector/VectorSupport.class", - "/modules/java.base/jdk/internal/vm/vector/VectorSupport$VectorPayload.class", - "/modules/java.base/jdk/internal/vm/vector/VectorSupport$Vector.class", - "/modules/java.base/jdk/internal/vm/vector/VectorSupport$VectorMask.class", - "/modules/java.base/jdk/internal/vm/vector/VectorSupport$VectorShuffle.class", - "/modules/java.base/jdk/internal/vm/FillerObject.class", - "/modules/java.base/java/lang/NullPointerException.class", - "/modules/java.base/java/lang/ArithmeticException.class", - "/modules/java.base/java/lang/IndexOutOfBoundsException.class", - "/modules/java.base/java/lang/ArrayIndexOutOfBoundsException.class", - "/modules/java.base/java/io/ObjectStreamField.class", - "/modules/java.base/java/util/Comparator.class", - "/modules/java.base/java/lang/String$CaseInsensitiveComparator.class", - "/modules/java.base/jdk/internal/misc/VM.class", - "/modules/java.base/java/lang/Module$ArchivedData.class", - "/modules/java.base/jdk/internal/misc/CDS.class", - "/modules/java.base/java/util/Set.class", - "/modules/java.base/java/util/ImmutableCollections$AbstractImmutableCollection.class", - "/modules/java.base/java/util/ImmutableCollections$AbstractImmutableSet.class", - "/modules/java.base/java/util/ImmutableCollections$Set12.class", - "/modules/java.base/java/util/Objects.class", - "/modules/java.base/java/util/ImmutableCollections.class", - "/modules/java.base/java/util/ImmutableCollections$AbstractImmutableList.class", - "/modules/java.base/java/util/ImmutableCollections$ListN.class", - "/modules/java.base/java/util/ImmutableCollections$SetN.class", - "/modules/java.base/java/util/ImmutableCollections$AbstractImmutableMap.class", - "/modules/java.base/java/util/ImmutableCollections$MapN.class", - "/modules/java.base/jdk/internal/access/JavaLangReflectAccess.class", - "/modules/java.base/java/lang/reflect/ReflectAccess.class", - "/modules/java.base/jdk/internal/access/SharedSecrets.class", - "/modules/java.base/jdk/internal/reflect/ReflectionFactory.class", - "/modules/java.base/java/io/ObjectStreamClass.class", - "/modules/java.base/java/lang/Math.class", - "/modules/java.base/jdk/internal/reflect/ReflectionFactory$Config.class", - "/modules/java.base/jdk/internal/access/JavaLangRefAccess.class", - "/modules/java.base/java/lang/ref/ReferenceQueue.class", - "/modules/java.base/java/lang/ref/ReferenceQueue$Null.class", - "/modules/java.base/java/lang/ref/ReferenceQueue$Lock.class", - "/modules/java.base/jdk/internal/access/JavaLangAccess.class", - "/modules/java.base/jdk/internal/util/SystemProps.class", - "/modules/java.base/jdk/internal/util/SystemProps$Raw.class", - "/modules/java.base/java/nio/charset/Charset.class", - "/modules/java.base/java/nio/charset/spi/CharsetProvider.class", - "/modules/java.base/sun/nio/cs/StandardCharsets.class", - "/modules/java.base/java/lang/StringLatin1.class", - "/modules/java.base/sun/nio/cs/HistoricallyNamedCharset.class", - "/modules/java.base/sun/nio/cs/Unicode.class", - "/modules/java.base/sun/nio/cs/UTF_8.class", - "/modules/java.base/java/util/HashMap.class", - "/modules/java.base/java/lang/StrictMath.class", - "/modules/java.base/jdk/internal/util/ArraysSupport.class", - "/modules/java.base/java/util/Map$Entry.class", - "/modules/java.base/java/util/HashMap$Node.class", - "/modules/java.base/java/util/LinkedHashMap$Entry.class", - "/modules/java.base/java/util/HashMap$TreeNode.class", - "/modules/java.base/java/lang/StringConcatHelper.class", - "/modules/java.base/java/lang/VersionProps.class", - "/modules/java.base/java/lang/Runtime.class", - "/modules/java.base/java/util/concurrent/locks/Lock.class", - "/modules/java.base/java/util/concurrent/locks/ReentrantLock.class", - "/modules/java.base/java/util/concurrent/ConcurrentHashMap$Segment.class", - "/modules/java.base/java/util/concurrent/ConcurrentHashMap$CounterCell.class", - "/modules/java.base/java/util/concurrent/ConcurrentHashMap$Node.class", - "/modules/java.base/java/util/concurrent/locks/LockSupport.class", - "/modules/java.base/java/util/concurrent/ConcurrentHashMap$ReservationNode.class", - "/modules/java.base/java/util/AbstractSet.class", - "/modules/java.base/java/util/HashMap$EntrySet.class", - "/modules/java.base/java/util/HashMap$HashIterator.class", - "/modules/java.base/java/util/HashMap$EntryIterator.class", - "/modules/java.base/jdk/internal/util/StaticProperty.class", - "/modules/java.base/java/io/FileInputStream.class", - "/modules/java.base/java/lang/System$In.class", - "/modules/java.base/java/io/FileDescriptor.class", - "/modules/java.base/jdk/internal/access/JavaIOFileDescriptorAccess.class", - "/modules/java.base/java/io/Flushable.class", - "/modules/java.base/java/io/OutputStream.class", - "/modules/java.base/java/io/FileOutputStream.class", - "/modules/java.base/java/lang/System$Out.class", - "/modules/java.base/java/io/FilterInputStream.class", - "/modules/java.base/java/io/BufferedInputStream.class", - "/modules/java.base/java/io/FilterOutputStream.class", - "/modules/java.base/java/io/PrintStream.class", - "/modules/java.base/java/io/BufferedOutputStream.class", - "/modules/java.base/java/io/Writer.class", - "/modules/java.base/java/io/OutputStreamWriter.class", - "/modules/java.base/sun/nio/cs/StreamEncoder.class", - "/modules/java.base/java/nio/charset/CharsetEncoder.class", - "/modules/java.base/sun/nio/cs/UTF_8$Encoder.class", - "/modules/java.base/java/nio/charset/CodingErrorAction.class", - "/modules/java.base/java/util/Arrays.class", - "/modules/java.base/java/nio/ByteBuffer.class", - "/modules/java.base/jdk/internal/misc/ScopedMemoryAccess.class", - "/modules/java.base/java/util/function/Function.class", - "/modules/java.base/jdk/internal/util/Preconditions.class", - "/modules/java.base/java/util/function/BiFunction.class", - "/modules/java.base/jdk/internal/access/JavaNioAccess.class", - "/modules/java.base/java/nio/HeapByteBuffer.class", - "/modules/java.base/java/nio/ByteOrder.class", - "/modules/java.base/java/io/BufferedWriter.class", - "/modules/java.base/java/lang/Terminator.class", - "/modules/java.base/jdk/internal/misc/Signal$Handler.class", - "/modules/java.base/jdk/internal/misc/Signal.class", - "/modules/java.base/java/util/Hashtable$Entry.class", - "/modules/java.base/jdk/internal/misc/Signal$NativeHandler.class", - "/modules/java.base/java/lang/Integer$IntegerCache.class", - "/modules/java.base/jdk/internal/misc/OSEnvironment.class", - "/modules/java.base/java/lang/Thread$State.class", - "/modules/java.base/java/lang/ref/Reference$ReferenceHandler.class", - "/modules/java.base/java/lang/Thread$ThreadIdentifiers.class", - "/modules/java.base/java/lang/ref/Finalizer$FinalizerThread.class", - "/modules/java.base/jdk/internal/ref/Cleaner.class", - "/modules/java.base/java/util/Collections.class", - "/modules/java.base/java/util/Collections$EmptySet.class", - "/modules/java.base/java/util/Collections$EmptyList.class", - "/modules/java.base/java/util/Collections$EmptyMap.class", - "/modules/java.base/java/lang/IllegalArgumentException.class", - "/modules/java.base/java/lang/invoke/MethodHandleStatics.class", - "/modules/java.base/java/lang/reflect/ClassFileFormatVersion.class", - "/modules/java.base/java/lang/CharacterData.class", - "/modules/java.base/java/lang/CharacterDataLatin1.class", - "/modules/java.base/jdk/internal/util/ClassFileDumper.class", - "/modules/java.base/java/util/HexFormat.class", - "/modules/java.base/java/lang/Character$CharacterCache.class", - "/modules/java.base/java/util/concurrent/atomic/AtomicInteger.class", - "/modules/java.base/jdk/internal/module/ModuleBootstrap.class", - "/modules/java.base/java/lang/module/ModuleDescriptor.class", - "/modules/java.base/java/lang/invoke/MethodHandles.class", - "/modules/java.base/java/lang/invoke/MemberName$Factory.class", - "/modules/java.base/jdk/internal/reflect/Reflection.class", - "/modules/java.base/java/lang/invoke/MethodHandles$Lookup.class", - "/modules/java.base/java/util/ImmutableCollections$MapN$MapNIterator.class", - "/modules/java.base/java/util/KeyValueHolder.class", - "/modules/java.base/sun/invoke/util/VerifyAccess.class", - "/modules/java.base/java/lang/reflect/Modifier.class", - "/modules/java.base/jdk/internal/access/JavaLangModuleAccess.class", - "/modules/java.base/java/io/File.class", - "/modules/java.base/java/io/DefaultFileSystem.class", - "/modules/java.base/java/io/FileSystem.class", - "/modules/java.base/java/io/UnixFileSystem.class", - "/modules/java.base/jdk/internal/util/DecimalDigits.class", - "/modules/java.base/jdk/internal/module/ModulePatcher.class", - "/modules/java.base/jdk/internal/module/ModuleBootstrap$IllegalNativeAccess.class", - "/modules/java.base/java/util/HashSet.class", - "/modules/java.base/jdk/internal/module/ModuleLoaderMap.class", - "/modules/java.base/jdk/internal/module/ModuleLoaderMap$Modules.class", - "/modules/java.base/jdk/internal/module/ModuleBootstrap$Counters.class", - "/modules/java.base/jdk/internal/module/ArchivedBootLayer.class", - "/modules/java.base/jdk/internal/module/ArchivedModuleGraph.class", - "/modules/java.base/jdk/internal/module/SystemModuleFinders.class", - "/modules/java.base/java/net/URI.class", - "/modules/java.base/jdk/internal/access/JavaNetUriAccess.class", - "/modules/java.base/jdk/internal/module/SystemModulesMap.class", - "/modules/java.base/jdk/internal/module/SystemModules.class", - "/modules/java.base/jdk/internal/module/ExplodedSystemModules.class", - "/modules/java.base/java/nio/file/Watchable.class", - "/modules/java.base/java/nio/file/Path.class", - "/modules/java.base/java/nio/file/FileSystems.class", - "/modules/java.base/sun/nio/fs/DefaultFileSystemProvider.class", - "/modules/java.base/java/nio/file/spi/FileSystemProvider.class", - "/modules/java.base/sun/nio/fs/AbstractFileSystemProvider.class", - "/modules/java.base/sun/nio/fs/UnixFileSystemProvider.class", - "/modules/java.base/sun/nio/fs/LinuxFileSystemProvider.class", - "/modules/java.base/java/nio/file/OpenOption.class", - "/modules/java.base/java/nio/file/StandardOpenOption.class", - "/modules/java.base/java/nio/file/FileSystem.class", - "/modules/java.base/sun/nio/fs/UnixFileSystem.class", - "/modules/java.base/sun/nio/fs/LinuxFileSystem.class", - "/modules/java.base/sun/nio/fs/UnixPath.class", - "/modules/java.base/sun/nio/fs/Util.class", - "/modules/java.base/java/lang/StringCoding.class", - "/modules/java.base/sun/nio/fs/UnixNativeDispatcher.class", - "/modules/java.base/jdk/internal/loader/BootLoader.class", - "/modules/java.base/java/lang/Module$EnableNativeAccess.class", - "/modules/java.base/jdk/internal/loader/NativeLibraries.class", - "/modules/java.base/jdk/internal/loader/ClassLoaderHelper.class", - "/modules/java.base/java/util/concurrent/ConcurrentHashMap$CollectionView.class", - "/modules/java.base/java/util/concurrent/ConcurrentHashMap$KeySetView.class", - "/modules/java.base/jdk/internal/loader/NativeLibraries$LibraryPaths.class", - "/modules/java.base/java/io/File$PathStatus.class", - "/modules/java.base/jdk/internal/loader/NativeLibraries$CountedLock.class", - "/modules/java.base/java/util/concurrent/locks/AbstractQueuedSynchronizer.class", - "/modules/java.base/java/util/concurrent/locks/ReentrantLock$Sync.class", - "/modules/java.base/java/util/concurrent/locks/ReentrantLock$NonfairSync.class", - "/modules/java.base/jdk/internal/loader/NativeLibraries$NativeLibraryContext.class", - "/modules/java.base/java/util/Queue.class", - "/modules/java.base/java/util/Deque.class", - "/modules/java.base/java/util/ArrayDeque.class", - "/modules/java.base/java/util/ArrayDeque$DeqIterator.class", - "/modules/java.base/jdk/internal/loader/NativeLibrary.class", - "/modules/java.base/jdk/internal/loader/NativeLibraries$NativeLibraryImpl.class", - "/modules/java.base/java/security/cert/Certificate.class", - "/modules/java.base/java/util/concurrent/ConcurrentHashMap$ValuesView.class", - "/modules/java.base/java/util/Enumeration.class", - "/modules/java.base/java/util/concurrent/ConcurrentHashMap$Traverser.class", - "/modules/java.base/java/util/concurrent/ConcurrentHashMap$BaseIterator.class", - "/modules/java.base/java/util/concurrent/ConcurrentHashMap$ValueIterator.class", - "/modules/java.base/java/nio/file/attribute/BasicFileAttributes.class", - "/modules/java.base/java/nio/file/attribute/PosixFileAttributes.class", - "/modules/java.base/sun/nio/fs/UnixFileAttributes.class", - "/modules/java.base/sun/nio/fs/UnixFileStoreAttributes.class", - "/modules/java.base/sun/nio/fs/UnixMountEntry.class", - "/modules/java.base/java/nio/file/CopyOption.class", - "/modules/java.base/java/nio/file/LinkOption.class", - "/modules/java.base/java/nio/file/Files.class", - "/modules/java.base/sun/nio/fs/NativeBuffers.class", - "/modules/java.base/java/lang/ThreadLocal.class", - "/modules/java.base/jdk/internal/misc/CarrierThreadLocal.class", - "/modules/java.base/jdk/internal/misc/TerminatingThreadLocal.class", - "/modules/java.base/java/lang/ThreadLocal$ThreadLocalMap.class", - "/modules/java.base/java/lang/ThreadLocal$ThreadLocalMap$Entry.class", - "/modules/java.base/java/util/IdentityHashMap.class", - "/modules/java.base/java/util/Collections$SetFromMap.class", - "/modules/java.base/java/util/IdentityHashMap$KeySet.class", - "/modules/java.base/sun/nio/fs/NativeBuffer.class", - "/modules/java.base/jdk/internal/ref/CleanerFactory.class", - "/modules/java.base/java/util/concurrent/ThreadFactory.class", - "/modules/java.base/java/lang/ref/Cleaner.class", - "/modules/java.base/jdk/internal/ref/CleanerImpl.class", - "/modules/java.base/jdk/internal/ref/CleanerImpl$CleanableList.class", - "/modules/java.base/jdk/internal/ref/CleanerImpl$CleanableList$Node.class", - "/modules/java.base/java/lang/ref/Cleaner$Cleanable.class", - "/modules/java.base/jdk/internal/ref/PhantomCleanable.class", - "/modules/java.base/jdk/internal/ref/CleanerImpl$CleanerCleanable.class", - "/modules/java.base/jdk/internal/misc/InnocuousThread.class", - "/modules/java.base/sun/nio/fs/NativeBuffer$Deallocator.class", - "/modules/java.base/jdk/internal/ref/CleanerImpl$PhantomCleanableRef.class", - "/modules/java.base/java/lang/module/ModuleFinder.class", - "/modules/java.base/jdk/internal/module/ModulePath.class", - "/modules/java.base/java/util/jar/Attributes$Name.class", - "/modules/java.base/java/lang/reflect/Array.class", - "/modules/java.base/jdk/internal/perf/PerfCounter.class", - "/modules/java.base/jdk/internal/perf/Perf.class", - "/modules/java.base/sun/nio/ch/DirectBuffer.class", - "/modules/java.base/java/nio/MappedByteBuffer.class", - "/modules/java.base/java/nio/DirectByteBuffer.class", - "/modules/java.base/java/nio/Bits.class", - "/modules/java.base/java/util/concurrent/atomic/AtomicLong.class", - "/modules/java.base/jdk/internal/misc/VM$BufferPool.class", - "/modules/java.base/java/nio/LongBuffer.class", - "/modules/java.base/java/nio/DirectLongBufferU.class", - "/modules/java.base/java/util/zip/ZipConstants.class", - "/modules/java.base/java/util/zip/ZipFile.class", - "/modules/java.base/java/util/jar/JarFile.class", - "/modules/java.base/java/util/BitSet.class", - "/modules/java.base/jdk/internal/access/JavaUtilZipFileAccess.class", - "/modules/java.base/jdk/internal/access/JavaUtilJarAccess.class", - "/modules/java.base/java/util/jar/JavaUtilJarAccessImpl.class", - "/modules/java.base/java/lang/Runtime$Version.class", - "/modules/java.base/java/util/ImmutableCollections$List12.class", - "/modules/java.base/java/util/Optional.class", - "/modules/java.base/java/nio/file/attribute/DosFileAttributes.class", - "/modules/java.base/java/nio/file/attribute/AttributeView.class", - "/modules/java.base/java/nio/file/attribute/FileAttributeView.class", - "/modules/java.base/java/nio/file/attribute/BasicFileAttributeView.class", - "/modules/java.base/java/nio/file/attribute/DosFileAttributeView.class", - "/modules/java.base/java/nio/file/attribute/UserDefinedFileAttributeView.class", - "/modules/java.base/sun/nio/fs/UnixFileAttributeViews.class", - "/modules/java.base/sun/nio/fs/DynamicFileAttributeView.class", - "/modules/java.base/sun/nio/fs/AbstractBasicFileAttributeView.class", - "/modules/java.base/sun/nio/fs/UnixFileAttributeViews$Basic.class", - "/modules/java.base/sun/nio/fs/UnixFileAttributes$UnixAsBasicFileAttributes.class", - "/modules/java.base/java/nio/file/DirectoryStream$Filter.class", - "/modules/java.base/java/nio/file/Files$AcceptAllFilter.class", - "/modules/java.base/java/nio/file/DirectoryStream.class", - "/modules/java.base/java/nio/file/SecureDirectoryStream.class", - "/modules/java.base/sun/nio/fs/UnixSecureDirectoryStream.class", - "/modules/java.base/sun/nio/fs/UnixDirectoryStream.class", - "/modules/java.base/java/util/concurrent/locks/ReadWriteLock.class", - "/modules/java.base/java/util/concurrent/locks/ReentrantReadWriteLock.class", - "/modules/java.base/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.class", - "/modules/java.base/java/util/concurrent/locks/ReentrantReadWriteLock$Sync.class", - "/modules/java.base/java/util/concurrent/locks/ReentrantReadWriteLock$FairSync.class", - "/modules/java.base/java/util/concurrent/locks/ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter.class", - "/modules/java.base/java/util/concurrent/locks/ReentrantReadWriteLock$ReadLock.class", - "/modules/java.base/java/util/concurrent/locks/ReentrantReadWriteLock$WriteLock.class", - "/modules/java.base/sun/nio/fs/UnixDirectoryStream$UnixDirectoryIterator.class", - "/modules/java.base/java/nio/file/attribute/FileAttribute.class", - "/modules/java.base/sun/nio/fs/UnixFileModeAttribute.class", - "/modules/java.base/sun/nio/fs/UnixChannelFactory.class", - "/modules/java.base/sun/nio/fs/UnixChannelFactory$Flags.class", - "/modules/java.base/java/util/Collections$EmptyIterator.class", - "/modules/java.base/java/nio/channels/Channel.class", - "/modules/java.base/java/nio/channels/ReadableByteChannel.class", - "/modules/java.base/java/nio/channels/WritableByteChannel.class", - "/modules/java.base/java/nio/channels/ByteChannel.class", - "/modules/java.base/java/nio/channels/SeekableByteChannel.class", - "/modules/java.base/java/nio/channels/GatheringByteChannel.class", - "/modules/java.base/java/nio/channels/ScatteringByteChannel.class", - "/modules/java.base/java/nio/channels/InterruptibleChannel.class", - "/modules/java.base/java/nio/channels/spi/AbstractInterruptibleChannel.class", - "/modules/java.base/java/nio/channels/FileChannel.class", - "/modules/java.base/sun/nio/ch/FileChannelImpl.class", - "/modules/java.base/sun/nio/ch/NativeDispatcher.class", - "/modules/java.base/sun/nio/ch/FileDispatcher.class", - "/modules/java.base/sun/nio/ch/UnixFileDispatcherImpl.class", - "/modules/java.base/sun/nio/ch/FileDispatcherImpl.class", - "/modules/java.base/sun/nio/ch/IOUtil.class", - "/modules/java.base/sun/nio/ch/Interruptible.class", - "/modules/java.base/sun/nio/ch/NativeThreadSet.class", - "/modules/java.base/sun/nio/ch/FileChannelImpl$Closer.class", - "/modules/java.base/java/nio/channels/Channels.class", - "/modules/java.base/sun/nio/ch/Streams.class", - "/modules/java.base/sun/nio/ch/SelChImpl.class", - "/modules/java.base/java/nio/channels/NetworkChannel.class", - "/modules/java.base/java/nio/channels/SelectableChannel.class", - "/modules/java.base/java/nio/channels/spi/AbstractSelectableChannel.class", - "/modules/java.base/java/nio/channels/SocketChannel.class", - "/modules/java.base/sun/nio/ch/SocketChannelImpl.class", - "/modules/java.base/sun/nio/ch/ChannelInputStream.class", - "/modules/java.base/java/lang/invoke/LambdaMetafactory.class", - "/modules/java.base/java/util/function/Supplier.class", - "/modules/java.base/jdk/internal/util/ReferencedKeySet.class", - "/modules/java.base/jdk/internal/util/ReferencedKeyMap.class", - "/modules/java.base/jdk/internal/util/ReferenceKey.class", - "/modules/java.base/jdk/internal/util/StrongReferenceKey.class", - "/modules/java.base/java/lang/invoke/MethodTypeForm.class", - "/modules/java.base/jdk/internal/util/WeakReferenceKey.class", - "/modules/java.base/sun/invoke/util/Wrapper.class", - "/modules/java.base/sun/invoke/util/Wrapper$Format.class", - "/modules/java.base/java/lang/constant/ConstantDescs.class", - "/modules/java.base/java/lang/constant/ClassDesc.class", - "/modules/java.base/jdk/internal/constant/ClassOrInterfaceDescImpl.class", - "/modules/java.base/jdk/internal/constant/ArrayClassDescImpl.class", - "/modules/java.base/jdk/internal/constant/ConstantUtils.class", - "/modules/java.base/java/lang/constant/DirectMethodHandleDesc$Kind.class", - "/modules/java.base/java/lang/constant/MethodTypeDesc.class", - "/modules/java.base/jdk/internal/constant/MethodTypeDescImpl.class", - "/modules/java.base/java/lang/constant/MethodHandleDesc.class", - "/modules/java.base/java/lang/constant/DirectMethodHandleDesc.class", - "/modules/java.base/jdk/internal/constant/DirectMethodHandleDescImpl.class", - "/modules/java.base/java/lang/constant/DynamicConstantDesc.class", - "/modules/java.base/jdk/internal/constant/PrimitiveClassDescImpl.class", - "/modules/java.base/java/lang/constant/DynamicConstantDesc$AnonymousDynamicConstantDesc.class", - "/modules/java.base/java/lang/invoke/LambdaForm$NamedFunction.class", - "/modules/java.base/java/lang/invoke/DirectMethodHandle$Holder.class", - "/modules/java.base/sun/invoke/util/ValueConversions.class", - "/modules/java.base/java/lang/invoke/MethodHandleImpl.class", - "/modules/java.base/java/lang/invoke/Invokers.class", - "/modules/java.base/java/lang/invoke/LambdaForm$Kind.class", - "/modules/java.base/java/lang/NoSuchMethodException.class", - "/modules/java.base/java/lang/invoke/LambdaForm$BasicType.class", - "/modules/java.base/java/lang/classfile/TypeKind.class", - "/modules/java.base/java/lang/invoke/LambdaForm$Name.class", - "/modules/java.base/java/lang/invoke/LambdaForm$Holder.class", - "/modules/java.base/java/lang/invoke/InvokerBytecodeGenerator.class", - "/modules/java.base/java/lang/classfile/AnnotationElement.class", - "/modules/java.base/java/lang/classfile/Annotation.class", - "/modules/java.base/java/lang/classfile/constantpool/ConstantPool.class", - "/modules/java.base/java/lang/classfile/constantpool/ConstantPoolBuilder.class", - "/modules/java.base/jdk/internal/classfile/impl/TemporaryConstantPool.class", - "/modules/java.base/java/lang/classfile/constantpool/PoolEntry.class", - "/modules/java.base/java/lang/classfile/constantpool/AnnotationConstantValueEntry.class", - "/modules/java.base/java/lang/classfile/constantpool/Utf8Entry.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$Utf8EntryImpl.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$Utf8EntryImpl$State.class", - "/modules/java.base/jdk/internal/classfile/impl/AnnotationImpl.class", - "/modules/java.base/java/lang/classfile/ClassFileElement.class", - "/modules/java.base/java/lang/classfile/Attribute.class", - "/modules/java.base/java/lang/classfile/ClassElement.class", - "/modules/java.base/java/lang/classfile/MethodElement.class", - "/modules/java.base/java/lang/classfile/FieldElement.class", - "/modules/java.base/java/lang/classfile/attribute/RuntimeVisibleAnnotationsAttribute.class", - "/modules/java.base/jdk/internal/classfile/impl/Util$Writable.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractElement.class", - "/modules/java.base/jdk/internal/classfile/impl/UnboundAttribute.class", - "/modules/java.base/jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeVisibleAnnotationsAttribute.class", - "/modules/java.base/java/lang/classfile/Attributes.class", - "/modules/java.base/java/lang/classfile/AttributeMapper.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractAttributeMapper.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractAttributeMapper$RuntimeVisibleAnnotationsMapper.class", - "/modules/java.base/java/lang/classfile/AttributeMapper$AttributeStability.class", - "/modules/java.base/java/lang/invoke/MethodHandleImpl$Intrinsic.class", - "/modules/java.base/jdk/internal/classfile/impl/SplitConstantPool.class", - "/modules/java.base/java/lang/classfile/BootstrapMethodEntry.class", - "/modules/java.base/jdk/internal/classfile/impl/BootstrapMethodEntryImpl.class", - "/modules/java.base/jdk/internal/classfile/impl/EntryMap.class", - "/modules/java.base/jdk/internal/classfile/impl/Util.class", - "/modules/java.base/java/lang/classfile/constantpool/LoadableConstantEntry.class", - "/modules/java.base/java/lang/classfile/constantpool/ClassEntry.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$AbstractRefEntry.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$AbstractNamedEntry.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$ClassEntryImpl.class", - "/modules/java.base/java/util/function/Consumer.class", - "/modules/java.base/java/lang/classfile/ClassFile.class", - "/modules/java.base/jdk/internal/classfile/impl/ClassFileImpl.class", - "/modules/java.base/java/lang/classfile/ClassFileBuilder.class", - "/modules/java.base/java/lang/classfile/ClassBuilder.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractDirectBuilder.class", - "/modules/java.base/jdk/internal/classfile/impl/DirectClassBuilder.class", - "/modules/java.base/jdk/internal/classfile/impl/AttributeHolder.class", - "/modules/java.base/java/lang/classfile/Superclass.class", - "/modules/java.base/jdk/internal/classfile/impl/SuperclassImpl.class", - "/modules/java.base/java/lang/classfile/attribute/SourceFileAttribute.class", - "/modules/java.base/jdk/internal/classfile/impl/UnboundAttribute$UnboundSourceFileAttribute.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractAttributeMapper$SourceFileMapper.class", - "/modules/java.base/jdk/internal/classfile/impl/BoundAttribute.class", - "/modules/java.base/java/lang/classfile/MethodBuilder.class", - "/modules/java.base/jdk/internal/classfile/impl/MethodInfo.class", - "/modules/java.base/jdk/internal/classfile/impl/TerminalMethodBuilder.class", - "/modules/java.base/jdk/internal/classfile/impl/DirectMethodBuilder.class", - "/modules/java.base/java/lang/classfile/constantpool/NameAndTypeEntry.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$AbstractRefsEntry.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$NameAndTypeEntryImpl.class", - "/modules/java.base/java/lang/classfile/constantpool/MemberRefEntry.class", - "/modules/java.base/java/lang/classfile/constantpool/FieldRefEntry.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$AbstractMemberRefEntry.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$FieldRefEntryImpl.class", - "/modules/java.base/java/lang/invoke/InvokerBytecodeGenerator$ClassData.class", - "/modules/java.base/java/lang/classfile/CodeBuilder.class", - "/modules/java.base/jdk/internal/classfile/impl/LabelContext.class", - "/modules/java.base/jdk/internal/classfile/impl/TerminalCodeBuilder.class", - "/modules/java.base/jdk/internal/classfile/impl/DirectCodeBuilder.class", - "/modules/java.base/java/lang/classfile/CodeElement.class", - "/modules/java.base/java/lang/classfile/PseudoInstruction.class", - "/modules/java.base/java/lang/classfile/instruction/CharacterRange.class", - "/modules/java.base/java/lang/classfile/instruction/LocalVariable.class", - "/modules/java.base/java/lang/classfile/instruction/LocalVariableType.class", - "/modules/java.base/jdk/internal/classfile/impl/DirectCodeBuilder$DeferredLabel.class", - "/modules/java.base/java/lang/classfile/BufWriter.class", - "/modules/java.base/jdk/internal/classfile/impl/BufWriterImpl.class", - "/modules/java.base/java/lang/classfile/Label.class", - "/modules/java.base/java/lang/classfile/instruction/LabelTarget.class", - "/modules/java.base/jdk/internal/classfile/impl/LabelImpl.class", - "/modules/java.base/sun/invoke/util/VerifyType.class", - "/modules/java.base/java/lang/classfile/Opcode.class", - "/modules/java.base/java/lang/classfile/Opcode$Kind.class", - "/modules/java.base/java/lang/classfile/constantpool/MethodRefEntry.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$MethodRefEntryImpl.class", - "/modules/java.base/sun/invoke/empty/Empty.class", - "/modules/java.base/jdk/internal/classfile/impl/BytecodeHelpers.class", - "/modules/java.base/jdk/internal/classfile/impl/UnboundAttribute$AdHocAttribute.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractAttributeMapper$CodeMapper.class", - "/modules/java.base/java/lang/classfile/FieldBuilder.class", - "/modules/java.base/jdk/internal/classfile/impl/TerminalFieldBuilder.class", - "/modules/java.base/jdk/internal/classfile/impl/DirectFieldBuilder.class", - "/modules/java.base/java/lang/classfile/CustomAttribute.class", - "/modules/java.base/jdk/internal/classfile/impl/AnnotationReader.class", - "/modules/java.base/java/util/ListIterator.class", - "/modules/java.base/java/util/ImmutableCollections$ListItr.class", - "/modules/java.base/jdk/internal/classfile/impl/StackMapGenerator.class", - "/modules/java.base/jdk/internal/classfile/impl/StackMapGenerator$Frame.class", - "/modules/java.base/jdk/internal/classfile/impl/StackMapGenerator$Type.class", - "/modules/java.base/jdk/internal/classfile/impl/RawBytecodeHelper.class", - "/modules/java.base/jdk/internal/classfile/impl/RawBytecodeHelper$CodeRange.class", - "/modules/java.base/jdk/internal/classfile/impl/ClassHierarchyImpl.class", - "/modules/java.base/java/lang/classfile/ClassHierarchyResolver.class", - "/modules/java.base/jdk/internal/classfile/impl/ClassHierarchyImpl$ClassLoadingClassHierarchyResolver.class", - "/modules/java.base/jdk/internal/classfile/impl/ClassHierarchyImpl$CachedClassHierarchyResolver.class", - "/modules/java.base/java/lang/classfile/ClassHierarchyResolver$ClassHierarchyInfo.class", - "/modules/java.base/jdk/internal/classfile/impl/ClassHierarchyImpl$ClassHierarchyInfoImpl.class", - "/modules/java.base/java/lang/classfile/ClassReader.class", - "/modules/java.base/jdk/internal/classfile/impl/ClassReaderImpl.class", - "/modules/java.base/jdk/internal/util/ModifiedUtf.class", - "/modules/java.base/java/lang/invoke/MethodHandles$Lookup$ClassDefiner.class", - "/modules/java.base/java/lang/IncompatibleClassChangeError.class", - "/modules/java.base/java/lang/NoSuchMethodError.class", - "/modules/java.base/java/lang/invoke/BootstrapMethodInvoker.class", - "/modules/java.base/java/lang/invoke/AbstractValidatingLambdaMetafactory.class", - "/modules/java.base/java/lang/invoke/InnerClassLambdaMetafactory.class", - "/modules/java.base/java/lang/invoke/MethodHandleInfo.class", - "/modules/java.base/java/lang/invoke/InfoFromMemberName.class", - "/modules/java.base/java/util/ImmutableCollections$Access.class", - "/modules/java.base/jdk/internal/access/JavaUtilCollectionAccess.class", - "/modules/java.base/java/lang/classfile/Interfaces.class", - "/modules/java.base/jdk/internal/classfile/impl/InterfacesImpl.class", - "/modules/java.base/java/lang/invoke/TypeConvertingMethodAdapter.class", - "/modules/java.base/java/lang/invoke/DirectMethodHandle$Constructor.class", - "/modules/java.base/jdk/internal/access/JavaLangInvokeAccess.class", - "/modules/java.base/java/lang/invoke/VarHandle$AccessMode.class", - "/modules/java.base/java/lang/invoke/VarHandle$AccessType.class", - "/modules/java.base/java/lang/invoke/Invokers$Holder.class", - "/modules/java.base/jdk/internal/module/ModuleInfo.class", - "/modules/java.base/java/io/DataInput.class", - "/modules/java.base/java/io/DataInputStream.class", - "/modules/java.base/jdk/internal/module/ModuleInfo$CountingDataInput.class", - "/modules/java.base/sun/nio/ch/NativeThread.class", - "/modules/java.base/jdk/internal/misc/Blocker.class", - "/modules/java.base/sun/nio/ch/Util.class", - "/modules/java.base/sun/nio/ch/Util$BufferCache.class", - "/modules/java.base/sun/nio/ch/IOStatus.class", - "/modules/java.base/jdk/internal/util/ByteArray.class", - "/modules/java.base/java/lang/invoke/VarHandles.class", - "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsShorts$ByteArrayViewVarHandle.class", - "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsShorts$ArrayHandle.class", - "/modules/java.base/java/lang/invoke/VarHandleGuards.class", - "/modules/java.base/java/lang/invoke/VarForm.class", - "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsChars$ByteArrayViewVarHandle.class", - "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsChars$ArrayHandle.class", - "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsInts$ByteArrayViewVarHandle.class", - "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsInts$ArrayHandle.class", - "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsFloats$ByteArrayViewVarHandle.class", - "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsFloats$ArrayHandle.class", - "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsLongs$ByteArrayViewVarHandle.class", - "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsLongs$ArrayHandle.class", - "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsDoubles$ByteArrayViewVarHandle.class", - "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsDoubles$ArrayHandle.class", - "/modules/java.base/java/lang/invoke/VarHandle$AccessDescriptor.class", - "/modules/java.base/jdk/internal/module/ModuleInfo$ConstantPool.class", - "/modules/java.base/jdk/internal/module/ModuleInfo$ConstantPool$Entry.class", - "/modules/java.base/jdk/internal/module/ModuleInfo$ConstantPool$IndexEntry.class", - "/modules/java.base/java/nio/charset/StandardCharsets.class", - "/modules/java.base/sun/nio/cs/US_ASCII.class", - "/modules/java.base/sun/nio/cs/ISO_8859_1.class", - "/modules/java.base/sun/nio/cs/UTF_16BE.class", - "/modules/java.base/sun/nio/cs/UTF_16LE.class", - "/modules/java.base/sun/nio/cs/UTF_16.class", - "/modules/java.base/sun/nio/cs/UTF_32BE.class", - "/modules/java.base/sun/nio/cs/UTF_32LE.class", - "/modules/java.base/sun/nio/cs/UTF_32.class", - "/modules/java.base/jdk/internal/module/ModuleInfo$ConstantPool$ValueEntry.class", - "/modules/java.base/java/lang/module/ModuleDescriptor$Builder.class", - "/modules/java.base/java/lang/module/ModuleDescriptor$Modifier.class", - "/modules/java.base/java/lang/reflect/AccessFlag.class", - "/modules/java.base/java/lang/reflect/AccessFlag$Location.class", - "/modules/java.base/java/lang/module/ModuleDescriptor$Requires$Modifier.class", - "/modules/java.base/java/lang/module/ModuleDescriptor$Requires.class", - "/modules/java.base/java/util/HashMap$KeySet.class", - "/modules/java.base/java/util/HashMap$KeyIterator.class", - "/modules/java.base/jdk/internal/module/Checks.class", - "/modules/java.base/java/util/ArrayList$Itr.class", - "/modules/java.base/java/lang/module/ModuleDescriptor$Provides.class", - "/modules/java.base/java/util/Collections$UnmodifiableCollection.class", - "/modules/java.base/java/util/Collections$UnmodifiableSet.class", - "/modules/java.base/java/util/HashMap$Values.class", - "/modules/java.base/java/util/HashMap$ValueIterator.class", - "/modules/java.base/java/util/ImmutableCollections$SetN$SetNIterator.class", - "/modules/java.base/jdk/internal/module/ModuleInfo$Attributes.class", - "/modules/java.base/jdk/internal/module/ModuleReferences.class", - "/modules/java.base/java/lang/module/ModuleReader.class", - "/modules/java.base/sun/nio/fs/UnixUriUtils.class", - "/modules/java.base/java/net/URI$Parser.class", - "/modules/java.base/java/lang/module/ModuleReference.class", - "/modules/java.base/jdk/internal/module/ModuleReferenceImpl.class", - "/modules/java.base/java/lang/module/ModuleDescriptor$Exports.class", - "/modules/java.base/java/lang/module/ModuleDescriptor$Opens.class", - "/modules/java.base/sun/nio/fs/UnixException.class", - "/modules/java.base/java/io/IOException.class", - "/modules/java.base/jdk/internal/loader/ArchivedClassLoaders.class", - "/modules/java.base/jdk/internal/loader/ClassLoaders$BootClassLoader.class", - "/modules/java.base/java/lang/ClassLoader$ParallelLoaders.class", - "/modules/java.base/java/util/WeakHashMap.class", - "/modules/java.base/java/util/WeakHashMap$Entry.class", - "/modules/java.base/java/util/WeakHashMap$KeySet.class", - "/modules/java.base/java/security/Principal.class", - "/modules/java.base/jdk/internal/loader/URLClassPath.class", - "/modules/java.base/java/net/URLStreamHandlerFactory.class", - "/modules/java.base/java/net/URL$DefaultFactory.class", - "/modules/java.base/jdk/internal/access/JavaNetURLAccess.class", - "/modules/java.base/sun/net/www/ParseUtil.class", - "/modules/java.base/java/net/URLStreamHandler.class", - "/modules/java.base/sun/net/www/protocol/file/Handler.class", - "/modules/java.base/sun/net/util/IPAddressUtil.class", - "/modules/java.base/sun/net/util/IPAddressUtil$MASKS.class", - "/modules/java.base/sun/net/www/protocol/jar/Handler.class", - "/modules/java.base/jdk/internal/module/ServicesCatalog.class", - "/modules/java.base/jdk/internal/loader/AbstractClassLoaderValue.class", - "/modules/java.base/jdk/internal/loader/ClassLoaderValue.class", - "/modules/java.base/jdk/internal/loader/BuiltinClassLoader$LoadedModule.class", - "/modules/java.base/jdk/internal/module/DefaultRoots.class", - "/modules/java.base/java/util/Spliterator.class", - "/modules/java.base/java/util/HashMap$HashMapSpliterator.class", - "/modules/java.base/java/util/HashMap$ValueSpliterator.class", - "/modules/java.base/java/util/stream/StreamSupport.class", - "/modules/java.base/java/util/stream/BaseStream.class", - "/modules/java.base/java/util/stream/Stream.class", - "/modules/java.base/java/util/stream/PipelineHelper.class", - "/modules/java.base/java/util/stream/AbstractPipeline.class", - "/modules/java.base/java/util/stream/ReferencePipeline.class", - "/modules/java.base/java/util/stream/ReferencePipeline$Head.class", - "/modules/java.base/java/util/stream/StreamOpFlag.class", - "/modules/java.base/java/util/stream/StreamOpFlag$Type.class", - "/modules/java.base/java/util/stream/StreamOpFlag$MaskBuilder.class", - "/modules/java.base/java/util/EnumMap.class", - "/modules/java.base/java/lang/Class$ReflectionData.class", - "/modules/java.base/java/lang/Class$Atomic.class", - "/modules/java.base/java/lang/PublicMethods$MethodList.class", - "/modules/java.base/java/lang/PublicMethods$Key.class", - "/modules/java.base/sun/reflect/annotation/AnnotationParser.class", - "/modules/java.base/jdk/internal/reflect/MethodHandleAccessorFactory.class", - "/modules/java.base/jdk/internal/reflect/MethodHandleAccessorFactory$LazyStaticHolder.class", - "/modules/java.base/java/lang/invoke/BoundMethodHandle.class", - "/modules/java.base/java/lang/invoke/ClassSpecializer.class", - "/modules/java.base/java/lang/invoke/BoundMethodHandle$Specializer.class", - "/modules/java.base/jdk/internal/vm/annotation/Stable.class", - "/modules/java.base/java/lang/invoke/ClassSpecializer$SpeciesData.class", - "/modules/java.base/java/lang/invoke/BoundMethodHandle$SpeciesData.class", - "/modules/java.base/java/lang/invoke/ClassSpecializer$Factory.class", - "/modules/java.base/java/lang/invoke/BoundMethodHandle$Specializer$Factory.class", - "/modules/java.base/java/lang/invoke/SimpleMethodHandle.class", - "/modules/java.base/java/lang/NoSuchFieldException.class", - "/modules/java.base/java/lang/invoke/BoundMethodHandle$Species_L.class", - "/modules/java.base/java/lang/invoke/DirectMethodHandle$Accessor.class", - "/modules/java.base/java/lang/invoke/DelegatingMethodHandle.class", - "/modules/java.base/java/lang/invoke/DelegatingMethodHandle$Holder.class", - "/modules/java.base/java/lang/invoke/LambdaFormEditor.class", - "/modules/java.base/java/lang/invoke/LambdaFormEditor$TransformKey.class", - "/modules/java.base/java/lang/invoke/LambdaFormBuffer.class", - "/modules/java.base/java/lang/invoke/LambdaFormEditor$Transform.class", - "/modules/java.base/jdk/internal/reflect/DirectMethodHandleAccessor.class", - "/modules/java.base/java/util/stream/Collectors.class", - "/modules/java.base/java/util/stream/Collector$Characteristics.class", - "/modules/java.base/java/util/EnumSet.class", - "/modules/java.base/java/util/RegularEnumSet.class", - "/modules/java.base/java/util/stream/Collector.class", - "/modules/java.base/java/util/stream/Collectors$CollectorImpl.class", - "/modules/java.base/java/util/function/BiConsumer.class", - "/modules/java.base/java/lang/invoke/DirectMethodHandle$Interface.class", - "/modules/java.base/java/lang/classfile/constantpool/InterfaceMethodRefEntry.class", - "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$InterfaceMethodRefEntryImpl.class", - "/modules/java.base/java/util/function/BinaryOperator.class", - "/modules/java.base/java/util/stream/ReduceOps.class", - "/modules/java.base/java/util/stream/TerminalOp.class", - "/modules/java.base/java/util/stream/ReduceOps$ReduceOp.class", - "/modules/java.base/java/util/stream/StreamShape.class", - "/modules/java.base/java/util/stream/Sink.class", - "/modules/java.base/java/util/stream/TerminalSink.class", - "/modules/java.base/java/util/stream/ReduceOps$AccumulatingSink.class", - "/modules/java.base/java/util/stream/ReduceOps$Box.class", - "/modules/java.base/java/util/HashMap$KeySpliterator.class", - "/modules/java.base/java/util/function/Predicate.class", - "/modules/java.base/java/util/stream/ReferencePipeline$StatelessOp.class", - "/modules/java.base/java/util/stream/Sink$ChainedReference.class", - "/modules/java.base/jdk/internal/module/ModuleResolution.class", - "/modules/java.base/java/util/stream/FindOps.class", - "/modules/java.base/java/util/stream/FindOps$FindSink.class", - "/modules/java.base/java/util/stream/FindOps$FindSink$OfRef.class", - "/modules/java.base/java/util/stream/FindOps$FindOp.class", - "/modules/java.base/java/util/Spliterators.class", - "/modules/java.base/java/util/Spliterators$IteratorSpliterator.class", - "/modules/java.base/java/lang/module/Configuration.class", - "/modules/java.base/java/lang/module/Resolver.class", - "/modules/java.base/java/lang/ModuleLayer.class", - "/modules/java.base/java/util/SequencedSet.class", - "/modules/java.base/java/util/LinkedHashSet.class", - "/modules/java.base/java/util/SequencedMap.class", - "/modules/java.base/java/util/LinkedHashMap.class", - "/modules/java.base/java/lang/module/ResolvedModule.class", - "/modules/java.base/jdk/internal/module/ModuleLoaderMap$Mapper.class", - "/modules/java.base/jdk/internal/loader/AbstractClassLoaderValue$Memoizer.class", - "/modules/java.base/jdk/internal/module/ServicesCatalog$ServiceProvider.class", - "/modules/java.base/java/util/concurrent/CopyOnWriteArrayList.class", - "/modules/java.base/java/lang/ModuleLayer$Controller.class", - "/modules/java.base/jdk/internal/module/ModuleBootstrap$SafeModuleFinder.class", - "/modules/java.base/jdk/internal/vm/ContinuationSupport.class", - "/modules/java.base/jdk/internal/vm/Continuation$Pinned.class", - "/modules/java.base/sun/launcher/LauncherHelper.class", - "/modules/java.base/sun/net/util/URLUtil.class", - "/modules/java.base/jdk/internal/loader/URLClassPath$Loader.class", - "/modules/java.base/jdk/internal/loader/URLClassPath$FileLoader.class", - "/modules/java.base/jdk/internal/loader/Resource.class", - "/modules/java.base/java/io/FileCleanable.class", - "/modules/java.base/sun/nio/ByteBuffered.class", - "/modules/java.base/java/security/SecureClassLoader$CodeSourceKey.class", - "/modules/java.base/java/security/PermissionCollection.class", - "/modules/java.base/java/security/Permissions.class", - "/modules/java.base/java/lang/NamedPackage.class", - "/modules/java.base/jdk/internal/misc/MethodFinder.class", - "/modules/java.base/java/lang/Readable.class", - "/modules/java.base/java/nio/CharBuffer.class", - "/modules/java.base/java/nio/HeapCharBuffer.class", - "/modules/java.base/java/nio/charset/CoderResult.class", - "/modules/java.base/java/util/IdentityHashMap$IdentityHashMapIterator.class", - "/modules/java.base/java/util/IdentityHashMap$KeyIterator.class", - "/modules/java.base/java/lang/Shutdown.class", - "/modules/java.base/java/lang/Shutdown$Lock.class"); + /// Note: This list is inherently a little fragile and may end up being more + /// trouble than it's worth to maintain. If it turns out that it needs to be + /// regenerated often when this benchmark is run, then a new approach should + /// be considered, such as: + /// * Limit the list of classes to non-internal ones. + /// * Calculate the list dynamically based on the running JVM. + /// * Build a custom jimage file similar to ImageReaderTest + private static final class ClassList { + /// Returns the names of resource nodes expected to be present in the + /// reader, excluding preview mode paths (i.e. "/META-INF/preview/"). + private static Set names() { + return INIT_CLASSES; + } + + /// Returns the number of resources present. + private static int count() { + return INIT_CLASSES.size(); + } + + /// Returns the resource nodes represented as a map from module name to + /// resource path. This is suitable for testing functions like + /// {@link ImageReader#containsResource(String, String)} without the + /// overhead of splitting resource names during the trial. + private static Map> pathMap() { + return MODULE_TO_PATHS; + } + + // Created by running "java -verbose:class", throwing away anonymous inner + // classes and anything without a reliable name, and grouping by the stated + // source. It's not perfect, but it's representative. + // + // /bin/java -verbose:class HelloWorld 2>&1 \ + // | fgrep '[class,load]' | cut -d' ' -f2 \ + // | tr '.' '/' \ + // | egrep -v '\$[0-9$]' \ + // | fgrep -v 'HelloWorld' \ + // | fgrep -v '/META-INF/preview/' \ + // | while read f ; do echo "${f}.class" ; done \ + // > initclasses.txt + // + // Output: + // java/lang/Object.class + // java/io/Serializable.class + // ... + // + // jimage list /images/jdk/lib/modules \ + // | awk '/^Module: */ { MOD=$2 }; /^ */ { print "/modules/"MOD"/"$1 }' \ + // > fullpaths.txt + // + // Output: + // ... + // /modules/java.base/java/lang/Object.class + // /modules/java.base/java/lang/OutOfMemoryError.class + // ... + // + // while read c ; do grep "/$c" fullpaths.txt ; done < initclasses.txt \ + // | while read c ; do printf ' "%s",\n' "$c" ; done \ + // > initpaths.txt + // + // Output: + private static final Set INIT_CLASSES = Set.of( + "/modules/java.base/java/lang/Object.class", + "/modules/java.base/java/io/Serializable.class", + "/modules/java.base/java/lang/Comparable.class", + "/modules/java.base/java/lang/CharSequence.class", + "/modules/java.base/java/lang/constant/Constable.class", + "/modules/java.base/java/lang/constant/ConstantDesc.class", + "/modules/java.base/java/lang/String.class", + "/modules/java.base/java/lang/reflect/AnnotatedElement.class", + "/modules/java.base/java/lang/reflect/GenericDeclaration.class", + "/modules/java.base/java/lang/reflect/Type.class", + "/modules/java.base/java/lang/invoke/TypeDescriptor.class", + "/modules/java.base/java/lang/invoke/TypeDescriptor$OfField.class", + "/modules/java.base/java/lang/Class.class", + "/modules/java.base/java/lang/Cloneable.class", + "/modules/java.base/java/lang/ClassLoader.class", + "/modules/java.base/java/lang/System.class", + "/modules/java.base/java/lang/Throwable.class", + "/modules/java.base/java/lang/Error.class", + "/modules/java.base/java/lang/Exception.class", + "/modules/java.base/java/lang/RuntimeException.class", + "/modules/java.base/java/security/ProtectionDomain.class", + "/modules/java.base/java/security/SecureClassLoader.class", + "/modules/java.base/java/lang/ReflectiveOperationException.class", + "/modules/java.base/java/lang/ClassNotFoundException.class", + "/modules/java.base/java/lang/Record.class", + "/modules/java.base/java/lang/LinkageError.class", + "/modules/java.base/java/lang/NoClassDefFoundError.class", + "/modules/java.base/java/lang/ClassCastException.class", + "/modules/java.base/java/lang/ArrayStoreException.class", + "/modules/java.base/java/lang/VirtualMachineError.class", + "/modules/java.base/java/lang/InternalError.class", + "/modules/java.base/java/lang/OutOfMemoryError.class", + "/modules/java.base/java/lang/StackOverflowError.class", + "/modules/java.base/java/lang/IllegalMonitorStateException.class", + "/modules/java.base/java/lang/ref/Reference.class", + "/modules/java.base/java/lang/IllegalCallerException.class", + "/modules/java.base/java/lang/ref/SoftReference.class", + "/modules/java.base/java/lang/ref/WeakReference.class", + "/modules/java.base/java/lang/ref/FinalReference.class", + "/modules/java.base/java/lang/ref/PhantomReference.class", + "/modules/java.base/java/lang/ref/Finalizer.class", + "/modules/java.base/java/lang/Runnable.class", + "/modules/java.base/java/lang/Thread.class", + "/modules/java.base/java/lang/Thread$FieldHolder.class", + "/modules/java.base/java/lang/Thread$Constants.class", + "/modules/java.base/java/lang/Thread$UncaughtExceptionHandler.class", + "/modules/java.base/java/lang/ThreadGroup.class", + "/modules/java.base/java/lang/BaseVirtualThread.class", + "/modules/java.base/java/lang/VirtualThread.class", + "/modules/java.base/java/lang/ThreadBuilders$BoundVirtualThread.class", + "/modules/java.base/java/util/Map.class", + "/modules/java.base/java/util/Dictionary.class", + "/modules/java.base/java/util/Hashtable.class", + "/modules/java.base/java/util/Properties.class", + "/modules/java.base/java/lang/Module.class", + "/modules/java.base/java/lang/reflect/AccessibleObject.class", + "/modules/java.base/java/lang/reflect/Member.class", + "/modules/java.base/java/lang/reflect/Field.class", + "/modules/java.base/java/lang/reflect/Parameter.class", + "/modules/java.base/java/lang/reflect/Executable.class", + "/modules/java.base/java/lang/reflect/Method.class", + "/modules/java.base/java/lang/reflect/Constructor.class", + "/modules/java.base/jdk/internal/vm/ContinuationScope.class", + "/modules/java.base/jdk/internal/vm/Continuation.class", + "/modules/java.base/jdk/internal/vm/StackChunk.class", + "/modules/java.base/jdk/internal/reflect/MethodAccessor.class", + "/modules/java.base/jdk/internal/reflect/MethodAccessorImpl.class", + "/modules/java.base/jdk/internal/reflect/ConstantPool.class", + "/modules/java.base/java/lang/annotation/Annotation.class", + "/modules/java.base/jdk/internal/reflect/CallerSensitive.class", + "/modules/java.base/jdk/internal/reflect/ConstructorAccessor.class", + "/modules/java.base/jdk/internal/reflect/ConstructorAccessorImpl.class", + "/modules/java.base/jdk/internal/reflect/DirectConstructorHandleAccessor$NativeAccessor.class", + "/modules/java.base/java/lang/invoke/MethodHandle.class", + "/modules/java.base/java/lang/invoke/DirectMethodHandle.class", + "/modules/java.base/java/lang/invoke/VarHandle.class", + "/modules/java.base/java/lang/invoke/MemberName.class", + "/modules/java.base/java/lang/invoke/ResolvedMethodName.class", + "/modules/java.base/java/lang/invoke/MethodHandleNatives.class", + "/modules/java.base/java/lang/invoke/LambdaForm.class", + "/modules/java.base/java/lang/invoke/TypeDescriptor$OfMethod.class", + "/modules/java.base/java/lang/invoke/MethodType.class", + "/modules/java.base/java/lang/BootstrapMethodError.class", + "/modules/java.base/java/lang/invoke/CallSite.class", + "/modules/java.base/jdk/internal/foreign/abi/NativeEntryPoint.class", + "/modules/java.base/jdk/internal/foreign/abi/ABIDescriptor.class", + "/modules/java.base/jdk/internal/foreign/abi/VMStorage.class", + "/modules/java.base/jdk/internal/foreign/abi/UpcallLinker$CallRegs.class", + "/modules/java.base/java/lang/invoke/ConstantCallSite.class", + "/modules/java.base/java/lang/invoke/MutableCallSite.class", + "/modules/java.base/java/lang/invoke/VolatileCallSite.class", + "/modules/java.base/java/lang/AssertionStatusDirectives.class", + "/modules/java.base/java/lang/Appendable.class", + "/modules/java.base/java/lang/AbstractStringBuilder.class", + "/modules/java.base/java/lang/StringBuffer.class", + "/modules/java.base/java/lang/StringBuilder.class", + "/modules/java.base/jdk/internal/misc/UnsafeConstants.class", + "/modules/java.base/jdk/internal/misc/Unsafe.class", + "/modules/java.base/jdk/internal/module/Modules.class", + "/modules/java.base/java/lang/AutoCloseable.class", + "/modules/java.base/java/io/Closeable.class", + "/modules/java.base/java/io/InputStream.class", + "/modules/java.base/java/io/ByteArrayInputStream.class", + "/modules/java.base/java/net/URL.class", + "/modules/java.base/java/lang/Enum.class", + "/modules/java.base/java/util/jar/Manifest.class", + "/modules/java.base/jdk/internal/loader/BuiltinClassLoader.class", + "/modules/java.base/jdk/internal/loader/ClassLoaders.class", + "/modules/java.base/jdk/internal/loader/ClassLoaders$AppClassLoader.class", + "/modules/java.base/jdk/internal/loader/ClassLoaders$PlatformClassLoader.class", + "/modules/java.base/java/security/CodeSource.class", + "/modules/java.base/java/util/concurrent/ConcurrentMap.class", + "/modules/java.base/java/util/AbstractMap.class", + "/modules/java.base/java/util/concurrent/ConcurrentHashMap.class", + "/modules/java.base/java/lang/Iterable.class", + "/modules/java.base/java/util/Collection.class", + "/modules/java.base/java/util/SequencedCollection.class", + "/modules/java.base/java/util/List.class", + "/modules/java.base/java/util/RandomAccess.class", + "/modules/java.base/java/util/AbstractCollection.class", + "/modules/java.base/java/util/AbstractList.class", + "/modules/java.base/java/util/ArrayList.class", + "/modules/java.base/java/lang/StackTraceElement.class", + "/modules/java.base/java/nio/Buffer.class", + "/modules/java.base/java/lang/StackWalker.class", + "/modules/java.base/java/lang/StackStreamFactory$AbstractStackWalker.class", + "/modules/java.base/java/lang/StackWalker$StackFrame.class", + "/modules/java.base/java/lang/ClassFrameInfo.class", + "/modules/java.base/java/lang/StackFrameInfo.class", + "/modules/java.base/java/lang/LiveStackFrame.class", + "/modules/java.base/java/lang/LiveStackFrameInfo.class", + "/modules/java.base/java/util/concurrent/locks/AbstractOwnableSynchronizer.class", + "/modules/java.base/java/lang/Boolean.class", + "/modules/java.base/java/lang/Character.class", + "/modules/java.base/java/lang/Number.class", + "/modules/java.base/java/lang/Float.class", + "/modules/java.base/java/lang/Double.class", + "/modules/java.base/java/lang/Byte.class", + "/modules/java.base/java/lang/Short.class", + "/modules/java.base/java/lang/Integer.class", + "/modules/java.base/java/lang/Long.class", + "/modules/java.base/java/lang/Void.class", + "/modules/java.base/java/util/Iterator.class", + "/modules/java.base/java/lang/reflect/RecordComponent.class", + "/modules/java.base/jdk/internal/vm/vector/VectorSupport.class", + "/modules/java.base/jdk/internal/vm/vector/VectorSupport$VectorPayload.class", + "/modules/java.base/jdk/internal/vm/vector/VectorSupport$Vector.class", + "/modules/java.base/jdk/internal/vm/vector/VectorSupport$VectorMask.class", + "/modules/java.base/jdk/internal/vm/vector/VectorSupport$VectorShuffle.class", + "/modules/java.base/jdk/internal/vm/FillerObject.class", + "/modules/java.base/java/lang/NullPointerException.class", + "/modules/java.base/java/lang/ArithmeticException.class", + "/modules/java.base/java/lang/IndexOutOfBoundsException.class", + "/modules/java.base/java/lang/ArrayIndexOutOfBoundsException.class", + "/modules/java.base/java/io/ObjectStreamField.class", + "/modules/java.base/java/util/Comparator.class", + "/modules/java.base/java/lang/String$CaseInsensitiveComparator.class", + "/modules/java.base/jdk/internal/misc/VM.class", + "/modules/java.base/java/lang/Module$ArchivedData.class", + "/modules/java.base/jdk/internal/misc/CDS.class", + "/modules/java.base/java/util/Set.class", + "/modules/java.base/java/util/ImmutableCollections$AbstractImmutableCollection.class", + "/modules/java.base/java/util/ImmutableCollections$AbstractImmutableSet.class", + "/modules/java.base/java/util/ImmutableCollections$Set12.class", + "/modules/java.base/java/util/Objects.class", + "/modules/java.base/java/util/ImmutableCollections.class", + "/modules/java.base/java/util/ImmutableCollections$AbstractImmutableList.class", + "/modules/java.base/java/util/ImmutableCollections$ListN.class", + "/modules/java.base/java/util/ImmutableCollections$SetN.class", + "/modules/java.base/java/util/ImmutableCollections$AbstractImmutableMap.class", + "/modules/java.base/java/util/ImmutableCollections$MapN.class", + "/modules/java.base/jdk/internal/access/JavaLangReflectAccess.class", + "/modules/java.base/java/lang/reflect/ReflectAccess.class", + "/modules/java.base/jdk/internal/access/SharedSecrets.class", + "/modules/java.base/jdk/internal/reflect/ReflectionFactory.class", + "/modules/java.base/java/io/ObjectStreamClass.class", + "/modules/java.base/java/lang/Math.class", + "/modules/java.base/jdk/internal/reflect/ReflectionFactory$Config.class", + "/modules/java.base/jdk/internal/access/JavaLangRefAccess.class", + "/modules/java.base/java/lang/ref/ReferenceQueue.class", + "/modules/java.base/java/lang/ref/ReferenceQueue$Null.class", + "/modules/java.base/java/lang/ref/ReferenceQueue$Lock.class", + "/modules/java.base/jdk/internal/access/JavaLangAccess.class", + "/modules/java.base/jdk/internal/util/SystemProps.class", + "/modules/java.base/jdk/internal/util/SystemProps$Raw.class", + "/modules/java.base/java/nio/charset/Charset.class", + "/modules/java.base/java/nio/charset/spi/CharsetProvider.class", + "/modules/java.base/sun/nio/cs/StandardCharsets.class", + "/modules/java.base/java/lang/StringLatin1.class", + "/modules/java.base/sun/nio/cs/HistoricallyNamedCharset.class", + "/modules/java.base/sun/nio/cs/Unicode.class", + "/modules/java.base/sun/nio/cs/UTF_8.class", + "/modules/java.base/java/util/HashMap.class", + "/modules/java.base/java/lang/StrictMath.class", + "/modules/java.base/jdk/internal/util/ArraysSupport.class", + "/modules/java.base/java/util/Map$Entry.class", + "/modules/java.base/java/util/HashMap$Node.class", + "/modules/java.base/java/util/LinkedHashMap$Entry.class", + "/modules/java.base/java/util/HashMap$TreeNode.class", + "/modules/java.base/java/lang/StringConcatHelper.class", + "/modules/java.base/java/lang/VersionProps.class", + "/modules/java.base/java/lang/Runtime.class", + "/modules/java.base/java/util/concurrent/locks/Lock.class", + "/modules/java.base/java/util/concurrent/locks/ReentrantLock.class", + "/modules/java.base/java/util/concurrent/ConcurrentHashMap$Segment.class", + "/modules/java.base/java/util/concurrent/ConcurrentHashMap$CounterCell.class", + "/modules/java.base/java/util/concurrent/ConcurrentHashMap$Node.class", + "/modules/java.base/java/util/concurrent/locks/LockSupport.class", + "/modules/java.base/java/util/concurrent/ConcurrentHashMap$ReservationNode.class", + "/modules/java.base/java/util/AbstractSet.class", + "/modules/java.base/java/util/HashMap$EntrySet.class", + "/modules/java.base/java/util/HashMap$HashIterator.class", + "/modules/java.base/java/util/HashMap$EntryIterator.class", + "/modules/java.base/jdk/internal/util/StaticProperty.class", + "/modules/java.base/java/io/FileInputStream.class", + "/modules/java.base/java/lang/System$In.class", + "/modules/java.base/java/io/FileDescriptor.class", + "/modules/java.base/jdk/internal/access/JavaIOFileDescriptorAccess.class", + "/modules/java.base/java/io/Flushable.class", + "/modules/java.base/java/io/OutputStream.class", + "/modules/java.base/java/io/FileOutputStream.class", + "/modules/java.base/java/lang/System$Out.class", + "/modules/java.base/java/io/FilterInputStream.class", + "/modules/java.base/java/io/BufferedInputStream.class", + "/modules/java.base/java/io/FilterOutputStream.class", + "/modules/java.base/java/io/PrintStream.class", + "/modules/java.base/java/io/BufferedOutputStream.class", + "/modules/java.base/java/io/Writer.class", + "/modules/java.base/java/io/OutputStreamWriter.class", + "/modules/java.base/sun/nio/cs/StreamEncoder.class", + "/modules/java.base/java/nio/charset/CharsetEncoder.class", + "/modules/java.base/sun/nio/cs/UTF_8$Encoder.class", + "/modules/java.base/java/nio/charset/CodingErrorAction.class", + "/modules/java.base/java/util/Arrays.class", + "/modules/java.base/java/nio/ByteBuffer.class", + "/modules/java.base/jdk/internal/misc/ScopedMemoryAccess.class", + "/modules/java.base/java/util/function/Function.class", + "/modules/java.base/jdk/internal/util/Preconditions.class", + "/modules/java.base/java/util/function/BiFunction.class", + "/modules/java.base/jdk/internal/access/JavaNioAccess.class", + "/modules/java.base/java/nio/HeapByteBuffer.class", + "/modules/java.base/java/nio/ByteOrder.class", + "/modules/java.base/java/io/BufferedWriter.class", + "/modules/java.base/java/lang/Terminator.class", + "/modules/java.base/jdk/internal/misc/Signal$Handler.class", + "/modules/java.base/jdk/internal/misc/Signal.class", + "/modules/java.base/java/util/Hashtable$Entry.class", + "/modules/java.base/jdk/internal/misc/Signal$NativeHandler.class", + "/modules/java.base/java/lang/Integer$IntegerCache.class", + "/modules/java.base/jdk/internal/misc/OSEnvironment.class", + "/modules/java.base/java/lang/Thread$State.class", + "/modules/java.base/java/lang/ref/Reference$ReferenceHandler.class", + "/modules/java.base/java/lang/Thread$ThreadIdentifiers.class", + "/modules/java.base/java/lang/ref/Finalizer$FinalizerThread.class", + "/modules/java.base/jdk/internal/ref/Cleaner.class", + "/modules/java.base/java/util/Collections.class", + "/modules/java.base/java/util/Collections$EmptySet.class", + "/modules/java.base/java/util/Collections$EmptyList.class", + "/modules/java.base/java/util/Collections$EmptyMap.class", + "/modules/java.base/java/lang/IllegalArgumentException.class", + "/modules/java.base/java/lang/invoke/MethodHandleStatics.class", + "/modules/java.base/java/lang/reflect/ClassFileFormatVersion.class", + "/modules/java.base/java/lang/CharacterData.class", + "/modules/java.base/java/lang/CharacterDataLatin1.class", + "/modules/java.base/jdk/internal/util/ClassFileDumper.class", + "/modules/java.base/java/util/HexFormat.class", + "/modules/java.base/java/lang/Character$CharacterCache.class", + "/modules/java.base/java/util/concurrent/atomic/AtomicInteger.class", + "/modules/java.base/jdk/internal/module/ModuleBootstrap.class", + "/modules/java.base/java/lang/module/ModuleDescriptor.class", + "/modules/java.base/java/lang/invoke/MethodHandles.class", + "/modules/java.base/java/lang/invoke/MemberName$Factory.class", + "/modules/java.base/jdk/internal/reflect/Reflection.class", + "/modules/java.base/java/lang/invoke/MethodHandles$Lookup.class", + "/modules/java.base/java/util/ImmutableCollections$MapN$MapNIterator.class", + "/modules/java.base/java/util/KeyValueHolder.class", + "/modules/java.base/sun/invoke/util/VerifyAccess.class", + "/modules/java.base/java/lang/reflect/Modifier.class", + "/modules/java.base/jdk/internal/access/JavaLangModuleAccess.class", + "/modules/java.base/java/io/File.class", + "/modules/java.base/java/io/DefaultFileSystem.class", + "/modules/java.base/java/io/FileSystem.class", + "/modules/java.base/java/io/UnixFileSystem.class", + "/modules/java.base/jdk/internal/util/DecimalDigits.class", + "/modules/java.base/jdk/internal/module/ModulePatcher.class", + "/modules/java.base/jdk/internal/module/ModuleBootstrap$IllegalNativeAccess.class", + "/modules/java.base/java/util/HashSet.class", + "/modules/java.base/jdk/internal/module/ModuleLoaderMap.class", + "/modules/java.base/jdk/internal/module/ModuleLoaderMap$Modules.class", + "/modules/java.base/jdk/internal/module/ModuleBootstrap$Counters.class", + "/modules/java.base/jdk/internal/module/ArchivedBootLayer.class", + "/modules/java.base/jdk/internal/module/ArchivedModuleGraph.class", + "/modules/java.base/jdk/internal/module/SystemModuleFinders.class", + "/modules/java.base/java/net/URI.class", + "/modules/java.base/jdk/internal/access/JavaNetUriAccess.class", + "/modules/java.base/jdk/internal/module/SystemModulesMap.class", + "/modules/java.base/jdk/internal/module/SystemModules.class", + "/modules/java.base/jdk/internal/module/ExplodedSystemModules.class", + "/modules/java.base/java/nio/file/Watchable.class", + "/modules/java.base/java/nio/file/Path.class", + "/modules/java.base/java/nio/file/FileSystems.class", + "/modules/java.base/sun/nio/fs/DefaultFileSystemProvider.class", + "/modules/java.base/java/nio/file/spi/FileSystemProvider.class", + "/modules/java.base/sun/nio/fs/AbstractFileSystemProvider.class", + "/modules/java.base/sun/nio/fs/UnixFileSystemProvider.class", + "/modules/java.base/sun/nio/fs/LinuxFileSystemProvider.class", + "/modules/java.base/java/nio/file/OpenOption.class", + "/modules/java.base/java/nio/file/StandardOpenOption.class", + "/modules/java.base/java/nio/file/FileSystem.class", + "/modules/java.base/sun/nio/fs/UnixFileSystem.class", + "/modules/java.base/sun/nio/fs/LinuxFileSystem.class", + "/modules/java.base/sun/nio/fs/UnixPath.class", + "/modules/java.base/sun/nio/fs/Util.class", + "/modules/java.base/java/lang/StringCoding.class", + "/modules/java.base/sun/nio/fs/UnixNativeDispatcher.class", + "/modules/java.base/jdk/internal/loader/BootLoader.class", + "/modules/java.base/java/lang/Module$EnableNativeAccess.class", + "/modules/java.base/jdk/internal/loader/NativeLibraries.class", + "/modules/java.base/jdk/internal/loader/ClassLoaderHelper.class", + "/modules/java.base/java/util/concurrent/ConcurrentHashMap$CollectionView.class", + "/modules/java.base/java/util/concurrent/ConcurrentHashMap$KeySetView.class", + "/modules/java.base/jdk/internal/loader/NativeLibraries$LibraryPaths.class", + "/modules/java.base/java/io/File$PathStatus.class", + "/modules/java.base/jdk/internal/loader/NativeLibraries$CountedLock.class", + "/modules/java.base/java/util/concurrent/locks/AbstractQueuedSynchronizer.class", + "/modules/java.base/java/util/concurrent/locks/ReentrantLock$Sync.class", + "/modules/java.base/java/util/concurrent/locks/ReentrantLock$NonfairSync.class", + "/modules/java.base/jdk/internal/loader/NativeLibraries$NativeLibraryContext.class", + "/modules/java.base/java/util/Queue.class", + "/modules/java.base/java/util/Deque.class", + "/modules/java.base/java/util/ArrayDeque.class", + "/modules/java.base/java/util/ArrayDeque$DeqIterator.class", + "/modules/java.base/jdk/internal/loader/NativeLibrary.class", + "/modules/java.base/jdk/internal/loader/NativeLibraries$NativeLibraryImpl.class", + "/modules/java.base/java/security/cert/Certificate.class", + "/modules/java.base/java/util/concurrent/ConcurrentHashMap$ValuesView.class", + "/modules/java.base/java/util/Enumeration.class", + "/modules/java.base/java/util/concurrent/ConcurrentHashMap$Traverser.class", + "/modules/java.base/java/util/concurrent/ConcurrentHashMap$BaseIterator.class", + "/modules/java.base/java/util/concurrent/ConcurrentHashMap$ValueIterator.class", + "/modules/java.base/java/nio/file/attribute/BasicFileAttributes.class", + "/modules/java.base/java/nio/file/attribute/PosixFileAttributes.class", + "/modules/java.base/sun/nio/fs/UnixFileAttributes.class", + "/modules/java.base/sun/nio/fs/UnixFileStoreAttributes.class", + "/modules/java.base/sun/nio/fs/UnixMountEntry.class", + "/modules/java.base/java/nio/file/CopyOption.class", + "/modules/java.base/java/nio/file/LinkOption.class", + "/modules/java.base/java/nio/file/Files.class", + "/modules/java.base/sun/nio/fs/NativeBuffers.class", + "/modules/java.base/java/lang/ThreadLocal.class", + "/modules/java.base/jdk/internal/misc/CarrierThreadLocal.class", + "/modules/java.base/jdk/internal/misc/TerminatingThreadLocal.class", + "/modules/java.base/java/lang/ThreadLocal$ThreadLocalMap.class", + "/modules/java.base/java/lang/ThreadLocal$ThreadLocalMap$Entry.class", + "/modules/java.base/java/util/IdentityHashMap.class", + "/modules/java.base/java/util/Collections$SetFromMap.class", + "/modules/java.base/java/util/IdentityHashMap$KeySet.class", + "/modules/java.base/sun/nio/fs/NativeBuffer.class", + "/modules/java.base/jdk/internal/ref/CleanerFactory.class", + "/modules/java.base/java/util/concurrent/ThreadFactory.class", + "/modules/java.base/java/lang/ref/Cleaner.class", + "/modules/java.base/jdk/internal/ref/CleanerImpl.class", + "/modules/java.base/jdk/internal/ref/CleanerImpl$CleanableList.class", + "/modules/java.base/jdk/internal/ref/CleanerImpl$CleanableList$Node.class", + "/modules/java.base/java/lang/ref/Cleaner$Cleanable.class", + "/modules/java.base/jdk/internal/ref/PhantomCleanable.class", + "/modules/java.base/jdk/internal/ref/CleanerImpl$CleanerCleanable.class", + "/modules/java.base/jdk/internal/misc/InnocuousThread.class", + "/modules/java.base/sun/nio/fs/NativeBuffer$Deallocator.class", + "/modules/java.base/jdk/internal/ref/CleanerImpl$PhantomCleanableRef.class", + "/modules/java.base/java/lang/module/ModuleFinder.class", + "/modules/java.base/jdk/internal/module/ModulePath.class", + "/modules/java.base/java/util/jar/Attributes$Name.class", + "/modules/java.base/java/lang/reflect/Array.class", + "/modules/java.base/jdk/internal/perf/PerfCounter.class", + "/modules/java.base/jdk/internal/perf/Perf.class", + "/modules/java.base/sun/nio/ch/DirectBuffer.class", + "/modules/java.base/java/nio/MappedByteBuffer.class", + "/modules/java.base/java/nio/DirectByteBuffer.class", + "/modules/java.base/java/nio/Bits.class", + "/modules/java.base/java/util/concurrent/atomic/AtomicLong.class", + "/modules/java.base/jdk/internal/misc/VM$BufferPool.class", + "/modules/java.base/java/nio/LongBuffer.class", + "/modules/java.base/java/nio/DirectLongBufferU.class", + "/modules/java.base/java/util/zip/ZipConstants.class", + "/modules/java.base/java/util/zip/ZipFile.class", + "/modules/java.base/java/util/jar/JarFile.class", + "/modules/java.base/java/util/BitSet.class", + "/modules/java.base/jdk/internal/access/JavaUtilZipFileAccess.class", + "/modules/java.base/jdk/internal/access/JavaUtilJarAccess.class", + "/modules/java.base/java/util/jar/JavaUtilJarAccessImpl.class", + "/modules/java.base/java/lang/Runtime$Version.class", + "/modules/java.base/java/util/ImmutableCollections$List12.class", + "/modules/java.base/java/util/Optional.class", + "/modules/java.base/java/nio/file/attribute/DosFileAttributes.class", + "/modules/java.base/java/nio/file/attribute/AttributeView.class", + "/modules/java.base/java/nio/file/attribute/FileAttributeView.class", + "/modules/java.base/java/nio/file/attribute/BasicFileAttributeView.class", + "/modules/java.base/java/nio/file/attribute/DosFileAttributeView.class", + "/modules/java.base/java/nio/file/attribute/UserDefinedFileAttributeView.class", + "/modules/java.base/sun/nio/fs/UnixFileAttributeViews.class", + "/modules/java.base/sun/nio/fs/DynamicFileAttributeView.class", + "/modules/java.base/sun/nio/fs/AbstractBasicFileAttributeView.class", + "/modules/java.base/sun/nio/fs/UnixFileAttributeViews$Basic.class", + "/modules/java.base/sun/nio/fs/UnixFileAttributes$UnixAsBasicFileAttributes.class", + "/modules/java.base/java/nio/file/DirectoryStream$Filter.class", + "/modules/java.base/java/nio/file/Files$AcceptAllFilter.class", + "/modules/java.base/java/nio/file/DirectoryStream.class", + "/modules/java.base/java/nio/file/SecureDirectoryStream.class", + "/modules/java.base/sun/nio/fs/UnixSecureDirectoryStream.class", + "/modules/java.base/sun/nio/fs/UnixDirectoryStream.class", + "/modules/java.base/java/util/concurrent/locks/ReadWriteLock.class", + "/modules/java.base/java/util/concurrent/locks/ReentrantReadWriteLock.class", + "/modules/java.base/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.class", + "/modules/java.base/java/util/concurrent/locks/ReentrantReadWriteLock$Sync.class", + "/modules/java.base/java/util/concurrent/locks/ReentrantReadWriteLock$FairSync.class", + "/modules/java.base/java/util/concurrent/locks/ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter.class", + "/modules/java.base/java/util/concurrent/locks/ReentrantReadWriteLock$ReadLock.class", + "/modules/java.base/java/util/concurrent/locks/ReentrantReadWriteLock$WriteLock.class", + "/modules/java.base/sun/nio/fs/UnixDirectoryStream$UnixDirectoryIterator.class", + "/modules/java.base/java/nio/file/attribute/FileAttribute.class", + "/modules/java.base/sun/nio/fs/UnixFileModeAttribute.class", + "/modules/java.base/sun/nio/fs/UnixChannelFactory.class", + "/modules/java.base/sun/nio/fs/UnixChannelFactory$Flags.class", + "/modules/java.base/java/util/Collections$EmptyIterator.class", + "/modules/java.base/java/nio/channels/Channel.class", + "/modules/java.base/java/nio/channels/ReadableByteChannel.class", + "/modules/java.base/java/nio/channels/WritableByteChannel.class", + "/modules/java.base/java/nio/channels/ByteChannel.class", + "/modules/java.base/java/nio/channels/SeekableByteChannel.class", + "/modules/java.base/java/nio/channels/GatheringByteChannel.class", + "/modules/java.base/java/nio/channels/ScatteringByteChannel.class", + "/modules/java.base/java/nio/channels/InterruptibleChannel.class", + "/modules/java.base/java/nio/channels/spi/AbstractInterruptibleChannel.class", + "/modules/java.base/java/nio/channels/FileChannel.class", + "/modules/java.base/sun/nio/ch/FileChannelImpl.class", + "/modules/java.base/sun/nio/ch/NativeDispatcher.class", + "/modules/java.base/sun/nio/ch/FileDispatcher.class", + "/modules/java.base/sun/nio/ch/UnixFileDispatcherImpl.class", + "/modules/java.base/sun/nio/ch/FileDispatcherImpl.class", + "/modules/java.base/sun/nio/ch/IOUtil.class", + "/modules/java.base/sun/nio/ch/Interruptible.class", + "/modules/java.base/sun/nio/ch/NativeThreadSet.class", + "/modules/java.base/sun/nio/ch/FileChannelImpl$Closer.class", + "/modules/java.base/java/nio/channels/Channels.class", + "/modules/java.base/sun/nio/ch/Streams.class", + "/modules/java.base/sun/nio/ch/SelChImpl.class", + "/modules/java.base/java/nio/channels/NetworkChannel.class", + "/modules/java.base/java/nio/channels/SelectableChannel.class", + "/modules/java.base/java/nio/channels/spi/AbstractSelectableChannel.class", + "/modules/java.base/java/nio/channels/SocketChannel.class", + "/modules/java.base/sun/nio/ch/SocketChannelImpl.class", + "/modules/java.base/sun/nio/ch/ChannelInputStream.class", + "/modules/java.base/java/lang/invoke/LambdaMetafactory.class", + "/modules/java.base/java/util/function/Supplier.class", + "/modules/java.base/jdk/internal/util/ReferencedKeySet.class", + "/modules/java.base/jdk/internal/util/ReferencedKeyMap.class", + "/modules/java.base/jdk/internal/util/ReferenceKey.class", + "/modules/java.base/jdk/internal/util/StrongReferenceKey.class", + "/modules/java.base/java/lang/invoke/MethodTypeForm.class", + "/modules/java.base/jdk/internal/util/WeakReferenceKey.class", + "/modules/java.base/sun/invoke/util/Wrapper.class", + "/modules/java.base/sun/invoke/util/Wrapper$Format.class", + "/modules/java.base/java/lang/constant/ConstantDescs.class", + "/modules/java.base/java/lang/constant/ClassDesc.class", + "/modules/java.base/jdk/internal/constant/ClassOrInterfaceDescImpl.class", + "/modules/java.base/jdk/internal/constant/ArrayClassDescImpl.class", + "/modules/java.base/jdk/internal/constant/ConstantUtils.class", + "/modules/java.base/java/lang/constant/DirectMethodHandleDesc$Kind.class", + "/modules/java.base/java/lang/constant/MethodTypeDesc.class", + "/modules/java.base/jdk/internal/constant/MethodTypeDescImpl.class", + "/modules/java.base/java/lang/constant/MethodHandleDesc.class", + "/modules/java.base/java/lang/constant/DirectMethodHandleDesc.class", + "/modules/java.base/jdk/internal/constant/DirectMethodHandleDescImpl.class", + "/modules/java.base/java/lang/constant/DynamicConstantDesc.class", + "/modules/java.base/jdk/internal/constant/PrimitiveClassDescImpl.class", + "/modules/java.base/java/lang/constant/DynamicConstantDesc$AnonymousDynamicConstantDesc.class", + "/modules/java.base/java/lang/invoke/LambdaForm$NamedFunction.class", + "/modules/java.base/java/lang/invoke/DirectMethodHandle$Holder.class", + "/modules/java.base/sun/invoke/util/ValueConversions.class", + "/modules/java.base/java/lang/invoke/MethodHandleImpl.class", + "/modules/java.base/java/lang/invoke/Invokers.class", + "/modules/java.base/java/lang/invoke/LambdaForm$Kind.class", + "/modules/java.base/java/lang/NoSuchMethodException.class", + "/modules/java.base/java/lang/invoke/LambdaForm$BasicType.class", + "/modules/java.base/java/lang/classfile/TypeKind.class", + "/modules/java.base/java/lang/invoke/LambdaForm$Name.class", + "/modules/java.base/java/lang/invoke/LambdaForm$Holder.class", + "/modules/java.base/java/lang/invoke/InvokerBytecodeGenerator.class", + "/modules/java.base/java/lang/classfile/AnnotationElement.class", + "/modules/java.base/java/lang/classfile/Annotation.class", + "/modules/java.base/java/lang/classfile/constantpool/ConstantPool.class", + "/modules/java.base/java/lang/classfile/constantpool/ConstantPoolBuilder.class", + "/modules/java.base/jdk/internal/classfile/impl/TemporaryConstantPool.class", + "/modules/java.base/java/lang/classfile/constantpool/PoolEntry.class", + "/modules/java.base/java/lang/classfile/constantpool/AnnotationConstantValueEntry.class", + "/modules/java.base/java/lang/classfile/constantpool/Utf8Entry.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$Utf8EntryImpl.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$Utf8EntryImpl$State.class", + "/modules/java.base/jdk/internal/classfile/impl/AnnotationImpl.class", + "/modules/java.base/java/lang/classfile/ClassFileElement.class", + "/modules/java.base/java/lang/classfile/Attribute.class", + "/modules/java.base/java/lang/classfile/ClassElement.class", + "/modules/java.base/java/lang/classfile/MethodElement.class", + "/modules/java.base/java/lang/classfile/FieldElement.class", + "/modules/java.base/java/lang/classfile/attribute/RuntimeVisibleAnnotationsAttribute.class", + "/modules/java.base/jdk/internal/classfile/impl/Util$Writable.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractElement.class", + "/modules/java.base/jdk/internal/classfile/impl/UnboundAttribute.class", + "/modules/java.base/jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeVisibleAnnotationsAttribute.class", + "/modules/java.base/java/lang/classfile/Attributes.class", + "/modules/java.base/java/lang/classfile/AttributeMapper.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractAttributeMapper.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractAttributeMapper$RuntimeVisibleAnnotationsMapper.class", + "/modules/java.base/java/lang/classfile/AttributeMapper$AttributeStability.class", + "/modules/java.base/java/lang/invoke/MethodHandleImpl$Intrinsic.class", + "/modules/java.base/jdk/internal/classfile/impl/SplitConstantPool.class", + "/modules/java.base/java/lang/classfile/BootstrapMethodEntry.class", + "/modules/java.base/jdk/internal/classfile/impl/BootstrapMethodEntryImpl.class", + "/modules/java.base/jdk/internal/classfile/impl/EntryMap.class", + "/modules/java.base/jdk/internal/classfile/impl/Util.class", + "/modules/java.base/java/lang/classfile/constantpool/LoadableConstantEntry.class", + "/modules/java.base/java/lang/classfile/constantpool/ClassEntry.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$AbstractRefEntry.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$AbstractNamedEntry.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$ClassEntryImpl.class", + "/modules/java.base/java/util/function/Consumer.class", + "/modules/java.base/java/lang/classfile/ClassFile.class", + "/modules/java.base/jdk/internal/classfile/impl/ClassFileImpl.class", + "/modules/java.base/java/lang/classfile/ClassFileBuilder.class", + "/modules/java.base/java/lang/classfile/ClassBuilder.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractDirectBuilder.class", + "/modules/java.base/jdk/internal/classfile/impl/DirectClassBuilder.class", + "/modules/java.base/jdk/internal/classfile/impl/AttributeHolder.class", + "/modules/java.base/java/lang/classfile/Superclass.class", + "/modules/java.base/jdk/internal/classfile/impl/SuperclassImpl.class", + "/modules/java.base/java/lang/classfile/attribute/SourceFileAttribute.class", + "/modules/java.base/jdk/internal/classfile/impl/UnboundAttribute$UnboundSourceFileAttribute.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractAttributeMapper$SourceFileMapper.class", + "/modules/java.base/jdk/internal/classfile/impl/BoundAttribute.class", + "/modules/java.base/java/lang/classfile/MethodBuilder.class", + "/modules/java.base/jdk/internal/classfile/impl/MethodInfo.class", + "/modules/java.base/jdk/internal/classfile/impl/TerminalMethodBuilder.class", + "/modules/java.base/jdk/internal/classfile/impl/DirectMethodBuilder.class", + "/modules/java.base/java/lang/classfile/constantpool/NameAndTypeEntry.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$AbstractRefsEntry.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$NameAndTypeEntryImpl.class", + "/modules/java.base/java/lang/classfile/constantpool/MemberRefEntry.class", + "/modules/java.base/java/lang/classfile/constantpool/FieldRefEntry.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$AbstractMemberRefEntry.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$FieldRefEntryImpl.class", + "/modules/java.base/java/lang/invoke/InvokerBytecodeGenerator$ClassData.class", + "/modules/java.base/java/lang/classfile/CodeBuilder.class", + "/modules/java.base/jdk/internal/classfile/impl/LabelContext.class", + "/modules/java.base/jdk/internal/classfile/impl/TerminalCodeBuilder.class", + "/modules/java.base/jdk/internal/classfile/impl/DirectCodeBuilder.class", + "/modules/java.base/java/lang/classfile/CodeElement.class", + "/modules/java.base/java/lang/classfile/PseudoInstruction.class", + "/modules/java.base/java/lang/classfile/instruction/CharacterRange.class", + "/modules/java.base/java/lang/classfile/instruction/LocalVariable.class", + "/modules/java.base/java/lang/classfile/instruction/LocalVariableType.class", + "/modules/java.base/jdk/internal/classfile/impl/DirectCodeBuilder$DeferredLabel.class", + "/modules/java.base/java/lang/classfile/BufWriter.class", + "/modules/java.base/jdk/internal/classfile/impl/BufWriterImpl.class", + "/modules/java.base/java/lang/classfile/Label.class", + "/modules/java.base/java/lang/classfile/instruction/LabelTarget.class", + "/modules/java.base/jdk/internal/classfile/impl/LabelImpl.class", + "/modules/java.base/sun/invoke/util/VerifyType.class", + "/modules/java.base/java/lang/classfile/Opcode.class", + "/modules/java.base/java/lang/classfile/Opcode$Kind.class", + "/modules/java.base/java/lang/classfile/constantpool/MethodRefEntry.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$MethodRefEntryImpl.class", + "/modules/java.base/sun/invoke/empty/Empty.class", + "/modules/java.base/jdk/internal/classfile/impl/BytecodeHelpers.class", + "/modules/java.base/jdk/internal/classfile/impl/UnboundAttribute$AdHocAttribute.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractAttributeMapper$CodeMapper.class", + "/modules/java.base/java/lang/classfile/FieldBuilder.class", + "/modules/java.base/jdk/internal/classfile/impl/TerminalFieldBuilder.class", + "/modules/java.base/jdk/internal/classfile/impl/DirectFieldBuilder.class", + "/modules/java.base/java/lang/classfile/CustomAttribute.class", + "/modules/java.base/jdk/internal/classfile/impl/AnnotationReader.class", + "/modules/java.base/java/util/ListIterator.class", + "/modules/java.base/java/util/ImmutableCollections$ListItr.class", + "/modules/java.base/jdk/internal/classfile/impl/StackMapGenerator.class", + "/modules/java.base/jdk/internal/classfile/impl/StackMapGenerator$Frame.class", + "/modules/java.base/jdk/internal/classfile/impl/StackMapGenerator$Type.class", + "/modules/java.base/jdk/internal/classfile/impl/RawBytecodeHelper.class", + "/modules/java.base/jdk/internal/classfile/impl/RawBytecodeHelper$CodeRange.class", + "/modules/java.base/jdk/internal/classfile/impl/ClassHierarchyImpl.class", + "/modules/java.base/java/lang/classfile/ClassHierarchyResolver.class", + "/modules/java.base/jdk/internal/classfile/impl/ClassHierarchyImpl$ClassLoadingClassHierarchyResolver.class", + "/modules/java.base/jdk/internal/classfile/impl/ClassHierarchyImpl$CachedClassHierarchyResolver.class", + "/modules/java.base/java/lang/classfile/ClassHierarchyResolver$ClassHierarchyInfo.class", + "/modules/java.base/jdk/internal/classfile/impl/ClassHierarchyImpl$ClassHierarchyInfoImpl.class", + "/modules/java.base/java/lang/classfile/ClassReader.class", + "/modules/java.base/jdk/internal/classfile/impl/ClassReaderImpl.class", + "/modules/java.base/jdk/internal/util/ModifiedUtf.class", + "/modules/java.base/java/lang/invoke/MethodHandles$Lookup$ClassDefiner.class", + "/modules/java.base/java/lang/IncompatibleClassChangeError.class", + "/modules/java.base/java/lang/NoSuchMethodError.class", + "/modules/java.base/java/lang/invoke/BootstrapMethodInvoker.class", + "/modules/java.base/java/lang/invoke/AbstractValidatingLambdaMetafactory.class", + "/modules/java.base/java/lang/invoke/InnerClassLambdaMetafactory.class", + "/modules/java.base/java/lang/invoke/MethodHandleInfo.class", + "/modules/java.base/java/lang/invoke/InfoFromMemberName.class", + "/modules/java.base/java/util/ImmutableCollections$Access.class", + "/modules/java.base/jdk/internal/access/JavaUtilCollectionAccess.class", + "/modules/java.base/java/lang/classfile/Interfaces.class", + "/modules/java.base/jdk/internal/classfile/impl/InterfacesImpl.class", + "/modules/java.base/java/lang/invoke/TypeConvertingMethodAdapter.class", + "/modules/java.base/java/lang/invoke/DirectMethodHandle$Constructor.class", + "/modules/java.base/jdk/internal/access/JavaLangInvokeAccess.class", + "/modules/java.base/java/lang/invoke/VarHandle$AccessMode.class", + "/modules/java.base/java/lang/invoke/VarHandle$AccessType.class", + "/modules/java.base/java/lang/invoke/Invokers$Holder.class", + "/modules/java.base/jdk/internal/module/ModuleInfo.class", + "/modules/java.base/java/io/DataInput.class", + "/modules/java.base/java/io/DataInputStream.class", + "/modules/java.base/jdk/internal/module/ModuleInfo$CountingDataInput.class", + "/modules/java.base/sun/nio/ch/NativeThread.class", + "/modules/java.base/jdk/internal/misc/Blocker.class", + "/modules/java.base/sun/nio/ch/Util.class", + "/modules/java.base/sun/nio/ch/Util$BufferCache.class", + "/modules/java.base/sun/nio/ch/IOStatus.class", + "/modules/java.base/jdk/internal/util/ByteArray.class", + "/modules/java.base/java/lang/invoke/VarHandles.class", + "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsShorts$ByteArrayViewVarHandle.class", + "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsShorts$ArrayHandle.class", + "/modules/java.base/java/lang/invoke/VarHandleGuards.class", + "/modules/java.base/java/lang/invoke/VarForm.class", + "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsChars$ByteArrayViewVarHandle.class", + "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsChars$ArrayHandle.class", + "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsInts$ByteArrayViewVarHandle.class", + "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsInts$ArrayHandle.class", + "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsFloats$ByteArrayViewVarHandle.class", + "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsFloats$ArrayHandle.class", + "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsLongs$ByteArrayViewVarHandle.class", + "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsLongs$ArrayHandle.class", + "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsDoubles$ByteArrayViewVarHandle.class", + "/modules/java.base/java/lang/invoke/VarHandleByteArrayAsDoubles$ArrayHandle.class", + "/modules/java.base/java/lang/invoke/VarHandle$AccessDescriptor.class", + "/modules/java.base/jdk/internal/module/ModuleInfo$ConstantPool.class", + "/modules/java.base/jdk/internal/module/ModuleInfo$ConstantPool$Entry.class", + "/modules/java.base/jdk/internal/module/ModuleInfo$ConstantPool$IndexEntry.class", + "/modules/java.base/java/nio/charset/StandardCharsets.class", + "/modules/java.base/sun/nio/cs/US_ASCII.class", + "/modules/java.base/sun/nio/cs/ISO_8859_1.class", + "/modules/java.base/sun/nio/cs/UTF_16BE.class", + "/modules/java.base/sun/nio/cs/UTF_16LE.class", + "/modules/java.base/sun/nio/cs/UTF_16.class", + "/modules/java.base/sun/nio/cs/UTF_32BE.class", + "/modules/java.base/sun/nio/cs/UTF_32LE.class", + "/modules/java.base/sun/nio/cs/UTF_32.class", + "/modules/java.base/jdk/internal/module/ModuleInfo$ConstantPool$ValueEntry.class", + "/modules/java.base/java/lang/module/ModuleDescriptor$Builder.class", + "/modules/java.base/java/lang/module/ModuleDescriptor$Modifier.class", + "/modules/java.base/java/lang/reflect/AccessFlag.class", + "/modules/java.base/java/lang/reflect/AccessFlag$Location.class", + "/modules/java.base/java/lang/module/ModuleDescriptor$Requires$Modifier.class", + "/modules/java.base/java/lang/module/ModuleDescriptor$Requires.class", + "/modules/java.base/java/util/HashMap$KeySet.class", + "/modules/java.base/java/util/HashMap$KeyIterator.class", + "/modules/java.base/jdk/internal/module/Checks.class", + "/modules/java.base/java/util/ArrayList$Itr.class", + "/modules/java.base/java/lang/module/ModuleDescriptor$Provides.class", + "/modules/java.base/java/util/Collections$UnmodifiableCollection.class", + "/modules/java.base/java/util/Collections$UnmodifiableSet.class", + "/modules/java.base/java/util/HashMap$Values.class", + "/modules/java.base/java/util/HashMap$ValueIterator.class", + "/modules/java.base/java/util/ImmutableCollections$SetN$SetNIterator.class", + "/modules/java.base/jdk/internal/module/ModuleInfo$Attributes.class", + "/modules/java.base/jdk/internal/module/ModuleReferences.class", + "/modules/java.base/java/lang/module/ModuleReader.class", + "/modules/java.base/sun/nio/fs/UnixUriUtils.class", + "/modules/java.base/java/net/URI$Parser.class", + "/modules/java.base/java/lang/module/ModuleReference.class", + "/modules/java.base/jdk/internal/module/ModuleReferenceImpl.class", + "/modules/java.base/java/lang/module/ModuleDescriptor$Exports.class", + "/modules/java.base/java/lang/module/ModuleDescriptor$Opens.class", + "/modules/java.base/sun/nio/fs/UnixException.class", + "/modules/java.base/java/io/IOException.class", + "/modules/java.base/jdk/internal/loader/ArchivedClassLoaders.class", + "/modules/java.base/jdk/internal/loader/ClassLoaders$BootClassLoader.class", + "/modules/java.base/java/lang/ClassLoader$ParallelLoaders.class", + "/modules/java.base/java/util/WeakHashMap.class", + "/modules/java.base/java/util/WeakHashMap$Entry.class", + "/modules/java.base/java/util/WeakHashMap$KeySet.class", + "/modules/java.base/java/security/Principal.class", + "/modules/java.base/jdk/internal/loader/URLClassPath.class", + "/modules/java.base/java/net/URLStreamHandlerFactory.class", + "/modules/java.base/java/net/URL$DefaultFactory.class", + "/modules/java.base/jdk/internal/access/JavaNetURLAccess.class", + "/modules/java.base/sun/net/www/ParseUtil.class", + "/modules/java.base/java/net/URLStreamHandler.class", + "/modules/java.base/sun/net/www/protocol/file/Handler.class", + "/modules/java.base/sun/net/util/IPAddressUtil.class", + "/modules/java.base/sun/net/util/IPAddressUtil$MASKS.class", + "/modules/java.base/sun/net/www/protocol/jar/Handler.class", + "/modules/java.base/jdk/internal/module/ServicesCatalog.class", + "/modules/java.base/jdk/internal/loader/AbstractClassLoaderValue.class", + "/modules/java.base/jdk/internal/loader/ClassLoaderValue.class", + "/modules/java.base/jdk/internal/loader/BuiltinClassLoader$LoadedModule.class", + "/modules/java.base/jdk/internal/module/DefaultRoots.class", + "/modules/java.base/java/util/Spliterator.class", + "/modules/java.base/java/util/HashMap$HashMapSpliterator.class", + "/modules/java.base/java/util/HashMap$ValueSpliterator.class", + "/modules/java.base/java/util/stream/StreamSupport.class", + "/modules/java.base/java/util/stream/BaseStream.class", + "/modules/java.base/java/util/stream/Stream.class", + "/modules/java.base/java/util/stream/PipelineHelper.class", + "/modules/java.base/java/util/stream/AbstractPipeline.class", + "/modules/java.base/java/util/stream/ReferencePipeline.class", + "/modules/java.base/java/util/stream/ReferencePipeline$Head.class", + "/modules/java.base/java/util/stream/StreamOpFlag.class", + "/modules/java.base/java/util/stream/StreamOpFlag$Type.class", + "/modules/java.base/java/util/stream/StreamOpFlag$MaskBuilder.class", + "/modules/java.base/java/util/EnumMap.class", + "/modules/java.base/java/lang/Class$ReflectionData.class", + "/modules/java.base/java/lang/Class$Atomic.class", + "/modules/java.base/java/lang/PublicMethods$MethodList.class", + "/modules/java.base/java/lang/PublicMethods$Key.class", + "/modules/java.base/sun/reflect/annotation/AnnotationParser.class", + "/modules/java.base/jdk/internal/reflect/MethodHandleAccessorFactory.class", + "/modules/java.base/jdk/internal/reflect/MethodHandleAccessorFactory$LazyStaticHolder.class", + "/modules/java.base/java/lang/invoke/BoundMethodHandle.class", + "/modules/java.base/java/lang/invoke/ClassSpecializer.class", + "/modules/java.base/java/lang/invoke/BoundMethodHandle$Specializer.class", + "/modules/java.base/jdk/internal/vm/annotation/Stable.class", + "/modules/java.base/java/lang/invoke/ClassSpecializer$SpeciesData.class", + "/modules/java.base/java/lang/invoke/BoundMethodHandle$SpeciesData.class", + "/modules/java.base/java/lang/invoke/ClassSpecializer$Factory.class", + "/modules/java.base/java/lang/invoke/BoundMethodHandle$Specializer$Factory.class", + "/modules/java.base/java/lang/invoke/SimpleMethodHandle.class", + "/modules/java.base/java/lang/NoSuchFieldException.class", + "/modules/java.base/java/lang/invoke/BoundMethodHandle$Species_L.class", + "/modules/java.base/java/lang/invoke/DirectMethodHandle$Accessor.class", + "/modules/java.base/java/lang/invoke/DelegatingMethodHandle.class", + "/modules/java.base/java/lang/invoke/DelegatingMethodHandle$Holder.class", + "/modules/java.base/java/lang/invoke/LambdaFormEditor.class", + "/modules/java.base/java/lang/invoke/LambdaFormEditor$TransformKey.class", + "/modules/java.base/java/lang/invoke/LambdaFormBuffer.class", + "/modules/java.base/java/lang/invoke/LambdaFormEditor$Transform.class", + "/modules/java.base/jdk/internal/reflect/DirectMethodHandleAccessor.class", + "/modules/java.base/java/util/stream/Collectors.class", + "/modules/java.base/java/util/stream/Collector$Characteristics.class", + "/modules/java.base/java/util/EnumSet.class", + "/modules/java.base/java/util/RegularEnumSet.class", + "/modules/java.base/java/util/stream/Collector.class", + "/modules/java.base/java/util/stream/Collectors$CollectorImpl.class", + "/modules/java.base/java/util/function/BiConsumer.class", + "/modules/java.base/java/lang/invoke/DirectMethodHandle$Interface.class", + "/modules/java.base/java/lang/classfile/constantpool/InterfaceMethodRefEntry.class", + "/modules/java.base/jdk/internal/classfile/impl/AbstractPoolEntry$InterfaceMethodRefEntryImpl.class", + "/modules/java.base/java/util/function/BinaryOperator.class", + "/modules/java.base/java/util/stream/ReduceOps.class", + "/modules/java.base/java/util/stream/TerminalOp.class", + "/modules/java.base/java/util/stream/ReduceOps$ReduceOp.class", + "/modules/java.base/java/util/stream/StreamShape.class", + "/modules/java.base/java/util/stream/Sink.class", + "/modules/java.base/java/util/stream/TerminalSink.class", + "/modules/java.base/java/util/stream/ReduceOps$AccumulatingSink.class", + "/modules/java.base/java/util/stream/ReduceOps$Box.class", + "/modules/java.base/java/util/HashMap$KeySpliterator.class", + "/modules/java.base/java/util/function/Predicate.class", + "/modules/java.base/java/util/stream/ReferencePipeline$StatelessOp.class", + "/modules/java.base/java/util/stream/Sink$ChainedReference.class", + "/modules/java.base/jdk/internal/module/ModuleResolution.class", + "/modules/java.base/java/util/stream/FindOps.class", + "/modules/java.base/java/util/stream/FindOps$FindSink.class", + "/modules/java.base/java/util/stream/FindOps$FindSink$OfRef.class", + "/modules/java.base/java/util/stream/FindOps$FindOp.class", + "/modules/java.base/java/util/Spliterators.class", + "/modules/java.base/java/util/Spliterators$IteratorSpliterator.class", + "/modules/java.base/java/lang/module/Configuration.class", + "/modules/java.base/java/lang/module/Resolver.class", + "/modules/java.base/java/lang/ModuleLayer.class", + "/modules/java.base/java/util/SequencedSet.class", + "/modules/java.base/java/util/LinkedHashSet.class", + "/modules/java.base/java/util/SequencedMap.class", + "/modules/java.base/java/util/LinkedHashMap.class", + "/modules/java.base/java/lang/module/ResolvedModule.class", + "/modules/java.base/jdk/internal/module/ModuleLoaderMap$Mapper.class", + "/modules/java.base/jdk/internal/loader/AbstractClassLoaderValue$Memoizer.class", + "/modules/java.base/jdk/internal/module/ServicesCatalog$ServiceProvider.class", + "/modules/java.base/java/util/concurrent/CopyOnWriteArrayList.class", + "/modules/java.base/java/lang/ModuleLayer$Controller.class", + "/modules/java.base/jdk/internal/module/ModuleBootstrap$SafeModuleFinder.class", + "/modules/java.base/jdk/internal/vm/ContinuationSupport.class", + "/modules/java.base/jdk/internal/vm/Continuation$Pinned.class", + "/modules/java.base/sun/launcher/LauncherHelper.class", + "/modules/java.base/sun/net/util/URLUtil.class", + "/modules/java.base/jdk/internal/loader/URLClassPath$Loader.class", + "/modules/java.base/jdk/internal/loader/URLClassPath$FileLoader.class", + "/modules/java.base/jdk/internal/loader/Resource.class", + "/modules/java.base/java/io/FileCleanable.class", + "/modules/java.base/sun/nio/ByteBuffered.class", + "/modules/java.base/java/security/SecureClassLoader$CodeSourceKey.class", + "/modules/java.base/java/security/PermissionCollection.class", + "/modules/java.base/java/security/Permissions.class", + "/modules/java.base/java/lang/NamedPackage.class", + "/modules/java.base/jdk/internal/misc/MethodFinder.class", + "/modules/java.base/java/lang/Readable.class", + "/modules/java.base/java/nio/CharBuffer.class", + "/modules/java.base/java/nio/HeapCharBuffer.class", + "/modules/java.base/java/nio/charset/CoderResult.class", + "/modules/java.base/java/util/IdentityHashMap$IdentityHashMapIterator.class", + "/modules/java.base/java/util/IdentityHashMap$KeyIterator.class", + "/modules/java.base/java/lang/Shutdown.class", + "/modules/java.base/java/lang/Shutdown$Lock.class"); + + private static final Pattern SPLIT_MODULE_AND_PATH = Pattern.compile("/modules/([^/]+)/(.*)"); + + private static final Map> MODULE_TO_PATHS = INIT_CLASSES.stream() + .map(name -> { + Matcher m = SPLIT_MODULE_AND_PATH.matcher(name); + if (!m.matches()) { + throw new IllegalArgumentException("Bad resource name: " + name); + } + return m.toMatchResult(); + }) + .collect(groupingBy(m -> m.group(1), mapping(m -> m.group(2), toList()))); + } }