diff --git a/.gitignore b/.gitignore index 9145a9fa67b..852b692f99b 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ NashornProfile.txt **/core.[0-9]* *.rej *.orig +test/benchmarks/**/target diff --git a/make/hotspot/lib/CompileJvm.gmk b/make/hotspot/lib/CompileJvm.gmk index b5a8b4ef2a1..fd574b9e42d 100644 --- a/make/hotspot/lib/CompileJvm.gmk +++ b/make/hotspot/lib/CompileJvm.gmk @@ -158,6 +158,10 @@ ifeq ($(call isTargetOs, windows), true) WIN_EXPORT_FILE := $(JVM_OUTPUTDIR)/win-exports.def endif + ifeq ($(SHIP_DEBUG_SYMBOLS), public) + CFLAGS_STRIPPED_DEBUGINFO := -DHAS_STRIPPED_DEBUGINFO + endif + JVM_LDFLAGS += -def:$(WIN_EXPORT_FILE) endif @@ -183,6 +187,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJVM, \ CFLAGS := $(JVM_CFLAGS), \ abstract_vm_version.cpp_CXXFLAGS := $(CFLAGS_VM_VERSION), \ arguments.cpp_CXXFLAGS := $(CFLAGS_VM_VERSION), \ + whitebox.cpp_CXXFLAGS := $(CFLAGS_STRIPPED_DEBUGINFO), \ DISABLED_WARNINGS_gcc := $(DISABLED_WARNINGS_gcc), \ DISABLED_WARNINGS_gcc_ad_$(HOTSPOT_TARGET_CPU_ARCH).cpp := nonnull, \ DISABLED_WARNINGS_gcc_bytecodeInterpreter.cpp := unused-label, \ diff --git a/make/langtools/tools/javacserver/server/CompilerThreadPool.java b/make/langtools/tools/javacserver/server/CompilerThreadPool.java index 1f9e3a195b6..460adcfba28 100644 --- a/make/langtools/tools/javacserver/server/CompilerThreadPool.java +++ b/make/langtools/tools/javacserver/server/CompilerThreadPool.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 @@ -61,7 +61,7 @@ public class CompilerThreadPool { } catch (InterruptedException ie) { // (Re-)Cancel if current thread also interrupted pool.shutdownNow(); - // Preserve interrupt status + // Preserve interrupted status Thread.currentThread().interrupt(); } } diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 51cdf8c71df..5734519301e 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 @@ -1734,7 +1734,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 @@ -2520,10 +2520,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 @@ -2538,34 +2538,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 @@ public: 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/c1_globals_aarch64.hpp b/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp index a8a2fa8b2ee..938a64dd399 100644 --- a/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp @@ -53,7 +53,6 @@ define_pd_global(size_t, CodeCacheExpansionSize, 32*K ); define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, NeverActAsServerClassMachine, true ); -define_pd_global(uint64_t,MaxRAM, 1ULL*G); define_pd_global(bool, CICompileOSR, true ); #endif // !COMPILER2 define_pd_global(bool, UseTypeProfile, false); diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index b61a0e4e378..328ef0c53e6 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -2203,114 +2203,117 @@ void C2_MacroAssembler::sve_gen_mask_imm(PRegister dst, BasicType bt, uint32_t l // Pack active elements of src, under the control of mask, into the lowest-numbered elements of dst. // Any remaining elements of dst will be filled with zero. // Clobbers: rscratch1 -// Preserves: src, mask +// Preserves: mask, vzr void C2_MacroAssembler::sve_compress_short(FloatRegister dst, FloatRegister src, PRegister mask, - FloatRegister vtmp1, FloatRegister vtmp2, - PRegister pgtmp) { + FloatRegister vzr, FloatRegister vtmp, + PRegister pgtmp, unsigned vector_length_in_bytes) { assert(pgtmp->is_governing(), "This register has to be a governing predicate register"); - assert_different_registers(dst, src, vtmp1, vtmp2); + // When called by sve_compress_byte, src and vtmp may be the same register. + assert_different_registers(dst, src, vzr); + assert_different_registers(dst, vtmp, vzr); assert_different_registers(mask, pgtmp); - - // Example input: src = 8888 7777 6666 5555 4444 3333 2222 1111 - // mask = 0001 0000 0000 0001 0001 0000 0001 0001 - // Expected result: dst = 0000 0000 0000 8888 5555 4444 2222 1111 - sve_dup(vtmp2, H, 0); + // high <-- low + // Example input: src = hh gg ff ee dd cc bb aa, one character is 8 bits. + // mask = 01 00 00 01 01 00 01 01, one character is 1 bit. + // Expected result: dst = 00 00 00 hh ee dd bb aa // Extend lowest half to type INT. - // dst = 00004444 00003333 00002222 00001111 + // dst = 00dd 00cc 00bb 00aa sve_uunpklo(dst, S, src); - // pgtmp = 00000001 00000000 00000001 00000001 + // pgtmp = 0001 0000 0001 0001 sve_punpklo(pgtmp, mask); // Pack the active elements in size of type INT to the right, // and fill the remainings with zero. - // dst = 00000000 00004444 00002222 00001111 + // dst = 0000 00dd 00bb 00aa sve_compact(dst, S, dst, pgtmp); // Narrow the result back to type SHORT. - // dst = 0000 0000 0000 0000 0000 4444 2222 1111 - sve_uzp1(dst, H, dst, vtmp2); + // dst = 00 00 00 00 00 dd bb aa + sve_uzp1(dst, H, dst, vzr); + + // Return if the vector length is no more than MaxVectorSize/2, since the + // highest half is invalid. + if (vector_length_in_bytes <= (MaxVectorSize >> 1)) { + return; + } + // Count the active elements of lowest half. // rscratch1 = 3 sve_cntp(rscratch1, S, ptrue, pgtmp); // Repeat to the highest half. - // pgtmp = 00000001 00000000 00000000 00000001 + // pgtmp = 0001 0000 0000 0001 sve_punpkhi(pgtmp, mask); - // vtmp1 = 00008888 00007777 00006666 00005555 - sve_uunpkhi(vtmp1, S, src); - // vtmp1 = 00000000 00000000 00008888 00005555 - sve_compact(vtmp1, S, vtmp1, pgtmp); - // vtmp1 = 0000 0000 0000 0000 0000 0000 8888 5555 - sve_uzp1(vtmp1, H, vtmp1, vtmp2); + // 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); - // 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); + // 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); + // Pack the active elements in size of type SHORT to the right, + // and fill the remainings with zero. + // 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 = 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); - // 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); - // Narrow the result back to type BYTE. - // dst = 00 00 00 00 00 44 22 11 - sve_uzp1(dst, B, dst, vtmp4); // 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); + // 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); - 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); + // 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 cb8ded142f4..09850a60c64 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp @@ -173,13 +173,12 @@ // lowest-numbered elements of dst. Any remaining elements of dst will // be filled with zero. void sve_compress_byte(FloatRegister dst, FloatRegister src, PRegister mask, - FloatRegister vtmp1, FloatRegister vtmp2, - FloatRegister vtmp3, FloatRegister vtmp4, - PRegister ptmp, PRegister pgtmp); + FloatRegister vtmp1, FloatRegister vtmp2, FloatRegister vtmp3, + PRegister ptmp, PRegister pgtmp, unsigned vector_length_in_bytes); void sve_compress_short(FloatRegister dst, FloatRegister src, PRegister mask, - FloatRegister vtmp1, FloatRegister vtmp2, - PRegister pgtmp); + FloatRegister vzr, FloatRegister vtmp, + PRegister pgtmp, unsigned vector_length_in_bytes); void neon_reverse_bits(FloatRegister dst, FloatRegister src, BasicType bt, bool isQ); diff --git a/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp index 94a80dec3ea..a0dea3643a1 100644 --- a/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp @@ -55,7 +55,6 @@ define_pd_global(size_t, InitialCodeCacheSize, 2496*K); // Integral multip define_pd_global(size_t, CodeCacheExpansionSize, 64*K); // Ergonomics related flags -define_pd_global(uint64_t,MaxRAM, 128ULL*G); define_pd_global(intx, RegisterCostAreaRatio, 16000); // Peephole and CISC spilling both break the graph, and so makes the diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index a37edab8578..14009789319 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -148,56 +148,34 @@ extern "C" void disnm(intptr_t p); // strictly should be 64 bit movz #imm16<<0 // 110___10100 (i.e. requires insn[31:21] == 11010010100) // -class RelocActions { -protected: - typedef int (*reloc_insn)(address insn_addr, address &target); - virtual reloc_insn adrpMem() = 0; - virtual reloc_insn adrpAdd() = 0; - virtual reloc_insn adrpMovk() = 0; +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: @@ -209,9 +187,9 @@ public: case 0b111100: case 0b111110: { // load/store - if ((Instruction_aarch64::extract(_insn, 29, 24) & 0b111011) == 0b011000) { + if ((Instruction_aarch64::extract(insn, 29, 24) & 0b111011) == 0b011000) { // Load register (literal) - instructions = loadStore(insn_addr, target); + instructions = T::loadStore(insn_addr, target); break; } else { // nothing to do @@ -224,27 +202,27 @@ public: case 0b101000: case 0b111000: { // adr/adrp - assert(Instruction_aarch64::extract(_insn, 28, 24) == 0b10000, "must be"); - int shift = Instruction_aarch64::extract(_insn, 31, 31); + assert(Instruction_aarch64::extract(insn, 28, 24) == 0b10000, "must be"); + int shift = Instruction_aarch64::extract(insn, 31, 31); if (shift) { - uint32_t insn2 = insn_at(1); + uint32_t insn2 = insn_at(insn_addr, 1); if (Instruction_aarch64::extract(insn2, 29, 24) == 0b111001 && - Instruction_aarch64::extract(_insn, 4, 0) == + Instruction_aarch64::extract(insn, 4, 0) == Instruction_aarch64::extract(insn2, 9, 5)) { - instructions = adrp(insn_addr, target, adrpMem()); + instructions = T::adrp(insn_addr, target, T::adrpMem); } else if (Instruction_aarch64::extract(insn2, 31, 22) == 0b1001000100 && - Instruction_aarch64::extract(_insn, 4, 0) == + Instruction_aarch64::extract(insn, 4, 0) == Instruction_aarch64::extract(insn2, 4, 0)) { - instructions = adrp(insn_addr, target, adrpAdd()); + instructions = T::adrp(insn_addr, target, T::adrpAdd); } else if (Instruction_aarch64::extract(insn2, 31, 21) == 0b11110010110 && - Instruction_aarch64::extract(_insn, 4, 0) == + Instruction_aarch64::extract(insn, 4, 0) == Instruction_aarch64::extract(insn2, 4, 0)) { - instructions = adrp(insn_addr, target, adrpMovk()); + instructions = T::adrp(insn_addr, target, T::adrpMovk); } else { ShouldNotReachHere(); } } else { - instructions = adr(insn_addr, target); + instructions = T::adr(insn_addr, target); } break; } @@ -252,7 +230,7 @@ public: case 0b011001: case 0b101001: case 0b111001: { - instructions = immediate(insn_addr, target); + instructions = T::immediate(insn_addr, target); break; } default: { @@ -260,42 +238,36 @@ public: } } - verify(insn_addr, target); + T::verify(insn_addr, target); return instructions * NativeInstruction::instruction_size; } }; -class Patcher : public RelocActions { - virtual reloc_insn adrpMem() { return &Patcher::adrpMem_impl; } - virtual reloc_insn adrpAdd() { return &Patcher::adrpAdd_impl; } - virtual reloc_insn adrpMovk() { return &Patcher::adrpMovk_impl; } - +class Patcher : public AllStatic { public: - Patcher(address insn_addr) : RelocActions(insn_addr) {} - - virtual int unconditionalBranch(address insn_addr, address &target) { + static int unconditionalBranch(address insn_addr, address &target) { intptr_t offset = (target - insn_addr) >> 2; Instruction_aarch64::spatch(insn_addr, 25, 0, offset); return 1; } - virtual int conditionalBranch(address insn_addr, address &target) { + static int conditionalBranch(address insn_addr, address &target) { intptr_t offset = (target - insn_addr) >> 2; Instruction_aarch64::spatch(insn_addr, 23, 5, offset); return 1; } - virtual int testAndBranch(address insn_addr, address &target) { + static int testAndBranch(address insn_addr, address &target) { intptr_t offset = (target - insn_addr) >> 2; Instruction_aarch64::spatch(insn_addr, 18, 5, offset); return 1; } - virtual int loadStore(address insn_addr, address &target) { + static int loadStore(address insn_addr, address &target) { intptr_t offset = (target - insn_addr) >> 2; Instruction_aarch64::spatch(insn_addr, 23, 5, offset); return 1; } - virtual int adr(address insn_addr, address &target) { + static int adr(address insn_addr, address &target) { #ifdef ASSERT - assert(Instruction_aarch64::extract(_insn, 28, 24) == 0b10000, "must be"); + assert(Instruction_aarch64::extract(insn_at(insn_addr, 0), 28, 24) == 0b10000, "must be"); #endif // PC-rel. addressing ptrdiff_t offset = target - insn_addr; @@ -305,17 +277,18 @@ public: Instruction_aarch64::patch(insn_addr, 30, 29, offset_lo); return 1; } - virtual int adrp(address insn_addr, address &target, reloc_insn inner) { + template + static int adrp(address insn_addr, address &target, U inner) { int instructions = 1; #ifdef ASSERT - assert(Instruction_aarch64::extract(_insn, 28, 24) == 0b10000, "must be"); + assert(Instruction_aarch64::extract(insn_at(insn_addr, 0), 28, 24) == 0b10000, "must be"); #endif ptrdiff_t offset = target - insn_addr; instructions = 2; precond(inner != nullptr); // Give the inner reloc a chance to modify the target. address adjusted_target = target; - instructions = (*inner)(insn_addr, adjusted_target); + instructions = inner(insn_addr, adjusted_target); uintptr_t pc_page = (uintptr_t)insn_addr >> 12; uintptr_t adr_page = (uintptr_t)adjusted_target >> 12; offset = adr_page - pc_page; @@ -325,7 +298,7 @@ public: Instruction_aarch64::patch(insn_addr, 30, 29, offset_lo); return instructions; } - static int adrpMem_impl(address insn_addr, address &target) { + static int adrpMem(address insn_addr, address &target) { uintptr_t dest = (uintptr_t)target; int offset_lo = dest & 0xfff; uint32_t insn2 = insn_at(insn_addr, 1); @@ -334,21 +307,21 @@ public: guarantee(((dest >> size) << size) == dest, "misaligned target"); return 2; } - static int adrpAdd_impl(address insn_addr, address &target) { + static int adrpAdd(address insn_addr, address &target) { uintptr_t dest = (uintptr_t)target; int offset_lo = dest & 0xfff; Instruction_aarch64::patch(insn_addr + sizeof (uint32_t), 21, 10, offset_lo); return 2; } - static int adrpMovk_impl(address insn_addr, address &target) { + static int adrpMovk(address insn_addr, address &target) { uintptr_t dest = uintptr_t(target); Instruction_aarch64::patch(insn_addr + sizeof (uint32_t), 20, 5, (uintptr_t)target >> 32); dest = (dest & 0xffffffffULL) | (uintptr_t(insn_addr) & 0xffff00000000ULL); target = address(dest); return 2; } - virtual int immediate(address insn_addr, address &target) { - assert(Instruction_aarch64::extract(_insn, 31, 21) == 0b11010010100, "must be"); + static int immediate(address insn_addr, address &target) { + assert(Instruction_aarch64::extract(insn_at(insn_addr, 0), 31, 21) == 0b11010010100, "must be"); uint64_t dest = (uint64_t)target; // Move wide constant assert(nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch"); @@ -358,7 +331,7 @@ public: Instruction_aarch64::patch(insn_addr+8, 20, 5, (dest >>= 16) & 0xffff); return 3; } - virtual void verify(address insn_addr, address &target) { + static void verify(address insn_addr, address &target) { #ifdef ASSERT address address_is = MacroAssembler::target_addr_for_insn(insn_addr); if (!(address_is == target)) { @@ -392,56 +365,54 @@ static bool offset_for(uint32_t insn1, uint32_t insn2, ptrdiff_t &byte_offset) { return false; } -class AArch64Decoder : public RelocActions { - virtual reloc_insn adrpMem() { return &AArch64Decoder::adrpMem_impl; } - virtual reloc_insn adrpAdd() { return &AArch64Decoder::adrpAdd_impl; } - virtual reloc_insn adrpMovk() { return &AArch64Decoder::adrpMovk_impl; } - +class AArch64Decoder : public AllStatic { public: - AArch64Decoder(address insn_addr, uint32_t insn) : RelocActions(insn_addr, insn) {} - virtual int loadStore(address insn_addr, address &target) { - intptr_t offset = Instruction_aarch64::sextract(_insn, 23, 5); + static int loadStore(address insn_addr, address &target) { + intptr_t offset = Instruction_aarch64::sextract(insn_at(insn_addr, 0), 23, 5); target = insn_addr + (offset << 2); return 1; } - virtual int unconditionalBranch(address insn_addr, address &target) { - intptr_t offset = Instruction_aarch64::sextract(_insn, 25, 0); + static int unconditionalBranch(address insn_addr, address &target) { + intptr_t offset = Instruction_aarch64::sextract(insn_at(insn_addr, 0), 25, 0); target = insn_addr + (offset << 2); return 1; } - virtual int conditionalBranch(address insn_addr, address &target) { - intptr_t offset = Instruction_aarch64::sextract(_insn, 23, 5); + static int conditionalBranch(address insn_addr, address &target) { + intptr_t offset = Instruction_aarch64::sextract(insn_at(insn_addr, 0), 23, 5); target = address(((uint64_t)insn_addr + (offset << 2))); return 1; } - virtual int testAndBranch(address insn_addr, address &target) { - intptr_t offset = Instruction_aarch64::sextract(_insn, 18, 5); + static int testAndBranch(address insn_addr, address &target) { + intptr_t offset = Instruction_aarch64::sextract(insn_at(insn_addr, 0), 18, 5); target = address(((uint64_t)insn_addr + (offset << 2))); return 1; } - virtual int adr(address insn_addr, address &target) { + static int adr(address insn_addr, address &target) { // PC-rel. addressing - intptr_t offset = Instruction_aarch64::extract(_insn, 30, 29); - offset |= Instruction_aarch64::sextract(_insn, 23, 5) << 2; + uint32_t insn = insn_at(insn_addr, 0); + intptr_t offset = Instruction_aarch64::extract(insn, 30, 29); + offset |= Instruction_aarch64::sextract(insn, 23, 5) << 2; target = address((uint64_t)insn_addr + offset); return 1; } - virtual int adrp(address insn_addr, address &target, reloc_insn inner) { - assert(Instruction_aarch64::extract(_insn, 28, 24) == 0b10000, "must be"); - intptr_t offset = Instruction_aarch64::extract(_insn, 30, 29); - offset |= Instruction_aarch64::sextract(_insn, 23, 5) << 2; + template + static int adrp(address insn_addr, address &target, U inner) { + uint32_t insn = insn_at(insn_addr, 0); + assert(Instruction_aarch64::extract(insn, 28, 24) == 0b10000, "must be"); + intptr_t offset = Instruction_aarch64::extract(insn, 30, 29); + offset |= Instruction_aarch64::sextract(insn, 23, 5) << 2; int shift = 12; offset <<= shift; uint64_t target_page = ((uint64_t)insn_addr) + offset; target_page &= ((uint64_t)-1) << shift; - uint32_t insn2 = insn_at(1); + uint32_t insn2 = insn_at(insn_addr, 1); target = address(target_page); precond(inner != nullptr); - (*inner)(insn_addr, target); + inner(insn_addr, target); return 2; } - static int adrpMem_impl(address insn_addr, address &target) { + static int adrpMem(address insn_addr, address &target) { uint32_t insn2 = insn_at(insn_addr, 1); // Load/store register (unsigned immediate) ptrdiff_t byte_offset = Instruction_aarch64::extract(insn2, 21, 10); @@ -450,14 +421,14 @@ public: target += byte_offset; return 2; } - static int adrpAdd_impl(address insn_addr, address &target) { + static int adrpAdd(address insn_addr, address &target) { uint32_t insn2 = insn_at(insn_addr, 1); // add (immediate) ptrdiff_t byte_offset = Instruction_aarch64::extract(insn2, 21, 10); target += byte_offset; return 2; } - static int adrpMovk_impl(address insn_addr, address &target) { + static int adrpMovk(address insn_addr, address &target) { uint32_t insn2 = insn_at(insn_addr, 1); uint64_t dest = uint64_t(target); dest = (dest & 0xffff0000ffffffff) | @@ -476,35 +447,33 @@ public: return 2; } } - virtual int immediate(address insn_addr, address &target) { + static int immediate(address insn_addr, address &target) { uint32_t *insns = (uint32_t *)insn_addr; - assert(Instruction_aarch64::extract(_insn, 31, 21) == 0b11010010100, "must be"); + assert(Instruction_aarch64::extract(insns[0], 31, 21) == 0b11010010100, "must be"); // Move wide constant: movz, movk, movk. See movptr(). assert(nativeInstruction_at(insns+1)->is_movk(), "wrong insns in patch"); assert(nativeInstruction_at(insns+2)->is_movk(), "wrong insns in patch"); - target = address(uint64_t(Instruction_aarch64::extract(_insn, 20, 5)) - + (uint64_t(Instruction_aarch64::extract(insns[1], 20, 5)) << 16) - + (uint64_t(Instruction_aarch64::extract(insns[2], 20, 5)) << 32)); + target = address(uint64_t(Instruction_aarch64::extract(insns[0], 20, 5)) + + (uint64_t(Instruction_aarch64::extract(insns[1], 20, 5)) << 16) + + (uint64_t(Instruction_aarch64::extract(insns[2], 20, 5)) << 32)); assert(nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch"); assert(nativeInstruction_at(insn_addr+8)->is_movk(), "wrong insns in patch"); return 3; } - virtual void verify(address insn_addr, address &target) { + static void verify(address insn_addr, address &target) { } }; -address MacroAssembler::target_addr_for_insn(address insn_addr, uint32_t insn) { - AArch64Decoder decoder(insn_addr, insn); +address MacroAssembler::target_addr_for_insn(address insn_addr) { address target; - decoder.run(insn_addr, target); + RelocActions::run(insn_addr, target); return target; } // Patch any kind of instruction; there may be several instructions. // Return the total length (in bytes) of the instructions. int MacroAssembler::pd_patch_instruction_size(address insn_addr, address target) { - Patcher patcher(insn_addr); - return patcher.run(insn_addr, target); + return RelocActions::run(insn_addr, target); } int MacroAssembler::patch_oop(address insn_addr, address o) { @@ -546,11 +515,11 @@ int MacroAssembler::patch_narrow_klass(address insn_addr, narrowKlass n) { return 2 * NativeInstruction::instruction_size; } -address MacroAssembler::target_addr_for_insn_or_null(address insn_addr, unsigned insn) { - if (NativeInstruction::is_ldrw_to_zr(address(&insn))) { +address MacroAssembler::target_addr_for_insn_or_null(address insn_addr) { + if (NativeInstruction::is_ldrw_to_zr(insn_addr)) { return nullptr; } - return MacroAssembler::target_addr_for_insn(insn_addr, insn); + return MacroAssembler::target_addr_for_insn(insn_addr); } void MacroAssembler::safepoint_poll(Label& slow_path, bool at_return, bool in_nmethod, Register tmp) { diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 705bd19093c..d5a16e424e4 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -676,16 +676,8 @@ public: static bool needs_explicit_null_check(intptr_t offset); static bool uses_implicit_null_check(void* address); - static address target_addr_for_insn(address insn_addr, unsigned insn); - static address target_addr_for_insn_or_null(address insn_addr, unsigned insn); - static address target_addr_for_insn(address insn_addr) { - unsigned insn = *(unsigned*)insn_addr; - return target_addr_for_insn(insn_addr, insn); - } - static address target_addr_for_insn_or_null(address insn_addr) { - unsigned insn = *(unsigned*)insn_addr; - return target_addr_for_insn_or_null(insn_addr, insn); - } + static address target_addr_for_insn(address insn_addr); + static address target_addr_for_insn_or_null(address insn_addr); // Required platform-specific helpers for Label::patch_instructions. // They _shadow_ the declarations in AbstractAssembler, which are undefined. diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 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/arm/c1_globals_arm.hpp b/src/hotspot/cpu/arm/c1_globals_arm.hpp index 396f206975b..1fe5f1a23ee 100644 --- a/src/hotspot/cpu/arm/c1_globals_arm.hpp +++ b/src/hotspot/cpu/arm/c1_globals_arm.hpp @@ -54,7 +54,6 @@ define_pd_global(size_t, CodeCacheExpansionSize, 32*K ); define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, NeverActAsServerClassMachine, true); -define_pd_global(uint64_t, MaxRAM, 1ULL*G); define_pd_global(bool, CICompileOSR, true ); #endif // COMPILER2 define_pd_global(bool, UseTypeProfile, false); diff --git a/src/hotspot/cpu/arm/c2_globals_arm.hpp b/src/hotspot/cpu/arm/c2_globals_arm.hpp index d739e67360a..0849bd594f0 100644 --- a/src/hotspot/cpu/arm/c2_globals_arm.hpp +++ b/src/hotspot/cpu/arm/c2_globals_arm.hpp @@ -80,9 +80,6 @@ define_pd_global(size_t, NonProfiledCodeHeapSize, 21*M); define_pd_global(size_t, ProfiledCodeHeapSize, 22*M); define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M ); define_pd_global(size_t, CodeCacheExpansionSize, 64*K); - -// Ergonomics related flags -define_pd_global(uint64_t, MaxRAM, 128ULL*G); #else // InitialCodeCacheSize derived from specjbb2000 run. define_pd_global(size_t, InitialCodeCacheSize, 1536*K); // Integral multiple of CodeCacheExpansionSize @@ -91,8 +88,6 @@ define_pd_global(size_t, NonProfiledCodeHeapSize, 13*M); define_pd_global(size_t, ProfiledCodeHeapSize, 14*M); define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M ); define_pd_global(size_t, CodeCacheExpansionSize, 32*K); -// Ergonomics related flags -define_pd_global(uint64_t, MaxRAM, 4ULL*G); #endif define_pd_global(size_t, CodeCacheMinBlockLength, 6); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); diff --git a/src/hotspot/cpu/ppc/c1_globals_ppc.hpp b/src/hotspot/cpu/ppc/c1_globals_ppc.hpp index ab014287250..77d9acd1cd1 100644 --- a/src/hotspot/cpu/ppc/c1_globals_ppc.hpp +++ b/src/hotspot/cpu/ppc/c1_globals_ppc.hpp @@ -53,7 +53,6 @@ define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, NeverActAsServerClassMachine, true); define_pd_global(size_t, NewSizeThreadIncrease, 16*K); -define_pd_global(uint64_t, MaxRAM, 1ULL*G); define_pd_global(size_t, InitialCodeCacheSize, 160*K); #endif // !COMPILER2 diff --git a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp index 706255d035a..d5a0ff10994 100644 --- a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp +++ b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp @@ -86,7 +86,6 @@ define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M ); define_pd_global(size_t, CodeCacheExpansionSize, 64*K); // Ergonomics related flags -define_pd_global(uint64_t, MaxRAM, 128ULL*G); define_pd_global(size_t, CodeCacheMinBlockLength, 6); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); 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 8df2cc5d273..503cc259432 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..75566b2dd80 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; } %} @@ -12381,27 +12381,27 @@ instruct countTrailingZerosL_cnttzd(iRegIdst dst, iRegLsrc src) %{ %} // Expand nodes for byte_reverse_int. -instruct insrwi_a(iRegIdst dst, iRegIsrc src, immI16 pos, immI16 shift) %{ - effect(DEF dst, USE src, USE pos, USE shift); +instruct insrwi_a(iRegIdst dst, iRegIsrc src, immI16 n, immI16 b) %{ + effect(DEF dst, USE src, USE n, USE b); predicate(false); - format %{ "INSRWI $dst, $src, $pos, $shift" %} + format %{ "INSRWI $dst, $src, $n, $b" %} size(4); ins_encode %{ - __ insrwi($dst$$Register, $src$$Register, $shift$$constant, $pos$$constant); + __ insrwi($dst$$Register, $src$$Register, $n$$constant, $b$$constant); %} ins_pipe(pipe_class_default); %} // As insrwi_a, but with USE_DEF. -instruct insrwi(iRegIdst dst, iRegIsrc src, immI16 pos, immI16 shift) %{ - effect(USE_DEF dst, USE src, USE pos, USE shift); +instruct insrwi(iRegIdst dst, iRegIsrc src, immI16 n, immI16 b) %{ + effect(USE_DEF dst, USE src, USE n, USE b); predicate(false); - format %{ "INSRWI $dst, $src, $pos, $shift" %} + format %{ "INSRWI $dst, $src, $n, $b" %} size(4); ins_encode %{ - __ insrwi($dst$$Register, $src$$Register, $shift$$constant, $pos$$constant); + __ insrwi($dst$$Register, $src$$Register, $n$$constant, $b$$constant); %} ins_pipe(pipe_class_default); %} @@ -12423,12 +12423,12 @@ instruct bytes_reverse_int_Ex(iRegIdst dst, iRegIsrc src) %{ iRegLdst tmpI3; urShiftI_reg_imm(tmpI1, src, imm24); - insrwi_a(dst, tmpI1, imm24, imm8); + insrwi_a(dst, tmpI1, imm8, imm24); urShiftI_reg_imm(tmpI2, src, imm16); - insrwi(dst, tmpI2, imm8, imm16); + insrwi(dst, tmpI2, imm16, imm8); urShiftI_reg_imm(tmpI3, src, imm8); insrwi(dst, tmpI3, imm8, imm8); - insrwi(dst, src, imm0, imm8); + insrwi(dst, src, imm8, imm0); %} %} @@ -12546,7 +12546,7 @@ instruct bytes_reverse_ushort_Ex(iRegIdst dst, iRegIsrc src) %{ immI16 imm8 %{ (int) 8 %} urShiftI_reg_imm(dst, src, imm8); - insrwi(dst, src, imm16, imm8); + insrwi(dst, src, imm8, imm16); %} %} @@ -12575,7 +12575,7 @@ instruct bytes_reverse_short_Ex(iRegIdst dst, iRegIsrc src) %{ iRegLdst tmpI1; urShiftI_reg_imm(tmpI1, src, imm8); - insrwi(tmpI1, src, imm16, imm8); + insrwi(tmpI1, src, imm8, imm16); extsh(dst, tmpI1); %} %} 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/riscv/c1_globals_riscv.hpp b/src/hotspot/cpu/riscv/c1_globals_riscv.hpp index d64b3b66fa2..b15bb5c23c3 100644 --- a/src/hotspot/cpu/riscv/c1_globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/c1_globals_riscv.hpp @@ -53,7 +53,6 @@ define_pd_global(size_t, CodeCacheExpansionSize, 32*K ); define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, NeverActAsServerClassMachine, true ); -define_pd_global(uint64_t, MaxRAM, 1ULL*G); define_pd_global(bool, CICompileOSR, true ); #endif // !COMPILER2 define_pd_global(bool, UseTypeProfile, false); diff --git a/src/hotspot/cpu/riscv/c2_globals_riscv.hpp b/src/hotspot/cpu/riscv/c2_globals_riscv.hpp index 372865fc291..648c24ee98b 100644 --- a/src/hotspot/cpu/riscv/c2_globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/c2_globals_riscv.hpp @@ -55,7 +55,6 @@ define_pd_global(size_t, InitialCodeCacheSize, 2496*K); // Integral multip define_pd_global(size_t, CodeCacheExpansionSize, 64*K); // Ergonomics related flags -define_pd_global(uint64_t,MaxRAM, 128ULL*G); define_pd_global(intx, RegisterCostAreaRatio, 16000); // Peephole and CISC spilling both break the graph, and so makes the 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 61f4aa3e722..692335d8c08 100644 --- a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp @@ -1073,9 +1073,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { } // start execution -#ifdef ASSERT __ verify_frame_setup(); -#endif // jvmti support __ notify_method_entry(); @@ -1541,9 +1539,7 @@ address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) { } // start execution -#ifdef ASSERT __ verify_frame_setup(); -#endif // jvmti support __ notify_method_entry(); diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp index 2697b3e46dc..bd4a89d8199 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); } @@ -3028,6 +3033,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; @@ -3115,6 +3121,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 @@ -3170,7 +3178,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/s390/c1_globals_s390.hpp b/src/hotspot/cpu/s390/c1_globals_s390.hpp index 1b2b698a737..25e46cd1509 100644 --- a/src/hotspot/cpu/s390/c1_globals_s390.hpp +++ b/src/hotspot/cpu/s390/c1_globals_s390.hpp @@ -53,7 +53,6 @@ define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, NeverActAsServerClassMachine, true); define_pd_global(size_t, NewSizeThreadIncrease, 16*K); -define_pd_global(uint64_t, MaxRAM, 1ULL*G); define_pd_global(size_t, InitialCodeCacheSize, 160*K); #endif // !COMPILER2 diff --git a/src/hotspot/cpu/s390/c2_globals_s390.hpp b/src/hotspot/cpu/s390/c2_globals_s390.hpp index f1e757889b0..431a36cda07 100644 --- a/src/hotspot/cpu/s390/c2_globals_s390.hpp +++ b/src/hotspot/cpu/s390/c2_globals_s390.hpp @@ -74,7 +74,6 @@ define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M); define_pd_global(size_t, CodeCacheExpansionSize, 64*K); // Ergonomics related flags -define_pd_global(uint64_t, MaxRAM, 128ULL*G); define_pd_global(size_t, CodeCacheMinBlockLength, 4); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); 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/javaFrameAnchor_s390.hpp b/src/hotspot/cpu/s390/javaFrameAnchor_s390.hpp index ae8b8766159..307034ca0cd 100644 --- a/src/hotspot/cpu/s390/javaFrameAnchor_s390.hpp +++ b/src/hotspot/cpu/s390/javaFrameAnchor_s390.hpp @@ -35,38 +35,32 @@ // 3 - restoring an old state (javaCalls). inline void clear(void) { + // No hardware barriers are necessary. All members are volatile and the profiler + // is run from a signal handler and only observers the thread its running on. + // Clearing _last_Java_sp must be first. - OrderAccess::release(); + _last_Java_sp = nullptr; - // Fence? - OrderAccess::fence(); _last_Java_pc = nullptr; } inline void set(intptr_t* sp, address pc) { _last_Java_pc = pc; - - OrderAccess::release(); _last_Java_sp = sp; } void copy(JavaFrameAnchor* src) { - // In order to make sure the transition state is valid for "this" + // No hardware barriers are necessary. All members are volatile and the profiler + // is run from a signal handler and only observers the thread its running on. + // we must clear _last_Java_sp before copying the rest of the new data. - // Hack Alert: Temporary bugfix for 4717480/4721647 - // To act like previous version (pd_cache_state) don't null _last_Java_sp - // unless the value is changing. - // if (_last_Java_sp != src->_last_Java_sp) { - OrderAccess::release(); _last_Java_sp = nullptr; - OrderAccess::fence(); } _last_Java_pc = src->_last_Java_pc; // Must be last so profiler will always see valid frame if has_last_frame() is true. - OrderAccess::release(); _last_Java_sp = src->_last_Java_sp; } @@ -80,7 +74,7 @@ intptr_t* last_Java_fp(void) { return nullptr; } intptr_t* last_Java_sp() const { return _last_Java_sp; } - void set_last_Java_sp(intptr_t* sp) { OrderAccess::release(); _last_Java_sp = sp; } + void set_last_Java_sp(intptr_t* sp) { _last_Java_sp = sp; } address last_Java_pc(void) { return _last_Java_pc; } 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/c1_globals_x86.hpp b/src/hotspot/cpu/x86/c1_globals_x86.hpp index be5c443a695..978b233bb63 100644 --- a/src/hotspot/cpu/x86/c1_globals_x86.hpp +++ b/src/hotspot/cpu/x86/c1_globals_x86.hpp @@ -52,7 +52,6 @@ define_pd_global(size_t, CodeCacheExpansionSize, 32*K ); define_pd_global(size_t, CodeCacheMinBlockLength, 1 ); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, NeverActAsServerClassMachine, true ); -define_pd_global(uint64_t, MaxRAM, 1ULL*G); define_pd_global(bool, CICompileOSR, true ); #endif // !COMPILER2 define_pd_global(bool, UseTypeProfile, false); diff --git a/src/hotspot/cpu/x86/c2_globals_x86.hpp b/src/hotspot/cpu/x86/c2_globals_x86.hpp index a25f5da5e56..afd46a6fb08 100644 --- a/src/hotspot/cpu/x86/c2_globals_x86.hpp +++ b/src/hotspot/cpu/x86/c2_globals_x86.hpp @@ -52,9 +52,6 @@ define_pd_global(intx, LoopUnrollLimit, 60); // InitialCodeCacheSize derived from specjbb2000 run. define_pd_global(size_t, InitialCodeCacheSize, 2496*K); // Integral multiple of CodeCacheExpansionSize define_pd_global(size_t, CodeCacheExpansionSize, 64*K); - -// Ergonomics related flags -define_pd_global(uint64_t, MaxRAM, 128ULL*G); #else define_pd_global(intx, InteriorEntryAlignment, 4); define_pd_global(size_t, NewSizeThreadIncrease, 4*K); @@ -62,9 +59,6 @@ define_pd_global(intx, LoopUnrollLimit, 50); // Design center r // InitialCodeCacheSize derived from specjbb2000 run. define_pd_global(size_t, InitialCodeCacheSize, 2304*K); // Integral multiple of CodeCacheExpansionSize define_pd_global(size_t, CodeCacheExpansionSize, 32*K); - -// Ergonomics related flags -define_pd_global(uint64_t, MaxRAM, 4ULL*G); #endif // AMD64 define_pd_global(intx, RegisterCostAreaRatio, 16000); diff --git a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp index 925444792ca..09c5d93dbb3 100644 --- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp @@ -471,33 +471,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; @@ -511,7 +511,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/stubDeclarations_x86.hpp b/src/hotspot/cpu/x86/stubDeclarations_x86.hpp index 30a93fa4917..3e0e1d5bf07 100644 --- a/src/hotspot/cpu/x86/stubDeclarations_x86.hpp +++ b/src/hotspot/cpu/x86/stubDeclarations_x86.hpp @@ -41,10 +41,6 @@ do_stub(initial, verify_mxcsr) \ do_arch_entry(x86, initial, verify_mxcsr, verify_mxcsr_entry, \ verify_mxcsr_entry) \ - do_stub(initial, get_previous_sp) \ - do_arch_entry(x86, initial, get_previous_sp, \ - get_previous_sp_entry, \ - get_previous_sp_entry) \ do_stub(initial, f2i_fixup) \ do_arch_entry(x86, initial, f2i_fixup, f2i_fixup, f2i_fixup) \ do_stub(initial, f2l_fixup) \ diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index 6eb641daaf9..efb0411aa39 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -541,22 +541,6 @@ address StubGenerator::generate_orderaccess_fence() { } -// Support for intptr_t get_previous_sp() -// -// This routine is used to find the previous stack pointer for the -// caller. -address StubGenerator::generate_get_previous_sp() { - StubId stub_id = StubId::stubgen_get_previous_sp_id; - StubCodeMark mark(this, stub_id); - address start = __ pc(); - - __ movptr(rax, rsp); - __ addptr(rax, 8); // return address is at the top of the stack. - __ ret(0); - - return start; -} - //---------------------------------------------------------------------------------------------------- // Support for void verify_mxcsr() // @@ -4083,8 +4067,6 @@ void StubGenerator::generate_initial_stubs() { StubRoutines::_catch_exception_entry = generate_catch_exception(); // platform dependent - StubRoutines::x86::_get_previous_sp_entry = generate_get_previous_sp(); - StubRoutines::x86::_verify_mxcsr_entry = generate_verify_mxcsr(); StubRoutines::x86::_f2i_fixup = generate_f2i_fixup(); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index 44d13bbbf31..36315535d16 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -68,12 +68,6 @@ class StubGenerator: public StubCodeGenerator { // Support for intptr_t OrderAccess::fence() address generate_orderaccess_fence(); - // Support for intptr_t get_previous_sp() - // - // This routine is used to find the previous stack pointer for the - // caller. - address generate_get_previous_sp(); - //---------------------------------------------------------------------------------------------------- // Support for void verify_mxcsr() // @@ -393,6 +387,8 @@ class StubGenerator: public StubCodeGenerator { XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, XMMRegister xmm8); void ghash_last_8_avx2(Register subkeyHtbl); + void check_key_offset(Register key, int offset, int load_size); + // Load key and shuffle operation void ev_load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask); void ev_load_key(XMMRegister xmmdst, Register key, int offset, Register rscratch); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp index 6f698b954ad..f0726ded7e5 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp @@ -1759,25 +1759,43 @@ void StubGenerator::roundDeclast(XMMRegister xmm_reg) { __ vaesdeclast(xmm8, xmm8, xmm_reg, Assembler::AVX_512bit); } +// Check incoming byte offset against the int[] len. key is the pointer to the int[0]. +// This check happens often, so it is important for it to be very compact. +void StubGenerator::check_key_offset(Register key, int offset, int load_size) { +#ifdef ASSERT + Address key_length(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)); + assert((offset + load_size) % 4 == 0, "Alignment is good: %d + %d", offset, load_size); + int end_offset = (offset + load_size) / 4; + Label L_good; + __ cmpl(key_length, end_offset); + __ jccb(Assembler::greaterEqual, L_good); + __ hlt(); + __ bind(L_good); +#endif +} // Utility routine for loading a 128-bit key word in little endian format void StubGenerator::load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask) { + check_key_offset(key, offset, 16); __ movdqu(xmmdst, Address(key, offset)); __ pshufb(xmmdst, xmm_shuf_mask); } void StubGenerator::load_key(XMMRegister xmmdst, Register key, int offset, Register rscratch) { + check_key_offset(key, offset, 16); __ movdqu(xmmdst, Address(key, offset)); __ pshufb(xmmdst, ExternalAddress(key_shuffle_mask_addr()), rscratch); } void StubGenerator::ev_load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask) { + check_key_offset(key, offset, 16); __ movdqu(xmmdst, Address(key, offset)); __ pshufb(xmmdst, xmm_shuf_mask); __ evshufi64x2(xmmdst, xmmdst, xmmdst, 0x0, Assembler::AVX_512bit); } void StubGenerator::ev_load_key(XMMRegister xmmdst, Register key, int offset, Register rscratch) { + check_key_offset(key, offset, 16); __ movdqu(xmmdst, Address(key, offset)); __ pshufb(xmmdst, ExternalAddress(key_shuffle_mask_addr()), rscratch); __ evshufi64x2(xmmdst, xmmdst, xmmdst, 0x0, Assembler::AVX_512bit); @@ -3205,12 +3223,12 @@ void StubGenerator::ghash16_encrypt_parallel16_avx512(Register in, Register out, //AES round 9 roundEncode(AESKEY2, B00_03, B04_07, B08_11, B12_15); - ev_load_key(AESKEY2, key, 11 * 16, rbx); //AES rounds up to 11 (AES192) or 13 (AES256) //AES128 is done __ cmpl(NROUNDS, 52); __ jcc(Assembler::less, last_aes_rnd); __ bind(aes_192); + ev_load_key(AESKEY2, key, 11 * 16, rbx); roundEncode(AESKEY1, B00_03, B04_07, B08_11, B12_15); ev_load_key(AESKEY1, key, 12 * 16, rbx); roundEncode(AESKEY2, B00_03, B04_07, B08_11, B12_15); diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index b40f9e2924a..62306b562d6 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) { @@ -756,7 +756,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 @@ -1658,7 +1658,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() @@ -1666,7 +1666,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; } @@ -1678,22 +1678,22 @@ bool Matcher::use_asm_for_ldiv_by_con( jlong divisor ) { } // Register for DIVI projection of divmodI -RegMask Matcher::divI_proj_mask() { +const RegMask& Matcher::divI_proj_mask() { return INT_RAX_REG_mask(); } // Register for MODI projection of divmodI -RegMask Matcher::modI_proj_mask() { +const RegMask& Matcher::modI_proj_mask() { return INT_RDX_REG_mask(); } // Register for DIVL projection of divmodL -RegMask Matcher::divL_proj_mask() { +const RegMask& Matcher::divL_proj_mask() { return LONG_RAX_REG_mask(); } // Register for MODL projection of divmodL -RegMask Matcher::modL_proj_mask() { +const RegMask& Matcher::modL_proj_mask() { return LONG_RDX_REG_mask(); } diff --git a/src/hotspot/cpu/zero/frame_zero.cpp b/src/hotspot/cpu/zero/frame_zero.cpp index 52ccad2fa68..69bbea2972a 100644 --- a/src/hotspot/cpu/zero/frame_zero.cpp +++ b/src/hotspot/cpu/zero/frame_zero.cpp @@ -245,8 +245,6 @@ void frame::zero_print_on_error(int frame_index, os::snprintf_checked(fieldbuf, buflen, "word[%d]", offset); os::snprintf_checked(valuebuf, buflen, PTR_FORMAT, *addr); zeroframe()->identify_word(frame_index, offset, fieldbuf, valuebuf, buflen); - fieldbuf[buflen - 1] = '\0'; - valuebuf[buflen - 1] = '\0'; // Print the result st->print_cr(" " PTR_FORMAT ": %-21s = %s", p2i(addr), fieldbuf, valuebuf); diff --git a/src/hotspot/cpu/zero/vm_version_zero.cpp b/src/hotspot/cpu/zero/vm_version_zero.cpp index 35cbd296a26..e1c7567a306 100644 --- a/src/hotspot/cpu/zero/vm_version_zero.cpp +++ b/src/hotspot/cpu/zero/vm_version_zero.cpp @@ -116,9 +116,8 @@ void VM_Version::initialize() { } // Enable error context decoding on known platforms -#if defined(IA32) || defined(AMD64) || defined(ARM) || \ - defined(AARCH64) || defined(PPC) || defined(RISCV) || \ - defined(S390) +#if defined(AMD64) || defined(ARM) || defined(AARCH64) || \ + defined(PPC) || defined(RISCV) || defined(S390) if (FLAG_IS_DEFAULT(DecodeErrorContext)) { FLAG_SET_DEFAULT(DecodeErrorContext, true); } diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 0c0c2808fa1..5f81912c0d6 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -1054,7 +1054,7 @@ static void* dll_load_library(const char *filename, int *eno, char *ebuf, int eb error_report = "dlerror returned no error description"; } if (ebuf != nullptr && ebuflen > 0) { - os::snprintf_checked(ebuf, ebuflen - 1, "%s, LIBPATH=%s, LD_LIBRARY_PATH=%s : %s", + os::snprintf_checked(ebuf, ebuflen, "%s, LIBPATH=%s, LD_LIBRARY_PATH=%s : %s", filename, ::getenv("LIBPATH"), ::getenv("LD_LIBRARY_PATH"), error_report); } Events::log_dll_message(nullptr, "Loading shared library %s failed, %s", filename, error_report); diff --git a/src/hotspot/os/aix/porting_aix.cpp b/src/hotspot/os/aix/porting_aix.cpp index 402abd7d579..7311afc197b 100644 --- a/src/hotspot/os/aix/porting_aix.cpp +++ b/src/hotspot/os/aix/porting_aix.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2012, 2024 SAP SE. All rights reserved. + * 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 @@ -1154,7 +1155,7 @@ bool os::pd_dll_unload(void* libhandle, char* ebuf, int ebuflen) { error_report = "dlerror returned no error description"; } if (ebuf != nullptr && ebuflen > 0) { - os::snprintf_checked(ebuf, ebuflen - 1, "%s", error_report); + os::snprintf_checked(ebuf, ebuflen, "%s", error_report); } assert(false, "os::pd_dll_unload() ::dlclose() failed"); } 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/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 8c5bbd58a84..3e5fa8b84e1 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -231,8 +231,6 @@ size_t os::rss() { // Cpu architecture string #if defined(ZERO) static char cpu_arch[] = ZERO_LIBARCH; -#elif defined(IA32) -static char cpu_arch[] = "i386"; #elif defined(AMD64) static char cpu_arch[] = "amd64"; #elif defined(ARM) @@ -1011,7 +1009,6 @@ bool os::dll_address_to_library_name(address addr, char* buf, // same architecture as Hotspot is running on void *os::Bsd::dlopen_helper(const char *filename, int mode, char *ebuf, int ebuflen) { -#ifndef IA32 bool ieee_handling = IEEE_subnormal_handling_OK(); if (!ieee_handling) { Events::log_dll_message(nullptr, "IEEE subnormal handling check failed before loading %s", filename); @@ -1034,14 +1031,9 @@ void *os::Bsd::dlopen_helper(const char *filename, int mode, char *ebuf, int ebu // numerical "accuracy", but we need to protect Java semantics first // and foremost. See JDK-8295159. - // This workaround is ineffective on IA32 systems because the MXCSR - // register (which controls flush-to-zero mode) is not stored in the - // legacy fenv. - fenv_t default_fenv; int rtn = fegetenv(&default_fenv); assert(rtn == 0, "fegetenv must succeed"); -#endif // IA32 void* result; JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);) @@ -1061,7 +1053,6 @@ void *os::Bsd::dlopen_helper(const char *filename, int mode, char *ebuf, int ebu } else { Events::log_dll_message(nullptr, "Loaded shared library %s", filename); log_info(os)("shared library load of %s was successful", filename); -#ifndef IA32 if (! IEEE_subnormal_handling_OK()) { // We just dlopen()ed a library that mangled the floating-point // flags. Silently fix things now. @@ -1086,7 +1077,6 @@ void *os::Bsd::dlopen_helper(const char *filename, int mode, char *ebuf, int ebu assert(false, "fesetenv didn't work"); } } -#endif // IA32 } return result; @@ -1195,9 +1185,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { {EM_68K, EM_68K, ELFCLASS32, ELFDATA2MSB, (char*)"M68k"} }; - #if (defined IA32) - static Elf32_Half running_arch_code=EM_386; - #elif (defined AMD64) + #if (defined AMD64) static Elf32_Half running_arch_code=EM_X86_64; #elif (defined __powerpc64__) static Elf32_Half running_arch_code=EM_PPC64; @@ -1219,7 +1207,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { static Elf32_Half running_arch_code=EM_68K; #else #error Method os::dll_load requires that one of following is defined:\ - IA32, AMD64, __powerpc__, ARM, S390, ALPHA, MIPS, MIPSEL, PARISC, M68K + AMD64, __powerpc__, ARM, S390, ALPHA, MIPS, MIPSEL, PARISC, M68K #endif // Identify compatibility class for VM's architecture and library's architecture @@ -2495,7 +2483,7 @@ bool os::pd_dll_unload(void* libhandle, char* ebuf, int ebuflen) { error_report = "dlerror returned no error description"; } if (ebuf != nullptr && ebuflen > 0) { - os::snprintf_checked(ebuf, ebuflen - 1, "%s", error_report); + os::snprintf_checked(ebuf, ebuflen, "%s", error_report); } } 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 id; + 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 (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 @@ retry: 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/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 772b170d11c..8137347dd4f 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -1795,9 +1795,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { {EM_LOONGARCH, EM_LOONGARCH, ELFCLASS64, ELFDATA2LSB, (char*)"LoongArch"}, }; -#if (defined IA32) - static Elf32_Half running_arch_code=EM_386; -#elif (defined AMD64) || (defined X32) +#if (defined AMD64) static Elf32_Half running_arch_code=EM_X86_64; #elif (defined __sparc) && (defined _LP64) static Elf32_Half running_arch_code=EM_SPARCV9; @@ -1831,7 +1829,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { static Elf32_Half running_arch_code=EM_LOONGARCH; #else #error Method os::dll_load requires that one of following is defined:\ - AARCH64, ALPHA, ARM, AMD64, IA32, LOONGARCH64, M68K, MIPS, MIPSEL, PARISC, __powerpc__, __powerpc64__, RISCV, S390, SH, __sparc + AARCH64, ALPHA, ARM, AMD64, LOONGARCH64, M68K, MIPS, MIPSEL, PARISC, __powerpc__, __powerpc64__, RISCV, S390, SH, __sparc #endif // Identify compatibility class for VM's architecture and library's architecture @@ -1893,7 +1891,6 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { } void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) { -#ifndef IA32 bool ieee_handling = IEEE_subnormal_handling_OK(); if (!ieee_handling) { Events::log_dll_message(nullptr, "IEEE subnormal handling check failed before loading %s", filename); @@ -1916,14 +1913,9 @@ void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) { // numerical "accuracy", but we need to protect Java semantics first // and foremost. See JDK-8295159. - // This workaround is ineffective on IA32 systems because the MXCSR - // register (which controls flush-to-zero mode) is not stored in the - // legacy fenv. - fenv_t default_fenv; int rtn = fegetenv(&default_fenv); assert(rtn == 0, "fegetenv must succeed"); -#endif // IA32 void* result; JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);) @@ -1943,7 +1935,6 @@ void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) { } else { Events::log_dll_message(nullptr, "Loaded shared library %s", filename); log_info(os)("shared library load of %s was successful", filename); -#ifndef IA32 // Quickly test to make sure subnormals are correctly handled. if (! IEEE_subnormal_handling_OK()) { // We just dlopen()ed a library that mangled the floating-point flags. @@ -1969,7 +1960,6 @@ void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) { assert(false, "fesetenv didn't work"); } } -#endif // IA32 } return result; } @@ -2613,7 +2603,7 @@ void os::print_memory_info(outputStream* st) { // before "flags" so if we find a second "model name", then the // "flags" field is considered missing. static bool print_model_name_and_flags(outputStream* st, char* buf, size_t buflen) { -#if defined(IA32) || defined(AMD64) +#if defined(AMD64) // Other platforms have less repetitive cpuinfo files FILE *fp = os::fopen("/proc/cpuinfo", "r"); if (fp) { @@ -2672,7 +2662,7 @@ static void print_sys_devices_cpu_info(outputStream* st) { } // we miss the cpufreq entries on Power and s390x -#if defined(IA32) || defined(AMD64) +#if defined(AMD64) _print_ascii_file_h("BIOS frequency limitation", "/sys/devices/system/cpu/cpu0/cpufreq/bios_limit", st); _print_ascii_file_h("Frequency switch latency (ns)", "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_transition_latency", st); _print_ascii_file_h("Available cpu frequencies", "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies", st); @@ -2725,7 +2715,7 @@ void os::jfr_report_memory_info() { #endif // INCLUDE_JFR -#if defined(AMD64) || defined(IA32) || defined(X32) +#if defined(AMD64) const char* search_string = "model name"; #elif defined(M68K) const char* search_string = "CPU"; @@ -2778,8 +2768,6 @@ void os::get_summary_cpu_info(char* cpuinfo, size_t length) { strncpy(cpuinfo, "x86_64", length); #elif defined(ARM) // Order wrt. AARCH64 is relevant! strncpy(cpuinfo, "ARM", length); -#elif defined(IA32) - strncpy(cpuinfo, "x86_32", length); #elif defined(PPC) strncpy(cpuinfo, "PPC64", length); #elif defined(RISCV) @@ -3079,14 +3067,9 @@ int os::Linux::sched_getcpu_syscall(void) { unsigned int cpu = 0; long retval = -1; -#if defined(IA32) - #ifndef SYS_getcpu - #define SYS_getcpu 318 - #endif - retval = syscall(SYS_getcpu, &cpu, nullptr, nullptr); -#elif defined(AMD64) -// Unfortunately we have to bring all these macros here from vsyscall.h -// to be able to compile on old linuxes. +#if defined(AMD64) + // Unfortunately we have to bring all these macros here from vsyscall.h + // to be able to compile on old linuxes. #define __NR_vgetcpu 2 #define VSYSCALL_START (-10UL << 20) #define VSYSCALL_SIZE 1024 @@ -4459,87 +4442,6 @@ void os::Linux::disable_numa(const char* reason, bool warning) { FLAG_SET_ERGO(UseNUMAInterleaving, false); } -#if defined(IA32) && !defined(ZERO) -/* - * Work-around (execute code at a high address) for broken NX emulation using CS limit, - * Red Hat patch "Exec-Shield" (IA32 only). - * - * Map and execute at a high VA to prevent CS lazy updates race with SMP MM - * invalidation.Further code generation by the JVM will no longer cause CS limit - * updates. - * - * Affects IA32: RHEL 5 & 6, Ubuntu 10.04 (LTS), 10.10, 11.04, 11.10, 12.04. - * @see JDK-8023956 - */ -static void workaround_expand_exec_shield_cs_limit() { - assert(os::Linux::initial_thread_stack_bottom() != nullptr, "sanity"); - size_t page_size = os::vm_page_size(); - - /* - * JDK-8197429 - * - * Expand the stack mapping to the end of the initial stack before - * attempting to install the codebuf. This is needed because newer - * Linux kernels impose a distance of a megabyte between stack - * memory and other memory regions. If we try to install the - * codebuf before expanding the stack the installation will appear - * to succeed but we'll get a segfault later if we expand the stack - * in Java code. - * - */ - if (os::is_primordial_thread()) { - address limit = os::Linux::initial_thread_stack_bottom(); - if (! DisablePrimordialThreadGuardPages) { - limit += StackOverflow::stack_red_zone_size() + - StackOverflow::stack_yellow_zone_size(); - } - os::Linux::expand_stack_to(limit); - } - - /* - * Take the highest VA the OS will give us and exec - * - * Although using -(pagesz) as mmap hint works on newer kernel as you would - * think, older variants affected by this work-around don't (search forward only). - * - * On the affected distributions, we understand the memory layout to be: - * - * TASK_LIMIT= 3G, main stack base close to TASK_LIMT. - * - * A few pages south main stack will do it. - * - * If we are embedded in an app other than launcher (initial != main stack), - * we don't have much control or understanding of the address space, just let it slide. - */ - char* hint = (char*)(os::Linux::initial_thread_stack_bottom() - - (StackOverflow::stack_guard_zone_size() + page_size)); - char* codebuf = os::attempt_reserve_memory_at(hint, page_size, mtThread); - - if (codebuf == nullptr) { - // JDK-8197429: There may be a stack gap of one megabyte between - // the limit of the stack and the nearest memory region: this is a - // Linux kernel workaround for CVE-2017-1000364. If we failed to - // map our codebuf, try again at an address one megabyte lower. - hint -= 1 * M; - codebuf = os::attempt_reserve_memory_at(hint, page_size, mtThread); - } - - if ((codebuf == nullptr) || (!os::commit_memory(codebuf, page_size, true))) { - return; // No matter, we tried, best effort. - } - - log_info(os)("[CS limit NX emulation work-around, exec code at: %p]", codebuf); - - // Some code to exec: the 'ret' instruction - codebuf[0] = 0xC3; - - // Call the code in the codebuf - __asm__ volatile("call *%0" : : "r"(codebuf)); - - // keep the page mapped so CS limit isn't reduced. -} -#endif // defined(IA32) && !defined(ZERO) - // this is called _after_ the global arguments have been parsed jint os::init_2(void) { @@ -4560,17 +4462,10 @@ jint os::init_2(void) { return JNI_ERR; } -#if defined(IA32) && !defined(ZERO) - // Need to ensure we've determined the process's initial stack to - // perform the workaround - Linux::capture_initial_stack(JavaThread::stack_size_at_create()); - workaround_expand_exec_shield_cs_limit(); -#else suppress_primordial_thread_resolution = Arguments::created_by_java_launcher(); if (!suppress_primordial_thread_resolution) { Linux::capture_initial_stack(JavaThread::stack_size_at_create()); } -#endif Linux::libpthread_init(); Linux::sched_getcpu_init(); @@ -5443,7 +5338,7 @@ bool os::pd_dll_unload(void* libhandle, char* ebuf, int ebuflen) { error_report = "dlerror returned no error description"; } if (ebuf != nullptr && ebuflen > 0) { - os::snprintf_checked(ebuf, ebuflen - 1, "%s", error_report); + os::snprintf_checked(ebuf, ebuflen, "%s", error_report); } } diff --git a/src/hotspot/os/linux/os_perf_linux.cpp b/src/hotspot/os/linux/os_perf_linux.cpp index 7caf8f98a00..5708194521f 100644 --- a/src/hotspot/os/linux/os_perf_linux.cpp +++ b/src/hotspot/os/linux/os_perf_linux.cpp @@ -705,11 +705,9 @@ bool SystemProcessInterface::SystemProcesses::ProcessIterator::is_valid_entry(st if (atoi(entry->d_name) != 0) { jio_snprintf(buffer, PATH_MAX, "/proc/%s", entry->d_name); - buffer[PATH_MAX - 1] = '\0'; if (is_dir(buffer)) { jio_snprintf(buffer, PATH_MAX, "/proc/%s/stat", entry->d_name); - buffer[PATH_MAX - 1] = '\0'; if (fsize(buffer, size) != OS_ERR) { return true; } @@ -724,7 +722,6 @@ void SystemProcessInterface::SystemProcesses::ProcessIterator::get_exe_name() { char buffer[PATH_MAX]; jio_snprintf(buffer, PATH_MAX, "/proc/%s/stat", _entry->d_name); - buffer[PATH_MAX - 1] = '\0'; if ((fp = os::fopen(buffer, "r")) != nullptr) { if (fgets(buffer, PATH_MAX, fp) != nullptr) { char* start, *end; @@ -752,7 +749,6 @@ char* SystemProcessInterface::SystemProcesses::ProcessIterator::get_cmdline() { char* cmdline = nullptr; jio_snprintf(buffer, PATH_MAX, "/proc/%s/cmdline", _entry->d_name); - buffer[PATH_MAX - 1] = '\0'; if ((fp = os::fopen(buffer, "r")) != nullptr) { size_t size = 0; char dummy; @@ -787,7 +783,6 @@ char* SystemProcessInterface::SystemProcesses::ProcessIterator::get_exe_path() { char buffer[PATH_MAX]; jio_snprintf(buffer, PATH_MAX, "/proc/%s/exe", _entry->d_name); - buffer[PATH_MAX - 1] = '\0'; return os::realpath(buffer, _exePath, PATH_MAX); } @@ -1001,7 +996,6 @@ int64_t NetworkPerformanceInterface::NetworkPerformance::read_counter(const char return -1; } - buf[num_bytes] = '\0'; int64_t value = strtoll(buf, nullptr, 10); return value; 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..ce2baeaf46c 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -1827,12 +1827,12 @@ void * os::dll_load(const char *name, char *ebuf, int ebuflen) { } if (lib_arch_str != nullptr) { - os::snprintf_checked(ebuf, ebuflen - 1, + os::snprintf_checked(ebuf, ebuflen, "Can't load %s-bit .dll on a %s-bit platform", lib_arch_str, running_arch_str); } else { // don't know what architecture this dll was build for - os::snprintf_checked(ebuf, ebuflen - 1, + os::snprintf_checked(ebuf, ebuflen, "Can't load this .dll (machine code=0x%x) on a %s-bit platform", lib_arch, running_arch_str); } @@ -3150,7 +3150,6 @@ void os::large_page_init() { _large_page_size = os::win32::large_page_init_decide_size(); const size_t default_page_size = os::vm_page_size(); if (_large_page_size > default_page_size) { -#if !defined(IA32) if (EnableAllLargePageSizesForWindows) { size_t min_size = GetLargePageMinimum(); @@ -3159,7 +3158,6 @@ void os::large_page_init() { _page_sizes.add(page_size); } } -#endif _page_sizes.add(_large_page_size); } @@ -4162,11 +4160,6 @@ void os::win32::initialize_system_info() { } _physical_memory = static_cast(ms.ullTotalPhys); - if (FLAG_IS_DEFAULT(MaxRAM)) { - // Adjust MaxRAM according to the maximum virtual address space available. - FLAG_SET_DEFAULT(MaxRAM, MIN2(MaxRAM, (uint64_t) ms.ullTotalVirtual)); - } - _is_windows_server = IsWindowsServer(); initialize_performance_counter(); @@ -6259,3 +6252,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..f54a2b52cca 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" @@ -41,11 +42,7 @@ #include #include #include - -typedef BOOL (WINAPI *SetSecurityDescriptorControlFnPtr)( - IN PSECURITY_DESCRIPTOR pSecurityDescriptor, - IN SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, - IN SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet); +#include // Standard Memory Implementation Details @@ -62,9 +59,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 +85,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 +108,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 +209,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 +221,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 +238,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 +475,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 +496,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 +522,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 +549,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 +670,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 +681,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 +745,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 +755,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 +766,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 +777,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 +820,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 +838,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 +864,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 +875,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 +900,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 +913,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 +927,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,39 +943,23 @@ 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; } - // if running on windows 2000 or later, set the automatic inheritance - // control flags. - SetSecurityDescriptorControlFnPtr _SetSecurityDescriptorControl; - _SetSecurityDescriptorControl = (SetSecurityDescriptorControlFnPtr) - GetProcAddress(GetModuleHandle(TEXT("advapi32.dll")), - "SetSecurityDescriptorControl"); - - if (_SetSecurityDescriptorControl != nullptr) { - // We do not want to further propagate inherited DACLs, so making them - // protected prevents that. - if (!_SetSecurityDescriptorControl(pSD, SE_DACL_PROTECTED, - SE_DACL_PROTECTED)) { - if (PrintMiscellaneous && Verbose) { - warning("SetSecurityDescriptorControl failure:" - " lasterror = %d \n", GetLastError()); - } - FREE_C_HEAP_ARRAY(char, newACL); - return false; - } + // We do not want to further propagate inherited DACLs, so making them + // protected prevents that. + if (!SetSecurityDescriptorControl(pSD, SE_DACL_PROTECTED, SE_DACL_PROTECTED)) { + log_debug(perf)("SetSecurityDescriptorControl failure: lasterror = %d", GetLastError()); + FREE_C_HEAP_ARRAY(char, newACL); + return false; } - // Note, the security descriptor maintains a reference to the newACL, not - // a copy of it. Therefore, the newACL is not freed here. It is freed when - // the security descriptor containing its reference is freed. - // - return true; + + // Note, the security descriptor maintains a reference to the newACL, not + // a copy of it. Therefore, the newACL is not freed here. It is freed when + // the security descriptor containing its reference is freed. + return true; } // method to create a security attributes structure, which contains a @@ -1057,10 +979,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 +1032,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 +1046,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 +1147,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 +1158,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 +1229,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 +1255,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 +1269,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 +1300,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 +1381,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 +1445,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 +1525,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 +1594,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_zero/os_linux_zero.cpp b/src/hotspot/os_cpu/linux_zero/os_linux_zero.cpp index 0ea379fec50..ee9c5e2dfb2 100644 --- a/src/hotspot/os_cpu/linux_zero/os_linux_zero.cpp +++ b/src/hotspot/os_cpu/linux_zero/os_linux_zero.cpp @@ -86,9 +86,7 @@ char* os::non_memory_address_word() { address os::Posix::ucontext_get_pc(const ucontext_t* uc) { if (DecodeErrorContext) { -#if defined(IA32) - return (address)uc->uc_mcontext.gregs[REG_EIP]; -#elif defined(AMD64) +#if defined(AMD64) return (address)uc->uc_mcontext.gregs[REG_RIP]; #elif defined(ARM) return (address)uc->uc_mcontext.arm_pc; @@ -117,9 +115,7 @@ void os::Posix::ucontext_set_pc(ucontext_t* uc, address pc) { intptr_t* os::Linux::ucontext_get_sp(const ucontext_t* uc) { if (DecodeErrorContext) { -#if defined(IA32) - return (intptr_t*)uc->uc_mcontext.gregs[REG_UESP]; -#elif defined(AMD64) +#if defined(AMD64) return (intptr_t*)uc->uc_mcontext.gregs[REG_RSP]; #elif defined(ARM) return (intptr_t*)uc->uc_mcontext.arm_sp; @@ -144,9 +140,7 @@ intptr_t* os::Linux::ucontext_get_sp(const ucontext_t* uc) { intptr_t* os::Linux::ucontext_get_fp(const ucontext_t* uc) { if (DecodeErrorContext) { -#if defined(IA32) - return (intptr_t*)uc->uc_mcontext.gregs[REG_EBP]; -#elif defined(AMD64) +#if defined(AMD64) return (intptr_t*)uc->uc_mcontext.gregs[REG_RBP]; #elif defined(ARM) return (intptr_t*)uc->uc_mcontext.arm_fp; 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..c688848c790 100644 --- a/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp +++ b/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp @@ -51,6 +51,8 @@ #include "utilities/vmError.hpp" #include "windbghelp.hpp" +#include + #undef REG_SP #undef REG_FP @@ -197,98 +199,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) { @@ -339,11 +249,15 @@ intptr_t* os::fetch_bcp_from_context(const void* ucVoid) { // Returns the current stack pointer. Accurate value needed for // os::verify_stack_alignment(). +// The function is intentionally not inlined. This way, the transfer of control +// into this method must be made with a call instruction. The MSVC +// _AddressOfReturnAddress() intrinsic returns the address of the return PC +// saved by that call instruction. Therefore, the stack pointer of the caller +// just before the call instruction, is acquired by skipping over the return PC +// slot in the stack. +__declspec(noinline) address os::current_stack_pointer() { - typedef address get_sp_func(); - get_sp_func* func = CAST_TO_FN_PTR(get_sp_func*, - StubRoutines::x86::get_previous_sp_entry()); - return (*func)(); + return ((address)_AddressOfReturnAddress()) + sizeof(void*); } bool os::win32::get_frame_at_stack_banging_point(JavaThread* thread, @@ -500,11 +414,7 @@ void os::setup_fpu() { #ifndef PRODUCT void os::verify_stack_alignment() { - // The current_stack_pointer() calls generated get_previous_sp stub routine. - // Only enable the assert after the routine becomes available. - if (StubRoutines::initial_stubs_code() != nullptr) { - assert(((intptr_t)os::current_stack_pointer() & (StackAlignmentInBytes-1)) == 0, "incorrect stack alignment"); - } + assert(((intptr_t)os::current_stack_pointer() & (StackAlignmentInBytes-1)) == 0, "incorrect stack alignment"); } #endif 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 b938d5b7560..182587d2f2f 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -2422,7 +2422,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..9cbd6aaf66f 100644 --- a/src/hotspot/share/adlc/output_c.cpp +++ b/src/hotspot/share/adlc/output_c.cpp @@ -2323,7 +2323,7 @@ private: if (strcmp(rep_var,"$Register") == 0) return "as_Register"; if (strcmp(rep_var,"$KRegister") == 0) return "as_KRegister"; if (strcmp(rep_var,"$FloatRegister") == 0) return "as_FloatRegister"; -#if defined(IA32) || defined(AMD64) +#if defined(AMD64) if (strcmp(rep_var,"$XMMRegister") == 0) return "as_XMMRegister"; #endif if (strcmp(rep_var,"$CondRegister") == 0) return "as_ConditionRegister"; @@ -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/c1/c1_CodeStubs.hpp b/src/hotspot/share/c1/c1_CodeStubs.hpp index a02368487c5..9a462006bcc 100644 --- a/src/hotspot/share/c1/c1_CodeStubs.hpp +++ b/src/hotspot/share/c1/c1_CodeStubs.hpp @@ -138,7 +138,7 @@ class ConversionStub: public CodeStub { public: ConversionStub(Bytecodes::Code bytecode, LIR_Opr input, LIR_Opr result) : _bytecode(bytecode), _input(input), _result(result) { - NOT_IA32( ShouldNotReachHere(); ) // used only on x86-32 + ShouldNotReachHere(); } Bytecodes::Code bytecode() { return _bytecode; } diff --git a/src/hotspot/share/c1/c1_LIRAssembler.cpp b/src/hotspot/share/c1/c1_LIRAssembler.cpp index e6963c00c6a..e22b5103514 100644 --- a/src/hotspot/share/c1/c1_LIRAssembler.cpp +++ b/src/hotspot/share/c1/c1_LIRAssembler.cpp @@ -527,16 +527,6 @@ void LIR_Assembler::emit_op1(LIR_Op1* op) { safepoint_poll(op->in_opr(), op->info()); break; -#ifdef IA32 - case lir_fxch: - fxch(op->in_opr()->as_jint()); - break; - - case lir_fld: - fld(op->in_opr()->as_jint()); - break; -#endif // IA32 - case lir_branch: break; @@ -612,12 +602,6 @@ void LIR_Assembler::emit_op0(LIR_Op0* op) { osr_entry(); break; -#ifdef IA32 - case lir_fpop_raw: - fpop(); - break; -#endif // IA32 - case lir_breakpoint: breakpoint(); break; diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp index 6cc3a81c2ae..ff47d00c484 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.cpp @@ -224,8 +224,46 @@ void AOTConstantPoolResolver::preresolve_field_and_method_cp_entries(JavaThread* bcs.next(); Bytecodes::Code raw_bc = bcs.raw_code(); switch (raw_bc) { + case Bytecodes::_getstatic: + case Bytecodes::_putstatic: + 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::_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 @@ -235,6 +273,7 @@ void AOTConstantPoolResolver::preresolve_field_and_method_cp_entries(JavaThread* case Bytecodes::_invokespecial: case Bytecodes::_invokevirtual: case Bytecodes::_invokeinterface: + case Bytecodes::_invokestatic: maybe_resolve_fmi_ref(ik, m, raw_bc, bcs.get_index_u2(), preresolve_list, THREAD); if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; // just ignore @@ -271,13 +310,31 @@ void AOTConstantPoolResolver::maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m } Klass* resolved_klass = cp->klass_ref_at(raw_index, bc, CHECK); + const char* is_static = ""; switch (bc) { + case Bytecodes::_getstatic: + case Bytecodes::_putstatic: + if (!VM_Version::supports_fast_class_init_checks()) { + return; // Do not resolve since interpreter lacks fast clinit barriers support + } + InterpreterRuntime::resolve_get_put(bc, raw_index, mh, cp, false /*initialize_holder*/, CHECK); + is_static = " *** static"; + break; + case Bytecodes::_getfield: case Bytecodes::_putfield: InterpreterRuntime::resolve_get_put(bc, raw_index, mh, cp, false /*initialize_holder*/, CHECK); break; + case Bytecodes::_invokestatic: + if (!VM_Version::supports_fast_class_init_checks()) { + return; // Do not resolve since interpreter lacks fast clinit barriers support + } + InterpreterRuntime::cds_resolve_invoke(bc, raw_index, cp, CHECK); + is_static = " *** static"; + break; + case Bytecodes::_invokevirtual: case Bytecodes::_invokespecial: case Bytecodes::_invokeinterface: @@ -297,11 +354,11 @@ void AOTConstantPoolResolver::maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m bool resolved = cp->is_resolved(raw_index, bc); Symbol* name = cp->name_ref_at(raw_index, bc); Symbol* signature = cp->signature_ref_at(raw_index, bc); - log_trace(aot, resolve)("%s %s [%3d] %s -> %s.%s:%s", + log_trace(aot, resolve)("%s %s [%3d] %s -> %s.%s:%s%s", (resolved ? "Resolved" : "Failed to resolve"), Bytecodes::name(bc), cp_index, ik->external_name(), resolved_klass->external_name(), - name->as_C_string(), signature->as_C_string()); + name->as_C_string(), signature->as_C_string(), is_static); } } 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 @@ public: 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 7c6b925470a..a5d1f78b76f 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -943,8 +943,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/cdsHeapVerifier.cpp b/src/hotspot/share/cds/cdsHeapVerifier.cpp index 3f72a7c6872..59b07190c09 100644 --- a/src/hotspot/share/cds/cdsHeapVerifier.cpp +++ b/src/hotspot/share/cds/cdsHeapVerifier.cpp @@ -28,6 +28,8 @@ #include "classfile/classLoaderDataGraph.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/moduleEntry.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" #include "classfile/systemDictionaryShared.hpp" #include "classfile/vmSymbols.hpp" #include "logging/log.hpp" @@ -153,9 +155,103 @@ CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0) # undef ADD_EXCL + if (CDSConfig::is_initing_classes_at_dump_time()) { + add_shared_secret_accessors(); + } ClassLoaderDataGraph::classes_do(this); } +// We allow only "stateless" accessors in the SharedSecrets class to be AOT-initialized, for example, +// in the following pattern: +// +// class URL { +// static { +// SharedSecrets.setJavaNetURLAccess( +// new JavaNetURLAccess() { ... }); +// } +// +// This initializes the field SharedSecrets::javaNetUriAccess, whose type (the inner case in the +// above example) has no fields (static or otherwise) and is not a hidden class, so it cannot possibly +// capture any transient state from the assembly phase that might become invalid in the production run. +// +class CDSHeapVerifier::SharedSecretsAccessorFinder : public FieldClosure { + CDSHeapVerifier* _verifier; + InstanceKlass* _ik; +public: + SharedSecretsAccessorFinder(CDSHeapVerifier* verifier, InstanceKlass* ik) + : _verifier(verifier), _ik(ik) {} + + void do_field(fieldDescriptor* fd) { + if (fd->field_type() == T_OBJECT) { + oop static_obj_field = _ik->java_mirror()->obj_field(fd->offset()); + if (static_obj_field != nullptr) { + Klass* field_type = static_obj_field->klass(); + + if (!field_type->is_instance_klass()) { + ResourceMark rm; + log_error(aot, heap)("jdk.internal.access.SharedSecrets::%s must not be an array", + fd->name()->as_C_string()); + AOTMetaspace::unrecoverable_writing_error(); + } + + InstanceKlass* field_type_ik = InstanceKlass::cast(field_type); + if (has_any_fields(field_type_ik) || field_type_ik->is_hidden()) { + // If field_type_ik is a hidden class, the accessor is probably initialized using a + // Lambda, which may contain transient states. + ResourceMark rm; + log_error(aot, heap)("jdk.internal.access.SharedSecrets::%s (%s) must be stateless", + fd->name()->as_C_string(), field_type_ik->external_name()); + AOTMetaspace::unrecoverable_writing_error(); + } + + _verifier->add_shared_secret_accessor(static_obj_field); + } + } + } + + // Does k (or any of its supertypes) have at least one (static or non-static) field? + static bool has_any_fields(InstanceKlass* k) { + if (k->static_field_size() != 0 || k->nonstatic_field_size() != 0) { + return true; + } + + if (k->super() != nullptr && has_any_fields(k->super())) { + return true; + } + + Array* interfaces = k->local_interfaces(); + int num_interfaces = interfaces->length(); + for (int index = 0; index < num_interfaces; index++) { + if (has_any_fields(interfaces->at(index))) { + return true; + } + } + + return false; + } +}; + +// This function is for allowing the following pattern in the core libraries: +// +// public class URLClassPath { +// private static final JavaNetURLAccess JNUA = SharedSecrets.getJavaNetURLAccess(); +// +// SharedSecrets::javaNetUriAccess has no states so it can be safely AOT-initialized. During +// the production run, even if URLClassPath. is re-executed, it will get back the same +// instance of javaNetUriAccess as it did during the assembly phase. +// +// Note: this will forbid complex accessors such as SharedSecrets::javaObjectInputFilterAccess +// to be initialized during the AOT assembly phase. +void CDSHeapVerifier::add_shared_secret_accessors() { + TempNewSymbol klass_name = SymbolTable::new_symbol("jdk/internal/access/SharedSecrets"); + InstanceKlass* ik = SystemDictionary::find_instance_klass(Thread::current(), klass_name, + Handle()); + assert(ik != nullptr, "must have been loaded"); + + SharedSecretsAccessorFinder finder(this, ik); + ik->do_local_static_fields(&finder); +} + CDSHeapVerifier::~CDSHeapVerifier() { if (_problems > 0) { log_error(aot, heap)("Scanned %d objects. Found %d case(s) where " @@ -181,13 +277,12 @@ public: return; } - if (fd->signature()->equals("Ljdk/internal/access/JavaLangAccess;")) { - // A few classes have static fields that point to SharedSecrets.getJavaLangAccess(). - // This object carries no state and we can create a new one in the production run. - return; - } oop static_obj_field = _ik->java_mirror()->obj_field(fd->offset()); if (static_obj_field != nullptr) { + if (_verifier->is_shared_secret_accessor(static_obj_field)) { + return; + } + Klass* field_type = static_obj_field->klass(); if (_exclusions != nullptr) { for (const char** p = _exclusions; *p != nullptr; p++) { diff --git a/src/hotspot/share/cds/cdsHeapVerifier.hpp b/src/hotspot/share/cds/cdsHeapVerifier.hpp index 1cc03975c5c..88ef13c9e90 100644 --- a/src/hotspot/share/cds/cdsHeapVerifier.hpp +++ b/src/hotspot/share/cds/cdsHeapVerifier.hpp @@ -38,6 +38,7 @@ class Symbol; class CDSHeapVerifier : public KlassClosure { class CheckStaticFields; + class SharedSecretsAccessorFinder; class TraceFields; int _archived_objs; @@ -55,6 +56,7 @@ class CDSHeapVerifier : public KlassClosure { HeapShared::oop_hash> _table; GrowableArray _exclusions; + GrowableArray _shared_secret_accessors; void add_exclusion(const char** excl) { _exclusions.append(excl); @@ -70,6 +72,22 @@ class CDSHeapVerifier : public KlassClosure { } return nullptr; } + + void add_shared_secret_accessors(); + + void add_shared_secret_accessor(oop obj) { + _shared_secret_accessors.append(obj); + } + + bool is_shared_secret_accessor(oop obj) { + for (int i = 0; i < _shared_secret_accessors.length(); i++) { + if (_shared_secret_accessors.at(i) == obj) { + return true; + } + } + return false; + } + static int trace_to_root(outputStream* st, oop orig_obj, oop orig_field, HeapShared::CachedOopInfo* p); CDSHeapVerifier(); diff --git a/src/hotspot/share/cds/classListWriter.cpp b/src/hotspot/share/cds/classListWriter.cpp index 882bb84036f..8e1f298e8e3 100644 --- a/src/hotspot/share/cds/classListWriter.cpp +++ b/src/hotspot/share/cds/classListWriter.cpp @@ -277,7 +277,9 @@ void ClassListWriter::write_resolved_constants_for(InstanceKlass* ik) { if (field_entries != nullptr) { for (int i = 0; i < field_entries->length(); i++) { ResolvedFieldEntry* rfe = field_entries->adr_at(i); - if (rfe->is_resolved(Bytecodes::_getfield) || + if (rfe->is_resolved(Bytecodes::_getstatic) || + rfe->is_resolved(Bytecodes::_putstatic) || + rfe->is_resolved(Bytecodes::_getfield) || rfe->is_resolved(Bytecodes::_putfield)) { list.at_put(rfe->constant_pool_index(), true); print = true; @@ -292,6 +294,7 @@ void ClassListWriter::write_resolved_constants_for(InstanceKlass* ik) { if (rme->is_resolved(Bytecodes::_invokevirtual) || rme->is_resolved(Bytecodes::_invokespecial) || rme->is_resolved(Bytecodes::_invokeinterface) || + rme->is_resolved(Bytecodes::_invokestatic) || rme->is_resolved(Bytecodes::_invokehandle)) { list.at_put(rme->constant_pool_index(), true); print = true; diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 8c2175622e9..050c1708efb 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -117,7 +117,6 @@ template static void get_header_version(char (&header_version) [N]) { // Append the hash code as eight hex digits. os::snprintf_checked(&header_version[JVM_IDENT_MAX-9], 9, "%08x", hash); - header_version[JVM_IDENT_MAX-1] = 0; // Null terminate. } assert(header_version[JVM_IDENT_MAX-1] == 0, "must be"); diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index dfe74acd6c1..bf8a760904c 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -89,7 +89,9 @@ void FinalImageRecipes::record_recipes_for_constantpool() { if (field_entries != nullptr) { for (int i = 0; i < field_entries->length(); i++) { ResolvedFieldEntry* rfe = field_entries->adr_at(i); - if (rfe->is_resolved(Bytecodes::_getfield) || + if (rfe->is_resolved(Bytecodes::_getstatic) || + rfe->is_resolved(Bytecodes::_putstatic) || + rfe->is_resolved(Bytecodes::_getfield) || rfe->is_resolved(Bytecodes::_putfield)) { cp_indices.append(rfe->constant_pool_index()); flags |= CP_RESOLVE_FIELD_AND_METHOD; @@ -127,6 +129,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 9e02956aceb..85fb4de8686 100644 --- a/src/hotspot/share/classfile/stackMapTable.cpp +++ b/src/hotspot/share/classfile/stackMapTable.cpp @@ -132,8 +132,16 @@ bool StackMapTable::match_stackmap( } void StackMapTable::check_jump_target( - StackMapFrame* frame, int32_t target, TRAPS) const { + StackMapFrame* frame, int bci, int offset, TRAPS) const { ErrorContext ctx; + // Jump targets must be within the method and the method size is limited. See JVMS 4.11 + int min_offset = -1 * max_method_code_size; + if (offset < min_offset || offset > max_method_code_size) { + frame->verifier()->verify_error(ErrorContext::bad_stackmap(bci, frame), + "Illegal target of jump or branch (bci %d + offset %d)", bci, offset); + return; + } + int target = bci + offset; bool match = match_stackmap( frame, target, true, false, &ctx, CHECK_VERIFY(frame->verifier())); if (!match || (target < 0 || target >= _code_length)) { diff --git a/src/hotspot/share/classfile/stackMapTable.hpp b/src/hotspot/share/classfile/stackMapTable.hpp index 6d4c0ce36c0..9b46fa89345 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/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 2d31a7c49f6..cb2ae96348e 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -855,6 +855,28 @@ public: } }; +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; 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 @@ private: 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 9b93e283362..38dba1d3d5f 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -781,7 +781,6 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) { // Merge with the next instruction { - int target; VerificationType type, type2; VerificationType atype; @@ -1606,9 +1605,8 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) { case Bytecodes::_ifle: current_frame.pop_stack( VerificationType::integer_type(), CHECK_VERIFY(this)); - target = bcs.dest(); stackmap_table.check_jump_target( - ¤t_frame, target, CHECK_VERIFY(this)); + ¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this)); no_control_flow = false; break; case Bytecodes::_if_acmpeq : case Bytecodes::_if_acmpne : @@ -1619,19 +1617,16 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) { case Bytecodes::_ifnonnull : current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); - target = bcs.dest(); stackmap_table.check_jump_target - (¤t_frame, target, CHECK_VERIFY(this)); + (¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this)); no_control_flow = false; break; case Bytecodes::_goto : - target = bcs.dest(); stackmap_table.check_jump_target( - ¤t_frame, target, CHECK_VERIFY(this)); + ¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this)); no_control_flow = true; break; case Bytecodes::_goto_w : - target = bcs.dest_w(); stackmap_table.check_jump_target( - ¤t_frame, target, CHECK_VERIFY(this)); + ¤t_frame, bcs.bci(), bcs.get_offset_s4(), CHECK_VERIFY(this)); no_control_flow = true; break; case Bytecodes::_tableswitch : case Bytecodes::_lookupswitch : @@ -2280,15 +2275,14 @@ void ClassVerifier::verify_switch( } } } - int target = bci + default_offset; - stackmap_table->check_jump_target(current_frame, target, CHECK_VERIFY(this)); + stackmap_table->check_jump_target(current_frame, bci, default_offset, CHECK_VERIFY(this)); for (int i = 0; i < keys; i++) { // Because check_jump_target() may safepoint, the bytecode could have // moved, which means 'aligned_bcp' is no good and needs to be recalculated. aligned_bcp = align_up(bcs->bcp() + 1, jintSize); - target = bci + (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize); + int offset = (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize); stackmap_table->check_jump_target( - current_frame, target, CHECK_VERIFY(this)); + current_frame, bci, offset, CHECK_VERIFY(this)); } NOT_PRODUCT(aligned_bcp = nullptr); // no longer valid at this point } diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index c9c5c925f86..0895418ef84 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -467,8 +467,8 @@ class methodHandle; do_intrinsic(_Reference_clear0, java_lang_ref_Reference, clear0_name, void_method_signature, F_RN) \ do_intrinsic(_PhantomReference_clear0, java_lang_ref_PhantomReference, clear0_name, void_method_signature, F_RN) \ \ - /* support for com.sun.crypto.provider.AESCrypt and some of its callers */ \ - do_class(com_sun_crypto_provider_aescrypt, "com/sun/crypto/provider/AESCrypt") \ + /* support for com.sun.crypto.provider.AES_Crypt and some of its callers */ \ + do_class(com_sun_crypto_provider_aescrypt, "com/sun/crypto/provider/AES_Crypt") \ do_intrinsic(_aescrypt_encryptBlock, com_sun_crypto_provider_aescrypt, encryptBlock_name, byteArray_int_byteArray_int_signature, F_R) \ do_intrinsic(_aescrypt_decryptBlock, com_sun_crypto_provider_aescrypt, decryptBlock_name, byteArray_int_byteArray_int_signature, F_R) \ do_name( encryptBlock_name, "implEncryptBlock") \ diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index 18e77520139..e901d560616 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -910,6 +910,7 @@ void CodeBlob::dump_for_addr(address addr, outputStream* st, bool verbose) const nm->print_nmethod(true); } else { nm->print_on(st); + nm->print_code_snippet(st, addr); } return; } diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 7274b627f3e..53c75c7ae43 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" @@ -1147,7 +1148,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; @@ -1322,6 +1323,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); @@ -1420,15 +1422,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; @@ -1444,6 +1437,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; @@ -1462,6 +1456,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; @@ -1751,9 +1754,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); @@ -2500,11 +2505,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; @@ -2596,7 +2638,7 @@ void nmethod::metadata_do(MetadataClosure* f) { // Main purpose is to reduce code cache pressure and get rid of // nmethods that don't seem to be all that relevant any longer. bool nmethod::is_cold() { - if (!MethodFlushing || is_native_method() || is_not_installed()) { + if (!MethodFlushing || is_not_installed()) { // No heuristic unloading at all return false; } @@ -4266,6 +4308,46 @@ void nmethod::print_value_on_impl(outputStream* st) const { #endif } +void nmethod::print_code_snippet(outputStream* st, address addr) const { + if (entry_point() <= addr && addr < code_end()) { + // Pointing into the nmethod's code. Try to disassemble some instructions around addr. + // Determine conservative start and end points. + address start; + if (frame_complete_offset() != CodeOffsets::frame_never_safe && + addr >= code_begin() + frame_complete_offset()) { + start = code_begin() + frame_complete_offset(); + } else { + start = (addr < verified_entry_point()) ? entry_point() : verified_entry_point(); + } + address start_for_hex_dump = start; // We can choose a different starting point for hex dump, below. + address end = code_end(); + + // Try using relocations to find closer instruction start and end points. + // (Some platforms have variable length instructions and can only + // disassemble correctly at instruction start addresses.) + RelocIterator iter((nmethod*)this, start); + while (iter.next() && iter.addr() < addr) { // find relocation before addr + // Note: There's a relocation which doesn't point to an instruction start: + // ZBarrierRelocationFormatStoreGoodAfterMov with ZGC on x86_64 + // We could detect and skip it, but hex dump is still usable when + // disassembler produces garbage in such a very rare case. + start = iter.addr(); + // We want at least 64 Bytes ahead in hex dump. + if (iter.addr() <= (addr - 64)) start_for_hex_dump = iter.addr(); + } + if (iter.has_current()) { + if (iter.addr() == addr) iter.next(); // find relocation after addr + if (iter.has_current()) end = iter.addr(); + } + + // Always print hex. Disassembler may still have problems when hitting an incorrect instruction start. + os::print_hex_dump(st, start_for_hex_dump, end, 1, /* print_ascii=*/false); + if (!Disassembler::is_abstract()) { + Disassembler::decode(start, end, st); + } + } +} + #ifndef PRODUCT void nmethod::print_calls(outputStream* st) { diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 2332766a47c..bce0181a3ec 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -250,6 +250,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. @@ -646,12 +647,11 @@ public: #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; } @@ -965,6 +965,8 @@ public: 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); } @@ -999,10 +1001,14 @@ public: // 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(); void print_value_on_impl(outputStream* st) const; + void print_code_snippet(outputStream* st, address addr) const; #if defined(SUPPORT_DATA_STRUCTS) // print output in opt build for disassembler library diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp index 177fd04fbbc..1cc44602186 100644 --- a/src/hotspot/share/compiler/compilationPolicy.cpp +++ b/src/hotspot/share/compiler/compilationPolicy.cpp @@ -423,13 +423,16 @@ void CompilationPolicy::print_counters_on(outputStream* st, const char* prefix, st->print(" %smax levels=%d,%d", prefix, m->highest_comp_level(), m->highest_osr_comp_level()); } -void CompilationPolicy::print_training_data_on(outputStream* st, const char* prefix, Method* method) { +void CompilationPolicy::print_training_data_on(outputStream* st, const char* prefix, Method* method, CompLevel cur_level) { methodHandle m(Thread::current(), method); st->print(" %smtd: ", prefix); MethodTrainingData* mtd = MethodTrainingData::find(m); if (mtd == nullptr) { st->print("null"); } else { + if (should_delay_standard_transition(m, cur_level, mtd)) { + st->print("delayed, "); + } MethodData* md = mtd->final_profile(); st->print("mdo="); if (md == nullptr) { @@ -536,9 +539,9 @@ void CompilationPolicy::print_event_on(outputStream *st, EventType type, Method* st->print("in-queue"); } else st->print("idle"); - print_training_data_on(st, "", m); + print_training_data_on(st, "", m, level); if (inlinee_event) { - print_training_data_on(st, "inlinee ", im); + print_training_data_on(st, "inlinee ", im, level); } } st->print_cr("]"); @@ -1153,7 +1156,7 @@ CompLevel CompilationPolicy::trained_transition_from_none(const methodHandle& me // Now handle the case of level 4. assert(highest_training_level == CompLevel_full_optimization, "Unexpected compilation level: %d", highest_training_level); if (!training_has_profile) { - // The method was a part of a level 4 compile, but don't have a stored profile, + // The method was a part of a level 4 compile, but doesn't have a stored profile, // we need to profile it. return CompLevel_full_profile; } @@ -1308,33 +1311,53 @@ CompLevel CompilationPolicy::common(const methodHandle& method, CompLevel cur_le if (mtd == nullptr) { // We haven't see compilations of this method in training. It's either very cold or the behavior changed. // Feed it to the standard TF with no profiling delay. - next_level = standard_transition(method, cur_level, false /*delay_profiling*/, disable_feedback); + next_level = standard_transition(method, cur_level, disable_feedback); } else { next_level = trained_transition(method, cur_level, mtd, THREAD); - if (cur_level == next_level) { + if (cur_level == next_level && !should_delay_standard_transition(method, cur_level, mtd)) { // trained_transtion() is going to return the same level if no startup/warmup optimizations apply. // In order to catch possible pathologies due to behavior change we feed the event to the regular // TF but with profiling delay. - next_level = standard_transition(method, cur_level, true /*delay_profiling*/, disable_feedback); + next_level = standard_transition(method, cur_level, disable_feedback); } } } else { - next_level = standard_transition(method, cur_level, false /*delay_profiling*/, disable_feedback); + next_level = standard_transition(method, cur_level, disable_feedback); } return (next_level != cur_level) ? limit_level(next_level) : next_level; } +bool CompilationPolicy::should_delay_standard_transition(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd) { + precond(mtd != nullptr); + CompLevel highest_training_level = static_cast(mtd->highest_top_level()); + if (highest_training_level != CompLevel_full_optimization && cur_level == CompLevel_limited_profile) { + // This is a lukewarm method - it hasn't been compiled with C2 during the tranining run and is currently + // running at level 2. Delay any further state changes until its counters exceed the training run counts. + MethodCounters* mc = method->method_counters(); + if (mc == nullptr) { + return false; + } + if (mc->invocation_counter()->carry() || mc->backedge_counter()->carry()) { + return false; + } + if (static_cast(mc->invocation_counter()->count()) <= mtd->invocation_count() && + static_cast(mc->backedge_counter()->count()) <= mtd->backedge_count()) { + return true; + } + } + return false; +} template -CompLevel CompilationPolicy::standard_transition(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback) { +CompLevel CompilationPolicy::standard_transition(const methodHandle& method, CompLevel cur_level, bool disable_feedback) { CompLevel next_level = cur_level; switch(cur_level) { default: break; case CompLevel_none: - next_level = transition_from_none(method, cur_level, delay_profiling, disable_feedback); + next_level = transition_from_none(method, cur_level, disable_feedback); break; case CompLevel_limited_profile: - next_level = transition_from_limited_profile(method, cur_level, delay_profiling, disable_feedback); + next_level = transition_from_limited_profile(method, cur_level, disable_feedback); break; case CompLevel_full_profile: next_level = transition_from_full_profile(method, cur_level); @@ -1343,16 +1366,8 @@ CompLevel CompilationPolicy::standard_transition(const methodHandle& method, Com return next_level; } -template static inline bool apply_predicate(const methodHandle& method, CompLevel cur_level, int i, int b, bool delay_profiling, double delay_profiling_scale) { - if (delay_profiling) { - return Predicate::apply_scaled(method, cur_level, i, b, delay_profiling_scale); - } else { - return Predicate::apply(method, cur_level, i, b); - } -} - template -CompLevel CompilationPolicy::transition_from_none(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback) { +CompLevel CompilationPolicy::transition_from_none(const methodHandle& method, CompLevel cur_level, bool disable_feedback) { precond(cur_level == CompLevel_none); CompLevel next_level = cur_level; int i = method->invocation_count(); @@ -1360,7 +1375,7 @@ CompLevel CompilationPolicy::transition_from_none(const methodHandle& method, Co // If we were at full profile level, would we switch to full opt? if (transition_from_full_profile(method, CompLevel_full_profile) == CompLevel_full_optimization) { next_level = CompLevel_full_optimization; - } else if (!CompilationModeFlag::disable_intermediate() && apply_predicate(method, cur_level, i, b, delay_profiling, Tier0ProfileDelayFactor)) { + } else if (!CompilationModeFlag::disable_intermediate() && Predicate::apply(method, cur_level, i, b)) { // C1-generated fully profiled code is about 30% slower than the limited profile // code that has only invocation and backedge counters. The observation is that // if C2 queue is large enough we can spend too much time in the fully profiled code @@ -1368,7 +1383,7 @@ CompLevel CompilationPolicy::transition_from_none(const methodHandle& method, Co // we introduce a feedback on the C2 queue size. If the C2 queue is sufficiently long // we choose to compile a limited profiled version and then recompile with full profiling // when the load on C2 goes down. - if (delay_profiling || (!disable_feedback && CompileBroker::queue_size(CompLevel_full_optimization) > Tier3DelayOn * compiler_count(CompLevel_full_optimization))) { + if (!disable_feedback && CompileBroker::queue_size(CompLevel_full_optimization) > Tier3DelayOn * compiler_count(CompLevel_full_optimization)) { next_level = CompLevel_limited_profile; } else { next_level = CompLevel_full_profile; @@ -1397,7 +1412,7 @@ CompLevel CompilationPolicy::transition_from_full_profile(const methodHandle& me } template -CompLevel CompilationPolicy::transition_from_limited_profile(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback) { +CompLevel CompilationPolicy::transition_from_limited_profile(const methodHandle& method, CompLevel cur_level, bool disable_feedback) { precond(cur_level == CompLevel_limited_profile); CompLevel next_level = cur_level; int i = method->invocation_count(); @@ -1407,7 +1422,7 @@ CompLevel CompilationPolicy::transition_from_limited_profile(const methodHandle& if (mdo->would_profile()) { if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <= Tier3DelayOff * compiler_count(CompLevel_full_optimization) && - apply_predicate(method, cur_level, i, b, delay_profiling, Tier2ProfileDelayFactor))) { + Predicate::apply(method, cur_level, i, b))) { next_level = CompLevel_full_profile; } } else { @@ -1417,7 +1432,7 @@ CompLevel CompilationPolicy::transition_from_limited_profile(const methodHandle& // If there is no MDO we need to profile if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <= Tier3DelayOff * compiler_count(CompLevel_full_optimization) && - apply_predicate(method, cur_level, i, b, delay_profiling, Tier2ProfileDelayFactor))) { + Predicate::apply(method, cur_level, i, b))) { next_level = CompLevel_full_profile; } } diff --git a/src/hotspot/share/compiler/compilationPolicy.hpp b/src/hotspot/share/compiler/compilationPolicy.hpp index d950ba418f9..3efc374d998 100644 --- a/src/hotspot/share/compiler/compilationPolicy.hpp +++ b/src/hotspot/share/compiler/compilationPolicy.hpp @@ -263,14 +263,15 @@ class CompilationPolicy : AllStatic { static CompLevel common(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD, bool disable_feedback = false); template - static CompLevel transition_from_none(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback); + static CompLevel transition_from_none(const methodHandle& method, CompLevel cur_level, bool disable_feedback); template - static CompLevel transition_from_limited_profile(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback); + static CompLevel transition_from_limited_profile(const methodHandle& method, CompLevel cur_level, bool disable_feedback); template static CompLevel transition_from_full_profile(const methodHandle& method, CompLevel cur_level); template - static CompLevel standard_transition(const methodHandle& method, CompLevel cur_level, bool delayprof, bool disable_feedback); + static CompLevel standard_transition(const methodHandle& method, CompLevel cur_level, bool disable_feedback); + static bool should_delay_standard_transition(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd); static CompLevel trained_transition_from_none(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd, JavaThread* THREAD); static CompLevel trained_transition_from_limited_profile(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd, JavaThread* THREAD); static CompLevel trained_transition_from_full_profile(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd, JavaThread* THREAD); @@ -284,7 +285,7 @@ class CompilationPolicy : AllStatic { // level. static CompLevel loop_event(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD); static void print_counters_on(outputStream* st, const char* prefix, Method* m); - static void print_training_data_on(outputStream* st, const char* prefix, Method* method); + static void print_training_data_on(outputStream* st, const char* prefix, Method* method, CompLevel cur_level); // Has a method been long around? // We don't remove old methods from the compile queue even if they have // very low activity (see select_task()). diff --git a/src/hotspot/share/compiler/compilerDefinitions.cpp b/src/hotspot/share/compiler/compilerDefinitions.cpp index 35201973dfe..aed1edc0db5 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.cpp +++ b/src/hotspot/share/compiler/compilerDefinitions.cpp @@ -215,11 +215,6 @@ void CompilerConfig::set_client_emulation_mode_flags() { if (FLAG_IS_DEFAULT(CodeCacheExpansionSize)) { FLAG_SET_ERGO(CodeCacheExpansionSize, 32*K); } - if (FLAG_IS_DEFAULT(MaxRAM)) { - // Do not use FLAG_SET_ERGO to update MaxRAM, as this will impact - // heap setting done based on available phys_mem (see Arguments::set_heap_size). - FLAG_SET_DEFAULT(MaxRAM, 1ULL*G); - } if (FLAG_IS_DEFAULT(CICompilerCount)) { FLAG_SET_ERGO(CICompilerCount, 1); } @@ -553,21 +548,36 @@ bool CompilerConfig::check_args_consistency(bool status) { return status; } -void CompilerConfig::ergo_initialize() { +bool CompilerConfig::should_set_client_emulation_mode_flags() { #if !COMPILER1_OR_COMPILER2 - return; + return false; #endif if (has_c1()) { if (!is_compilation_mode_selected()) { if (NeverActAsServerClassMachine) { - set_client_emulation_mode_flags(); + return true; } } else if (!has_c2() && !is_jvmci_compiler()) { - set_client_emulation_mode_flags(); + return true; } } + return false; +} + +void CompilerConfig::ergo_initialize() { +#if !COMPILER1_OR_COMPILER2 + return; +#endif + + // This property is also checked when selecting the heap size. Since client + // emulation mode influences Java heap memory usage, part of the logic must + // occur before choosing the heap size. + if (should_set_client_emulation_mode_flags()) { + set_client_emulation_mode_flags(); + } + set_legacy_emulation_flags(); set_compilation_policy_flags(); diff --git a/src/hotspot/share/compiler/compilerDefinitions.hpp b/src/hotspot/share/compiler/compilerDefinitions.hpp index 1c8c65b2a53..a9b052ff782 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.hpp +++ b/src/hotspot/share/compiler/compilerDefinitions.hpp @@ -151,6 +151,8 @@ public: inline static CompilerType compiler_type(); + static bool should_set_client_emulation_mode_flags(); + private: static bool is_compilation_mode_selected(); static void set_compilation_policy_flags(); diff --git a/src/hotspot/share/compiler/compiler_globals.hpp b/src/hotspot/share/compiler/compiler_globals.hpp index 605c0c58869..3919c596d74 100644 --- a/src/hotspot/share/compiler/compiler_globals.hpp +++ b/src/hotspot/share/compiler/compiler_globals.hpp @@ -271,13 +271,6 @@ "Maximum rate sampling interval (in milliseconds)") \ range(1, max_intx) \ \ - product(double, Tier0ProfileDelayFactor, 100.0, DIAGNOSTIC, \ - "Delay profiling/compiling of methods that were " \ - "observed to be lukewarm") \ - \ - product(double, Tier2ProfileDelayFactor, 250.0, DIAGNOSTIC, \ - "Delay profiling of methods that were observed to be lukewarm") \ - \ product(bool, SkipTier2IfPossible, false, DIAGNOSTIC, \ "Compile at tier 4 instead of tier 2 in training replay " \ "mode if posssible") \ diff --git a/src/hotspot/share/compiler/compiler_globals_pd.hpp b/src/hotspot/share/compiler/compiler_globals_pd.hpp index 90edd952c77..6a87fdaaaf1 100644 --- a/src/hotspot/share/compiler/compiler_globals_pd.hpp +++ b/src/hotspot/share/compiler/compiler_globals_pd.hpp @@ -72,12 +72,10 @@ define_pd_global(size_t, CodeCacheMinBlockLength, 1); define_pd_global(size_t, CodeCacheMinimumUseSpace, 200*K); #ifndef ZERO define_pd_global(bool, NeverActAsServerClassMachine, true); -define_pd_global(uint64_t,MaxRAM, 1ULL*G); #else // Zero runs without compilers. Do not let this code to force // the GC mode and default heap settings. define_pd_global(bool, NeverActAsServerClassMachine, false); -define_pd_global(uint64_t,MaxRAM, 128ULL*G); #endif #define CI_COMPILER_COUNT 0 #else 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 1e5c111a652..58a70ed6a60 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.hpp @@ -99,8 +99,7 @@ class G1BarrierSet: public CardTableBarrierSet { template void write_ref_field_pre(T* field); - inline void write_region(MemRegion mr); - void write_region(JavaThread* thread, MemRegion mr); + virtual void write_region(MemRegion mr); template void write_ref_field_post(T* field); diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp index 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 485caa9f6c0..84c16fe3e22 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -403,21 +403,25 @@ HeapWord* G1CollectedHeap::allocate_new_tlab(size_t min_size, assert_heap_not_locked_and_not_at_safepoint(); assert(!is_humongous(requested_size), "we do not allow humongous TLABs"); - return attempt_allocation(min_size, requested_size, actual_size); + // Do not allow a GC because we are allocating a new TLAB to avoid an issue + // with UseGCOverheadLimit: although this GC would return null if the overhead + // limit would be exceeded, but it would likely free at least some space. + // So the subsequent outside-TLAB allocation could be successful anyway and + // the indication that the overhead limit had been exceeded swallowed. + return attempt_allocation(min_size, requested_size, actual_size, false /* allow_gc */); } -HeapWord* -G1CollectedHeap::mem_allocate(size_t word_size) { +HeapWord* G1CollectedHeap::mem_allocate(size_t word_size) { assert_heap_not_locked_and_not_at_safepoint(); if (is_humongous(word_size)) { return attempt_allocation_humongous(word_size); } size_t dummy = 0; - return attempt_allocation(word_size, word_size, &dummy); + return attempt_allocation(word_size, word_size, &dummy, true /* allow_gc */); } -HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_size) { +HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_size, bool allow_gc) { ResourceMark rm; // For retrieving the thread names in log messages. // Make sure you read the note in attempt_allocation_humongous(). @@ -444,6 +448,8 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_ result = _allocator->attempt_allocation_locked(node_index, word_size); if (result != nullptr) { return result; + } else if (!allow_gc) { + return nullptr; } // Read the GC count while still holding the Heap_lock. @@ -461,8 +467,20 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_ log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating %zu words", Thread::current()->name(), word_size); + if (is_shutting_down()) { + stall_for_vm_shutdown(); + return nullptr; + } + + // Has the gc overhead limit been reached in the meantime? If so, this mutator + // should receive null even when unsuccessfully scheduling a collection as well + // for global consistency. + if (gc_overhead_limit_exceeded()) { + return nullptr; + } + // We can reach here if we were unsuccessful in scheduling a collection (because - // another thread beat us to it). In this case immeditealy retry the allocation + // another thread beat us to it). In this case immediately retry the allocation // attempt because another thread successfully performed a collection and possibly // reclaimed enough space. The first attempt (without holding the Heap_lock) is // here and the follow-on attempt will be at the start of the next loop @@ -479,11 +497,6 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_ log_warning(gc, alloc)("%s: Retried allocation %u times for %zu words", Thread::current()->name(), try_count, word_size); } - - if (is_shutting_down()) { - stall_for_vm_shutdown(); - return nullptr; - } } ShouldNotReachHere(); @@ -612,7 +625,8 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion range) { inline HeapWord* G1CollectedHeap::attempt_allocation(size_t min_word_size, size_t desired_word_size, - size_t* actual_word_size) { + size_t* actual_word_size, + bool allow_gc) { assert_heap_not_locked_and_not_at_safepoint(); assert(!is_humongous(desired_word_size), "attempt_allocation() should not " "be called for humongous allocation requests"); @@ -624,7 +638,7 @@ inline HeapWord* G1CollectedHeap::attempt_allocation(size_t min_word_size, if (result == nullptr) { *actual_word_size = desired_word_size; - result = attempt_allocation_slow(node_index, desired_word_size); + result = attempt_allocation_slow(node_index, desired_word_size, allow_gc); } assert_heap_not_locked(); @@ -707,6 +721,18 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) { log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating %zu", Thread::current()->name(), word_size); + if (is_shutting_down()) { + stall_for_vm_shutdown(); + return nullptr; + } + + // Has the gc overhead limit been reached in the meantime? If so, this mutator + // should receive null even when unsuccessfully scheduling a collection as well + // for global consistency. + if (gc_overhead_limit_exceeded()) { + return nullptr; + } + // We can reach here if we were unsuccessful in scheduling a collection (because // another thread beat us to it). // Humongous object allocation always needs a lock, so we wait for the retry @@ -718,11 +744,6 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) { log_warning(gc, alloc)("%s: Retried allocation %u times for %zu words", Thread::current()->name(), try_count, word_size); } - - if (is_shutting_down()) { - stall_for_vm_shutdown(); - return nullptr; - } } ShouldNotReachHere(); @@ -948,25 +969,62 @@ void G1CollectedHeap::resize_heap_after_young_collection(size_t allocation_word_ phase_times()->record_resize_heap_time((Ticks::now() - start).seconds() * 1000.0); } +void G1CollectedHeap::update_gc_overhead_counter() { + assert(SafepointSynchronize::is_at_safepoint(), "precondition"); + + if (!UseGCOverheadLimit) { + return; + } + + bool gc_time_over_limit = (_policy->analytics()->long_term_gc_time_ratio() * 100) >= GCTimeLimit; + double free_space_percent = percent_of(num_available_regions() * G1HeapRegion::GrainBytes, max_capacity()); + bool free_space_below_limit = free_space_percent < GCHeapFreeLimit; + + log_debug(gc)("GC Overhead Limit: GC Time %f Free Space %f Counter %zu", + (_policy->analytics()->long_term_gc_time_ratio() * 100), + free_space_percent, + _gc_overhead_counter); + + if (gc_time_over_limit && free_space_below_limit) { + _gc_overhead_counter++; + } else { + _gc_overhead_counter = 0; + } +} + +bool G1CollectedHeap::gc_overhead_limit_exceeded() { + return _gc_overhead_counter >= GCOverheadLimitThreshold; +} + HeapWord* G1CollectedHeap::satisfy_failed_allocation_helper(size_t word_size, bool do_gc, bool maximal_compaction, bool expect_null_mutator_alloc_region) { - // Let's attempt the allocation first. - HeapWord* result = - attempt_allocation_at_safepoint(word_size, - expect_null_mutator_alloc_region); - if (result != nullptr) { - return result; - } + // Skip allocation if GC overhead limit has been exceeded to let the mutator run + // into an OOME. It can either exit "gracefully" or try to free up memory asap. + // For the latter situation, keep running GCs. If the mutator frees up enough + // memory quickly enough, the overhead(s) will go below the threshold(s) again + // and the VM may continue running. + // If we did not continue garbage collections, the (gc overhead) limit may decrease + // enough by itself to not count as exceeding the limit any more, in the worst + // case bouncing back-and-forth all the time. + if (!gc_overhead_limit_exceeded()) { + // Let's attempt the allocation first. + HeapWord* result = + attempt_allocation_at_safepoint(word_size, + expect_null_mutator_alloc_region); + if (result != nullptr) { + return result; + } - // In a G1 heap, we're supposed to keep allocation from failing by - // incremental pauses. Therefore, at least for now, we'll favor - // expansion over collection. (This might change in the future if we can - // do something smarter than full collection to satisfy a failed alloc.) - result = expand_and_allocate(word_size); - if (result != nullptr) { - return result; + // In a G1 heap, we're supposed to keep allocation from failing by + // incremental pauses. Therefore, at least for now, we'll favor + // expansion over collection. (This might change in the future if we can + // do something smarter than full collection to satisfy a failed alloc.) + result = expand_and_allocate(word_size); + if (result != nullptr) { + return result; + } } if (do_gc) { @@ -990,6 +1048,10 @@ HeapWord* G1CollectedHeap::satisfy_failed_allocation_helper(size_t word_size, HeapWord* G1CollectedHeap::satisfy_failed_allocation(size_t word_size) { assert_at_safepoint_on_vm_thread(); + // Update GC overhead limits after the initial garbage collection leading to this + // allocation attempt. + update_gc_overhead_counter(); + // Attempts to allocate followed by Full GC. HeapWord* result = satisfy_failed_allocation_helper(word_size, @@ -1021,6 +1083,10 @@ HeapWord* G1CollectedHeap::satisfy_failed_allocation(size_t word_size) { return result; } + if (gc_overhead_limit_exceeded()) { + log_info(gc)("GC Overhead Limit exceeded too often (%zu).", GCOverheadLimitThreshold); + } + // What else? We might try synchronous finalization later. If the total // space available is large enough for the allocation, then a more // complete compaction phase than we've tried so far might be @@ -1202,6 +1268,7 @@ public: G1CollectedHeap::G1CollectedHeap() : CollectedHeap(), + _gc_overhead_counter(0), _service_thread(nullptr), _periodic_gc_task(nullptr), _free_arena_memory_task(nullptr), @@ -2481,15 +2548,15 @@ bool G1CollectedHeap::is_potential_eager_reclaim_candidate(G1HeapRegion* r) cons } #ifndef PRODUCT -void G1CollectedHeap::verify_region_attr_remset_is_tracked() { +void G1CollectedHeap::verify_region_attr_is_remset_tracked() { class VerifyRegionAttrRemSet : public G1HeapRegionClosure { public: virtual bool do_heap_region(G1HeapRegion* r) { G1CollectedHeap* g1h = G1CollectedHeap::heap(); - bool const remset_is_tracked = g1h->region_attr(r->bottom()).remset_is_tracked(); - assert(r->rem_set()->is_tracked() == remset_is_tracked, + const bool is_remset_tracked = g1h->region_attr(r->bottom()).is_remset_tracked(); + assert(r->rem_set()->is_tracked() == is_remset_tracked, "Region %u remset tracking status (%s) different to region attribute (%s)", - r->hrm_index(), BOOL_TO_STR(r->rem_set()->is_tracked()), BOOL_TO_STR(remset_is_tracked)); + r->hrm_index(), BOOL_TO_STR(r->rem_set()->is_tracked()), BOOL_TO_STR(is_remset_tracked)); return false; } } cl; @@ -3092,9 +3159,9 @@ G1HeapRegion* G1CollectedHeap::new_gc_alloc_region(size_t word_size, G1HeapRegio young_regions_cset_group()->add(new_alloc_region); } else { new_alloc_region->set_old(); + update_region_attr(new_alloc_region); } _policy->remset_tracker()->update_at_allocate(new_alloc_region); - register_region_with_region_attr(new_alloc_region); G1HeapRegionPrinter::alloc(new_alloc_region); return new_alloc_region; } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 7e3f8a30285..672800629cb 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -169,6 +169,17 @@ class G1CollectedHeap : public CollectedHeap { friend class G1CheckRegionAttrTableClosure; private: + // GC Overhead Limit functionality related members. + // + // The goal is to return null for allocations prematurely (before really going + // OOME) in case both GC CPU usage (>= GCTimeLimit) and not much available free + // memory (<= GCHeapFreeLimit) so that applications can exit gracefully or try + // to keep running by easing off memory. + uintx _gc_overhead_counter; // The number of consecutive garbage collections we were over the limits. + + void update_gc_overhead_counter(); + bool gc_overhead_limit_exceeded(); + G1ServiceThread* _service_thread; G1ServiceTask* _periodic_gc_task; G1MonotonicArenaFreeMemoryTask* _free_arena_memory_task; @@ -439,18 +450,14 @@ private: // // * If either call cannot satisfy the allocation request using the // current allocating region, they will try to get a new one. If - // this fails, they will attempt to do an evacuation pause and - // retry the allocation. - // - // * If all allocation attempts fail, even after trying to schedule - // an evacuation pause, allocate_new_tlab() will return null, - // whereas mem_allocate() will attempt a heap expansion and/or - // schedule a Full GC. + // this fails, (only) mem_allocate() will attempt to do an evacuation + // pause and retry the allocation. Allocate_new_tlab() will return null, + // deferring to the following mem_allocate(). // // * We do not allow humongous-sized TLABs. So, allocate_new_tlab // should never be called with word_size being humongous. All // humongous allocation requests should go to mem_allocate() which - // will satisfy them with a special path. + // will satisfy them in a special path. HeapWord* allocate_new_tlab(size_t min_size, size_t requested_size, @@ -463,12 +470,13 @@ private: // should only be used for non-humongous allocations. inline HeapWord* attempt_allocation(size_t min_word_size, size_t desired_word_size, - size_t* actual_word_size); - + size_t* actual_word_size, + bool allow_gc); // Second-level mutator allocation attempt: take the Heap_lock and // retry the allocation attempt, potentially scheduling a GC - // pause. This should only be used for non-humongous allocations. - HeapWord* attempt_allocation_slow(uint node_index, size_t word_size); + // pause if allow_gc is set. This should only be used for non-humongous + // allocations. + HeapWord* attempt_allocation_slow(uint node_index, size_t word_size, bool allow_gc); // Takes the Heap_lock and attempts a humongous allocation. It can // potentially schedule a GC pause. @@ -637,16 +645,17 @@ public: size_t word_size, bool update_remsets); - // We register a region with the fast "in collection set" test. We - // simply set to true the array slot corresponding to this region. - void register_young_region_with_region_attr(G1HeapRegion* r) { - _region_attr.set_in_young(r->hrm_index(), r->has_pinned_objects()); - } + // The following methods update the region attribute table, i.e. a compact + // representation of per-region information that is regularly accessed + // during GC. + inline void register_young_region_with_region_attr(G1HeapRegion* r); inline void register_new_survivor_region_with_region_attr(G1HeapRegion* r); - inline void register_region_with_region_attr(G1HeapRegion* r); - inline void register_old_region_with_region_attr(G1HeapRegion* r); + inline void register_old_collection_set_region_with_region_attr(G1HeapRegion* r); inline void register_optional_region_with_region_attr(G1HeapRegion* r); + // Updates region state without overwriting the type in the region attribute table. + inline void update_region_attr(G1HeapRegion* r); + void clear_region_attr(const G1HeapRegion* hr) { _region_attr.clear(hr); } @@ -657,7 +666,7 @@ public: // Verify that the G1RegionAttr remset tracking corresponds to actual remset tracking // for all regions. - void verify_region_attr_remset_is_tracked() PRODUCT_RETURN; + void verify_region_attr_is_remset_tracked() PRODUCT_RETURN; void clear_bitmap_for_region(G1HeapRegion* hr); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index fdc8585dbc0..abd61e72d57 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -191,18 +191,26 @@ void G1CollectedHeap::register_humongous_candidate_region_with_region_attr(uint _region_attr.set_humongous_candidate(index); } -void G1CollectedHeap::register_new_survivor_region_with_region_attr(G1HeapRegion* r) { - _region_attr.set_new_survivor_region(r->hrm_index()); +void G1CollectedHeap::register_young_region_with_region_attr(G1HeapRegion* r) { + assert(!is_in_cset(r), "should not already be registered as in collection set"); + _region_attr.set_in_young(r->hrm_index(), r->has_pinned_objects()); } -void G1CollectedHeap::register_region_with_region_attr(G1HeapRegion* r) { - _region_attr.set_remset_is_tracked(r->hrm_index(), r->rem_set()->is_tracked()); +void G1CollectedHeap::register_new_survivor_region_with_region_attr(G1HeapRegion* r) { + assert(!is_in_cset(r), "should not already be registered as in collection set"); + _region_attr.set_new_survivor_region(r->hrm_index(), r->has_pinned_objects()); +} + +void G1CollectedHeap::update_region_attr(G1HeapRegion* r) { + _region_attr.set_is_remset_tracked(r->hrm_index(), r->rem_set()->is_tracked()); _region_attr.set_is_pinned(r->hrm_index(), r->has_pinned_objects()); } -void G1CollectedHeap::register_old_region_with_region_attr(G1HeapRegion* r) { +void G1CollectedHeap::register_old_collection_set_region_with_region_attr(G1HeapRegion* r) { + assert(!is_in_cset(r), "should not already be registered as in collection set"); + assert(r->is_old(), "must be"); assert(r->rem_set()->is_complete(), "must be"); - _region_attr.set_in_old(r->hrm_index(), true); + _region_attr.set_in_old(r->hrm_index(), true, r->has_pinned_objects()); _rem_set->exclude_region_from_scan(r->hrm_index()); } diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index 037eb071072..eb50b7ae5be 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -126,8 +126,7 @@ void G1CollectionSet::add_old_region(G1HeapRegion* hr) { assert(!hr->rem_set()->has_cset_group(), "Should have already uninstalled group remset"); - assert(!hr->in_collection_set(), "should not already be in the collection set"); - _g1h->register_old_region_with_region_attr(hr); + _g1h->register_old_collection_set_region_with_region_attr(hr); assert(_regions_cur_length < _regions_max_length, "Collection set now larger than maximum size."); _regions[_regions_cur_length++] = hr->hrm_index(); @@ -724,7 +723,7 @@ bool G1CollectionSet::finalize_optional_for_evacuation(double remaining_pause_ti stop_incremental_building(); - _g1h->verify_region_attr_remset_is_tracked(); + _g1h->verify_region_attr_is_remset_tracked(); return num_regions_selected > 0; } @@ -736,7 +735,7 @@ void G1CollectionSet::abandon_optional_collection_set(G1ParScanThreadStateSet* p // Clear collection set marker and make sure that the remembered set information // is correct as we still need it later. _g1h->clear_region_attr(r); - _g1h->register_region_with_region_attr(r); + _g1h->update_region_attr(r); r->clear_index_in_opt_cset(); }; @@ -745,7 +744,7 @@ void G1CollectionSet::abandon_optional_collection_set(G1ParScanThreadStateSet* p _optional_groups.remove_selected(_optional_groups.length(), _optional_groups.num_regions()); } - _g1h->verify_region_attr_remset_is_tracked(); + _g1h->verify_region_attr_is_remset_tracked(); } #ifdef ASSERT diff --git a/src/hotspot/share/gc/g1/g1HeapRegionAttr.hpp b/src/hotspot/share/gc/g1/g1HeapRegionAttr.hpp index 6d9bcea0343..0c5f14ba3b2 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionAttr.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionAttr.hpp @@ -33,15 +33,15 @@ struct G1HeapRegionAttr { public: typedef int8_t region_type_t; - // remset_is_tracked_t is essentially bool, but we need precise control + // is_remset_tracked_t is essentially bool, but we need precise control // on the size, and sizeof(bool) is implementation specific. - typedef uint8_t remset_is_tracked_t; + typedef uint8_t is_remset_tracked_t; // _is_pinned_t is essentially bool, but we want precise control // on the size, and sizeof(bool) is implementation specific. typedef uint8_t is_pinned_t; private: - remset_is_tracked_t _remset_is_tracked; + is_remset_tracked_t _is_remset_tracked; region_type_t _type; is_pinned_t _is_pinned; @@ -63,8 +63,8 @@ public: static const region_type_t Old = 1; // The region is in the collection set and an old region. static const region_type_t Num = 2; - G1HeapRegionAttr(region_type_t type = NotInCSet, bool remset_is_tracked = false, bool is_pinned = false) : - _remset_is_tracked(remset_is_tracked ? 1 : 0), _type(type), _is_pinned(is_pinned ? 1 : 0) { + G1HeapRegionAttr(region_type_t type = NotInCSet, bool is_remset_tracked = false, bool is_pinned = false) : + _is_remset_tracked(is_remset_tracked ? 1 : 0), _type(type), _is_pinned(is_pinned ? 1 : 0) { assert(is_valid(), "Invalid type %d", _type); } @@ -82,9 +82,8 @@ public: } } - bool remset_is_tracked() const { return _remset_is_tracked != 0; } + bool is_remset_tracked() const { return _is_remset_tracked != 0; } - void set_new_survivor() { _type = NewSurvivor; } bool is_pinned() const { return _is_pinned != 0; } void set_old() { _type = Old; } @@ -93,7 +92,7 @@ public: _type = NotInCSet; } - void set_remset_is_tracked(bool value) { _remset_is_tracked = value ? 1 : 0; } + void set_is_remset_tracked(bool value) { _is_remset_tracked = value ? 1 : 0; } void set_is_pinned(bool value) { _is_pinned = value ? 1 : 0; } bool is_in_cset_or_humongous_candidate() const { return is_in_cset() || is_humongous_candidate(); } @@ -126,26 +125,26 @@ class G1HeapRegionAttrBiasedMappedArray : public G1BiasedMappedArrayset_new_survivor(); + set_by_index(index, G1HeapRegionAttr(G1HeapRegionAttr::NewSurvivor, true, region_is_pinned)); } void set_humongous_candidate(uintptr_t index) { assert(get_by_index(index).is_default(), "Region attributes at index " INTPTR_FORMAT " should be default but is %s", index, get_by_index(index).get_type_str()); // Humongous candidates must have complete remset. - const bool remset_is_tracked = true; + const bool is_remset_tracked = true; // Humongous candidates can not be pinned. const bool region_is_pinned = false; - set_by_index(index, G1HeapRegionAttr(G1HeapRegionAttr::HumongousCandidate, remset_is_tracked, region_is_pinned)); + set_by_index(index, G1HeapRegionAttr(G1HeapRegionAttr::HumongousCandidate, is_remset_tracked, region_is_pinned)); } void clear_humongous_candidate(uintptr_t index) { @@ -156,8 +155,8 @@ class G1HeapRegionAttrBiasedMappedArray : public G1BiasedMappedArrayis_humongous_candidate(); } - void set_remset_is_tracked(uintptr_t index, bool remset_is_tracked) { - get_ref_by_index(index)->set_remset_is_tracked(remset_is_tracked); + void set_is_remset_tracked(uintptr_t index, bool is_remset_tracked) { + get_ref_by_index(index)->set_is_remset_tracked(is_remset_tracked); } void set_is_pinned(uintptr_t index, bool is_pinned) { @@ -170,12 +169,10 @@ class G1HeapRegionAttrBiasedMappedArray : public G1BiasedMappedArray void G1ParScanThreadState::mark_card_if_tracked(G1HeapRegionA #ifdef ASSERT G1HeapRegion* const hr_obj = _g1h->heap_region_containing(o); - assert(region_attr.remset_is_tracked() == hr_obj->rem_set()->is_tracked(), + assert(region_attr.is_remset_tracked() == hr_obj->rem_set()->is_tracked(), "State flag indicating remset tracking disagrees (%s) with actual remembered set (%s) for region %u", - BOOL_TO_STR(region_attr.remset_is_tracked()), + BOOL_TO_STR(region_attr.is_remset_tracked()), BOOL_TO_STR(hr_obj->rem_set()->is_tracked()), hr_obj->hrm_index()); #endif - if (!region_attr.remset_is_tracked()) { + if (!region_attr.is_remset_tracked()) { return; } bool into_survivor = region_attr.is_new_survivor(); diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index a2a088ae47a..539e5a54353 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -426,7 +426,7 @@ class G1PrepareEvacuationTask : public WorkerTask { // Now check if region is a humongous candidate if (!hr->is_starts_humongous()) { - _g1h->register_region_with_region_attr(hr); + _g1h->update_region_attr(hr); return false; } @@ -436,7 +436,7 @@ class G1PrepareEvacuationTask : public WorkerTask { _worker_humongous_candidates++; // We will later handle the remembered sets of these regions. } else { - _g1h->register_region_with_region_attr(hr); + _g1h->update_region_attr(hr); } log_debug(gc, humongous)("Humongous region %u (object size %zu @ " PTR_FORMAT ") remset %zu code roots %zu " "marked %d pinned count %zu reclaim candidate %d type %s", diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp index df4312ebd75..566d6469686 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp @@ -25,6 +25,7 @@ #include "gc/parallel/mutableNUMASpace.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/pretouchTask.hpp" #include "gc/shared/spaceDecorator.hpp" #include "gc/shared/workerThread.hpp" #include "memory/allocation.inline.hpp" @@ -37,21 +38,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 +51,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 +119,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 +140,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 +201,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 +255,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 +269,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 +282,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 +291,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(); + size_t num_pages = mr.byte_size() / 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(); + 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 +353,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"); @@ -430,11 +389,18 @@ void MutableNUMASpace::initialize(MemRegion mr, bias_region(bottom_region, ls->lgrp_id()); bias_region(top_region, ls->lgrp_id()); + if (AlwaysPreTouch) { + PretouchTask::pretouch("ParallelGC PreTouch bottom_region", (char*)bottom_region.start(), (char*)bottom_region.end(), + page_size(), pretouch_workers); + + PretouchTask::pretouch("ParallelGC PreTouch top_region", (char*)top_region.start(), (char*)top_region.end(), + page_size(), pretouch_workers); + } + // 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..629690a6258 100644 --- a/src/hotspot/share/gc/parallel/parallelArguments.cpp +++ b/src/hotspot/share/gc/parallel/parallelArguments.cpp @@ -66,11 +66,6 @@ void ParallelArguments::initialize() { } } - // True in product build, since tests using debug build often stress GC - if (FLAG_IS_DEFAULT(UseGCOverheadLimit)) { - FLAG_SET_DEFAULT(UseGCOverheadLimit, trueInProduct); - } - if (InitialSurvivorRatio < MinSurvivorRatio) { if (FLAG_IS_CMDLINE(InitialSurvivorRatio)) { if (FLAG_IS_CMDLINE(MinSurvivorRatio)) { @@ -103,15 +98,10 @@ void ParallelArguments::initialize() { FullGCForwarding::initialize_flags(heap_reserved_size_bytes()); } -// The alignment used for spaces in young gen and old gen -static size_t default_space_alignment() { - return 64 * K * HeapWordSize; -} - void ParallelArguments::initialize_alignments() { // Initialize card size before initializing alignments CardTable::initialize_card_size(); - SpaceAlignment = default_space_alignment(); + SpaceAlignment = ParallelScavengeHeap::default_space_alignment(); HeapAlignment = compute_heap_alignment(); } @@ -123,12 +113,23 @@ void ParallelArguments::initialize_heap_flags_and_sizes_one_pass() { void ParallelArguments::initialize_heap_flags_and_sizes() { initialize_heap_flags_and_sizes_one_pass(); + if (!UseLargePages) { + ParallelScavengeHeap::set_desired_page_size(os::vm_page_size()); + return; + } + + // If using large-page, need to update SpaceAlignment so that spaces are page-size aligned. const size_t min_pages = 4; // 1 for eden + 1 for each survivor + 1 for old const size_t page_sz = os::page_size_for_region_aligned(MinHeapSize, min_pages); + ParallelScavengeHeap::set_desired_page_size(page_sz); - // 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); + 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; + } + + // 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 eb1552e3db6..f1baa4c4ff7 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -61,11 +61,18 @@ PSYoungGen* ParallelScavengeHeap::_young_gen = nullptr; PSOldGen* ParallelScavengeHeap::_old_gen = nullptr; PSAdaptiveSizePolicy* ParallelScavengeHeap::_size_policy = nullptr; GCPolicyCounters* ParallelScavengeHeap::_gc_policy_counters = nullptr; +size_t ParallelScavengeHeap::_desired_page_size = 0; jint ParallelScavengeHeap::initialize() { const size_t reserved_heap_size = ParallelArguments::heap_reserved_size_bytes(); - ReservedHeapSpace heap_rs = Universe::reserve_heap(reserved_heap_size, HeapAlignment); + assert(_desired_page_size != 0, "Should be initialized"); + ReservedHeapSpace heap_rs = Universe::reserve_heap(reserved_heap_size, HeapAlignment, _desired_page_size); + // Adjust SpaceAlignment based on actually used large page size. + if (UseLargePages) { + SpaceAlignment = MAX2(heap_rs.page_size(), default_space_alignment()); + } + assert(is_aligned(SpaceAlignment, heap_rs.page_size()), "inv"); trace_actual_reserved_page_size(reserved_heap_size, heap_rs); @@ -367,6 +374,13 @@ bool ParallelScavengeHeap::check_gc_overhead_limit() { bool little_mutator_time = _size_policy->mutator_time_percent() * 100 < (100 - GCTimeLimit); bool little_free_space = check_gc_heap_free_limit(_young_gen->free_in_bytes(), _young_gen->capacity_in_bytes()) && check_gc_heap_free_limit( _old_gen->free_in_bytes(), _old_gen->capacity_in_bytes()); + + log_debug(gc)("GC Overhead Limit: GC Time %f Free Space Young %f Old %f Counter %zu", + (100 - _size_policy->mutator_time_percent()), + percent_of(_young_gen->free_in_bytes(), _young_gen->capacity_in_bytes()), + percent_of(_old_gen->free_in_bytes(), _young_gen->capacity_in_bytes()), + _gc_overhead_counter); + if (little_mutator_time && little_free_space) { _gc_overhead_counter++; if (_gc_overhead_counter >= GCOverheadLimitThreshold) { @@ -419,7 +433,7 @@ HeapWord* ParallelScavengeHeap::satisfy_failed_allocation(size_t size, bool is_t } if (check_gc_overhead_limit()) { - log_info(gc)("GCOverheadLimitThreshold %zu reached.", GCOverheadLimitThreshold); + log_info(gc)("GC Overhead Limit exceeded too often (%zu).", GCOverheadLimitThreshold); return nullptr; } diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index bf777bda29e..962a3c4b15b 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp @@ -76,6 +76,9 @@ class ParallelScavengeHeap : public CollectedHeap { static PSAdaptiveSizePolicy* _size_policy; static GCPolicyCounters* _gc_policy_counters; + // At startup, calculate the desired OS page-size based on heap size and large-page flags. + static size_t _desired_page_size; + GCMemoryManager* _young_manager; GCMemoryManager* _old_manager; @@ -85,7 +88,7 @@ class ParallelScavengeHeap : public CollectedHeap { WorkerThreads _workers; - uint _gc_overhead_counter; + uintx _gc_overhead_counter; bool _is_heap_almost_full; @@ -128,6 +131,18 @@ public: _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..0a404579da8 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 @@ -189,7 +188,6 @@ public: ParMarkBitMap* mark_bitmap() { return _mark_bitmap; } // Save for later processing. Must not fail. - inline void push(oop obj); inline void push_region(size_t index); // Check mark and maybe push on marking stack. diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp b/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp index 2c0b8480726..663cd83be9c 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp @@ -56,14 +56,6 @@ inline bool ParCompactionManager::steal(int queue_num, size_t& region) { return region_task_queues()->steal(queue_num, region); } -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 @@ -78,24 +70,26 @@ void ParCompactionManager::push_region(size_t index) template inline void ParCompactionManager::mark_and_push(T* p) { T heap_oop = RawAccess<>::oop_load(p); - if (!CompressedOops::is_null(heap_oop)) { - oop obj = CompressedOops::decode_not_null(heap_oop); - assert(ParallelScavengeHeap::heap()->is_in(obj), "should be in heap"); - - if (mark_bitmap()->mark_obj(obj)) { - if (StringDedup::is_enabled() && - java_lang_String::is_instance(obj) && - psStringDedup::is_candidate_from_mark(obj)) { - _string_dedup_requests.add(obj); - } - - ContinuationGCSupport::transform_stack_chunk(obj); - - assert(_marking_stats_cache != nullptr, "inv"); - _marking_stats_cache->push(obj, obj->size()); - push(obj); - } + if (CompressedOops::is_null(heap_oop)) { + return; } + + oop obj = CompressedOops::decode_not_null(heap_oop); + if (!mark_bitmap()->mark_obj(obj)) { + // Marked by another worker. + return; + } + + if (StringDedup::is_enabled() && + java_lang_String::is_instance(obj) && + psStringDedup::is_candidate_from_mark(obj)) { + _string_dedup_requests.add(obj); + } + + ContinuationGCSupport::transform_stack_chunk(obj); + + _marking_stats_cache->push(obj, obj->size()); + marking_stack()->push(ScannerTask(obj)); } inline void ParCompactionManager::FollowStackClosure::do_void() { 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/psScavenge.cpp b/src/hotspot/share/gc/parallel/psScavenge.cpp index f633f40ef7f..e738a13d464 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.cpp +++ b/src/hotspot/share/gc/parallel/psScavenge.cpp @@ -521,31 +521,56 @@ void PSScavenge::clean_up_failed_promotion() { } bool PSScavenge::should_attempt_scavenge() { - ParallelScavengeHeap* heap = ParallelScavengeHeap::heap(); + const bool ShouldRunYoungGC = true; + const bool ShouldRunFullGC = false; + ParallelScavengeHeap* heap = ParallelScavengeHeap::heap(); PSYoungGen* young_gen = heap->young_gen(); PSOldGen* old_gen = heap->old_gen(); if (!young_gen->to_space()->is_empty()) { - log_debug(gc, ergo)("To-space is not empty; should run full-gc instead."); - return false; + log_debug(gc, ergo)("To-space is not empty; run full-gc instead."); + return ShouldRunFullGC; } - // Test to see if the scavenge will likely fail. + // Check if the predicted promoted bytes will overflow free space in old-gen. PSAdaptiveSizePolicy* policy = heap->size_policy(); size_t avg_promoted = (size_t) policy->padded_average_promoted_in_bytes(); size_t promotion_estimate = MIN2(avg_promoted, young_gen->used_in_bytes()); // Total free size after possible old gen expansion - size_t free_in_old_gen = old_gen->max_gen_size() - old_gen->used_in_bytes(); - bool result = promotion_estimate < free_in_old_gen; + size_t free_in_old_gen_with_expansion = old_gen->max_gen_size() - old_gen->used_in_bytes(); - log_trace(gc, ergo)("%s scavenge: average_promoted %zu padded_average_promoted %zu free in old gen %zu", - result ? "Do" : "Skip", (size_t) policy->average_promoted_in_bytes(), - (size_t) policy->padded_average_promoted_in_bytes(), - free_in_old_gen); + log_trace(gc, ergo)("average_promoted %zu; padded_average_promoted %zu", + (size_t) policy->average_promoted_in_bytes(), + (size_t) policy->padded_average_promoted_in_bytes()); - return result; + if (promotion_estimate >= free_in_old_gen_with_expansion) { + log_debug(gc, ergo)("Run full-gc; predicted promotion size >= max free space in old-gen: %zu >= %zu", + promotion_estimate, free_in_old_gen_with_expansion); + return ShouldRunFullGC; + } + + if (UseAdaptiveSizePolicy) { + // Also checking OS has enough free memory to commit and expand old-gen. + // Otherwise, the recorded gc-pause-time might be inflated to include time + // of OS preparing free memory, resulting in inaccurate young-gen resizing. + assert(old_gen->committed().byte_size() >= old_gen->used_in_bytes(), "inv"); + // Use uint64_t instead of size_t for 32bit compatibility. + uint64_t free_mem_in_os; + if (os::free_memory(free_mem_in_os)) { + size_t actual_free = (size_t)MIN2(old_gen->committed().byte_size() - old_gen->used_in_bytes() + free_mem_in_os, + (uint64_t)SIZE_MAX); + if (promotion_estimate > actual_free) { + log_debug(gc, ergo)("Run full-gc; predicted promotion size > free space in old-gen and OS: %zu > %zu", + promotion_estimate, actual_free); + return ShouldRunFullGC; + } + } + } + + // No particular reasons to run full-gc, so young-gc. + return ShouldRunYoungGC; } // Adaptive size policy support. 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..edcbf7647fc 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(), @@ -356,7 +356,7 @@ void PSYoungGen::compute_desired_sizes(bool is_survivor_overflowing, // Keep survivor and adjust eden to meet min-gen-size eden_size = min_gen_size() - 2 * survivor_size; } else if (max_gen_size() < new_gen_size) { - log_info(gc, ergo)("Requested sizes exceeds MaxNewSize (K): %zu vs %zu)", new_gen_size/K, max_gen_size()/K); + log_info(gc, ergo)("Requested sizes exceeds MaxNewSize (K): %zu vs %zu", new_gen_size/K, max_gen_size()/K); // New capacity would exceed max; need to revise these desired sizes. // Favor survivor over eden in order to reduce promotion (overflow). if (2 * survivor_size >= max_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 aef896182c0..413d80bebf4 100644 --- a/src/hotspot/share/gc/serial/defNewGeneration.cpp +++ b/src/hotspot/share/gc/serial/defNewGeneration.cpp @@ -297,9 +297,9 @@ void DefNewGeneration::init_spaces() { MemRegion edenMR((HeapWord*)eden_start, (HeapWord*)eden_end); // Reset the spaces for their new regions. - from()->initialize(fromMR, from()->is_empty(), SpaceDecorator::Mangle); - to()->initialize(toMR, true, SpaceDecorator::Mangle); - eden()->initialize(edenMR, true, SpaceDecorator::Mangle); + from()->initialize(fromMR, from()->is_empty()); + to()->initialize(toMR, true); + eden()->initialize(edenMR, true); post_resize(); } @@ -340,7 +340,7 @@ void DefNewGeneration::expand_eden_by(size_t delta_bytes) { } MemRegion eden_mr{eden()->bottom(), (HeapWord*)_virtual_space.high()}; - eden()->initialize(eden_mr, eden()->is_empty(), SpaceDecorator::Mangle); + eden()->initialize(eden_mr, eden()->is_empty()); post_resize(); } diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.cpp b/src/hotspot/share/gc/serial/tenuredGeneration.cpp index 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 e12e7b56e23..c4eefee5f65 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp @@ -115,7 +115,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) { @@ -124,7 +124,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()); } @@ -1171,7 +1171,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 @@ -1182,7 +1182,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); } } @@ -1190,10 +1190,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 @@ -1202,10 +1202,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); } } @@ -1213,16 +1213,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.hpp b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp index e97da234d16..a5646c303f3 100644 --- a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp +++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp @@ -61,10 +61,6 @@ public: CardTable* card_table() const { return _card_table; } - void write_region(JavaThread* thread, MemRegion mr) { - write_region(mr); - } - // Record a reference update. Note that these versions are precise! // The scanning code has to handle the fact that the write barrier may be // either precise or imprecise. We make non-virtual inline variants of diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index 956bffde156..938544b9a2c 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.hpp @@ -268,8 +268,9 @@ product(bool, AlwaysActAsServerClassMachine, false, \ "Always act like a server-class machine") \ \ - product_pd(uint64_t, MaxRAM, \ - "Real memory size (in bytes) used to set maximum heap size") \ + product(uint64_t, MaxRAM, 0, \ + "(Deprecated) Real memory size (in bytes) used to set maximum " \ + "heap size") \ range(0, 0XFFFFFFFFFFFFFFFF) \ \ product(bool, AggressiveHeap, false, \ @@ -357,7 +358,7 @@ "Initial ratio of young generation/survivor space size") \ range(3, max_uintx) \ \ - product(bool, UseGCOverheadLimit, true, \ + product(bool, UseGCOverheadLimit, falseInDebug, \ "Use policy to limit of proportion of time spent in GC " \ "before an OutOfMemory error is thrown") \ \ diff --git a/src/hotspot/share/gc/shared/modRefBarrierSet.hpp b/src/hotspot/share/gc/shared/modRefBarrierSet.hpp index 15ac7971118..c078d151233 100644 --- a/src/hotspot/share/gc/shared/modRefBarrierSet.hpp +++ b/src/hotspot/share/gc/shared/modRefBarrierSet.hpp @@ -53,8 +53,6 @@ public: // 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 1d15fbc3fa9..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), @@ -55,7 +54,7 @@ void ContiguousSpace::initialize(MemRegion mr, if (clear_space) { clear(SpaceDecorator::DontMangle); } - if (ZapUnusedHeapArea && mangle_space) { + if (ZapUnusedHeapArea) { mangle_unused_area(); } } diff --git a/src/hotspot/share/gc/shared/space.hpp b/src/hotspot/share/gc/shared/space.hpp index 75dd3f998d6..7f2887275b3 100644 --- a/src/hotspot/share/gc/shared/space.hpp +++ b/src/hotspot/share/gc/shared/space.hpp @@ -101,7 +101,7 @@ public: // 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/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index 8210718126b..35ec9ed5c8c 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -859,8 +859,8 @@ static void hide_strip_mined_loop(OuterStripMinedLoopNode* outer, CountedLoopNod phase->register_control(new_outer, phase->get_loop(outer), outer->in(LoopNode::EntryControl)); Node* new_le = new IfNode(le->in(0), le->in(1), le->_prob, le->_fcnt); phase->register_control(new_le, phase->get_loop(le), le->in(0)); - phase->lazy_replace(outer, new_outer); - phase->lazy_replace(le, new_le); + phase->replace_node_and_forward_ctrl(outer, new_outer); + phase->replace_node_and_forward_ctrl(le, new_le); inner->clear_strip_mined(); } @@ -1324,7 +1324,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) { Node* backedge = head->in(LoopNode::LoopBackControl); Node* new_head = new LoopNode(entry, backedge); phase->register_control(new_head, phase->get_loop(entry), entry); - phase->lazy_replace(head, new_head); + phase->replace_node_and_forward_ctrl(head, new_head); } } @@ -1777,7 +1777,7 @@ void MemoryGraphFixer::collect_memory_nodes() { if (u->adr_type() == TypePtr::BOTTOM) { fix_memory_uses(u, n, n, c); } else if (_phase->C->get_alias_index(u->adr_type()) == _alias) { - _phase->lazy_replace(u, n); + _phase->igvn().replace_node(u, n); --i; --imax; } } 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 @@ protected: } 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/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp index c7067b2e5ab..a2cccec8423 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp @@ -127,7 +127,7 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio // Reclaim humongous regions here, and count them as the immediate garbage #ifdef ASSERT bool reg_live = region->has_live(); - bool bm_live = heap->active_generation()->complete_marking_context()->is_marked(cast_to_oop(region->bottom())); + bool bm_live = _generation->complete_marking_context()->is_marked(cast_to_oop(region->bottom())); assert(reg_live == bm_live, "Humongous liveness and marks should agree. Region live: %s; Bitmap live: %s; Region Live Words: %zu", BOOL_TO_STR(reg_live), BOOL_TO_STR(bm_live), region->get_live_data_words()); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp index c8a0c3dc518..478c5696188 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -73,10 +73,11 @@ ShenandoahHeuristics::~ShenandoahHeuristics() { } void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) { - assert(collection_set->is_empty(), "Must be empty"); - ShenandoahHeap* heap = ShenandoahHeap::heap(); + assert(collection_set->is_empty(), "Must be empty"); + assert(!heap->mode()->is_generational(), "Wrong heuristic for heap mode"); + // Check all pinned regions have updated status before choosing the collection set. heap->assert_pinned_region_status(); @@ -120,7 +121,7 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec // Reclaim humongous regions here, and count them as the immediate garbage #ifdef ASSERT bool reg_live = region->has_live(); - bool bm_live = heap->gc_generation()->complete_marking_context()->is_marked(cast_to_oop(region->bottom())); + bool bm_live = heap->global_generation()->complete_marking_context()->is_marked(cast_to_oop(region->bottom())); assert(reg_live == bm_live, "Humongous liveness and marks should agree. Region live: %s; Bitmap live: %s; Region Live Words: %zu", BOOL_TO_STR(reg_live), BOOL_TO_STR(bm_live), region->get_live_data_words()); diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.cpp index fa784d5bb90..79c4ecabcf4 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.cpp @@ -31,7 +31,7 @@ void ShenandoahGenerationalMode::initialize_flags() const { -#if !(defined AARCH64 || defined AMD64 || defined IA32 || defined PPC64 || defined RISCV64) +#if !(defined AARCH64 || defined AMD64 || defined PPC64 || defined RISCV64) vm_exit_during_initialization("Shenandoah Generational GC is not supported on this platform."); #endif diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp index 20f8ecc43e8..4c0bc209d78 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp @@ -48,11 +48,6 @@ void ShenandoahPassiveMode::initialize_flags() const { SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahCloneBarrier); SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahStackWatermarkBarrier); SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahCardBarrier); - - // Final configuration checks - // Passive mode does not instantiate the machinery to support the card table. - // Exit if the flag has been explicitly set. - SHENANDOAH_CHECK_FLAG_UNSET(ShenandoahCardBarrier); } ShenandoahHeuristics* ShenandoahPassiveMode::initialize_heuristics(ShenandoahSpaceInfo* space_info) const { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp index 1ff001c5abd..a7cf8e638dd 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp @@ -39,7 +39,7 @@ #include "utilities/defaultStream.hpp" void ShenandoahArguments::initialize() { -#if !(defined AARCH64 || defined AMD64 || defined IA32 || defined PPC64 || defined RISCV64) +#if !(defined AARCH64 || defined AMD64 || defined PPC64 || defined RISCV64) vm_exit_during_initialization("Shenandoah GC is not supported on this platform."); #endif diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp index 3d9fa10b0fc..baeaffb9c7b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp @@ -425,6 +425,16 @@ void ShenandoahAsserts::assert_marked_strong(void *interior_loc, oop obj, const } } +void ShenandoahAsserts::assert_mark_complete(HeapWord* obj, const char* file, int line) { + const ShenandoahHeap* heap = ShenandoahHeap::heap(); + const ShenandoahHeapRegion* region = heap->heap_region_containing(obj); + const ShenandoahGeneration* generation = heap->generation_for(region->affiliation()); + if (!generation->is_mark_complete()) { + ShenandoahMessageBuffer msg("Marking should be complete for object " PTR_FORMAT " in the %s generation", p2i(obj), generation->name()); + report_vm_error(file, line, msg.buffer()); + } +} + void ShenandoahAsserts::assert_in_cset(void* interior_loc, oop obj, const char* file, int line) { assert_correct(interior_loc, obj, file, line); @@ -542,23 +552,6 @@ void ShenandoahAsserts::assert_control_or_vm_thread_at_safepoint(bool at_safepoi report_vm_error(file, line, msg.buffer()); } -void ShenandoahAsserts::assert_generations_reconciled(const char* file, int line) { - if (!ShenandoahSafepoint::is_at_shenandoah_safepoint()) { - // Only shenandoah safepoint operations participate in the active/gc generation scheme - return; - } - - ShenandoahHeap* heap = ShenandoahHeap::heap(); - ShenandoahGeneration* ggen = heap->gc_generation(); - ShenandoahGeneration* agen = heap->active_generation(); - if (agen == ggen) { - return; - } - - ShenandoahMessageBuffer msg("Active(%s) & GC(%s) Generations aren't reconciled", agen->name(), ggen->name()); - report_vm_error(file, line, msg.buffer()); -} - bool ShenandoahAsserts::extract_klass_safely(oop obj, narrowKlass& nk, const Klass*& k) { nk = 0; k = nullptr; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp index e31ef7c99aa..d0fc3e213c8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp @@ -65,6 +65,9 @@ public: static void assert_marked(void* interior_loc, oop obj, const char* file, int line); static void assert_marked_weak(void* interior_loc, oop obj, const char* file, int line); static void assert_marked_strong(void* interior_loc, oop obj, const char* file, int line); + + // Assert that marking is complete for the generation where this obj resides + static void assert_mark_complete(HeapWord* obj, const char* file, int line); static void assert_in_cset(void* interior_loc, oop obj, const char* file, int line); static void assert_not_in_cset(void* interior_loc, oop obj, const char* file, int line); static void assert_not_in_cset_loc(void* interior_loc, const char* file, int line); @@ -76,7 +79,6 @@ public: static void assert_heaplocked_or_safepoint(const char* file, int line); static void assert_control_or_vm_thread_at_safepoint(bool at_safepoint, const char* file, int line); static void assert_generational(const char* file, int line); - static void assert_generations_reconciled(const char* file, int line); // Given a possibly invalid oop, extract narrowKlass (if UCCP) and Klass* // from it safely. @@ -133,6 +135,9 @@ public: #define shenandoah_assert_marked_strong(interior_loc, obj) \ ShenandoahAsserts::assert_marked_strong(interior_loc, obj, __FILE__, __LINE__) +#define shenandoah_assert_mark_complete(obj) \ + ShenandoahAsserts::assert_mark_complete(obj, __FILE__, __LINE__) + #define shenandoah_assert_in_cset_if(interior_loc, obj, condition) \ if (condition) ShenandoahAsserts::assert_in_cset(interior_loc, obj, __FILE__, __LINE__) #define shenandoah_assert_in_cset_except(interior_loc, obj, exception) \ @@ -184,10 +189,6 @@ public: #define shenandoah_assert_generational() \ ShenandoahAsserts::assert_generational(__FILE__, __LINE__) -// Some limited sanity checking of the _gc_generation and _active_generation fields of ShenandoahHeap -#define shenandoah_assert_generations_reconciled() \ - ShenandoahAsserts::assert_generations_reconciled(__FILE__, __LINE__) - #else #define shenandoah_assert_in_heap_bounds(interior_loc, obj) #define shenandoah_assert_in_heap_bounds_or_null(interior_loc, obj) @@ -217,6 +218,8 @@ public: #define shenandoah_assert_marked_strong_except(interior_loc, obj, exception) #define shenandoah_assert_marked_strong(interior_loc, obj) +#define shenandoah_assert_mark_complete(obj) + #define shenandoah_assert_in_cset_if(interior_loc, obj, condition) #define shenandoah_assert_in_cset_except(interior_loc, obj, exception) #define shenandoah_assert_in_cset(interior_loc, obj) @@ -241,7 +244,6 @@ public: #define shenandoah_assert_control_or_vm_thread() #define shenandoah_assert_control_or_vm_thread_at_safepoint() #define shenandoah_assert_generational() -#define shenandoah_assert_generations_reconciled() #endif diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 456b9fe6d3c..9613422496a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -91,8 +91,8 @@ public: }; ShenandoahConcurrentGC::ShenandoahConcurrentGC(ShenandoahGeneration* generation, bool do_old_gc_bootstrap) : + ShenandoahGC(generation), _mark(generation), - _generation(generation), _degen_point(ShenandoahDegenPoint::_degenerated_unset), _abbreviated(false), _do_old_gc_bootstrap(do_old_gc_bootstrap) { @@ -576,7 +576,7 @@ void ShenandoahConcurrentGC::entry_promote_in_place() const { ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::promote_in_place); EventMark em("%s", "Promote in place"); - ShenandoahGenerationalHeap::heap()->promote_regions_in_place(true); + ShenandoahGenerationalHeap::heap()->promote_regions_in_place(_generation, true); } void ShenandoahConcurrentGC::entry_update_thread_roots() { @@ -706,7 +706,7 @@ void ShenandoahConcurrentGC::op_init_mark() { if (ShenandoahVerify) { ShenandoahTimingsTracker v(ShenandoahPhaseTimings::init_mark_verify); - heap->verifier()->verify_before_concmark(); + heap->verifier()->verify_before_concmark(_generation); } if (VerifyBeforeGC) { @@ -763,7 +763,7 @@ void ShenandoahConcurrentGC::op_final_mark() { assert(!heap->has_forwarded_objects(), "No forwarded objects on this path"); if (ShenandoahVerify) { - heap->verifier()->verify_roots_no_forwarded(); + heap->verifier()->verify_roots_no_forwarded(_generation); } if (!heap->cancelled_gc()) { @@ -791,7 +791,7 @@ void ShenandoahConcurrentGC::op_final_mark() { if (ShenandoahVerify) { ShenandoahTimingsTracker v(ShenandoahPhaseTimings::final_mark_verify); - heap->verifier()->verify_before_evacuation(); + heap->verifier()->verify_before_evacuation(_generation); } heap->set_evacuation_in_progress(true); @@ -806,9 +806,9 @@ void ShenandoahConcurrentGC::op_final_mark() { if (ShenandoahVerify) { ShenandoahTimingsTracker v(ShenandoahPhaseTimings::final_mark_verify); if (has_in_place_promotions(heap)) { - heap->verifier()->verify_after_concmark_with_promotions(); + heap->verifier()->verify_after_concmark_with_promotions(_generation); } else { - heap->verifier()->verify_after_concmark(); + heap->verifier()->verify_after_concmark(_generation); } } } @@ -877,18 +877,20 @@ void ShenandoahConcurrentGC::op_weak_refs() { class ShenandoahEvacUpdateCleanupOopStorageRootsClosure : public BasicOopIterateClosure { private: ShenandoahHeap* const _heap; + ShenandoahGeneration* const _generation; ShenandoahMarkingContext* const _mark_context; bool _evac_in_progress; Thread* const _thread; public: - ShenandoahEvacUpdateCleanupOopStorageRootsClosure(); + explicit ShenandoahEvacUpdateCleanupOopStorageRootsClosure(ShenandoahGeneration* generation); void do_oop(oop* p); void do_oop(narrowOop* p); }; -ShenandoahEvacUpdateCleanupOopStorageRootsClosure::ShenandoahEvacUpdateCleanupOopStorageRootsClosure() : +ShenandoahEvacUpdateCleanupOopStorageRootsClosure::ShenandoahEvacUpdateCleanupOopStorageRootsClosure(ShenandoahGeneration* generation) : _heap(ShenandoahHeap::heap()), + _generation(generation), _mark_context(ShenandoahHeap::heap()->marking_context()), _evac_in_progress(ShenandoahHeap::heap()->is_evacuation_in_progress()), _thread(Thread::current()) { @@ -898,8 +900,7 @@ void ShenandoahEvacUpdateCleanupOopStorageRootsClosure::do_oop(oop* p) { const oop obj = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(obj)) { if (!_mark_context->is_marked(obj)) { - shenandoah_assert_generations_reconciled(); - if (_heap->is_in_active_generation(obj)) { + if (_generation->contains(obj)) { // Note: The obj is dead here. Do not touch it, just clear. ShenandoahHeap::atomic_clear_oop(p, obj); } @@ -942,14 +943,16 @@ private: ShenandoahClassLoaderDataRoots _cld_roots; ShenandoahConcurrentNMethodIterator _nmethod_itr; + ShenandoahGeneration* _generation; ShenandoahPhaseTimings::Phase _phase; public: - ShenandoahConcurrentWeakRootsEvacUpdateTask(ShenandoahPhaseTimings::Phase phase) : + ShenandoahConcurrentWeakRootsEvacUpdateTask(ShenandoahGeneration* generation, ShenandoahPhaseTimings::Phase phase) : WorkerTask("Shenandoah Evacuate/Update Concurrent Weak Roots"), _vm_roots(phase), _cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers(), false /*heap iteration*/), _nmethod_itr(ShenandoahCodeRoots::table()), + _generation(generation), _phase(phase) {} ~ShenandoahConcurrentWeakRootsEvacUpdateTask() { @@ -957,14 +960,14 @@ public: _vm_roots.report_num_dead(); } - void work(uint worker_id) { + void work(uint worker_id) override { ShenandoahConcurrentWorkerSession worker_session(worker_id); ShenandoahSuspendibleThreadSetJoiner sts_join; { ShenandoahEvacOOMScope oom; // jni_roots and weak_roots are OopStorage backed roots, concurrent iteration // may race against OopStorage::release() calls. - ShenandoahEvacUpdateCleanupOopStorageRootsClosure cl; + ShenandoahEvacUpdateCleanupOopStorageRootsClosure cl(_generation); _vm_roots.oops_do(&cl, worker_id); } @@ -999,7 +1002,7 @@ void ShenandoahConcurrentGC::op_weak_roots() { // Concurrent weak root processing ShenandoahTimingsTracker t(ShenandoahPhaseTimings::conc_weak_roots_work); ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::conc_weak_roots_work); - ShenandoahConcurrentWeakRootsEvacUpdateTask task(ShenandoahPhaseTimings::conc_weak_roots_work); + ShenandoahConcurrentWeakRootsEvacUpdateTask task(_generation, ShenandoahPhaseTimings::conc_weak_roots_work); heap->workers()->run_task(&task); } @@ -1105,19 +1108,19 @@ void ShenandoahConcurrentGC::op_cleanup_early() { } void ShenandoahConcurrentGC::op_evacuate() { - ShenandoahHeap::heap()->evacuate_collection_set(true /*concurrent*/); + ShenandoahHeap::heap()->evacuate_collection_set(_generation, true /*concurrent*/); } void ShenandoahConcurrentGC::op_init_update_refs() { - ShenandoahHeap* const heap = ShenandoahHeap::heap(); if (ShenandoahVerify) { + ShenandoahHeap* const heap = ShenandoahHeap::heap(); ShenandoahTimingsTracker v(ShenandoahPhaseTimings::init_update_refs_verify); - heap->verifier()->verify_before_update_refs(); + heap->verifier()->verify_before_update_refs(_generation); } } void ShenandoahConcurrentGC::op_update_refs() { - ShenandoahHeap::heap()->update_heap_references(true /*concurrent*/); + ShenandoahHeap::heap()->update_heap_references(_generation, true /*concurrent*/); } class ShenandoahUpdateThreadHandshakeClosure : public HandshakeClosure { @@ -1163,7 +1166,7 @@ void ShenandoahConcurrentGC::op_final_update_refs() { // Has to be done before cset is clear if (ShenandoahVerify) { - heap->verifier()->verify_roots_in_to_space(); + heap->verifier()->verify_roots_in_to_space(_generation); } // If we are running in generational mode and this is an aging cycle, this will also age active @@ -1198,7 +1201,7 @@ void ShenandoahConcurrentGC::op_final_update_refs() { if (ShenandoahVerify) { ShenandoahTimingsTracker v(ShenandoahPhaseTimings::final_update_refs_verify); - heap->verifier()->verify_after_update_refs(); + heap->verifier()->verify_after_update_refs(_generation); } if (VerifyAfterGC) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp index d81c49363a2..54d43416fdb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp @@ -47,7 +47,6 @@ class ShenandoahConcurrentGC : public ShenandoahGC { protected: ShenandoahConcurrentMark _mark; - ShenandoahGeneration* const _generation; private: ShenandoahDegenPoint _degen_point; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp index 005d6c42f8c..7a195f64cbd 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp @@ -56,18 +56,11 @@ public: } void work(uint worker_id) { - ShenandoahHeap* heap = ShenandoahHeap::heap(); ShenandoahConcurrentWorkerSession worker_session(worker_id); ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::conc_mark, ShenandoahPhaseTimings::ParallelMark, worker_id, true); ShenandoahSuspendibleThreadSetJoiner stsj; - // Do not use active_generation() : we must use the gc_generation() set by - // ShenandoahGCScope on the ControllerThread's stack; no safepoint may - // intervene to update active_generation, so we can't - // shenandoah_assert_generations_reconciled() here. - ShenandoahReferenceProcessor* rp = heap->gc_generation()->ref_processor(); - assert(rp != nullptr, "need reference processor"); StringDedup::Requests requests; - _cm->mark_loop(worker_id, _terminator, rp, GENERATION, true /*cancellable*/, + _cm->mark_loop(worker_id, _terminator, GENERATION, true /*cancellable*/, ShenandoahStringDedup::is_enabled() ? ENQUEUE_DEDUP : NO_DEDUP, &requests); } @@ -106,9 +99,6 @@ public: ShenandoahParallelWorkerSession worker_session(worker_id); StringDedup::Requests requests; - ShenandoahReferenceProcessor* rp = heap->gc_generation()->ref_processor(); - shenandoah_assert_generations_reconciled(); - // First drain remaining SATB buffers. { ShenandoahObjToScanQueue* q = _cm->get_queue(worker_id); @@ -122,7 +112,7 @@ public: ShenandoahSATBAndRemarkThreadsClosure tc(satb_mq_set); Threads::possibly_parallel_threads_do(true /* is_par */, &tc); } - _cm->mark_loop(worker_id, _terminator, rp, GENERATION, false /*not cancellable*/, + _cm->mark_loop(worker_id, _terminator, GENERATION, false /*not cancellable*/, _dedup_string ? ENQUEUE_DEDUP : NO_DEDUP, &requests); assert(_cm->task_queues()->is_empty(), "Should be empty"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index b918bf67b34..53b83edd47b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -46,9 +46,8 @@ #include "utilities/events.hpp" ShenandoahDegenGC::ShenandoahDegenGC(ShenandoahDegenPoint degen_point, ShenandoahGeneration* generation) : - ShenandoahGC(), + ShenandoahGC(generation), _degen_point(degen_point), - _generation(generation), _abbreviated(false) { } @@ -96,6 +95,12 @@ void ShenandoahDegenGC::op_degenerated() { // some phase, we have to upgrade the Degenerate GC to Full GC. heap->clear_cancelled_gc(); + // If it's passive mode with ShenandoahCardBarrier turned on: clean the write table + // without swapping the tables since no scan happens in passive mode anyway + if (ShenandoahCardBarrier && !heap->mode()->is_generational()) { + heap->old_generation()->card_scan()->mark_write_table_as_clean(); + } + #ifdef ASSERT if (heap->mode()->is_generational()) { ShenandoahOldGeneration* old_generation = heap->old_generation(); @@ -260,7 +265,7 @@ void ShenandoahDegenGC::op_degenerated() { } else if (has_in_place_promotions(heap)) { // We have nothing to evacuate, but there are still regions to promote in place. ShenandoahGCPhase phase(ShenandoahPhaseTimings::degen_gc_promote_regions); - ShenandoahGenerationalHeap::heap()->promote_regions_in_place(false /* concurrent*/); + ShenandoahGenerationalHeap::heap()->promote_regions_in_place(_generation, false /* concurrent*/); } // Update collector state regardless of whether there are forwarded objects @@ -300,7 +305,7 @@ void ShenandoahDegenGC::op_degenerated() { } if (ShenandoahVerify) { - heap->verifier()->verify_after_degenerated(); + heap->verifier()->verify_after_degenerated(_generation); } if (VerifyAfterGC) { @@ -337,11 +342,11 @@ void ShenandoahDegenGC::op_finish_mark() { void ShenandoahDegenGC::op_prepare_evacuation() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); if (ShenandoahVerify) { - heap->verifier()->verify_roots_no_forwarded(); + heap->verifier()->verify_roots_no_forwarded(_generation); } // STW cleanup weak roots and unload classes - heap->parallel_cleaning(false /*full gc*/); + heap->parallel_cleaning(_generation, false /*full gc*/); // Prepare regions and collection set _generation->prepare_regions_and_collection_set(false /*concurrent*/); @@ -358,7 +363,7 @@ void ShenandoahDegenGC::op_prepare_evacuation() { if (!heap->collection_set()->is_empty()) { if (ShenandoahVerify) { - heap->verifier()->verify_before_evacuation(); + heap->verifier()->verify_before_evacuation(_generation); } heap->set_evacuation_in_progress(true); @@ -366,9 +371,9 @@ void ShenandoahDegenGC::op_prepare_evacuation() { } else { if (ShenandoahVerify) { if (has_in_place_promotions(heap)) { - heap->verifier()->verify_after_concmark_with_promotions(); + heap->verifier()->verify_after_concmark_with_promotions(_generation); } else { - heap->verifier()->verify_after_concmark(); + heap->verifier()->verify_after_concmark(_generation); } } @@ -388,7 +393,7 @@ void ShenandoahDegenGC::op_cleanup_early() { void ShenandoahDegenGC::op_evacuate() { ShenandoahGCPhase phase(ShenandoahPhaseTimings::degen_gc_stw_evac); - ShenandoahHeap::heap()->evacuate_collection_set(false /* concurrent*/); + ShenandoahHeap::heap()->evacuate_collection_set(_generation, false /* concurrent*/); } void ShenandoahDegenGC::op_init_update_refs() { @@ -402,7 +407,7 @@ void ShenandoahDegenGC::op_update_refs() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); ShenandoahGCPhase phase(ShenandoahPhaseTimings::degen_gc_update_refs); // Handed over from concurrent update references phase - heap->update_heap_references(false /*concurrent*/); + heap->update_heap_references(_generation, false /*concurrent*/); heap->set_update_refs_in_progress(false); heap->set_has_forwarded_objects(false); @@ -416,7 +421,7 @@ void ShenandoahDegenGC::op_update_roots() { heap->update_heap_region_states(false /*concurrent*/); if (ShenandoahVerify) { - heap->verifier()->verify_after_update_refs(); + heap->verifier()->verify_after_update_refs(_generation); } if (VerifyAfterGC) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp index 971bd67eb0d..34b9688106c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp @@ -34,12 +34,11 @@ class ShenandoahDegenGC : public ShenandoahGC { friend class VM_ShenandoahDegeneratedGC; private: const ShenandoahDegenPoint _degen_point; - ShenandoahGeneration* _generation; bool _abbreviated; public: ShenandoahDegenGC(ShenandoahDegenPoint degen_point, ShenandoahGeneration* generation); - bool collect(GCCause::Cause cause); + bool collect(GCCause::Cause cause) override; private: void vmop_degenerated(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index 2e486a23363..78218f5e403 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -68,6 +68,7 @@ #include "utilities/growableArray.hpp" ShenandoahFullGC::ShenandoahFullGC() : + ShenandoahGC(ShenandoahHeap::heap()->global_generation()), _gc_timer(ShenandoahHeap::heap()->gc_timer()), _preserved_marks(new PreservedMarksSet(true)) {} @@ -124,7 +125,7 @@ void ShenandoahFullGC::op_full(GCCause::Cause cause) { } // Regardless if progress was made, we record that we completed a "successful" full GC. - heap->global_generation()->heuristics()->record_success_full(); + _generation->heuristics()->record_success_full(); heap->shenandoah_policy()->record_success_full(); { @@ -141,7 +142,7 @@ void ShenandoahFullGC::do_it(GCCause::Cause gc_cause) { } if (ShenandoahVerify) { - heap->verifier()->verify_before_fullgc(); + heap->verifier()->verify_before_fullgc(_generation); } if (VerifyBeforeGC) { @@ -194,7 +195,7 @@ void ShenandoahFullGC::do_it(GCCause::Cause gc_cause) { } // d. Abandon reference discovery and clear all discovered references. - ShenandoahReferenceProcessor* rp = heap->global_generation()->ref_processor(); + ShenandoahReferenceProcessor* rp = _generation->ref_processor(); rp->abandon_partial_discovery(); // e. Sync pinned region status from the CP marks @@ -273,7 +274,7 @@ void ShenandoahFullGC::do_it(GCCause::Cause gc_cause) { heap->set_full_gc_in_progress(false); if (ShenandoahVerify) { - heap->verifier()->verify_after_fullgc(); + heap->verifier()->verify_after_fullgc(_generation); } if (VerifyAfterGC) { @@ -292,19 +293,19 @@ void ShenandoahFullGC::phase1_mark_heap() { ShenandoahHeap* heap = ShenandoahHeap::heap(); - heap->global_generation()->reset_mark_bitmap(); + _generation->reset_mark_bitmap(); assert(heap->marking_context()->is_bitmap_clear(), "sanity"); - assert(!heap->global_generation()->is_mark_complete(), "sanity"); + assert(!_generation->is_mark_complete(), "sanity"); - heap->set_unload_classes(heap->global_generation()->heuristics()->can_unload_classes()); + heap->set_unload_classes(_generation->heuristics()->can_unload_classes()); - ShenandoahReferenceProcessor* rp = heap->global_generation()->ref_processor(); + ShenandoahReferenceProcessor* rp = _generation->ref_processor(); // enable ("weak") refs discovery rp->set_soft_reference_policy(true); // forcefully purge all soft references - ShenandoahSTWMark mark(heap->global_generation(), true /*full_gc*/); + ShenandoahSTWMark mark(_generation, true /*full_gc*/); mark.mark(); - heap->parallel_cleaning(true /* full_gc */); + heap->parallel_cleaning(_generation, true /* full_gc */); if (ShenandoahHeap::heap()->mode()->is_generational()) { ShenandoahGenerationalFullGC::log_live_in_old(heap); @@ -350,10 +351,12 @@ public: return _empty_regions_pos; } - void do_object(oop p) { + void do_object(oop p) override { + shenandoah_assert_mark_complete(cast_from_oop(p)); assert(_from_region != nullptr, "must set before work"); - assert(_heap->gc_generation()->complete_marking_context()->is_marked(p), "must be marked"); - assert(!_heap->gc_generation()->complete_marking_context()->allocated_after_mark_start(p), "must be truly marked"); + assert(_heap->global_generation()->is_mark_complete(), "marking must be finished"); + assert(_heap->marking_context()->is_marked(p), "must be marked"); + assert(!_heap->marking_context()->allocated_after_mark_start(p), "must be truly marked"); size_t obj_size = p->size(); if (_compact_point + obj_size > _to_region->end()) { @@ -523,12 +526,8 @@ void ShenandoahFullGC::calculate_target_humongous_objects() { } class ShenandoahEnsureHeapActiveClosure: public ShenandoahHeapRegionClosure { -private: - ShenandoahHeap* const _heap; - public: - ShenandoahEnsureHeapActiveClosure() : _heap(ShenandoahHeap::heap()) {} - void heap_region_do(ShenandoahHeapRegion* r) { + void heap_region_do(ShenandoahHeapRegion* r) override { if (r->is_trash()) { r->try_recycle_under_lock(); } @@ -760,7 +759,6 @@ void ShenandoahFullGC::phase2_calculate_target_addresses(ShenandoahHeapRegionSet class ShenandoahAdjustPointersClosure : public MetadataVisitingOopIterateClosure { private: - ShenandoahHeap* const _heap; ShenandoahMarkingContext* const _ctx; template @@ -778,8 +776,7 @@ private: public: ShenandoahAdjustPointersClosure() : - _heap(ShenandoahHeap::heap()), - _ctx(ShenandoahHeap::heap()->gc_generation()->complete_marking_context()) {} + _ctx(ShenandoahHeap::heap()->global_generation()->complete_marking_context()) {} void do_oop(oop* p) { do_oop_work(p); } void do_oop(narrowOop* p) { do_oop_work(p); } @@ -789,15 +786,12 @@ public: class ShenandoahAdjustPointersObjectClosure : public ObjectClosure { private: - ShenandoahHeap* const _heap; ShenandoahAdjustPointersClosure _cl; public: - ShenandoahAdjustPointersObjectClosure() : - _heap(ShenandoahHeap::heap()) { - } - void do_object(oop p) { - assert(_heap->gc_generation()->complete_marking_context()->is_marked(p), "must be marked"); + void do_object(oop p) override { + assert(ShenandoahHeap::heap()->global_generation()->is_mark_complete(), "marking must be complete"); + assert(ShenandoahHeap::heap()->marking_context()->is_marked(p), "must be marked"); p->oop_iterate(&_cl); } }; @@ -813,7 +807,7 @@ public: _heap(ShenandoahHeap::heap()) { } - void work(uint worker_id) { + void work(uint worker_id) override { ShenandoahParallelWorkerSession worker_session(worker_id); ShenandoahAdjustPointersObjectClosure obj_cl; ShenandoahHeapRegion* r = _regions.next(); @@ -839,7 +833,7 @@ public: _rp(rp), _preserved_marks(preserved_marks) {} - void work(uint worker_id) { + void work(uint worker_id) override { ShenandoahParallelWorkerSession worker_session(worker_id); ShenandoahAdjustPointersClosure cl; _rp->roots_do(worker_id, &cl); @@ -873,15 +867,15 @@ void ShenandoahFullGC::phase3_update_references() { class ShenandoahCompactObjectsClosure : public ObjectClosure { private: - ShenandoahHeap* const _heap; - uint const _worker_id; + uint const _worker_id; public: - ShenandoahCompactObjectsClosure(uint worker_id) : - _heap(ShenandoahHeap::heap()), _worker_id(worker_id) {} + explicit ShenandoahCompactObjectsClosure(uint worker_id) : + _worker_id(worker_id) {} - void do_object(oop p) { - assert(_heap->gc_generation()->complete_marking_context()->is_marked(p), "must be marked"); + void do_object(oop p) override { + assert(ShenandoahHeap::heap()->global_generation()->is_mark_complete(), "marking must be finished"); + assert(ShenandoahHeap::heap()->marking_context()->is_marked(p), "must be marked"); size_t size = p->size(); if (FullGCForwarding::is_forwarded(p)) { HeapWord* compact_from = cast_from_oop(p); @@ -908,7 +902,7 @@ public: _worker_slices(worker_slices) { } - void work(uint worker_id) { + void work(uint worker_id) override { ShenandoahParallelWorkerSession worker_session(worker_id); ShenandoahHeapRegionSetIterator slice(_worker_slices[worker_id]); @@ -945,7 +939,7 @@ public: _heap->free_set()->clear(); } - void heap_region_do(ShenandoahHeapRegion* r) { + void heap_region_do(ShenandoahHeapRegion* r) override { assert (!r->is_cset(), "cset regions should have been demoted already"); // Need to reset the complete-top-at-mark-start pointer here because @@ -954,7 +948,7 @@ public: // NOTE: See blurb at ShenandoahMCResetCompleteBitmapTask on why we need to skip // pinned regions. if (!r->is_pinned()) { - _heap->gc_generation()->complete_marking_context()->reset_top_at_mark_start(r); + _heap->marking_context()->reset_top_at_mark_start(r); } size_t live = r->used(); @@ -1079,7 +1073,7 @@ void ShenandoahFullGC::compact_humongous_objects() { // we need to remain able to walk pinned regions. // Since pinned region do not move and don't get compacted, we will get holes with // unreachable objects in them (which may have pointers to unloaded Klasses and thus -// cannot be iterated over using oop->size(). The only way to safely iterate over those is using +// cannot be iterated over using oop->size()). The only way to safely iterate over those is using // a valid marking bitmap and valid TAMS pointer. This class only resets marking // bitmaps for un-pinned regions, and later we only reset TAMS for unpinned regions. class ShenandoahMCResetCompleteBitmapTask : public WorkerTask { @@ -1091,11 +1085,12 @@ public: WorkerTask("Shenandoah Reset Bitmap") { } - void work(uint worker_id) { + void work(uint worker_id) override { ShenandoahParallelWorkerSession worker_session(worker_id); ShenandoahHeapRegion* region = _regions.next(); ShenandoahHeap* heap = ShenandoahHeap::heap(); - ShenandoahMarkingContext* const ctx = heap->gc_generation()->complete_marking_context(); + ShenandoahMarkingContext* const ctx = heap->marking_context(); + assert(heap->global_generation()->is_mark_complete(), "Marking must be complete"); while (region != nullptr) { if (heap->is_bitmap_slice_committed(region) && !region->is_pinned() && region->has_live()) { ctx->clear_bitmap(region); @@ -1163,7 +1158,7 @@ ShenandoahGenerationalHeap::TransferResult ShenandoahFullGC::phase5_epilog() { heap->free_set()->finish_rebuild(young_cset_regions, old_cset_regions, num_old); // Set mark incomplete because the marking bitmaps have been reset except pinned regions. - heap->global_generation()->set_mark_incomplete(); + _generation->set_mark_incomplete(); heap->clear_cancelled_gc(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.hpp index b0b8c7bf0c5..8b8244f2ce3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.hpp @@ -68,7 +68,7 @@ private: public: ShenandoahFullGC(); ~ShenandoahFullGC(); - bool collect(GCCause::Cause cause); + bool collect(GCCause::Cause cause) override; private: // GC entries diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGC.hpp index f08bdce0a20..7182665f2e3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGC.hpp @@ -44,6 +44,8 @@ * Full GC --------> (finish) */ +class ShenandoahGeneration; + class ShenandoahGC : public StackObj { public: // Fail point from concurrent GC @@ -57,12 +59,17 @@ public: _DEGENERATED_LIMIT }; + explicit ShenandoahGC(ShenandoahGeneration* generation) : _generation(generation) {} + // Returns false if the collection was cancelled, true otherwise. virtual bool collect(GCCause::Cause cause) = 0; static const char* degen_point_to_string(ShenandoahDegenPoint point); + ShenandoahGeneration* generation() const { return _generation; } protected: static void update_roots(bool full_gc); + + ShenandoahGeneration* _generation; }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHGC_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp index e6597b3c1e4..d2e25176c1f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp @@ -142,6 +142,11 @@ private: 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); @@ -198,7 +203,7 @@ private: bool is_bitmap_clear(); // We need to track the status of marking for different generations. - bool is_mark_complete() { return _is_marking_complete.is_set(); } + bool is_mark_complete() const { return _is_marking_complete.is_set(); } virtual void set_mark_complete(); virtual void set_mark_incomplete(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp index 971129beea8..ccabdb7b9da 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp @@ -50,10 +50,12 @@ public: }; ShenandoahGenerationalEvacuationTask::ShenandoahGenerationalEvacuationTask(ShenandoahGenerationalHeap* heap, + ShenandoahGeneration* generation, ShenandoahRegionIterator* iterator, bool concurrent, bool only_promote_regions) : WorkerTask("Shenandoah Evacuation"), _heap(heap), + _generation(generation), _regions(iterator), _concurrent(concurrent), _only_promote_regions(only_promote_regions) @@ -169,13 +171,12 @@ void ShenandoahGenerationalEvacuationTask::maybe_promote_region(ShenandoahHeapRe // We identify the entirety of the region as DIRTY to force the next remembered set scan to identify the "interesting pointers" // contained herein. void ShenandoahGenerationalEvacuationTask::promote_in_place(ShenandoahHeapRegion* region) { - assert(!_heap->gc_generation()->is_old(), "Sanity check"); + assert(!_generation->is_old(), "Sanity check"); ShenandoahMarkingContext* const marking_context = _heap->young_generation()->complete_marking_context(); HeapWord* const tams = marking_context->top_at_mark_start(region); { const size_t old_garbage_threshold = (ShenandoahHeapRegion::region_size_bytes() * ShenandoahOldGarbageThreshold) / 100; - shenandoah_assert_generations_reconciled(); assert(!_heap->is_concurrent_old_mark_in_progress(), "Cannot promote in place during old marking"); assert(region->garbage_before_padded_for_promote() < old_garbage_threshold, "Region %zu has too much garbage for promotion", region->index()); assert(region->is_young(), "Only young regions can be promoted"); @@ -259,8 +260,7 @@ void ShenandoahGenerationalEvacuationTask::promote_in_place(ShenandoahHeapRegion void ShenandoahGenerationalEvacuationTask::promote_humongous(ShenandoahHeapRegion* region) { ShenandoahMarkingContext* marking_context = _heap->marking_context(); oop obj = cast_to_oop(region->bottom()); - assert(_heap->gc_generation()->is_mark_complete(), "sanity"); - shenandoah_assert_generations_reconciled(); + assert(_generation->is_mark_complete(), "sanity"); assert(region->is_young(), "Only young regions can be promoted"); assert(region->is_humongous_start(), "Should not promote humongous continuation in isolation"); assert(_heap->is_tenurable(region), "Only promote regions that are sufficiently aged"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp index 0c402d6c90a..de47184ffff 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp @@ -36,12 +36,14 @@ class ShenandoahRegionIterator; class ShenandoahGenerationalEvacuationTask : public WorkerTask { private: ShenandoahGenerationalHeap* const _heap; + ShenandoahGeneration* const _generation; ShenandoahRegionIterator* _regions; bool _concurrent; bool _only_promote_regions; public: ShenandoahGenerationalEvacuationTask(ShenandoahGenerationalHeap* sh, + ShenandoahGeneration* generation, ShenandoahRegionIterator* iterator, bool concurrent, bool only_promote_regions); void work(uint worker_id) override; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp index c4a7408e032..8d8091472fc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp @@ -53,8 +53,7 @@ void assert_usage_not_more_than_regions_used(ShenandoahGeneration* generation) { void ShenandoahGenerationalFullGC::prepare() { auto heap = ShenandoahGenerationalHeap::heap(); // Since we may arrive here from degenerated GC failure of either young or old, establish generation as GLOBAL. - heap->set_gc_generation(heap->global_generation()); - heap->set_active_generation(); + heap->set_active_generation(heap->global_generation()); // No need for old_gen->increase_used() as this was done when plabs were allocated. heap->reset_generation_reserves(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index 34f217ada25..3db810a9d9d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -178,15 +178,15 @@ bool ShenandoahGenerationalHeap::requires_barriers(stackChunkOop obj) const { return false; } -void ShenandoahGenerationalHeap::evacuate_collection_set(bool concurrent) { +void ShenandoahGenerationalHeap::evacuate_collection_set(ShenandoahGeneration* generation, bool concurrent) { ShenandoahRegionIterator regions; - ShenandoahGenerationalEvacuationTask task(this, ®ions, concurrent, false /* only promote regions */); + ShenandoahGenerationalEvacuationTask task(this, generation, ®ions, concurrent, false /* only promote regions */); workers()->run_task(&task); } -void ShenandoahGenerationalHeap::promote_regions_in_place(bool concurrent) { +void ShenandoahGenerationalHeap::promote_regions_in_place(ShenandoahGeneration* generation, bool concurrent) { ShenandoahRegionIterator regions; - ShenandoahGenerationalEvacuationTask task(this, ®ions, concurrent, true /* only promote regions */); + ShenandoahGenerationalEvacuationTask task(this, generation, ®ions, concurrent, true /* only promote regions */); workers()->run_task(&task); } @@ -757,23 +757,27 @@ void ShenandoahGenerationalHeap::coalesce_and_fill_old_regions(bool concurrent) template class ShenandoahGenerationalUpdateHeapRefsTask : public WorkerTask { private: + // For update refs, _generation will be young or global. Mixed collections use the young generation. + ShenandoahGeneration* _generation; ShenandoahGenerationalHeap* _heap; ShenandoahRegionIterator* _regions; ShenandoahRegionChunkIterator* _work_chunks; public: - explicit ShenandoahGenerationalUpdateHeapRefsTask(ShenandoahRegionIterator* regions, - ShenandoahRegionChunkIterator* work_chunks) : + ShenandoahGenerationalUpdateHeapRefsTask(ShenandoahGeneration* generation, + ShenandoahRegionIterator* regions, + ShenandoahRegionChunkIterator* work_chunks) : WorkerTask("Shenandoah Update References"), + _generation(generation), _heap(ShenandoahGenerationalHeap::heap()), _regions(regions), _work_chunks(work_chunks) { - bool old_bitmap_stable = _heap->old_generation()->is_mark_complete(); + const bool old_bitmap_stable = _heap->old_generation()->is_mark_complete(); log_debug(gc, remset)("Update refs, scan remembered set using bitmap: %s", BOOL_TO_STR(old_bitmap_stable)); } - void work(uint worker_id) { + void work(uint worker_id) override { if (CONCURRENT) { ShenandoahConcurrentWorkerSession worker_session(worker_id); ShenandoahSuspendibleThreadSetJoiner stsj; @@ -803,10 +807,8 @@ private: // If !CONCURRENT, there's no value in expanding Mutator free set ShenandoahHeapRegion* r = _regions->next(); - // We update references for global, old, and young collections. - ShenandoahGeneration* const gc_generation = _heap->gc_generation(); - shenandoah_assert_generations_reconciled(); - assert(gc_generation->is_mark_complete(), "Expected complete marking"); + // We update references for global, mixed, and young collections. + assert(_generation->is_mark_complete(), "Expected complete marking"); ShenandoahMarkingContext* const ctx = _heap->marking_context(); bool is_mixed = _heap->collection_set()->has_old_regions(); while (r != nullptr) { @@ -818,7 +820,7 @@ private: if (r->is_young()) { _heap->marked_object_oop_iterate(r, &cl, update_watermark); } else if (r->is_old()) { - if (gc_generation->is_global()) { + if (_generation->is_global()) { _heap->marked_object_oop_iterate(r, &cl, update_watermark); } @@ -847,7 +849,7 @@ private: r = _regions->next(); } - if (!gc_generation->is_global()) { + if (_generation->is_young()) { // Since this is generational and not GLOBAL, we have to process the remembered set. There's no remembered // set processing if not in generational mode or if GLOBAL mode. @@ -961,15 +963,15 @@ private: } }; -void ShenandoahGenerationalHeap::update_heap_references(bool concurrent) { +void ShenandoahGenerationalHeap::update_heap_references(ShenandoahGeneration* generation, bool concurrent) { assert(!is_full_gc_in_progress(), "Only for concurrent and degenerated GC"); const uint nworkers = workers()->active_workers(); ShenandoahRegionChunkIterator work_list(nworkers); if (concurrent) { - ShenandoahGenerationalUpdateHeapRefsTask task(&_update_refs_iterator, &work_list); + ShenandoahGenerationalUpdateHeapRefsTask task(generation, &_update_refs_iterator, &work_list); workers()->run_task(&task); } else { - ShenandoahGenerationalUpdateHeapRefsTask task(&_update_refs_iterator, &work_list); + ShenandoahGenerationalUpdateHeapRefsTask task(generation, &_update_refs_iterator, &work_list); workers()->run_task(&task); } @@ -1044,7 +1046,7 @@ public: void ShenandoahGenerationalHeap::final_update_refs_update_region_states() { ShenandoahSynchronizePinnedRegionStates pins; - ShenandoahUpdateRegionAges ages(active_generation()->complete_marking_context()); + ShenandoahUpdateRegionAges ages(marking_context()); auto cl = ShenandoahCompositeRegionClosure::of(pins, ages); parallel_heap_region_iterate(&cl); } @@ -1088,6 +1090,10 @@ void ShenandoahGenerationalHeap::complete_concurrent_cycle() { entry_global_coalesce_and_fill(); } + log_info(gc, cset)("Concurrent cycle complete, promotions reserved: %zu, promotions expended: %zu, failed count: %zu, failed bytes: %zu", + old_generation()->get_promoted_reserve(), old_generation()->get_promoted_expended(), + old_generation()->get_promotion_failed_count(), old_generation()->get_promotion_failed_words() * HeapWordSize); + TransferResult result; { ShenandoahHeapLocker locker(lock()); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp index 6960562b31d..4c8c9fe0118 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp @@ -25,7 +25,6 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALHEAP #define SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALHEAP -#include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" #include "memory/universe.hpp" #include "utilities/checkedCast.hpp" @@ -44,13 +43,13 @@ public: void initialize_heuristics() override; static ShenandoahGenerationalHeap* heap() { - shenandoah_assert_generational(); + assert(ShenandoahCardBarrier, "Should have card barrier to use genenrational heap"); CollectedHeap* heap = Universe::heap(); return cast(heap); } static ShenandoahGenerationalHeap* cast(CollectedHeap* heap) { - shenandoah_assert_generational(); + assert(ShenandoahCardBarrier, "Should have card barrier to use genenrational heap"); return checked_cast(heap); } @@ -88,8 +87,11 @@ public: oop evacuate_object(oop p, Thread* thread) override; oop try_evacuate_object(oop p, Thread* thread, ShenandoahHeapRegion* from_region, ShenandoahAffiliation target_gen); - void evacuate_collection_set(bool concurrent) override; - void promote_regions_in_place(bool concurrent); + + // In the generational mode, we will use these two functions for young, mixed, and global collections. + // For young and mixed, the generation argument will be the young generation, otherwise it will be the global generation. + void evacuate_collection_set(ShenandoahGeneration* generation, bool concurrent) override; + void promote_regions_in_place(ShenandoahGeneration* generation, bool concurrent); size_t plab_min_size() const { return _min_plab_size; } size_t plab_max_size() const { return _max_plab_size; } @@ -99,7 +101,9 @@ public: // ---------- Update References // - void update_heap_references(bool concurrent) override; + // In the generational mode, we will use this function for young, mixed, and global collections. + // For young and mixed, the generation argument will be the young generation, otherwise it will be the global generation. + void update_heap_references(ShenandoahGeneration* generation, bool concurrent) override; void final_update_refs_update_region_states() override; private: diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index b2fd32d2fd0..344811449ab 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -248,6 +248,13 @@ jint ShenandoahHeap::initialize() { // Now we know the number of regions and heap sizes, initialize the heuristics. initialize_heuristics(); + // If ShenandoahCardBarrier is enabled but it's not generational mode + // it means we're under passive mode and we have to initialize old gen + // for the purpose of having card table. + if (ShenandoahCardBarrier && !(mode()->is_generational())) { + _old_generation = new ShenandoahOldGeneration(max_workers(), max_capacity()); + } + assert(_heap_region.byte_size() == heap_rs.size(), "Need to know reserved size for card table"); // @@ -529,7 +536,6 @@ void ShenandoahHeap::initialize_heuristics() { ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : CollectedHeap(), - _gc_generation(nullptr), _active_generation(nullptr), _initial_size(0), _committed(0), @@ -1257,7 +1263,8 @@ private: ShenandoahGCStatePropagatorHandshakeClosure _propagator; }; -void ShenandoahHeap::evacuate_collection_set(bool concurrent) { +void ShenandoahHeap::evacuate_collection_set(ShenandoahGeneration* generation, bool concurrent) { + assert(generation->is_global(), "Only global generation expected here"); ShenandoahEvacuationTask task(this, _collection_set, concurrent); workers()->run_task(&task); } @@ -1576,8 +1583,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 +1594,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 { @@ -1656,17 +1666,11 @@ void ShenandoahHeap::print_tracing_info() const { } } -void ShenandoahHeap::set_gc_generation(ShenandoahGeneration* generation) { - shenandoah_assert_control_or_vm_thread_at_safepoint(); - _gc_generation = generation; -} - // Active generation may only be set by the VM thread at a safepoint. -void ShenandoahHeap::set_active_generation() { +void ShenandoahHeap::set_active_generation(ShenandoahGeneration* generation) { assert(Thread::current()->is_VM_thread(), "Only the VM Thread"); assert(SafepointSynchronize::is_at_safepoint(), "Only at a safepoint!"); - assert(_gc_generation != nullptr, "Will set _active_generation to nullptr"); - _active_generation = _gc_generation; + _active_generation = generation; } void ShenandoahHeap::on_cycle_start(GCCause::Cause cause, ShenandoahGeneration* generation) { @@ -1675,17 +1679,14 @@ void ShenandoahHeap::on_cycle_start(GCCause::Cause cause, ShenandoahGeneration* const GCCause::Cause current = gc_cause(); assert(current == GCCause::_no_gc, "Over-writing cause: %s, with: %s", GCCause::to_string(current), GCCause::to_string(cause)); - assert(_gc_generation == nullptr, "Over-writing _gc_generation"); set_gc_cause(cause); - set_gc_generation(generation); generation->heuristics()->record_cycle_start(); } void ShenandoahHeap::on_cycle_end(ShenandoahGeneration* generation) { assert(gc_cause() != GCCause::_no_gc, "cause wasn't set"); - assert(_gc_generation != nullptr, "_gc_generation wasn't set"); generation->heuristics()->record_cycle_end(); if (mode()->is_generational() && generation->is_global()) { @@ -1694,14 +1695,13 @@ void ShenandoahHeap::on_cycle_end(ShenandoahGeneration* generation) { old_generation()->heuristics()->record_cycle_end(); } - set_gc_generation(nullptr); set_gc_cause(GCCause::_no_gc); } void ShenandoahHeap::verify(VerifyOption vo) { if (ShenandoahSafepoint::is_at_shenandoah_safepoint()) { if (ShenandoahVerify) { - verifier()->verify_generic(vo); + verifier()->verify_generic(active_generation(), vo); } else { // TODO: Consider allocating verification bitmaps on demand, // and turn this on unconditionally. @@ -2061,14 +2061,13 @@ void ShenandoahHeap::do_class_unloading() { } } -void ShenandoahHeap::stw_weak_refs(bool full_gc) { +void ShenandoahHeap::stw_weak_refs(ShenandoahGeneration* generation, bool full_gc) { // Weak refs processing ShenandoahPhaseTimings::Phase phase = full_gc ? ShenandoahPhaseTimings::full_gc_weakrefs : ShenandoahPhaseTimings::degen_gc_weakrefs; ShenandoahTimingsTracker t(phase); ShenandoahGCWorkerPhase worker_phase(phase); - shenandoah_assert_generations_reconciled(); - gc_generation()->ref_processor()->process_references(phase, workers(), false /* concurrent */); + generation->ref_processor()->process_references(phase, workers(), false /* concurrent */); } void ShenandoahHeap::prepare_update_heap_references() { @@ -2309,13 +2308,13 @@ void ShenandoahHeap::stw_process_weak_roots(bool full_gc) { } } -void ShenandoahHeap::parallel_cleaning(bool full_gc) { +void ShenandoahHeap::parallel_cleaning(ShenandoahGeneration* generation, bool full_gc) { assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); assert(is_stw_gc_in_progress(), "Only for Degenerated and Full GC"); ShenandoahGCPhase phase(full_gc ? ShenandoahPhaseTimings::full_gc_purge : ShenandoahPhaseTimings::degen_gc_purge); - stw_weak_refs(full_gc); + stw_weak_refs(generation, full_gc); stw_process_weak_roots(full_gc); stw_unload_classes(full_gc); } @@ -2423,11 +2422,8 @@ void ShenandoahHeap::sync_pinned_region_status() { void ShenandoahHeap::assert_pinned_region_status() { for (size_t i = 0; i < num_regions(); i++) { ShenandoahHeapRegion* r = get_region(i); - shenandoah_assert_generations_reconciled(); - if (gc_generation()->contains(r)) { - assert((r->is_pinned() && r->pin_count() > 0) || (!r->is_pinned() && r->pin_count() == 0), - "Region %zu pinning status is inconsistent", i); - } + assert((r->is_pinned() && r->pin_count() > 0) || (!r->is_pinned() && r->pin_count() == 0), + "Region %zu pinning status is inconsistent", i); } } #endif @@ -2530,7 +2526,8 @@ private: } }; -void ShenandoahHeap::update_heap_references(bool concurrent) { +void ShenandoahHeap::update_heap_references(ShenandoahGeneration* generation, bool concurrent) { + assert(generation->is_global(), "Should only get global generation here"); assert(!is_full_gc_in_progress(), "Only for concurrent and degenerated GC"); if (concurrent) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 8bcb04e5766..eae975b9409 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -145,17 +145,10 @@ class ShenandoahHeap : public CollectedHeap { private: ShenandoahHeapLock _lock; - // Indicates the generation whose collection is in - // progress. Mutator threads aren't allowed to read - // this field. - ShenandoahGeneration* _gc_generation; - // This is set and cleared by only the VMThread - // at each STW pause (safepoint) to the value seen in - // _gc_generation. This allows the value to be always consistently + // at each STW pause (safepoint) to the value given to the VM operation. + // This allows the value to be always consistently // seen by all mutators as well as all GC worker threads. - // In that sense, it's a stable snapshot of _gc_generation that is - // updated at each STW pause associated with a ShenandoahVMOp. ShenandoahGeneration* _active_generation; protected: @@ -167,25 +160,13 @@ public: return &_lock; } - ShenandoahGeneration* gc_generation() const { - // We don't want this field read by a mutator thread - assert(!Thread::current()->is_Java_thread(), "Not allowed"); - // value of _gc_generation field, see above - return _gc_generation; - } - ShenandoahGeneration* active_generation() const { // value of _active_generation field, see above return _active_generation; } - // Set the _gc_generation field - void set_gc_generation(ShenandoahGeneration* generation); - - // Copy the value in the _gc_generation field into - // the _active_generation field: can only be called at - // a safepoint by the VMThread. - void set_active_generation(); + // Update the _active_generation field: can only be called at a safepoint by the VMThread. + void set_active_generation(ShenandoahGeneration* generation); ShenandoahHeuristics* heuristics(); @@ -482,7 +463,7 @@ private: // GC support // Evacuation - virtual void evacuate_collection_set(bool concurrent); + virtual void evacuate_collection_set(ShenandoahGeneration* generation, bool concurrent); // Concurrent root processing void prepare_concurrent_roots(); void finish_concurrent_roots(); @@ -497,7 +478,7 @@ private: // Turn off weak roots flag, purge old satb buffers in generational mode void concurrent_final_roots(HandshakeClosure* handshake_closure = nullptr); - virtual void update_heap_references(bool concurrent); + virtual void update_heap_references(ShenandoahGeneration* generation, bool concurrent); // Final update region states void update_heap_region_states(bool concurrent); virtual void final_update_refs_update_region_states(); @@ -549,7 +530,7 @@ public: } ShenandoahOldGeneration* old_generation() const { - assert(mode()->is_generational(), "Old generation requires generational mode"); + assert(ShenandoahCardBarrier, "Card mark barrier should be on"); return _old_generation; } @@ -605,12 +586,12 @@ public: bool unload_classes() const; // Perform STW class unloading and weak root cleaning - void parallel_cleaning(bool full_gc); + void parallel_cleaning(ShenandoahGeneration* generation, bool full_gc); private: void stw_unload_classes(bool full_gc); void stw_process_weak_roots(bool full_gc); - void stw_weak_refs(bool full_gc); + void stw_weak_refs(ShenandoahGeneration* generation, bool full_gc); inline void assert_lock_for_affiliation(ShenandoahAffiliation orig_affiliation, ShenandoahAffiliation new_affiliation); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp index df45a59433e..ca0f7460d54 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp @@ -315,9 +315,9 @@ void ShenandoahHeapRegion::make_trash_immediate() { // On this path, we know there are no marked objects in the region, // tell marking context about it to bypass bitmap resets. - assert(ShenandoahHeap::heap()->gc_generation()->is_mark_complete(), "Marking should be complete here."); - shenandoah_assert_generations_reconciled(); - ShenandoahHeap::heap()->marking_context()->reset_top_bitmap(this); + const ShenandoahHeap* heap = ShenandoahHeap::heap(); + assert(heap->generation_for(affiliation())->is_mark_complete(), "Marking should be complete here."); + heap->marking_context()->reset_top_bitmap(this); } void ShenandoahHeapRegion::make_empty() { @@ -461,9 +461,9 @@ bool ShenandoahHeapRegion::oop_coalesce_and_fill(bool cancellable) { ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); ShenandoahMarkingContext* marking_context = heap->marking_context(); - // Expect marking to be completed before these threads invoke this service. - assert(heap->gc_generation()->is_mark_complete(), "sanity"); - shenandoah_assert_generations_reconciled(); + // Expect marking to be completed for the old generation before we fill in unmarked objects + assert(heap->old_generation()->is_mark_complete(), "sanity"); + assert(is_old(), "Only need to coalesce and fill old regions"); // All objects above TAMS are considered live even though their mark bits will not be set. Note that young- // gen evacuations that interrupt a long-running old-gen concurrent mark may promote objects into old-gen diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp index 2a4149ee44d..a3c28e2c6d3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp @@ -55,10 +55,10 @@ ShenandoahMark::ShenandoahMark(ShenandoahGeneration* generation) : } template -void ShenandoahMark::mark_loop_prework(uint w, TaskTerminator *t, ShenandoahReferenceProcessor *rp, StringDedup::Requests* const req, bool update_refs) { +void ShenandoahMark::mark_loop_prework(uint w, TaskTerminator *t, StringDedup::Requests* const req, bool update_refs) { ShenandoahObjToScanQueue* q = get_queue(w); ShenandoahObjToScanQueue* old_q = get_old_queue(w); - + ShenandoahReferenceProcessor *rp = _generation->ref_processor(); ShenandoahHeap* const heap = ShenandoahHeap::heap(); ShenandoahLiveData* ld = heap->get_liveness_cache(w); @@ -78,22 +78,22 @@ void ShenandoahMark::mark_loop_prework(uint w, TaskTerminator *t, ShenandoahRefe } template -void ShenandoahMark::mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahReferenceProcessor *rp, - ShenandoahGenerationType generation, StringDedup::Requests* const req) { +void ShenandoahMark::mark_loop(uint worker_id, TaskTerminator* terminator, + ShenandoahGenerationType generation_type, StringDedup::Requests* const req) { bool update_refs = ShenandoahHeap::heap()->has_forwarded_objects(); - switch (generation) { + switch (generation_type) { case YOUNG: - mark_loop_prework(worker_id, terminator, rp, req, update_refs); + mark_loop_prework(worker_id, terminator, req, update_refs); break; case OLD: // Old generation collection only performs marking, it should not update references. - mark_loop_prework(worker_id, terminator, rp, req, false); + mark_loop_prework(worker_id, terminator, req, false); break; case GLOBAL: - mark_loop_prework(worker_id, terminator, rp, req, update_refs); + mark_loop_prework(worker_id, terminator, req, update_refs); break; case NON_GEN: - mark_loop_prework(worker_id, terminator, rp, req, update_refs); + mark_loop_prework(worker_id, terminator, req, update_refs); break; default: ShouldNotReachHere(); @@ -101,30 +101,30 @@ void ShenandoahMark::mark_loop(uint worker_id, TaskTerminator* terminator, Shena } } -void ShenandoahMark::mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahReferenceProcessor *rp, - ShenandoahGenerationType generation, bool cancellable, StringDedupMode dedup_mode, StringDedup::Requests* const req) { +void ShenandoahMark::mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahGenerationType generation_type, + bool cancellable, StringDedupMode dedup_mode, StringDedup::Requests* const req) { if (cancellable) { switch(dedup_mode) { case NO_DEDUP: - mark_loop(worker_id, terminator, rp, generation, req); + mark_loop(worker_id, terminator, generation_type, req); break; case ENQUEUE_DEDUP: - mark_loop(worker_id, terminator, rp, generation, req); + mark_loop(worker_id, terminator, generation_type, req); break; case ALWAYS_DEDUP: - mark_loop(worker_id, terminator, rp, generation, req); + mark_loop(worker_id, terminator, generation_type, req); break; } } else { switch(dedup_mode) { case NO_DEDUP: - mark_loop(worker_id, terminator, rp, generation, req); + mark_loop(worker_id, terminator, generation_type, req); break; case ENQUEUE_DEDUP: - mark_loop(worker_id, terminator, rp, generation, req); + mark_loop(worker_id, terminator, generation_type, req); break; case ALWAYS_DEDUP: - mark_loop(worker_id, terminator, rp, generation, req); + mark_loop(worker_id, terminator, generation_type, req); break; } } @@ -139,12 +139,8 @@ void ShenandoahMark::mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint w ShenandoahObjToScanQueue* q; ShenandoahMarkTask t; - // Do not use active_generation() : we must use the gc_generation() set by - // ShenandoahGCScope on the ControllerThread's stack; no safepoint may - // intervene to update active_generation, so we can't - // shenandoah_assert_generations_reconciled() here. - assert(heap->gc_generation()->type() == GENERATION, "Sanity: %d != %d", heap->gc_generation()->type(), GENERATION); - heap->gc_generation()->ref_processor()->set_mark_closure(worker_id, cl); + assert(_generation->type() == GENERATION, "Sanity: %d != %d", _generation->type(), GENERATION); + _generation->ref_processor()->set_mark_closure(worker_id, cl); /* * Process outstanding queues, if any. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp index 4aef14f2c9a..2fbb106f4d7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp @@ -41,7 +41,6 @@ enum StringDedupMode { }; class ShenandoahMarkingContext; -class ShenandoahReferenceProcessor; // Base class for mark // Mark class does not maintain states. Instead, mark states are @@ -72,7 +71,7 @@ public: inline ShenandoahObjToScanQueue* get_queue(uint index) const; inline ShenandoahObjToScanQueue* get_old_queue(uint index) const; - inline ShenandoahGeneration* generation() { return _generation; }; + ShenandoahGeneration* generation() const { return _generation; }; private: // ---------- Marking loop and tasks @@ -93,7 +92,7 @@ private: void mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint worker_id, TaskTerminator *t, StringDedup::Requests* const req); template - void mark_loop_prework(uint worker_id, TaskTerminator *terminator, ShenandoahReferenceProcessor *rp, StringDedup::Requests* const req, bool update_refs); + void mark_loop_prework(uint worker_id, TaskTerminator *terminator, StringDedup::Requests* const req, bool update_refs); template static bool in_generation(ShenandoahHeap* const heap, oop obj); @@ -109,11 +108,11 @@ private: inline void dedup_string(oop obj, StringDedup::Requests* const req); protected: template - void mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahReferenceProcessor *rp, - ShenandoahGenerationType generation, StringDedup::Requests* const req); + void mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahGenerationType generation_type, + StringDedup::Requests* const req); - void mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahReferenceProcessor *rp, - ShenandoahGenerationType generation, bool cancellable, StringDedupMode dedup_mode, StringDedup::Requests* const req); + void mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahGenerationType generation_type, + bool cancellable, StringDedupMode dedup_mode, StringDedup::Requests* const req); }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHMARK_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp index 1724fc2849f..3e9f3a490df 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp @@ -49,7 +49,7 @@ void ShenandoahOldGC::op_final_mark() { assert(!heap->has_forwarded_objects(), "No forwarded objects on this path"); if (ShenandoahVerify) { - heap->verifier()->verify_roots_no_forwarded(); + heap->verifier()->verify_roots_no_forwarded(_old_generation); } if (!heap->cancelled_gc()) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index ac3107eb396..095aa0079ae 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -204,6 +204,8 @@ ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues, size_t max_cap _promoted_expended(0), _promotion_potential(0), _pad_for_promote_in_place(0), + _promotion_failure_count(0), + _promotion_failure_words(0), _promotable_humongous_regions(0), _promotable_regular_regions(0), _is_parsable(true), @@ -240,7 +242,9 @@ void ShenandoahOldGeneration::augment_promoted_reserve(size_t increment) { void ShenandoahOldGeneration::reset_promoted_expended() { shenandoah_assert_heaplocked_or_safepoint(); - AtomicAccess::store(&_promoted_expended, (size_t) 0); + AtomicAccess::store(&_promoted_expended, static_cast(0)); + AtomicAccess::store(&_promotion_failure_count, static_cast(0)); + AtomicAccess::store(&_promotion_failure_words, static_cast(0)); } size_t ShenandoahOldGeneration::expend_promoted(size_t increment) { @@ -669,38 +673,42 @@ void ShenandoahOldGeneration::handle_failed_evacuation() { } void ShenandoahOldGeneration::handle_failed_promotion(Thread* thread, size_t size) { + AtomicAccess::inc(&_promotion_failure_count); + AtomicAccess::add(&_promotion_failure_words, size); + + LogTarget(Debug, gc, plab) lt; + LogStream ls(lt); + if (lt.is_enabled()) { + log_failed_promotion(ls, thread, size); + } +} + +void ShenandoahOldGeneration::log_failed_promotion(LogStream& ls, Thread* thread, size_t size) const { // We squelch excessive reports to reduce noise in logs. - const size_t MaxReportsPerEpoch = 4; + constexpr size_t MaxReportsPerEpoch = 4; static size_t last_report_epoch = 0; static size_t epoch_report_count = 0; - auto heap = ShenandoahGenerationalHeap::heap(); - - size_t promotion_reserve; - size_t promotion_expended; + const auto heap = ShenandoahGenerationalHeap::heap(); const size_t gc_id = heap->control_thread()->get_gc_id(); - if ((gc_id != last_report_epoch) || (epoch_report_count++ < MaxReportsPerEpoch)) { - { - // Promotion failures should be very rare. Invest in providing useful diagnostic info. - ShenandoahHeapLocker locker(heap->lock()); - promotion_reserve = get_promoted_reserve(); - promotion_expended = get_promoted_expended(); - } + // Promotion failures should be very rare. Invest in providing useful diagnostic info. PLAB* const plab = ShenandoahThreadLocalData::plab(thread); const size_t words_remaining = (plab == nullptr)? 0: plab->words_remaining(); const char* promote_enabled = ShenandoahThreadLocalData::allow_plab_promotions(thread)? "enabled": "disabled"; - log_info(gc, ergo)("Promotion failed, size %zu, has plab? %s, PLAB remaining: %zu" - ", plab promotions %s, promotion reserve: %zu, promotion expended: %zu" - ", old capacity: %zu, old_used: %zu, old unaffiliated regions: %zu", - size * HeapWordSize, plab == nullptr? "no": "yes", - words_remaining * HeapWordSize, promote_enabled, promotion_reserve, promotion_expended, - max_capacity(), used(), free_unaffiliated_regions()); + // Promoted reserve is only changed by vm or control thread. Promoted expended is always accessed atomically. + const size_t promotion_reserve = get_promoted_reserve(); + const size_t promotion_expended = get_promoted_expended(); - if ((gc_id == last_report_epoch) && (epoch_report_count >= MaxReportsPerEpoch)) { - log_debug(gc, ergo)("Squelching additional promotion failure reports for current epoch"); - } else if (gc_id != last_report_epoch) { + ls.print_cr("Promotion failed, size %zu, has plab? %s, PLAB remaining: %zu" + ", plab promotions %s, promotion reserve: %zu, promotion expended: %zu" + ", old capacity: %zu, old_used: %zu, old unaffiliated regions: %zu", + size * HeapWordSize, plab == nullptr? "no": "yes", + words_remaining * HeapWordSize, promote_enabled, promotion_reserve, promotion_expended, + max_capacity(), used(), free_unaffiliated_regions()); + + if (gc_id != last_report_epoch) { last_report_epoch = gc_id; epoch_report_count = 1; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index abc865c31cd..28d2a30493c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -32,6 +32,7 @@ #include "gc/shenandoah/shenandoahScanRemembered.hpp" #include "gc/shenandoah/shenandoahSharedVariables.hpp" +class LogStream; class ShenandoahHeapRegion; class ShenandoahHeapRegionClosure; class ShenandoahOldHeuristics; @@ -65,16 +66,21 @@ private: // remaining in a PLAB when it is retired. size_t _promoted_expended; - // Represents the quantity of live bytes we expect to promote in place during the next - // evacuation cycle. This value is used by the young heuristic to trigger mixed collections. + // Represents the quantity of live bytes we expect to promote during the next evacuation + // cycle. This value is used by the young heuristic to trigger mixed collections. // It is also used when computing the optimum size for the old generation. size_t _promotion_potential; // When a region is selected to be promoted in place, the remaining free memory is filled // in to prevent additional allocations (preventing premature promotion of newly allocated - // objects. This field records the total amount of padding used for such regions. + // objects). This field records the total amount of padding used for such regions. size_t _pad_for_promote_in_place; + // Keep track of the number and size of promotions that failed. Perhaps we should use this to increase + // the size of the old generation for the next collection cycle. + size_t _promotion_failure_count; + size_t _promotion_failure_words; + // During construction of the collection set, we keep track of regions that are eligible // for promotion in place. These fields track the count of those humongous and regular regions. // This data is used to force the evacuation phase even when the collection set is otherwise @@ -119,6 +125,10 @@ public: // This is used on the allocation path to gate promotions that would exceed the reserve size_t get_promoted_expended() const; + // Return the count and size (in words) of failed promotions since the last reset + size_t get_promotion_failed_count() const { return AtomicAccess::load(&_promotion_failure_count); } + size_t get_promotion_failed_words() const { return AtomicAccess::load(&_promotion_failure_words); } + // Test if there is enough memory reserved for this promotion bool can_promote(size_t requested_bytes) const { size_t promotion_avail = get_promoted_reserve(); @@ -137,9 +147,10 @@ public: // See description in field declaration void set_region_balance(ssize_t balance) { _region_balance = balance; } ssize_t get_region_balance() const { return _region_balance; } + // See description in field declaration - void set_promotion_potential(size_t val) { _promotion_potential = val; }; - size_t get_promotion_potential() const { return _promotion_potential; }; + void set_promotion_potential(size_t val) { _promotion_potential = val; } + size_t get_promotion_potential() const { return _promotion_potential; } // See description in field declaration void set_pad_for_promote_in_place(size_t pad) { _pad_for_promote_in_place = pad; } @@ -161,8 +172,9 @@ public: // This will signal the control thread to run a full GC instead of a futile degenerated gc void handle_failed_evacuation(); - // This logs that an evacuation to the old generation has failed + // Increment promotion failure counters, optionally log a more detailed message void handle_failed_promotion(Thread* thread, size_t size); + void log_failed_promotion(LogStream& ls, Thread* thread, size_t size) const; // A successful evacuation re-dirties the cards and registers the object with the remembered set void handle_evacuation(HeapWord* obj, size_t words, bool promotion); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp index 4ca6f2fdf49..f37329d1c44 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp @@ -329,25 +329,31 @@ bool ShenandoahReferenceProcessor::should_drop(oop reference, ReferenceType type return true; } + shenandoah_assert_mark_complete(raw_referent); ShenandoahHeap* heap = ShenandoahHeap::heap(); - // Check if the referent is still alive, in which case we should - // drop the reference. + // Check if the referent is still alive, in which case we should drop the reference. if (type == REF_PHANTOM) { - return heap->active_generation()->complete_marking_context()->is_marked(raw_referent); + return heap->marking_context()->is_marked(raw_referent); } else { - return heap->active_generation()->complete_marking_context()->is_marked_strong(raw_referent); + return heap->marking_context()->is_marked_strong(raw_referent); } } template void ShenandoahReferenceProcessor::make_inactive(oop reference, ReferenceType type) const { if (type == REF_FINAL) { +#ifdef ASSERT + auto referent = reference_referent_raw(reference); + auto heap = ShenandoahHeap::heap(); + shenandoah_assert_mark_complete(referent); + assert(reference_next(reference) == nullptr, "Already inactive"); + assert(heap->marking_context()->is_marked(referent), "only make inactive final refs with alive referents"); +#endif + // Don't clear referent. It is needed by the Finalizer thread to make the call // to finalize(). A FinalReference is instead made inactive by self-looping the // next field. An application can't call FinalReference.enqueue(), so there is // no race to worry about when setting the next field. - assert(reference_next(reference) == nullptr, "Already inactive"); - assert(ShenandoahHeap::heap()->active_generation()->complete_marking_context()->is_marked(reference_referent_raw(reference)), "only make inactive final refs with alive referents"); reference_set_next(reference, reference); } else { // Clear referent @@ -437,8 +443,12 @@ oop ShenandoahReferenceProcessor::drop(oop reference, ReferenceType type) { HeapWord* raw_referent = reference_referent_raw(reference); #ifdef ASSERT - assert(raw_referent == nullptr || ShenandoahHeap::heap()->active_generation()->complete_marking_context()->is_marked(raw_referent), - "only drop references with alive referents"); + if (raw_referent != nullptr) { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + ShenandoahHeapRegion* region = heap->heap_region_containing(raw_referent); + ShenandoahMarkingContext* ctx = heap->generation_for(region->affiliation())->complete_marking_context(); + assert(ctx->is_marked(raw_referent), "only drop references with alive referents"); + } #endif // Unlink and return next in list 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 @@ public: 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/shenandoahRootVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp index 11ff92cd9cc..23edc780e47 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp @@ -61,7 +61,7 @@ ShenandoahGCStateResetter::~ShenandoahGCStateResetter() { assert(_heap->gc_state() == _saved_gc_state, "Should be restored"); } -void ShenandoahRootVerifier::roots_do(OopIterateClosure* oops) { +void ShenandoahRootVerifier::roots_do(OopIterateClosure* oops, ShenandoahGeneration* generation) { ShenandoahGCStateResetter resetter; shenandoah_assert_safepoint(); @@ -75,9 +75,9 @@ void ShenandoahRootVerifier::roots_do(OopIterateClosure* oops) { OopStorageSet::storage(id)->oops_do(oops); } - ShenandoahHeap* heap = ShenandoahHeap::heap(); - if (heap->mode()->is_generational() && heap->active_generation()->is_young()) { + if (generation->is_young()) { shenandoah_assert_safepoint(); + shenandoah_assert_generational(); ShenandoahGenerationalHeap::heap()->old_generation()->card_scan()->roots_do(oops); } @@ -87,7 +87,7 @@ void ShenandoahRootVerifier::roots_do(OopIterateClosure* oops) { Threads::possibly_parallel_oops_do(true, oops, nullptr); } -void ShenandoahRootVerifier::strong_roots_do(OopIterateClosure* oops) { +void ShenandoahRootVerifier::strong_roots_do(OopIterateClosure* oops, ShenandoahGeneration* generation) { ShenandoahGCStateResetter resetter; shenandoah_assert_safepoint(); @@ -98,8 +98,8 @@ void ShenandoahRootVerifier::strong_roots_do(OopIterateClosure* oops) { OopStorageSet::storage(id)->oops_do(oops); } - ShenandoahHeap* heap = ShenandoahHeap::heap(); - if (heap->mode()->is_generational() && heap->active_generation()->is_young()) { + if (generation->is_young()) { + shenandoah_assert_generational(); ShenandoahGenerationalHeap::heap()->old_generation()->card_scan()->roots_do(oops); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp index 405c69c4160..1f3cb400465 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp @@ -43,8 +43,10 @@ public: class ShenandoahRootVerifier : public AllStatic { public: // Used to seed ShenandoahVerifier, do not honor root type filter - static void roots_do(OopIterateClosure* cl); - static void strong_roots_do(OopIterateClosure* cl); + // The generation parameter here may be young or global. If it is young, + // then the roots will include the remembered set. + static void roots_do(OopIterateClosure* cl, ShenandoahGeneration* generation); + static void strong_roots_do(OopIterateClosure* cl, ShenandoahGeneration* generation); }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHROOTVERIFIER_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp index 53391a3e224..117984a6d41 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp @@ -77,15 +77,13 @@ void ShenandoahSTWMark::mark() { ShenandoahCodeRoots::arm_nmethods_for_mark(); // Weak reference processing - assert(ShenandoahHeap::heap()->gc_generation() == _generation, "Marking unexpected generation"); ShenandoahReferenceProcessor* rp = _generation->ref_processor(); - shenandoah_assert_generations_reconciled(); rp->reset_thread_locals(); // Init mark, do not expect forwarded pointers in roots if (ShenandoahVerify) { assert(Thread::current()->is_VM_thread(), "Must be"); - heap->verifier()->verify_roots_no_forwarded(); + heap->verifier()->verify_roots_no_forwarded(_generation); } start_mark(); @@ -119,7 +117,6 @@ void ShenandoahSTWMark::mark() { } void ShenandoahSTWMark::mark_roots(uint worker_id) { - assert(ShenandoahHeap::heap()->gc_generation() == _generation, "Marking unexpected generation"); ShenandoahReferenceProcessor* rp = _generation->ref_processor(); auto queue = task_queues()->queue(worker_id); switch (_generation->type()) { @@ -148,14 +145,10 @@ void ShenandoahSTWMark::mark_roots(uint worker_id) { } void ShenandoahSTWMark::finish_mark(uint worker_id) { - assert(ShenandoahHeap::heap()->gc_generation() == _generation, "Marking unexpected generation"); ShenandoahPhaseTimings::Phase phase = _full_gc ? ShenandoahPhaseTimings::full_gc_mark : ShenandoahPhaseTimings::degen_gc_stw_mark; ShenandoahWorkerTimingsTracker timer(phase, ShenandoahPhaseTimings::ParallelMark, worker_id); - ShenandoahReferenceProcessor* rp = _generation->ref_processor(); - shenandoah_assert_generations_reconciled(); StringDedup::Requests requests; - mark_loop(worker_id, &_terminator, rp, - _generation->type(), false /* not cancellable */, + mark_loop(worker_id, &_terminator, _generation->type(), false /* not cancellable */, ShenandoahStringDedup::is_enabled() ? ALWAYS_DEDUP : NO_DEDUP, &requests); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp index 4a0215f15f1..af6cd6d39ab 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp @@ -123,6 +123,18 @@ void ShenandoahDirectCardMarkRememberedSet::mark_read_table_as_clean() { log_develop_debug(gc, barrier)("Cleaned read_table from " PTR_FORMAT " to " PTR_FORMAT, p2i(&(read_table[0])), p2i(end_bp)); } +void ShenandoahDirectCardMarkRememberedSet::mark_write_table_as_clean() { + CardValue* write_table = _card_table->write_byte_map(); + CardValue* bp = &(write_table)[0]; + CardValue* end_bp = &(write_table)[_card_table->last_valid_index()]; + + while (bp <= end_bp) { + *bp++ = CardTable::clean_card_val(); + } + + log_develop_debug(gc, barrier)("Cleaned write_table from " PTR_FORMAT " to " PTR_FORMAT, p2i(&(write_table[0])), p2i(end_bp)); +} + // No lock required because arguments align with card boundaries. void ShenandoahCardCluster::reset_object_range(HeapWord* from, HeapWord* to) { assert(((((unsigned long long) from) & (CardTable::card_size() - 1)) == 0) && @@ -330,6 +342,10 @@ void ShenandoahScanRemembered::mark_read_table_as_clean() { _rs->mark_read_table_as_clean(); } +void ShenandoahScanRemembered::mark_write_table_as_clean() { + _rs->mark_write_table_as_clean(); +} + void ShenandoahScanRemembered::reset_object_range(HeapWord* from, HeapWord* to) { _scc->reset_object_range(from, to); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp index 5df34159c0f..d04a768530f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp @@ -244,6 +244,8 @@ public: // See comment in ShenandoahScanRemembered inline void mark_read_table_as_clean(); + inline void mark_write_table_as_clean(); + // Merge any dirty values from write table into the read table, while leaving // the write table unchanged. void merge_write_table(HeapWord* start, size_t word_count); @@ -769,6 +771,8 @@ public: // the "write" table. void mark_read_table_as_clean(); + void mark_write_table_as_clean(); + // Swaps read and write card tables pointers in effect setting a clean card // table for the next GC cycle. void swap_card_tables() { _rs->swap_card_tables(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp index 0137492f06f..6b45842f781 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp @@ -50,16 +50,14 @@ void VM_ShenandoahOperation::doit_epilogue() { void VM_ShenandoahOperation::log_active_generation(const char* prefix) { ShenandoahGeneration* agen = ShenandoahHeap::heap()->active_generation(); - ShenandoahGeneration* ggen = ShenandoahHeap::heap()->gc_generation(); - log_debug(gc, heap)("%s: active_generation is %s, gc_generation is %s", prefix, - agen == nullptr ? "nullptr" : shenandoah_generation_name(agen->type()), - ggen == nullptr ? "nullptr" : shenandoah_generation_name(ggen->type())); + log_debug(gc, heap)("%s: active_generation is %s", prefix, + agen == nullptr ? "nullptr" : shenandoah_generation_name(agen->type())); } void VM_ShenandoahOperation::set_active_generation() { if (evaluate_at_safepoint()) { assert(SafepointSynchronize::is_at_safepoint(), "Error??"); - ShenandoahHeap::heap()->set_active_generation(); + ShenandoahHeap::heap()->set_active_generation(_generation); } } @@ -77,42 +75,70 @@ void VM_ShenandoahReferenceOperation::doit_epilogue() { Heap_lock->unlock(); } +VM_ShenandoahInitMark::VM_ShenandoahInitMark(ShenandoahConcurrentGC* gc) + : VM_ShenandoahOperation(gc->generation()), _gc(gc) { +} + void VM_ShenandoahInitMark::doit() { ShenandoahGCPauseMark mark(_gc_id, "Init Mark", SvcGCMarker::CONCURRENT); set_active_generation(); _gc->entry_init_mark(); } +VM_ShenandoahFinalMarkStartEvac::VM_ShenandoahFinalMarkStartEvac(ShenandoahConcurrentGC* gc) + : VM_ShenandoahOperation(gc->generation()), _gc(gc) { +} + void VM_ShenandoahFinalMarkStartEvac::doit() { ShenandoahGCPauseMark mark(_gc_id, "Final Mark", SvcGCMarker::CONCURRENT); set_active_generation(); _gc->entry_final_mark(); } +VM_ShenandoahFullGC::VM_ShenandoahFullGC(GCCause::Cause gc_cause, ShenandoahFullGC* full_gc) + : VM_ShenandoahReferenceOperation(full_gc->generation()), _gc_cause(gc_cause), _full_gc(full_gc) { +} + void VM_ShenandoahFullGC::doit() { ShenandoahGCPauseMark mark(_gc_id, "Full GC", SvcGCMarker::FULL); set_active_generation(); _full_gc->entry_full(_gc_cause); } +VM_ShenandoahDegeneratedGC::VM_ShenandoahDegeneratedGC(ShenandoahDegenGC* gc) + : VM_ShenandoahReferenceOperation(gc->generation()), _gc(gc) { +} + void VM_ShenandoahDegeneratedGC::doit() { ShenandoahGCPauseMark mark(_gc_id, "Degenerated GC", SvcGCMarker::CONCURRENT); set_active_generation(); _gc->entry_degenerated(); } +VM_ShenandoahInitUpdateRefs::VM_ShenandoahInitUpdateRefs(ShenandoahConcurrentGC* gc) + : VM_ShenandoahOperation(gc->generation()), _gc(gc) { +} + void VM_ShenandoahInitUpdateRefs::doit() { ShenandoahGCPauseMark mark(_gc_id, "Init Update Refs", SvcGCMarker::CONCURRENT); set_active_generation(); _gc->entry_init_update_refs(); } +VM_ShenandoahFinalUpdateRefs::VM_ShenandoahFinalUpdateRefs(ShenandoahConcurrentGC* gc) + : VM_ShenandoahOperation(gc->generation()), _gc(gc) { +} + void VM_ShenandoahFinalUpdateRefs::doit() { ShenandoahGCPauseMark mark(_gc_id, "Final Update Refs", SvcGCMarker::CONCURRENT); set_active_generation(); _gc->entry_final_update_refs(); } +VM_ShenandoahFinalRoots::VM_ShenandoahFinalRoots(ShenandoahConcurrentGC* gc) + : VM_ShenandoahOperation(gc->generation()), _gc(gc) { +} + void VM_ShenandoahFinalRoots::doit() { ShenandoahGCPauseMark mark(_gc_id, "Final Roots", SvcGCMarker::CONCURRENT); set_active_generation(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp index 291fadd1887..d565a3df22c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp @@ -46,10 +46,15 @@ class ShenandoahFullGC; class VM_ShenandoahOperation : public VM_Operation { protected: uint _gc_id; + ShenandoahGeneration* _generation; void set_active_generation(); public: - VM_ShenandoahOperation() : _gc_id(GCId::current()) {}; + explicit VM_ShenandoahOperation(ShenandoahGeneration* generation) + : _gc_id(GCId::current()) + , _generation(generation) { + } + bool skip_thread_oop_barriers() const override { return true; } void log_active_generation(const char* prefix); @@ -61,93 +66,74 @@ public: class VM_ShenandoahReferenceOperation : public VM_ShenandoahOperation { public: - VM_ShenandoahReferenceOperation() : VM_ShenandoahOperation() {}; + explicit VM_ShenandoahReferenceOperation(ShenandoahGeneration* generation) + : VM_ShenandoahOperation(generation) {}; bool doit_prologue() override; void doit_epilogue() override; }; class VM_ShenandoahInitMark: public VM_ShenandoahOperation { -private: ShenandoahConcurrentGC* const _gc; public: - VM_ShenandoahInitMark(ShenandoahConcurrentGC* gc) : - VM_ShenandoahOperation(), - _gc(gc) {}; - VM_Operation::VMOp_Type type() const { return VMOp_ShenandoahInitMark; } - const char* name() const { return "Shenandoah Init Marking"; } - virtual void doit(); + explicit VM_ShenandoahInitMark(ShenandoahConcurrentGC* gc); + VM_Operation::VMOp_Type type() const override { return VMOp_ShenandoahInitMark; } + const char* name() const override { return "Shenandoah Init Marking"; } + void doit() override; }; class VM_ShenandoahFinalMarkStartEvac: public VM_ShenandoahOperation { -private: ShenandoahConcurrentGC* const _gc; public: - VM_ShenandoahFinalMarkStartEvac(ShenandoahConcurrentGC* gc) : - VM_ShenandoahOperation(), - _gc(gc) {}; - VM_Operation::VMOp_Type type() const { return VMOp_ShenandoahFinalMarkStartEvac; } - const char* name() const { return "Shenandoah Final Mark and Start Evacuation"; } - virtual void doit(); + explicit VM_ShenandoahFinalMarkStartEvac(ShenandoahConcurrentGC* gc); + VM_Operation::VMOp_Type type() const override { return VMOp_ShenandoahFinalMarkStartEvac; } + const char* name() const override { return "Shenandoah Final Mark and Start Evacuation"; } + void doit() override; }; class VM_ShenandoahDegeneratedGC: public VM_ShenandoahReferenceOperation { -private: ShenandoahDegenGC* const _gc; public: - VM_ShenandoahDegeneratedGC(ShenandoahDegenGC* gc) : - VM_ShenandoahReferenceOperation(), - _gc(gc) {}; - - VM_Operation::VMOp_Type type() const { return VMOp_ShenandoahDegeneratedGC; } - const char* name() const { return "Shenandoah Degenerated GC"; } - virtual void doit(); + explicit VM_ShenandoahDegeneratedGC(ShenandoahDegenGC* gc); + VM_Operation::VMOp_Type type() const override { return VMOp_ShenandoahDegeneratedGC; } + const char* name() const override { return "Shenandoah Degenerated GC"; } + void doit() override; }; class VM_ShenandoahFullGC : public VM_ShenandoahReferenceOperation { -private: GCCause::Cause _gc_cause; ShenandoahFullGC* const _full_gc; public: - VM_ShenandoahFullGC(GCCause::Cause gc_cause, ShenandoahFullGC* full_gc) : - VM_ShenandoahReferenceOperation(), - _gc_cause(gc_cause), - _full_gc(full_gc) {}; - VM_Operation::VMOp_Type type() const { return VMOp_ShenandoahFullGC; } - const char* name() const { return "Shenandoah Full GC"; } - virtual void doit(); + explicit VM_ShenandoahFullGC(GCCause::Cause gc_cause, ShenandoahFullGC* full_gc); + VM_Operation::VMOp_Type type() const override { return VMOp_ShenandoahFullGC; } + const char* name() const override { return "Shenandoah Full GC"; } + void doit() override; }; class VM_ShenandoahInitUpdateRefs: public VM_ShenandoahOperation { ShenandoahConcurrentGC* const _gc; public: - VM_ShenandoahInitUpdateRefs(ShenandoahConcurrentGC* gc) : - VM_ShenandoahOperation(), - _gc(gc) {}; - VM_Operation::VMOp_Type type() const { return VMOp_ShenandoahInitUpdateRefs; } - const char* name() const { return "Shenandoah Init Update References"; } - virtual void doit(); + explicit VM_ShenandoahInitUpdateRefs(ShenandoahConcurrentGC* gc); + VM_Operation::VMOp_Type type() const override { return VMOp_ShenandoahInitUpdateRefs; } + const char* name() const override { return "Shenandoah Init Update References"; } + void doit() override; }; class VM_ShenandoahFinalUpdateRefs: public VM_ShenandoahOperation { ShenandoahConcurrentGC* const _gc; public: - VM_ShenandoahFinalUpdateRefs(ShenandoahConcurrentGC* gc) : - VM_ShenandoahOperation(), - _gc(gc) {}; - VM_Operation::VMOp_Type type() const { return VMOp_ShenandoahFinalUpdateRefs; } - const char* name() const { return "Shenandoah Final Update References"; } - virtual void doit(); + explicit VM_ShenandoahFinalUpdateRefs(ShenandoahConcurrentGC* gc); + VM_Operation::VMOp_Type type() const override { return VMOp_ShenandoahFinalUpdateRefs; } + const char* name() const override { return "Shenandoah Final Update References"; } + void doit() override; }; class VM_ShenandoahFinalRoots: public VM_ShenandoahOperation { ShenandoahConcurrentGC* const _gc; public: - VM_ShenandoahFinalRoots(ShenandoahConcurrentGC* gc) : - VM_ShenandoahOperation(), - _gc(gc) {}; - VM_Operation::VMOp_Type type() const { return VMOp_ShenandoahFinalRoots; } - const char* name() const { return "Shenandoah Final Roots"; } - virtual void doit(); + explicit VM_ShenandoahFinalRoots(ShenandoahConcurrentGC* gc); + VM_Operation::VMOp_Type type() const override { return VMOp_ShenandoahFinalRoots; } + const char* name() const override { return "Shenandoah Final Roots"; } + void doit() override; }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHVMOPERATIONS_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index c84a2a65677..fb5fbbd00a1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -70,7 +70,8 @@ private: ShenandoahGeneration* _generation; public: - ShenandoahVerifyOopClosure(ShenandoahVerifierStack* stack, MarkBitMap* map, ShenandoahLivenessData* ld, + ShenandoahVerifyOopClosure(ShenandoahGeneration* generation, ShenandoahVerifierStack* stack, + MarkBitMap* map, ShenandoahLivenessData* ld, const char* phase, ShenandoahVerifier::VerifyOptions options) : _phase(phase), _options(options), @@ -80,7 +81,7 @@ public: _ld(ld), _interior_loc(nullptr), _loc(nullptr), - _generation(nullptr) { + _generation(generation) { if (options._verify_marked == ShenandoahVerifier::_verify_marked_complete_except_references || options._verify_marked == ShenandoahVerifier::_verify_marked_complete_satb_empty || options._verify_marked == ShenandoahVerifier::_verify_marked_disable) { @@ -92,12 +93,6 @@ public: // Otherwise do all fields. _ref_mode = DO_FIELDS; } - - if (_heap->mode()->is_generational()) { - _generation = _heap->gc_generation(); - assert(_generation != nullptr, "Expected active generation in this mode"); - shenandoah_assert_generations_reconciled(); - } } ReferenceIterationMode reference_iteration_mode() override { @@ -131,11 +126,7 @@ private: } } - bool in_generation(oop obj) { - if (_generation == nullptr) { - return true; - } - + bool in_generation(oop obj) const { ShenandoahHeapRegion* region = _heap->heap_region_containing(obj); return _generation->contains(region); } @@ -197,9 +188,8 @@ private: // fallthrough for fast failure for un-live regions: case ShenandoahVerifier::_verify_liveness_conservative: check(ShenandoahAsserts::_safe_oop, obj, obj_reg->has_live() || - (obj_reg->is_old() && _heap->gc_generation()->is_young()), + (obj_reg->is_old() && _generation->is_young()), "Object must belong to region with live data"); - shenandoah_assert_generations_reconciled(); break; default: assert(false, "Unhandled liveness verification"); @@ -276,12 +266,12 @@ private: "Must be marked in incomplete bitmap"); break; case ShenandoahVerifier::_verify_marked_complete: - check(ShenandoahAsserts::_safe_all, obj, _heap->gc_generation()->complete_marking_context()->is_marked(obj), + check(ShenandoahAsserts::_safe_all, obj, _generation->complete_marking_context()->is_marked(obj), "Must be marked in complete bitmap"); break; case ShenandoahVerifier::_verify_marked_complete_except_references: case ShenandoahVerifier::_verify_marked_complete_satb_empty: - check(ShenandoahAsserts::_safe_all, obj, _heap->gc_generation()->complete_marking_context()->is_marked(obj), + check(ShenandoahAsserts::_safe_all, obj, _generation->complete_marking_context()->is_marked(obj), "Must be marked in complete bitmap, except j.l.r.Reference referents"); break; default: @@ -571,9 +561,11 @@ private: ShenandoahLivenessData* _ld; MarkBitMap* _bitmap; volatile size_t _processed; + ShenandoahGeneration* _generation; public: - ShenandoahVerifierReachableTask(MarkBitMap* bitmap, + ShenandoahVerifierReachableTask(ShenandoahGeneration* generation, + MarkBitMap* bitmap, ShenandoahLivenessData* ld, const char* label, ShenandoahVerifier::VerifyOptions options) : @@ -583,7 +575,8 @@ public: _heap(ShenandoahHeap::heap()), _ld(ld), _bitmap(bitmap), - _processed(0) {}; + _processed(0), + _generation(generation) {}; size_t processed() const { return _processed; @@ -599,20 +592,20 @@ public: // extended parallelism would buy us out. if (((ShenandoahVerifyLevel == 2) && (worker_id == 0)) || (ShenandoahVerifyLevel >= 3)) { - ShenandoahVerifyOopClosure cl(&stack, _bitmap, _ld, + ShenandoahVerifyOopClosure cl(_generation, &stack, _bitmap, _ld, ShenandoahMessageBuffer("%s, Roots", _label), _options); if (_heap->unload_classes()) { - ShenandoahRootVerifier::strong_roots_do(&cl); + ShenandoahRootVerifier::strong_roots_do(&cl, _generation); } else { - ShenandoahRootVerifier::roots_do(&cl); + ShenandoahRootVerifier::roots_do(&cl, _generation); } } size_t processed = 0; if (ShenandoahVerifyLevel >= 3) { - ShenandoahVerifyOopClosure cl(&stack, _bitmap, _ld, + ShenandoahVerifyOopClosure cl(_generation, &stack, _bitmap, _ld, ShenandoahMessageBuffer("%s, Reachable", _label), _options); while (!stack.is_empty()) { @@ -648,7 +641,8 @@ private: ShenandoahGeneration* _generation; public: - ShenandoahVerifierMarkedRegionTask(MarkBitMap* bitmap, + ShenandoahVerifierMarkedRegionTask(ShenandoahGeneration* generation, + MarkBitMap* bitmap, ShenandoahLivenessData* ld, const char* label, ShenandoahVerifier::VerifyOptions options) : @@ -660,13 +654,7 @@ public: _ld(ld), _claimed(0), _processed(0), - _generation(nullptr) { - if (_heap->mode()->is_generational()) { - _generation = _heap->gc_generation(); - assert(_generation != nullptr, "Expected active generation in this mode."); - shenandoah_assert_generations_reconciled(); - } - }; + _generation(generation) {} size_t processed() { return AtomicAccess::load(&_processed); @@ -679,7 +667,7 @@ public: } ShenandoahVerifierStack stack; - ShenandoahVerifyOopClosure cl(&stack, _bitmap, _ld, + ShenandoahVerifyOopClosure cl(_generation, &stack, _bitmap, _ld, ShenandoahMessageBuffer("%s, Marked", _label), _options); @@ -702,14 +690,14 @@ public: } } - bool in_generation(ShenandoahHeapRegion* r) { - return _generation == nullptr || _generation->contains(r); + bool in_generation(ShenandoahHeapRegion* r) const { + return _generation->contains(r); } virtual void work_humongous(ShenandoahHeapRegion *r, ShenandoahVerifierStack& stack, ShenandoahVerifyOopClosure& cl) { size_t processed = 0; HeapWord* obj = r->bottom(); - if (_heap->gc_generation()->complete_marking_context()->is_marked(cast_to_oop(obj))) { + if (_generation->complete_marking_context()->is_marked(cast_to_oop(obj))) { verify_and_follow(obj, stack, cl, &processed); } AtomicAccess::add(&_processed, processed, memory_order_relaxed); @@ -717,7 +705,7 @@ public: virtual void work_regular(ShenandoahHeapRegion *r, ShenandoahVerifierStack &stack, ShenandoahVerifyOopClosure &cl) { size_t processed = 0; - ShenandoahMarkingContext* ctx = _heap->gc_generation()->complete_marking_context(); + ShenandoahMarkingContext* ctx = _generation->complete_marking_context(); HeapWord* tams = ctx->top_at_mark_start(r); // Bitmaps, before TAMS @@ -794,7 +782,8 @@ public: } }; -void ShenandoahVerifier::verify_at_safepoint(const char* label, +void ShenandoahVerifier::verify_at_safepoint(ShenandoahGeneration* generation, + const char* label, VerifyRememberedSet remembered, VerifyForwarded forwarded, VerifyMarked marked, @@ -896,16 +885,7 @@ void ShenandoahVerifier::verify_at_safepoint(const char* label, log_debug(gc)("Safepoint verification finished heap usage verification"); - ShenandoahGeneration* generation; if (_heap->mode()->is_generational()) { - generation = _heap->gc_generation(); - guarantee(generation != nullptr, "Need to know which generation to verify."); - shenandoah_assert_generations_reconciled(); - } else { - generation = nullptr; - } - - if (generation != nullptr) { ShenandoahHeapLocker lock(_heap->lock()); switch (remembered) { @@ -952,11 +932,7 @@ void ShenandoahVerifier::verify_at_safepoint(const char* label, // Internal heap region checks if (ShenandoahVerifyLevel >= 1) { ShenandoahVerifyHeapRegionClosure cl(label, regions); - if (generation != nullptr) { - generation->heap_region_iterate(&cl); - } else { - _heap->heap_region_iterate(&cl); - } + generation->heap_region_iterate(&cl); } log_debug(gc)("Safepoint verification finished heap region closure verification"); @@ -980,7 +956,7 @@ void ShenandoahVerifier::verify_at_safepoint(const char* label, // This verifies what application can see, since it only cares about reachable objects. size_t count_reachable = 0; if (ShenandoahVerifyLevel >= 2) { - ShenandoahVerifierReachableTask task(_verification_bit_map, ld, label, options); + ShenandoahVerifierReachableTask task(generation, _verification_bit_map, ld, label, options); _heap->workers()->run_task(&task); count_reachable = task.processed(); } @@ -999,8 +975,8 @@ void ShenandoahVerifier::verify_at_safepoint(const char* label, (marked == _verify_marked_complete || marked == _verify_marked_complete_except_references || marked == _verify_marked_complete_satb_empty)) { - guarantee(_heap->gc_generation()->is_mark_complete(), "Marking context should be complete"); - ShenandoahVerifierMarkedRegionTask task(_verification_bit_map, ld, label, options); + guarantee(generation->is_mark_complete(), "Marking context should be complete"); + ShenandoahVerifierMarkedRegionTask task(generation, _verification_bit_map, ld, label, options); _heap->workers()->run_task(&task); count_marked = task.processed(); } else { @@ -1015,7 +991,7 @@ void ShenandoahVerifier::verify_at_safepoint(const char* label, if (ShenandoahVerifyLevel >= 4 && marked == _verify_marked_complete && liveness == _verify_liveness_complete) { for (size_t i = 0; i < _heap->num_regions(); i++) { ShenandoahHeapRegion* r = _heap->get_region(i); - if (generation != nullptr && !generation->contains(r)) { + if (!generation->contains(r)) { continue; } @@ -1042,16 +1018,15 @@ void ShenandoahVerifier::verify_at_safepoint(const char* label, } log_debug(gc)("Safepoint verification finished accumulation of liveness data"); - - log_info(gc)("Verify %s, Level %zd (%zu reachable, %zu marked)", label, ShenandoahVerifyLevel, count_reachable, count_marked); FREE_C_HEAP_ARRAY(ShenandoahLivenessData, ld); } -void ShenandoahVerifier::verify_generic(VerifyOption vo) { +void ShenandoahVerifier::verify_generic(ShenandoahGeneration* generation, VerifyOption vo) { verify_at_safepoint( + generation, "Generic Verification", _verify_remembered_disable, // do not verify remembered set _verify_forwarded_allow, // conservatively allow forwarded @@ -1064,7 +1039,7 @@ void ShenandoahVerifier::verify_generic(VerifyOption vo) { ); } -void ShenandoahVerifier::verify_before_concmark() { +void ShenandoahVerifier::verify_before_concmark(ShenandoahGeneration* generation) { VerifyRememberedSet verify_remembered_set = _verify_remembered_before_marking; if (_heap->mode()->is_generational() && !_heap->old_generation()->is_mark_complete()) { @@ -1072,6 +1047,7 @@ void ShenandoahVerifier::verify_before_concmark() { verify_remembered_set = _verify_remembered_disable; } verify_at_safepoint( + generation, "Before Mark", verify_remembered_set, // verify read-only remembered set from bottom() to top() @@ -1085,8 +1061,9 @@ void ShenandoahVerifier::verify_before_concmark() { ); } -void ShenandoahVerifier::verify_after_concmark() { +void ShenandoahVerifier::verify_after_concmark(ShenandoahGeneration* generation) { verify_at_safepoint( + generation, "After Mark", _verify_remembered_disable, // do not verify remembered set _verify_forwarded_none, // no forwarded references @@ -1099,8 +1076,9 @@ void ShenandoahVerifier::verify_after_concmark() { ); } -void ShenandoahVerifier::verify_after_concmark_with_promotions() { +void ShenandoahVerifier::verify_after_concmark_with_promotions(ShenandoahGeneration* generation) { verify_at_safepoint( + generation, "After Mark", _verify_remembered_disable, // do not verify remembered set _verify_forwarded_none, // no forwarded references @@ -1114,8 +1092,9 @@ void ShenandoahVerifier::verify_after_concmark_with_promotions() { ); } -void ShenandoahVerifier::verify_before_evacuation() { +void ShenandoahVerifier::verify_before_evacuation(ShenandoahGeneration* generation) { verify_at_safepoint( + generation, "Before Evacuation", _verify_remembered_disable, // do not verify remembered set _verify_forwarded_none, // no forwarded references @@ -1129,13 +1108,14 @@ void ShenandoahVerifier::verify_before_evacuation() { ); } -void ShenandoahVerifier::verify_before_update_refs() { +void ShenandoahVerifier::verify_before_update_refs(ShenandoahGeneration* generation) { VerifyRememberedSet verify_remembered_set = _verify_remembered_before_updating_references; if (_heap->mode()->is_generational() && !_heap->old_generation()->is_mark_complete()) { verify_remembered_set = _verify_remembered_disable; } verify_at_safepoint( + generation, "Before Updating References", verify_remembered_set, // verify read-write remembered set _verify_forwarded_allow, // forwarded references allowed @@ -1149,8 +1129,9 @@ void ShenandoahVerifier::verify_before_update_refs() { } // We have not yet cleanup (reclaimed) the collection set -void ShenandoahVerifier::verify_after_update_refs() { +void ShenandoahVerifier::verify_after_update_refs(ShenandoahGeneration* generation) { verify_at_safepoint( + generation, "After Updating References", _verify_remembered_disable, // do not verify remembered set _verify_forwarded_none, // no forwarded references @@ -1163,8 +1144,9 @@ void ShenandoahVerifier::verify_after_update_refs() { ); } -void ShenandoahVerifier::verify_after_degenerated() { +void ShenandoahVerifier::verify_after_degenerated(ShenandoahGeneration* generation) { verify_at_safepoint( + generation, "After Degenerated GC", _verify_remembered_disable, // do not verify remembered set _verify_forwarded_none, // all objects are non-forwarded @@ -1177,8 +1159,9 @@ void ShenandoahVerifier::verify_after_degenerated() { ); } -void ShenandoahVerifier::verify_before_fullgc() { +void ShenandoahVerifier::verify_before_fullgc(ShenandoahGeneration* generation) { verify_at_safepoint( + generation, "Before Full GC", _verify_remembered_disable, // do not verify remembered set _verify_forwarded_allow, // can have forwarded objects @@ -1191,8 +1174,9 @@ void ShenandoahVerifier::verify_before_fullgc() { ); } -void ShenandoahVerifier::verify_after_fullgc() { +void ShenandoahVerifier::verify_after_fullgc(ShenandoahGeneration* generation) { verify_at_safepoint( + generation, "After Full GC", _verify_remembered_after_full_gc, // verify read-write remembered set _verify_forwarded_none, // all objects are non-forwarded @@ -1257,14 +1241,14 @@ public: void do_oop(oop* p) override { do_oop_work(p); } }; -void ShenandoahVerifier::verify_roots_in_to_space() { +void ShenandoahVerifier::verify_roots_in_to_space(ShenandoahGeneration* generation) { ShenandoahVerifyInToSpaceClosure cl; - ShenandoahRootVerifier::roots_do(&cl); + ShenandoahRootVerifier::roots_do(&cl, generation); } -void ShenandoahVerifier::verify_roots_no_forwarded() { +void ShenandoahVerifier::verify_roots_no_forwarded(ShenandoahGeneration* generation) { ShenandoahVerifyNoForwarded cl; - ShenandoahRootVerifier::roots_do(&cl); + ShenandoahRootVerifier::roots_do(&cl, generation); } template @@ -1300,7 +1284,6 @@ public: template void ShenandoahVerifier::help_verify_region_rem_set(Scanner* scanner, ShenandoahHeapRegion* r, HeapWord* registration_watermark, const char* message) { - shenandoah_assert_generations_reconciled(); ShenandoahOldGeneration* old_gen = _heap->old_generation(); assert(old_gen->is_mark_complete() || old_gen->is_parsable(), "Sanity"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp index aba6379e022..e49990fdc62 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp @@ -196,7 +196,8 @@ public: }; private: - void verify_at_safepoint(const char* label, + void verify_at_safepoint(ShenandoahGeneration* generation, + const char* label, VerifyRememberedSet remembered, VerifyForwarded forwarded, VerifyMarked marked, @@ -210,20 +211,20 @@ public: ShenandoahVerifier(ShenandoahHeap* heap, MarkBitMap* verification_bitmap) : _heap(heap), _verification_bit_map(verification_bitmap) {}; - void verify_before_concmark(); - void verify_after_concmark(); - void verify_after_concmark_with_promotions(); - void verify_before_evacuation(); - void verify_before_update_refs(); - void verify_after_update_refs(); - void verify_before_fullgc(); - void verify_after_fullgc(); - void verify_after_degenerated(); - void verify_generic(VerifyOption option); + void verify_before_concmark(ShenandoahGeneration* generation); + void verify_after_concmark(ShenandoahGeneration* generation); + void verify_after_concmark_with_promotions(ShenandoahGeneration* generation); + void verify_before_evacuation(ShenandoahGeneration* generation); + void verify_before_update_refs(ShenandoahGeneration* generation); + void verify_after_update_refs(ShenandoahGeneration* generation); + void verify_before_fullgc(ShenandoahGeneration* generation); + void verify_after_fullgc(ShenandoahGeneration* generation); + void verify_after_degenerated(ShenandoahGeneration* generation); + void verify_generic(ShenandoahGeneration* generation, VerifyOption option); // Roots should only contain to-space oops - void verify_roots_in_to_space(); - void verify_roots_no_forwarded(); + void verify_roots_in_to_space(ShenandoahGeneration* generation); + void verify_roots_no_forwarded(ShenandoahGeneration* generation); // Check that generation usages are accurate before rebuilding free set void verify_before_rebuilding_free_set(); 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 @@ public: 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 640e3ab3fff..b6a2255b468 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.cpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp @@ -258,7 +258,8 @@ bool AbstractInterpreter::is_not_reached(const methodHandle& method, int bci) { case Bytecodes::_invokedynamic: { assert(invoke_bc.has_index_u4(code), "sanity"); int method_index = invoke_bc.get_index_u4(code); - return cpool->resolved_indy_entry_at(method_index)->is_resolved(); + bool is_resolved = cpool->resolved_indy_entry_at(method_index)->is_resolved(); + return !is_resolved; } case Bytecodes::_invokevirtual: // fall-through case Bytecodes::_invokeinterface: // fall-through diff --git a/src/hotspot/share/interpreter/abstractInterpreter.hpp b/src/hotspot/share/interpreter/abstractInterpreter.hpp index 6f7523fd00a..23618cb037e 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.hpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.hpp @@ -255,7 +255,7 @@ class AbstractInterpreter: AllStatic { return stackElementWords * i; } -#if !defined(ZERO) && (defined(IA32) || defined(AMD64)) +#if !defined(ZERO) && defined(AMD64) static Address::ScaleFactor stackElementScale() { return NOT_LP64(Address::times_4) LP64_ONLY(Address::times_8); } 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/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index cec8f70d655..6e067c77287 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -912,6 +912,7 @@ void InterpreterRuntime::cds_resolve_invoke(Bytecodes::Code bytecode, int method switch (bytecode) { case Bytecodes::_invokevirtual: LinkResolver::cds_resolve_virtual_call (call_info, link_info, CHECK); break; case Bytecodes::_invokeinterface: LinkResolver::cds_resolve_interface_call(call_info, link_info, CHECK); break; + case Bytecodes::_invokestatic: LinkResolver::cds_resolve_static_call (call_info, link_info, CHECK); break; case Bytecodes::_invokespecial: LinkResolver::cds_resolve_special_call (call_info, link_info, CHECK); break; default: fatal("Unimplemented: %s", Bytecodes::name(bytecode)); @@ -1459,7 +1460,7 @@ JRT_ENTRY(void, InterpreterRuntime::prepare_native_call(JavaThread* current, Met // preparing the same method will be sure to see non-null entry & mirror. JRT_END -#if defined(IA32) || defined(AMD64) || defined(ARM) +#if defined(AMD64) || defined(ARM) JRT_LEAF(void, InterpreterRuntime::popframe_move_outgoing_args(JavaThread* current, void* src_address, void* dest_address)) assert(current == JavaThread::current(), "pre-condition"); if (src_address == dest_address) { diff --git a/src/hotspot/share/interpreter/interpreterRuntime.hpp b/src/hotspot/share/interpreter/interpreterRuntime.hpp index 3cc7a938a6a..cdee0c9daa7 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.hpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.hpp @@ -146,8 +146,8 @@ private: Method* method, intptr_t* from, intptr_t* to); -#if defined(IA32) || defined(AMD64) || defined(ARM) - // Popframe support (only needed on x86, AMD64 and ARM) +#if defined(AMD64) || defined(ARM) + // Popframe support (only needed on AMD64 and ARM) static void popframe_move_outgoing_args(JavaThread* current, void* src_address, void* dest_address); #endif diff --git a/src/hotspot/share/interpreter/linkResolver.cpp b/src/hotspot/share/interpreter/linkResolver.cpp index 22199baef8e..d46ccdb4d1c 100644 --- a/src/hotspot/share/interpreter/linkResolver.cpp +++ b/src/hotspot/share/interpreter/linkResolver.cpp @@ -1126,6 +1126,10 @@ void LinkResolver::resolve_static_call(CallInfo& result, JFR_ONLY(Jfr::on_resolution(result, CHECK);) } +void LinkResolver::cds_resolve_static_call(CallInfo& result, const LinkInfo& link_info, TRAPS) { + resolve_static_call(result, link_info, /*initialize_class*/false, CHECK); +} + // throws linktime exceptions Method* LinkResolver::linktime_resolve_static_method(const LinkInfo& link_info, TRAPS) { diff --git a/src/hotspot/share/interpreter/linkResolver.hpp b/src/hotspot/share/interpreter/linkResolver.hpp index 69bdf56137d..18fb4ee6ccb 100644 --- a/src/hotspot/share/interpreter/linkResolver.hpp +++ b/src/hotspot/share/interpreter/linkResolver.hpp @@ -328,6 +328,7 @@ class LinkResolver: AllStatic { static void cds_resolve_virtual_call (CallInfo& result, const LinkInfo& link_info, TRAPS); static void cds_resolve_interface_call(CallInfo& result, const LinkInfo& link_info, TRAPS); + static void cds_resolve_static_call(CallInfo& result, const LinkInfo& link_info, TRAPS); static void cds_resolve_special_call (CallInfo& result, const LinkInfo& link_info, TRAPS); // same as above for compile-time resolution; but returns null handle instead of throwing 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/jfr/utilities/jfrBigEndian.hpp b/src/hotspot/share/jfr/utilities/jfrBigEndian.hpp index 7661b35863c..cf849a7654c 100644 --- a/src/hotspot/share/jfr/utilities/jfrBigEndian.hpp +++ b/src/hotspot/share/jfr/utilities/jfrBigEndian.hpp @@ -101,7 +101,7 @@ inline R JfrBigEndian::read_unaligned(const address location) { } inline bool JfrBigEndian::platform_supports_unaligned_reads(void) { -#if defined(IA32) || defined(AMD64) || defined(PPC) || defined(S390) +#if defined(AMD64) || defined(PPC) || defined(S390) return true; #elif defined(ARM) || defined(AARCH64) || defined(RISCV) return false; diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index 6d0bd117ad9..3ad6a197d07 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.hpp @@ -41,6 +41,7 @@ class outputStream; LOG_TAG(aot) \ LOG_TAG(arguments) \ LOG_TAG(array) \ + LOG_TAG(asan) \ LOG_TAG(attach) \ LOG_TAG(barrier) \ LOG_TAG(blocks) \ diff --git a/src/hotspot/share/memory/arena.cpp b/src/hotspot/share/memory/arena.cpp index db0bb8add21..b9968083e0e 100644 --- a/src/hotspot/share/memory/arena.cpp +++ b/src/hotspot/share/memory/arena.cpp @@ -39,19 +39,26 @@ // It is used very early in the vm initialization, in allocation // code and other areas. For many calls, the current thread has not // been created so we cannot use Mutex. -static PlatformMutex* GlobalChunkPoolMutex = nullptr; +static DeferredStatic GlobalChunkPoolMutex; void Arena::initialize_chunk_pool() { - GlobalChunkPoolMutex = new PlatformMutex(); + GlobalChunkPoolMutex.initialize(); } -ChunkPoolLocker::ChunkPoolLocker() { - assert(GlobalChunkPoolMutex != nullptr, "must be initialized"); - GlobalChunkPoolMutex->lock(); +ChunkPoolLocker::ChunkPoolLocker(LockStrategy ls) { + if (ls == LockStrategy::Lock) { + GlobalChunkPoolMutex->lock(); + _locked = true; + } else { + assert(ls == LockStrategy::Try, "must be"); + _locked = GlobalChunkPoolMutex->try_lock(); + } }; ChunkPoolLocker::~ChunkPoolLocker() { - GlobalChunkPoolMutex->unlock(); + if (_locked) { + GlobalChunkPoolMutex->unlock(); + } }; // Pre-defined default chunk sizes must be arena-aligned, see Chunk::operator new() diff --git a/src/hotspot/share/memory/arena.hpp b/src/hotspot/share/memory/arena.hpp index e2169ee406e..b4a0546babf 100644 --- a/src/hotspot/share/memory/arena.hpp +++ b/src/hotspot/share/memory/arena.hpp @@ -38,8 +38,11 @@ #define ARENA_ALIGN(x) (align_up((x), ARENA_AMALLOC_ALIGNMENT)) class ChunkPoolLocker : public StackObj { + bool _locked; public: - ChunkPoolLocker(); + enum class LockStrategy { Lock, Try }; + + ChunkPoolLocker(LockStrategy ls = LockStrategy::Lock); ~ChunkPoolLocker(); }; diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index 424e43c5e83..756619bff33 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -955,7 +955,7 @@ void Universe::initialize_tlab() { } } -ReservedHeapSpace Universe::reserve_heap(size_t heap_size, size_t alignment) { +ReservedHeapSpace Universe::reserve_heap(size_t heap_size, size_t alignment, size_t desired_page_size) { assert(alignment <= Arguments::conservative_max_heap_alignment(), "actual alignment %zu must be within maximum heap alignment %zu", @@ -966,15 +966,21 @@ ReservedHeapSpace Universe::reserve_heap(size_t heap_size, size_t alignment) { assert(!UseCompressedOops || (total_reserved <= (OopEncodingHeapMax - os::vm_page_size())), "heap size is too big for compressed oops"); - size_t page_size = os::vm_page_size(); - if (UseLargePages && is_aligned(alignment, os::large_page_size())) { - page_size = os::large_page_size(); + size_t page_size; + if (desired_page_size == 0) { + if (UseLargePages) { + page_size = os::large_page_size(); + } else { + page_size = os::vm_page_size(); + } } else { // Parallel is the only collector that might opt out of using large pages // for the heap. - assert(!UseLargePages || UseParallelGC , "Wrong alignment to use large pages"); + assert(UseParallelGC , "only Parallel"); + // Use caller provided value. + page_size = desired_page_size; } - + assert(is_aligned(heap_size, page_size), "inv"); // Now create the space. ReservedHeapSpace rhs = HeapReserver::reserve(total_reserved, alignment, page_size, AllocateHeapAt); diff --git a/src/hotspot/share/memory/universe.hpp b/src/hotspot/share/memory/universe.hpp index 3b1f2523ed8..37ca965062e 100644 --- a/src/hotspot/share/memory/universe.hpp +++ b/src/hotspot/share/memory/universe.hpp @@ -315,7 +315,7 @@ class Universe: AllStatic { DEBUG_ONLY(static bool is_in_heap_or_null(const void* p) { return p == nullptr || is_in_heap(p); }) // Reserve Java heap and determine CompressedOops mode - static ReservedHeapSpace reserve_heap(size_t heap_size, size_t alignment); + static ReservedHeapSpace reserve_heap(size_t heap_size, size_t alignment, size_t desired_page_size = 0); // Global OopStorages static OopStorage* vm_weak(); diff --git a/src/hotspot/share/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/nmt/mallocTracker.cpp b/src/hotspot/share/nmt/mallocTracker.cpp index 75089dffc30..a61a27db25d 100644 --- a/src/hotspot/share/nmt/mallocTracker.cpp +++ b/src/hotspot/share/nmt/mallocTracker.cpp @@ -65,7 +65,11 @@ void MallocMemorySnapshot::copy_to(MallocMemorySnapshot* s) { // Use lock to make sure that mtChunks don't get deallocated while the // copy is going on, because their size is adjusted using this // buffer in make_adjustment(). - ChunkPoolLocker lock; + ChunkPoolLocker::LockStrategy ls = ChunkPoolLocker::LockStrategy::Lock; + if (VMError::is_error_reported() && VMError::is_error_reported_in_current_thread()) { + ls = ChunkPoolLocker::LockStrategy::Try; + } + ChunkPoolLocker cpl(ls); s->_all_mallocs = _all_mallocs; size_t total_size = 0; size_t total_count = 0; diff --git a/src/hotspot/share/nmt/nmtUsage.cpp b/src/hotspot/share/nmt/nmtUsage.cpp index 3a9a232a36e..9e6fc3e183b 100644 --- a/src/hotspot/share/nmt/nmtUsage.cpp +++ b/src/hotspot/share/nmt/nmtUsage.cpp @@ -30,6 +30,7 @@ #include "nmt/nmtUsage.hpp" #include "nmt/threadStackTracker.hpp" #include "runtime/mutexLocker.hpp" +#include "utilities/vmError.hpp" // Enabled all options for snapshot. const NMTUsageOptions NMTUsage::OptionsAll = { true, true, true }; @@ -58,7 +59,11 @@ void NMTUsage::update_malloc_usage() { // Lock needed to keep values in sync, total area size // is deducted from mtChunk in the end to give correct values. { - ChunkPoolLocker lock; + ChunkPoolLocker::LockStrategy ls = ChunkPoolLocker::LockStrategy::Lock; + if (VMError::is_error_reported() && VMError::is_error_reported_in_current_thread()) { + ls = ChunkPoolLocker::LockStrategy::Try; + } + ChunkPoolLocker cpl(ls); ms = MallocMemorySummary::as_snapshot(); } diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 5d5c0548215..b072c7f26ec 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -538,18 +538,23 @@ void ConstantPool::remove_resolved_klass_if_non_deterministic(int cp_index) { assert(ArchiveBuilder::current()->is_in_buffer_space(this), "must be"); assert(tag_at(cp_index).is_klass(), "must be resolved"); - Klass* k = resolved_klass_at(cp_index); bool can_archive; + Klass* k = nullptr; - if (k == nullptr) { - // We'd come here if the referenced class has been excluded via - // SystemDictionaryShared::is_excluded_class(). As a result, ArchiveBuilder - // has cleared the resolved_klasses()->at(...) pointer to null. Thus, we - // need to revert the tag to JVM_CONSTANT_UnresolvedClass. + if (CDSConfig::is_dumping_preimage_static_archive()) { can_archive = false; } else { - ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(this); - can_archive = AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index); + k = resolved_klass_at(cp_index); + if (k == nullptr) { + // We'd come here if the referenced class has been excluded via + // SystemDictionaryShared::is_excluded_class(). As a result, ArchiveBuilder + // has cleared the resolved_klasses()->at(...) pointer to null. Thus, we + // need to revert the tag to JVM_CONSTANT_UnresolvedClass. + can_archive = false; + } else { + ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(this); + can_archive = AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index); + } } if (!can_archive) { diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index 941ceac8de1..75cdcb5310a 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -175,7 +175,8 @@ void ConstantPoolCache::set_direct_or_vtable_call(Bytecodes::Code invoke_code, } if (invoke_code == Bytecodes::_invokestatic) { assert(method->method_holder()->is_initialized() || - method->method_holder()->is_reentrant_initialization(JavaThread::current()), + method->method_holder()->is_reentrant_initialization(JavaThread::current()) || + (CDSConfig::is_dumping_archive() && VM_Version::supports_fast_class_init_checks()), "invalid class initialization state for invoke_static"); if (!VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) { @@ -428,28 +429,33 @@ void ConstantPoolCache::remove_resolved_field_entries_if_non_deterministic() { ResolvedFieldEntry* rfi = _resolved_field_entries->adr_at(i); int cp_index = rfi->constant_pool_index(); 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)) { + bool resolved = false; + + if (rfi->is_resolved(Bytecodes::_getfield) || rfi->is_resolved(Bytecodes::_putfield) || + ((rfi->is_resolved(Bytecodes::_getstatic) || rfi->is_resolved(Bytecodes::_putstatic)) && VM_Version::supports_fast_class_init_checks())) { + resolved = true; + } + + 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%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(), + rfi->is_resolved(Bytecodes::_getstatic) || rfi->is_resolved(Bytecodes::_putstatic) ? " *** static" : ""); } ArchiveBuilder::alloc_stats()->record_field_cp_entry(archived, resolved && !archived); } @@ -465,40 +471,37 @@ void ConstantPoolCache::remove_resolved_method_entries_if_non_deterministic() { bool resolved = rme->is_resolved(Bytecodes::_invokevirtual) || rme->is_resolved(Bytecodes::_invokespecial) || rme->is_resolved(Bytecodes::_invokeinterface) || - rme->is_resolved(Bytecodes::_invokehandle); + rme->is_resolved(Bytecodes::_invokehandle) || + (rme->is_resolved(Bytecodes::_invokestatic) && VM_Version::supports_fast_class_init_checks()); - // 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); } + ArchiveBuilder::alloc_stats()->record_method_cp_entry(archived, resolved && !archived); } } @@ -510,33 +513,33 @@ 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); } } bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, ResolvedMethodEntry* method_entry) { + LogStreamHandle(Trace, aot, resolve) log; InstanceKlass* pool_holder = constant_pool()->pool_holder(); if (pool_holder->defined_by_other_loaders()) { // Archiving resolved cp entries for classes from non-builtin loaders @@ -557,6 +560,12 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv if (method_entry->method()->is_continuation_native_intrinsic()) { return false; // FIXME: corresponding stub is generated on demand during method resolution (see LinkResolver::resolve_static_call). } + if (method_entry->is_resolved(Bytecodes::_invokehandle) && !CDSConfig::is_dumping_method_handles()) { + return false; + } + if (method_entry->method()->is_method_handle_intrinsic() && !CDSConfig::is_dumping_method_handles()) { + return false; + } } int cp_index = method_entry->constant_pool_index(); @@ -565,21 +574,7 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv if (!AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) { return false; } - - if (method_entry->is_resolved(Bytecodes::_invokeinterface) || - method_entry->is_resolved(Bytecodes::_invokevirtual) || - method_entry->is_resolved(Bytecodes::_invokespecial)) { - return true; - } else if (method_entry->is_resolved(Bytecodes::_invokehandle)) { - if (CDSConfig::is_dumping_method_handles()) { - // invokehandle depends on archived MethodType and LambdaForms. - return true; - } else { - return false; - } - } else { - return false; - } + return true; } #endif // INCLUDE_CDS diff --git a/src/hotspot/share/oops/trainingData.cpp b/src/hotspot/share/oops/trainingData.cpp index 24f82a53843..27ae3404f41 100644 --- a/src/hotspot/share/oops/trainingData.cpp +++ b/src/hotspot/share/oops/trainingData.cpp @@ -35,6 +35,7 @@ #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/method.hpp" +#include "oops/method.inline.hpp" #include "oops/methodCounters.hpp" #include "oops/trainingData.hpp" #include "runtime/arguments.hpp" @@ -352,6 +353,8 @@ void MethodTrainingData::prepare(Visitor& visitor) { _final_counters = holder()->method_counters(); _final_profile = holder()->method_data(); assert(_final_profile == nullptr || _final_profile->method() == holder(), ""); + _invocation_count = holder()->invocation_count(); + _backedge_count = holder()->backedge_count(); } for (int i = 0; i < CompLevel_count - 1; i++) { CompileTrainingData* ctd = _last_toplevel_compiles[i]; diff --git a/src/hotspot/share/oops/trainingData.hpp b/src/hotspot/share/oops/trainingData.hpp index 7ab5e179eea..c549004e76e 100644 --- a/src/hotspot/share/oops/trainingData.hpp +++ b/src/hotspot/share/oops/trainingData.hpp @@ -748,6 +748,9 @@ class MethodTrainingData : public TrainingData { MethodCounters* _final_counters; MethodData* _final_profile; + int _invocation_count; + int _backedge_count; + MethodTrainingData(); MethodTrainingData(Method* method, KlassTrainingData* ktd) : TrainingData(method) { _klass = ktd; @@ -758,6 +761,8 @@ class MethodTrainingData : public TrainingData { _highest_top_level = CompLevel_none; _level_mask = 0; _was_toplevel = false; + _invocation_count = 0; + _backedge_count = 0; } static int level_mask(int level) { @@ -772,6 +777,8 @@ class MethodTrainingData : public TrainingData { bool saw_level(CompLevel l) const { return (_level_mask & level_mask(l)) != 0; } int highest_top_level() const { return _highest_top_level; } MethodData* final_profile() const { return _final_profile; } + int invocation_count() const { return _invocation_count; } + int backedge_count() const { return _backedge_count; } Symbol* name() const { precond(has_holder()); diff --git a/src/hotspot/share/opto/arraycopynode.cpp b/src/hotspot/share/opto/arraycopynode.cpp index 85b6bd21aec..c02aefc7943 100644 --- a/src/hotspot/share/opto/arraycopynode.cpp +++ b/src/hotspot/share/opto/arraycopynode.cpp @@ -212,6 +212,15 @@ Node* ArrayCopyNode::try_clone_instance(PhaseGVN *phase, bool can_reshape, int c } } + const TypeInstPtr* dest_type = phase->type(base_dest)->is_instptr(); + if (dest_type->instance_klass() != ik) { + // At parse time, the exact type of the object to clone was not known. That inexact type was captured by the CheckCastPP + // of the newly allocated cloned object (in dest). The exact type is now known (in src), but the type for the cloned object + // (dest) was not updated. When copying the fields below, Store nodes may write to offsets for fields that don't exist in + // the inexact class. The stores would then be assigned an incorrect slice. + return NodeSentinel; + } + assert(ik->nof_nonstatic_fields() <= ArrayCopyLoadStoreMaxElem, "too many fields"); BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index ef1ebc5cef9..ad6548a649e 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -72,7 +72,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------------------------------------------ @@ -82,7 +82,7 @@ Node *StartNode::match( const ProjNode *proj, const Matcher *match ) { 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: @@ -777,12 +777,12 @@ Node *CallNode::match( const ProjNode *proj, const Matcher *match ) { 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::Parms+1: // For LONG & DOUBLE returns assert(tf()->range()->field_at(TypeFunc::Parms+1) == Type::HALF, ""); // 2nd half of doubles and longs - return new MachProjNode(this,proj->_con, RegMask::Empty, (uint)OptoReg::Bad); + return new MachProjNode(this,proj->_con, RegMask::EMPTY, (uint)OptoReg::Bad); case TypeFunc::Parms: { // Normal returns uint ideal_reg = tf()->range()->field_at(TypeFunc::Parms)->ideal_reg(); @@ -798,14 +798,14 @@ Node *CallNode::match( const ProjNode *proj, const Matcher *match ) { 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,proj->_con,rm,ideal_reg); } @@ -1492,12 +1492,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; } @@ -1608,7 +1610,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 { @@ -1659,7 +1661,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 ef912ff471a..0293f42d791 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -1014,7 +1014,7 @@ bool RegionNode::optimize_trichotomy(PhaseIterGVN* igvn) { } const RegMask &RegionNode::out_RegMask() const { - return RegMask::Empty; + return RegMask::EMPTY; } #ifndef PRODUCT @@ -2859,13 +2859,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]); } @@ -2892,22 +2894,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 fffe00a4114..78ad085e03d 100644 --- a/src/hotspot/share/opto/cfgnode.hpp +++ b/src/hotspot/share/opto/cfgnode.hpp @@ -741,7 +741,7 @@ public: // 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 45a91350626..8c8c2b0ed4e 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; @@ -973,7 +975,6 @@ void PhaseChaitin::gather_lrg_masks( bool after_aggressive ) { // ------------------- reg_pressure -------------------- // Each entry is reg_pressure_per_value,number_of_regs // RegL RegI RegFlags RegF RegD INTPRESSURE FLOATPRESSURE - // IA32 2 1 1 1 1 6 6 // SPARC 2 2 2 2 2 48 (24) 52 (26) // SPARCV9 2 2 2 2 2 48 (24) 52 (26) // AMD64 1 1 1 1 1 14 15 @@ -989,12 +990,6 @@ void PhaseChaitin::gather_lrg_masks( bool after_aggressive ) { // Define platform specific register pressure #if defined(ARM32) lrg.set_reg_pressure(2); -#elif defined(IA32) - if( ireg == Op_RegL ) { - lrg.set_reg_pressure(2); - } else { - lrg.set_reg_pressure(1); - } #else lrg.set_reg_pressure(1); // normally one value per register #endif @@ -1126,7 +1121,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 +1159,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 +1400,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 +1454,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 +1505,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 +1528,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 +1540,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 +1617,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 +1696,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 @@ public: 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 @@ public: // 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 @@ private: 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 @@ public: } 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 823745ea8e7..cf5bc8ce643 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -938,15 +938,11 @@ const Type* DivDNode::Value(PhaseGVN* phase) const { if( t2 == TypeD::ONE ) return t1; - // IA32 would only execute this for non-strict FP, which is never the - // case now. -#if ! defined(IA32) // If divisor is a constant and not zero, divide them numbers if( t1->base() == Type::DoubleCon && t2->base() == Type::DoubleCon && t2->getd() != 0.0 ) // could be negative zero return TypeD::make( t1->getd()/t2->getd() ); -#endif // If the dividend is a constant zero // Note: if t1 and t2 are zero then result is NaN (JVMS page 213) @@ -1668,10 +1664,10 @@ Node *DivModINode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; if (proj->_con == div_proj_num) { - rm = match->divI_proj_mask(); + rm.assignFrom(match->divI_proj_mask()); } else { assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm = match->modI_proj_mask(); + rm.assignFrom(match->modI_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } @@ -1683,10 +1679,10 @@ Node *DivModLNode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; if (proj->_con == div_proj_num) { - rm = match->divL_proj_mask(); + rm.assignFrom(match->divL_proj_mask()); } else { assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm = match->modL_proj_mask(); + rm.assignFrom(match->modL_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } @@ -1721,10 +1717,10 @@ Node* UDivModINode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; if (proj->_con == div_proj_num) { - rm = match->divI_proj_mask(); + rm.assignFrom(match->divI_proj_mask()); } else { assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm = match->modI_proj_mask(); + rm.assignFrom(match->modI_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } @@ -1736,10 +1732,10 @@ Node* UDivModLNode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; if (proj->_con == div_proj_num) { - rm = match->divL_proj_mask(); + rm.assignFrom(match->divL_proj_mask()); } else { assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm = match->modL_proj_mask(); + rm.assignFrom(match->modL_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index ad7b64f93f5..754b0fa8d1c 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -102,6 +102,8 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool (orig_callee->intrinsic_id() == vmIntrinsics::_linkToVirtual) || (orig_callee->intrinsic_id() == vmIntrinsics::_linkToInterface); + const bool check_access = !orig_callee->is_method_handle_intrinsic(); // method handle intrinsics don't perform access checks + // Dtrace currently doesn't work unless all calls are vanilla if (env()->dtrace_method_probes()) { allow_inline = false; @@ -239,7 +241,8 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool // this invoke because it may lead to bimorphic inlining which // a speculative type should help us avoid. receiver_method = callee->resolve_invoke(jvms->method()->holder(), - speculative_receiver_type); + speculative_receiver_type, + check_access); if (receiver_method == nullptr) { speculative_receiver_type = nullptr; } else { @@ -256,8 +259,9 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool (morphism == 2 && UseBimorphicInlining))) { // receiver_method = profile.method(); // Profiles do not suggest methods now. Look it up in the major receiver. + assert(check_access, "required"); receiver_method = callee->resolve_invoke(jvms->method()->holder(), - profile.receiver(0)); + profile.receiver(0)); } if (receiver_method != nullptr) { // The single majority receiver sufficiently outweighs the minority. @@ -268,8 +272,9 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool CallGenerator* next_hit_cg = nullptr; ciMethod* next_receiver_method = nullptr; if (morphism == 2 && UseBimorphicInlining) { + assert(check_access, "required"); next_receiver_method = callee->resolve_invoke(jvms->method()->holder(), - profile.receiver(1)); + profile.receiver(1)); if (next_receiver_method != nullptr) { next_hit_cg = this->call_generator(next_receiver_method, vtable_index, !call_does_dispatch, jvms, @@ -342,6 +347,7 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool if (singleton != nullptr) { assert(singleton != declared_interface, "not a unique implementor"); + assert(check_access, "required"); ciMethod* cha_monomorphic_target = callee->find_monomorphic_target(caller->holder(), declared_interface, singleton); diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index cbf0666c00e..a148b167ee3 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -1296,9 +1296,8 @@ void ConnectionGraph::reduce_phi(PhiNode* ophi, GrowableArray &alloc_wo castpps.push(use); } else if (use->is_AddP() || use->is_Cmp()) { others.push(use); - } else if (use->is_SafePoint()) { - // processed later } else { + // Safepoints to be processed later; other users aren't expected here assert(use->is_SafePoint(), "Unexpected user of reducible Phi %d -> %d:%s:%d", ophi->_idx, use->_idx, use->Name(), use->outcnt()); } } diff --git a/src/hotspot/share/opto/gcm.cpp b/src/hotspot/share/opto/gcm.cpp index 72c001a64c4..4a1553b1e00 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/idealGraphPrinter.cpp b/src/hotspot/share/opto/idealGraphPrinter.cpp index 19eaf1b369e..9a35691f7ff 100644 --- a/src/hotspot/share/opto/idealGraphPrinter.cpp +++ b/src/hotspot/share/opto/idealGraphPrinter.cpp @@ -161,6 +161,7 @@ void IdealGraphPrinter::init(const char* file_name, bool use_multiple_files, boo _current_method = nullptr; _network_stream = nullptr; _append = append; + _parse = nullptr; if (file_name != nullptr) { init_file_stream(file_name, use_multiple_files); @@ -399,6 +400,14 @@ void IdealGraphPrinter::set_traverse_outs(bool b) { _traverse_outs = b; } +const Parse* IdealGraphPrinter::parse() { + return _parse; +} + +void IdealGraphPrinter::set_parse(const Parse* parse) { + _parse = parse; +} + void IdealGraphPrinter::visit_node(Node* n, bool edges) { if (edges) { @@ -996,6 +1005,17 @@ void IdealGraphPrinter::print(const char* name, Node* node, GrowableArraymap() == nullptr) { + print_prop("map", "-"); + } else { + print_prop("map", _parse->map()->_idx); + } + print_prop("block", _parse->block()->rpo()); + stringStream shortStr; + _parse->flow()->method()->print_short_name(&shortStr); + print_prop("method", shortStr.freeze()); + } tail(PROPERTIES_ELEMENT); head(NODES_ELEMENT); diff --git a/src/hotspot/share/opto/idealGraphPrinter.hpp b/src/hotspot/share/opto/idealGraphPrinter.hpp index 7e68ce6c00f..df1c6b254d5 100644 --- a/src/hotspot/share/opto/idealGraphPrinter.hpp +++ b/src/hotspot/share/opto/idealGraphPrinter.hpp @@ -42,6 +42,7 @@ class Node; class InlineTree; class ciMethod; class JVMState; +class Parse; class IdealGraphPrinter : public CHeapObj { private: @@ -115,6 +116,7 @@ class IdealGraphPrinter : public CHeapObj { Compile *C; double _max_freq; bool _append; + const Parse* _parse; // Walk the native stack and print relevant C2 frames as IGV properties (if // graph_name == nullptr) or the graph name based on the highest C2 frame (if @@ -156,6 +158,8 @@ class IdealGraphPrinter : public CHeapObj { bool traverse_outs(); void set_traverse_outs(bool b); + const Parse* parse(); + void set_parse(const Parse* parser); void print_inlining(); void begin_method(); void end_method(); 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 b397c2c5852..83e975b95a2 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 fd7644f8587..53a503866fa 100644 --- a/src/hotspot/share/opto/lcm.cpp +++ b/src/hotspot/share/opto/lcm.cpp @@ -855,12 +855,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); } } } @@ -884,7 +884,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 @@ -907,12 +907,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()->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++); @@ -1164,10 +1164,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/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index ee3a3d3ba47..bd8a550b9ab 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -7273,7 +7273,7 @@ bool LibraryCallKit::inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id) { const TypeInstPtr* tinst = _gvn.type(cipherBlockChaining_object)->isa_instptr(); assert(tinst != nullptr, "CBC obj is null"); assert(tinst->is_loaded(), "CBC obj is not loaded"); - ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AESCrypt")); + ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AES_Crypt")); assert(klass_AESCrypt->is_loaded(), "predicate checks that this class is loaded"); ciInstanceKlass* instklass_AESCrypt = klass_AESCrypt->as_instance_klass(); @@ -7359,7 +7359,7 @@ bool LibraryCallKit::inline_electronicCodeBook_AESCrypt(vmIntrinsics::ID id) { const TypeInstPtr* tinst = _gvn.type(electronicCodeBook_object)->isa_instptr(); assert(tinst != nullptr, "ECB obj is null"); assert(tinst->is_loaded(), "ECB obj is not loaded"); - ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AESCrypt")); + ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AES_Crypt")); assert(klass_AESCrypt->is_loaded(), "predicate checks that this class is loaded"); ciInstanceKlass* instklass_AESCrypt = klass_AESCrypt->as_instance_klass(); @@ -7429,7 +7429,7 @@ bool LibraryCallKit::inline_counterMode_AESCrypt(vmIntrinsics::ID id) { const TypeInstPtr* tinst = _gvn.type(counterMode_object)->isa_instptr(); assert(tinst != nullptr, "CTR obj is null"); assert(tinst->is_loaded(), "CTR obj is not loaded"); - ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AESCrypt")); + ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AES_Crypt")); assert(klass_AESCrypt->is_loaded(), "predicate checks that this class is loaded"); ciInstanceKlass* instklass_AESCrypt = klass_AESCrypt->as_instance_klass(); const TypeKlassPtr* aklass = TypeKlassPtr::make(instklass_AESCrypt); @@ -7469,7 +7469,7 @@ Node * LibraryCallKit::get_key_start_from_aescrypt_object(Node *aescrypt_object) // However, ppc64 vncipher processes MixColumns and requires the same round keys with encryption. // The ppc64 and riscv64 stubs of encryption and decryption use the same round keys (sessionK[0]). Node* objSessionK = load_field_from_object(aescrypt_object, "sessionK", "[[I"); - assert (objSessionK != nullptr, "wrong version of com.sun.crypto.provider.AESCrypt"); + assert (objSessionK != nullptr, "wrong version of com.sun.crypto.provider.AES_Crypt"); if (objSessionK == nullptr) { return (Node *) nullptr; } @@ -7477,7 +7477,7 @@ Node * LibraryCallKit::get_key_start_from_aescrypt_object(Node *aescrypt_object) #else Node* objAESCryptKey = load_field_from_object(aescrypt_object, "K", "[I"); #endif // PPC64 - assert (objAESCryptKey != nullptr, "wrong version of com.sun.crypto.provider.AESCrypt"); + assert (objAESCryptKey != nullptr, "wrong version of com.sun.crypto.provider.AES_Crypt"); if (objAESCryptKey == nullptr) return (Node *) nullptr; // now have the array, need to get the start address of the K array @@ -7512,7 +7512,7 @@ Node* LibraryCallKit::inline_cipherBlockChaining_AESCrypt_predicate(bool decrypt assert(tinst->is_loaded(), "CBCobj is not loaded"); // we want to do an instanceof comparison against the AESCrypt class - ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AESCrypt")); + ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AES_Crypt")); if (!klass_AESCrypt->is_loaded()) { // if AESCrypt is not even loaded, we never take the intrinsic fast path Node* ctrl = control(); @@ -7575,7 +7575,7 @@ Node* LibraryCallKit::inline_electronicCodeBook_AESCrypt_predicate(bool decrypti assert(tinst->is_loaded(), "ECBobj is not loaded"); // we want to do an instanceof comparison against the AESCrypt class - ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AESCrypt")); + ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AES_Crypt")); if (!klass_AESCrypt->is_loaded()) { // if AESCrypt is not even loaded, we never take the intrinsic fast path Node* ctrl = control(); @@ -7635,7 +7635,7 @@ Node* LibraryCallKit::inline_counterMode_AESCrypt_predicate() { assert(tinst->is_loaded(), "CTRobj is not loaded"); // we want to do an instanceof comparison against the AESCrypt class - ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AESCrypt")); + ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AES_Crypt")); if (!klass_AESCrypt->is_loaded()) { // if AESCrypt is not even loaded, we never take the intrinsic fast path Node* ctrl = control(); @@ -8608,7 +8608,7 @@ bool LibraryCallKit::inline_galoisCounterMode_AESCrypt() { const TypeInstPtr* tinst = _gvn.type(gctr_object)->isa_instptr(); assert(tinst != nullptr, "GCTR obj is null"); assert(tinst->is_loaded(), "GCTR obj is not loaded"); - ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AESCrypt")); + ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AES_Crypt")); assert(klass_AESCrypt->is_loaded(), "predicate checks that this class is loaded"); ciInstanceKlass* instklass_AESCrypt = klass_AESCrypt->as_instance_klass(); const TypeKlassPtr* aklass = TypeKlassPtr::make(instklass_AESCrypt); @@ -8662,7 +8662,7 @@ Node* LibraryCallKit::inline_galoisCounterMode_AESCrypt_predicate() { assert(tinst->is_loaded(), "GCTR obj is not loaded"); // we want to do an instanceof comparison against the AESCrypt class - ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AESCrypt")); + ciKlass* klass_AESCrypt = tinst->instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AES_Crypt")); if (!klass_AESCrypt->is_loaded()) { // if AESCrypt is not even loaded, we never take the intrinsic fast path Node* ctrl = control(); diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index 561f3ce75cb..61a7ed29c3e 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -127,7 +127,7 @@ IfTrueNode* PhaseIdealLoop::create_new_if_for_predicate(const ParsePredicateSucc } // Move nodes pinned on the projection or whose control is set to // the projection to the region. - lazy_replace(uncommon_proj_orig, uncommon_trap); + replace_node_and_forward_ctrl(uncommon_proj_orig, uncommon_trap); } else { // Find region's edge corresponding to uncommon_proj for (; proj_index < uncommon_trap->req(); proj_index++) diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index f92833e9e1c..94c781d82c2 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -4071,7 +4071,7 @@ bool PhaseIdealLoop::intrinsify_fill(IdealLoopTree* lpt) { Node* outer_sfpt = head->outer_safepoint(); Node* in = outer_sfpt->in(0); Node* outer_out = head->outer_loop_exit(); - lazy_replace(outer_out, in); + replace_node_and_forward_ctrl(outer_out, in); _igvn.replace_input_of(outer_sfpt, 0, C->top()); } @@ -4080,7 +4080,7 @@ bool PhaseIdealLoop::intrinsify_fill(IdealLoopTree* lpt) { // state of the loop. It's safe in this case to replace it with the // result_mem. _igvn.replace_node(store->in(MemNode::Memory), result_mem); - lazy_replace(exit, result_ctrl); + replace_node_and_forward_ctrl(exit, result_ctrl); _igvn.replace_node(store, result_mem); // Any uses the increment outside of the loop become the loop limit. _igvn.replace_node(head->incr(), head->limit()); diff --git a/src/hotspot/share/opto/loopUnswitch.cpp b/src/hotspot/share/opto/loopUnswitch.cpp index b40a0492df5..287f8354dc1 100644 --- a/src/hotspot/share/opto/loopUnswitch.cpp +++ b/src/hotspot/share/opto/loopUnswitch.cpp @@ -520,9 +520,12 @@ IfTrueNode* PhaseIdealLoop::create_new_if_for_multiversion(IfTrueNode* multivers region->add_req(new_if_false); register_control(region, lp, new_multiversion_slow_proj); - // Hook region into slow_path, in stead of the multiversion_slow_proj. + // Hook region into slow_path, instead 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 replace_node_and_forward_ctrl 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. + replace_node_and_forward_ctrl(multiversion_slow_proj, region); return new_if_true; } diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index e8058edb4e5..b6c4d1b8574 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -1709,8 +1709,8 @@ LoopNode* PhaseIdealLoop::create_inner_head(IdealLoopTree* loop, BaseCountedLoop set_loop(new_inner_exit, loop); set_idom(new_inner_head, idom(head), dom_depth(head)); set_idom(new_inner_exit, idom(exit_test), dom_depth(exit_test)); - lazy_replace(head, new_inner_head); - lazy_replace(exit_test, new_inner_exit); + replace_node_and_forward_ctrl(head, new_inner_head); + replace_node_and_forward_ctrl(exit_test, new_inner_exit); loop->_head = new_inner_head; return new_inner_head; } @@ -2382,7 +2382,7 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_ return false; } if (is_deleteable_safept(backedge_sfpt)) { - lazy_replace(backedge_sfpt, iftrue); + replace_node_and_forward_ctrl(backedge_sfpt, iftrue); if (loop->_safepts != nullptr) { loop->_safepts->yank(backedge_sfpt); } @@ -2483,8 +2483,8 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_ set_loop(iff2, get_loop(iffalse)); // Lazy update of 'get_ctrl' mechanism. - lazy_replace(iffalse, iff2); - lazy_replace(iftrue, ift2); + replace_node_and_forward_ctrl(iffalse, iff2); + replace_node_and_forward_ctrl(iftrue, ift2); // Swap names iffalse = iff2; @@ -2499,7 +2499,7 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_ set_idom(iftrue, le, dd+1); set_idom(iffalse, le, dd+1); assert(iff->outcnt() == 0, "should be dead now"); - lazy_replace( iff, le ); // fix 'get_ctrl' + replace_node_and_forward_ctrl(iff, le); // fix 'get_ctrl' Node* entry_control = init_control; bool strip_mine_loop = iv_bt == T_INT && @@ -2525,7 +2525,7 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_ loop->_head = l; // Fix all data nodes placed at the old loop head. // Uses the lazy-update mechanism of 'get_ctrl'. - lazy_replace( x, l ); + replace_node_and_forward_ctrl(x, l); set_idom(l, entry_control, dom_depth(entry_control) + 1); if (iv_bt == T_INT && (LoopStripMiningIter == 0 || strip_mine_loop)) { @@ -2551,7 +2551,7 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_ register_control(sfpt_clone, outer_ilt, iffalse, body_populated); set_idom(outer_le, sfpt_clone, dom_depth(sfpt_clone)); } - lazy_replace(sfpt, sfpt->in(TypeFunc::Control)); + replace_node_and_forward_ctrl(sfpt, sfpt->in(TypeFunc::Control)); if (loop->_safepts != nullptr) { loop->_safepts->yank(sfpt); } @@ -3516,7 +3516,7 @@ void OuterStripMinedLoopNode::transform_to_counted_loop(PhaseIterGVN* igvn, Phas if (iloop == nullptr) { igvn->replace_node(outer_le, new_end); } else { - iloop->lazy_replace(outer_le, new_end); + iloop->replace_node_and_forward_ctrl(outer_le, new_end); } // the backedge of the inner loop must be rewired to the new loop end Node* backedge = cle->proj_out(true); @@ -4487,7 +4487,7 @@ void IdealLoopTree::remove_safepoints(PhaseIdealLoop* phase, bool keep_one) { Node* n = sfpts->at(i); assert(phase->get_loop(n) == this, ""); if (n != keep && phase->is_deleteable_safept(n)) { - phase->lazy_replace(n, n->in(TypeFunc::Control)); + phase->replace_node_and_forward_ctrl(n, n->in(TypeFunc::Control)); } } } @@ -6145,7 +6145,7 @@ void PhaseIdealLoop::build_loop_early( VectorSet &visited, Node_List &worklist, if( !_verify_only && !_verify_me && ilt->_has_sfpt && n->Opcode() == Op_SafePoint && is_deleteable_safept(n)) { Node *in = n->in(TypeFunc::Control); - lazy_replace(n,in); // Pull safepoint now + replace_node_and_forward_ctrl(n, in); // Pull safepoint now if (ilt->_safepts != nullptr) { ilt->_safepts->yank(n); } diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 1101de81595..aee934b0d11 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -890,6 +890,9 @@ class PhaseIdealLoop : public PhaseTransform { friend class AutoNodeBudget; // Map loop membership for CFG nodes, and ctrl for non-CFG nodes. + // + // Exception: dead CFG nodes may instead have a ctrl/idom forwarding + // installed. See: forward_ctrl Node_List _loop_or_ctrl; // Pre-computed def-use info @@ -956,7 +959,13 @@ class PhaseIdealLoop : public PhaseTransform { public: // Set/get control node out. Set lower bit to distinguish from IdealLoopTree - // Returns true if "n" is a data node, false if it's a control node. + // Returns true if "n" is a data node, false if it's a CFG node. + // + // Exception: + // control nodes that are dead because of "replace_node_and_forward_ctrl" + // or have otherwise modified their ctrl state by "forward_ctrl". + // They return "true", because they have a ctrl "forwarding" to the other ctrl node they + // were replaced with. bool has_ctrl(const Node* n) const { return ((intptr_t)_loop_or_ctrl[n->_idx]) & 1; } private: @@ -1070,17 +1079,18 @@ public: } set_ctrl(n, ctrl); } - // Control nodes can be replaced or subsumed. During this pass they - // get their replacement Node in slot 1. Instead of updating the block - // location of all Nodes in the subsumed block, we lazily do it. As we - // pull such a subsumed block out of the array, we write back the final - // correct block. + + // Retrieves the ctrl for a data node i. Node* get_ctrl(const Node* i) { - assert(has_node(i), ""); - Node *n = get_ctrl_no_update(i); + assert(has_node(i) && has_ctrl(i), "must be data node with ctrl"); + Node* n = get_ctrl_no_update(i); + // We store the found ctrl in the side-table again. In most cases, + // this is a no-op, since we just read from _loop_or_ctrl. But in cases + // where there was a ctrl forwarding via dead ctrl nodes, this shortens the path. + // See: forward_ctrl _loop_or_ctrl.map(i->_idx, (Node*)((intptr_t)n + 1)); - assert(has_node(i) && has_ctrl(i), ""); - assert(n == find_non_split_ctrl(n), "must return legal ctrl" ); + assert(has_node(i) && has_ctrl(i), "must still be data node with ctrl"); + assert(n == find_non_split_ctrl(n), "must return legal ctrl"); return n; } @@ -1097,24 +1107,34 @@ public: } } +private: Node* get_ctrl_no_update_helper(const Node* i) const { - assert(has_ctrl(i), "should be control, not loop"); + // We expect only data nodes (which must have a ctrl set), or + // dead ctrl nodes that have a ctrl "forwarding". + // See: forward_ctrl. + assert(has_ctrl(i), "only data nodes or ctrl nodes with ctrl forwarding expected"); return (Node*)(((intptr_t)_loop_or_ctrl[i->_idx]) & ~1); } + // Compute the ctrl of node i, jumping over ctrl forwardings. Node* get_ctrl_no_update(const Node* i) const { - assert( has_ctrl(i), "" ); - Node *n = get_ctrl_no_update_helper(i); - if (!n->in(0)) { - // Skip dead CFG nodes + assert(has_ctrl(i), "only data nodes expected"); + Node* n = get_ctrl_no_update_helper(i); + if (n->in(0) == nullptr) { + // We encountered a dead CFG node. + // If everything went right, this dead CFG node should have had a ctrl + // forwarding installed, using "forward_ctrl". We now have to jump from + // the old (dead) ctrl node to the new (live) ctrl node, in possibly + // multiple ctrl forwarding steps. do { n = get_ctrl_no_update_helper(n); - } while (!n->in(0)); + } while (n->in(0) == nullptr); n = find_non_split_ctrl(n); } return n; } +public: // Check for loop being set // "n" must be a control node. Returns true if "n" is known to be in a loop. bool has_loop( Node *n ) const { @@ -1125,19 +1145,43 @@ public: void set_loop( Node *n, IdealLoopTree *loop ) { _loop_or_ctrl.map(n->_idx, (Node*)loop); } - // Lazy-dazy update of 'get_ctrl' and 'idom_at' mechanisms. Replace - // the 'old_node' with 'new_node'. Kill old-node. Add a reference - // from old_node to new_node to support the lazy update. Reference - // replaces loop reference, since that is not needed for dead node. - void lazy_update(Node *old_node, Node *new_node) { + + // Install a ctrl "forwarding" from an old (dead) control node. + // This is a "lazy" update of the "get_ctrl" and "idom" mechanism: + // - Install a forwarding from old_node (dead ctrl) to new_node. + // - When querying "get_ctrl": jump from data node over possibly + // multiple dead ctrl nodes with ctrl forwarding to eventually + // reach a live ctrl node. Shorten the path to avoid chasing the + // forwarding in the future. + // - When querying "idom": from some node get its old idom, which + // may be dead but has an idom forwarding to the new and live + // idom. Shorten the path to avoid chasing the forwarding in the + // future. + // Note: while the "idom" information is stored in the "_idom" + // side-table, the idom forwarding piggybacks on the ctrl + // forwarding on "_loop_or_ctrl". + // Using "forward_ctrl" allows us to only edit the entry for the old + // dead node now, and we do not have to update all the nodes that had + // the old_node as their "get_ctrl" or "idom". We clean up the forwarding + // links when we query "get_ctrl" or "idom" for these nodes the next time. + void forward_ctrl(Node* old_node, Node* new_node) { + assert(!has_ctrl(old_node) && old_node->is_CFG() && old_node->in(0) == nullptr, + "must be dead ctrl (CFG) node"); + assert(!has_ctrl(new_node) && new_node->is_CFG() && new_node->in(0) != nullptr, + "must be live ctrl (CFG) node"); assert(old_node != new_node, "no cycles please"); // Re-use the side array slot for this node to provide the // forwarding pointer. _loop_or_ctrl.map(old_node->_idx, (Node*)((intptr_t)new_node + 1)); + assert(has_ctrl(old_node), "must have installed ctrl forwarding"); } - void lazy_replace(Node *old_node, Node *new_node) { + + // Replace the old ctrl node with a new ctrl node. + // - Update the node inputs of all uses. + // - Lazily update the ctrl and idom info of all uses, via a ctrl/idom forwarding. + void replace_node_and_forward_ctrl(Node* old_node, Node* new_node) { _igvn.replace_node(old_node, new_node); - lazy_update(old_node, new_node); + forward_ctrl(old_node, new_node); } private: @@ -1208,29 +1252,41 @@ private: Node* insert_convert_node_if_needed(BasicType target, Node* input); -public: Node* idom_no_update(Node* d) const { return idom_no_update(d->_idx); } - Node* idom_no_update(uint didx) const { - assert(didx < _idom_size, "oob"); - Node* n = _idom[didx]; + Node* idom_no_update(uint node_idx) const { + assert(node_idx < _idom_size, "oob"); + Node* n = _idom[node_idx]; assert(n != nullptr,"Bad immediate dominator info."); while (n->in(0) == nullptr) { // Skip dead CFG nodes + // We encountered a dead CFG node. + // If everything went right, this dead CFG node should have had an idom + // forwarding installed, using "forward_ctrl". We now have to jump from + // the old (dead) idom node to the new (live) idom node, in possibly + // multiple idom forwarding steps. + // Note that we piggyback on "_loop_or_ctrl" to do the forwarding, + // since we forward both "get_ctrl" and "idom" from the dead to the + // new live ctrl/idom nodes. n = (Node*)(((intptr_t)_loop_or_ctrl[n->_idx]) & ~1); assert(n != nullptr,"Bad immediate dominator info."); } return n; } - Node *idom(Node* d) const { - return idom(d->_idx); +public: + Node* idom(Node* n) const { + return idom(n->_idx); } - Node *idom(uint didx) const { - Node *n = idom_no_update(didx); - _idom[didx] = n; // Lazily remove dead CFG nodes from table. + Node* idom(uint node_idx) const { + Node* n = idom_no_update(node_idx); + // We store the found idom in the side-table again. In most cases, + // this is a no-op, since we just read from _idom. But in cases where + // there was an idom forwarding via dead idom nodes, this shortens the path. + // See: forward_ctrl + _idom[node_idx] = n; return n; } diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index ae7b318ece4..71e52f373f7 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -2742,12 +2742,13 @@ void PhaseIdealLoop::fix_ctrl_uses(const Node_List& body, const IdealLoopTree* l } assert(use->is_Proj(), "loop exit should be projection"); - // lazy_replace() below moves all nodes that are: + // replace_node_and_forward_ctrl() below moves all nodes that are: // - control dependent on the loop exit or // - have control set to the loop exit - // below the post-loop merge point. lazy_replace() takes a dead control as first input. To make it - // possible to use it, the loop exit projection is cloned and becomes the new exit projection. The initial one - // becomes dead and is "replaced" by the region. + // below the post-loop merge point. + // replace_node_and_forward_ctrl() takes a dead control as first input. + // To make it possible to use it, the loop exit projection is cloned and becomes the + // new exit projection. The initial one becomes dead and is "replaced" by the region. Node* use_clone = use->clone(); register_control(use_clone, use_loop, idom(use), dom_depth(use)); // Now finish up 'r' @@ -2756,7 +2757,7 @@ void PhaseIdealLoop::fix_ctrl_uses(const Node_List& body, const IdealLoopTree* l _igvn.register_new_node_with_optimizer(r); set_loop(r, use_loop); set_idom(r, (side_by_side_idom == nullptr) ? newuse->in(0) : side_by_side_idom, dd_r); - lazy_replace(use, r); + replace_node_and_forward_ctrl(use, r); // Map the (cloned) old use to the new merge point old_new.map(use_clone->_idx, r); } // End of if a loop-exit test diff --git a/src/hotspot/share/opto/machnode.cpp b/src/hotspot/share/opto/machnode.cpp index 5da929e4748..e58befd8032 100644 --- a/src/hotspot/share/opto/machnode.cpp +++ b/src/hotspot/share/opto/machnode.cpp @@ -525,7 +525,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; } } @@ -619,8 +619,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 43e9a35df34..093f466678c 100644 --- a/src/hotspot/share/opto/machnode.hpp +++ b/src/hotspot/share/opto/machnode.hpp @@ -99,7 +99,7 @@ public: return ::as_FloatRegister(reg(ra_, node, idx)); } -#if defined(IA32) || defined(AMD64) +#if defined(AMD64) KRegister as_KRegister(PhaseRegAlloc *ra_, const Node *node) const { return ::as_KRegister(reg(ra_, node)); } @@ -737,7 +737,7 @@ public: 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; @@ -769,7 +769,7 @@ public: 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 7d73487cf88..c63cefe7ac2 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -176,12 +176,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 a Java-signature return type into return register-value @@ -195,9 +195,9 @@ void Matcher::match( ) { OptoRegPair regs = return_value(ireg); // And mask for same - _return_value_mask = RegMask(regs.first()); + _return_value_mask.assignFrom(RegMask(regs.first())); if( OptoReg::is_valid(regs.second()) ) - _return_value_mask.Insert(regs.second()); + _return_value_mask.insert(regs.second()); } // --------------- @@ -261,7 +261,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; @@ -273,11 +273,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); @@ -422,11 +422,11 @@ static RegMask *init_input_masks( uint size, RegMask &ret_adr, RegMask &fp ) { new (rms + i) RegMask(Compile::current()->comp_arena()); } // Do all the pre-defined register masks - rms[TypeFunc::Control ] = RegMask::Empty; - rms[TypeFunc::I_O ] = RegMask::Empty; - rms[TypeFunc::Memory ] = RegMask::Empty; - rms[TypeFunc::ReturnAdr] = ret_adr; - rms[TypeFunc::FramePtr ] = fp; + rms[TypeFunc::Control ].assignFrom(RegMask::EMPTY); + rms[TypeFunc::I_O ].assignFrom(RegMask::EMPTY); + rms[TypeFunc::Memory ].assignFrom(RegMask::EMPTY); + rms[TypeFunc::ReturnAdr].assignFrom(ret_adr); + rms[TypeFunc::FramePtr ].assignFrom(fp); return rms; } @@ -471,15 +471,15 @@ 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()); @@ -488,44 +488,44 @@ void Matcher::init_first_stack_mask() { assert(aligned_stack_mask.is_infinite_stack(), "should be infinite stack"); RegMask scalable_stack_mask(aligned_stack_mask, C->comp_arena()); - *idealreg2spillmask[Op_RegP] = *idealreg2regmask[Op_RegP]; + idealreg2spillmask[Op_RegP]->assignFrom(*idealreg2regmask[Op_RegP]); #ifdef _LP64 - *idealreg2spillmask[Op_RegN] = *idealreg2regmask[Op_RegN]; - idealreg2spillmask[Op_RegN]->OR(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)) { @@ -538,45 +538,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()) { @@ -586,31 +586,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) { @@ -618,20 +618,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 } @@ -639,40 +639,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_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] = *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_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_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_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---------------------------------- @@ -702,8 +702,9 @@ void Matcher::Fixup_Save_On_Entry( ) { RegMask *ret_rms = init_input_masks( ret_edge_cnt + soe_cnt, _return_addr_mask, c_frame_ptr_mask ); // Returns have 0 or 1 returned values depending on call signature. // Return register is specified by return_value in the AD file. - if (ret_edge_cnt > TypeFunc::Parms) - ret_rms[TypeFunc::Parms+0] = _return_value_mask; + if (ret_edge_cnt > TypeFunc::Parms) { + ret_rms[TypeFunc::Parms + 0].assignFrom(_return_value_mask); + } // Input RegMask array shared by all ForwardExceptions uint forw_exc_edge_cnt = TypeFunc::Parms; @@ -715,10 +716,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 } @@ -737,8 +738,8 @@ void Matcher::Fixup_Save_On_Entry( ) { for( i=1; i < root->req(); i++ ) { MachReturnNode *m = root->in(i)->as_MachReturn(); if( m->ideal_Opcode() == Op_TailCall ) { - tail_call_rms[TypeFunc::Parms+0] = m->MachNode::in_RegMask(TypeFunc::Parms+0); - tail_call_rms[TypeFunc::Parms+1] = m->MachNode::in_RegMask(TypeFunc::Parms+1); + tail_call_rms[TypeFunc::Parms + 0].assignFrom(m->MachNode::in_RegMask(TypeFunc::Parms + 0)); + tail_call_rms[TypeFunc::Parms + 1].assignFrom(m->MachNode::in_RegMask(TypeFunc::Parms + 1)); break; } } @@ -750,8 +751,8 @@ void Matcher::Fixup_Save_On_Entry( ) { for( i=1; i < root->req(); i++ ) { MachReturnNode *m = root->in(i)->as_MachReturn(); if( m->ideal_Opcode() == Op_TailJump ) { - tail_jump_rms[TypeFunc::Parms+0] = m->MachNode::in_RegMask(TypeFunc::Parms+0); - tail_jump_rms[TypeFunc::Parms+1] = m->MachNode::in_RegMask(TypeFunc::Parms+1); + tail_jump_rms[TypeFunc::Parms + 0].assignFrom(m->MachNode::in_RegMask(TypeFunc::Parms + 0)); + tail_jump_rms[TypeFunc::Parms + 1].assignFrom(m->MachNode::in_RegMask(TypeFunc::Parms + 1)); break; } } @@ -784,14 +785,14 @@ void Matcher::Fixup_Save_On_Entry( ) { if( is_save_on_entry(i) ) { // Add the save-on-entry to the mask array - ret_rms [ ret_edge_cnt] = mreg2regmask[i]; - reth_rms [ reth_edge_cnt] = mreg2regmask[i]; - tail_call_rms[tail_call_edge_cnt] = mreg2regmask[i]; - tail_jump_rms[tail_jump_edge_cnt] = mreg2regmask[i]; - forw_exc_rms [ forw_exc_edge_cnt] = mreg2regmask[i]; + ret_rms [ ret_edge_cnt].assignFrom(mreg2regmask[i]); + reth_rms [ reth_edge_cnt].assignFrom(mreg2regmask[i]); + tail_call_rms[tail_call_edge_cnt].assignFrom(mreg2regmask[i]); + tail_jump_rms[tail_jump_edge_cnt].assignFrom(mreg2regmask[i]); + forw_exc_rms [ forw_exc_edge_cnt].assignFrom(mreg2regmask[i]); // Halts need the SOE registers, but only in the stack as debug info. // A just-prior uncommon-trap or deoptimization will use the SOE regs. - halt_rms [ halt_edge_cnt] = *idealreg2spillmask[_register_save_type[i]]; + halt_rms [ halt_edge_cnt].assignFrom(*idealreg2spillmask[_register_save_type[i]]); Node *mproj; @@ -802,12 +803,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 } @@ -815,12 +816,12 @@ void Matcher::Fixup_Save_On_Entry( ) { _register_save_type[i-1] == Op_RegF && _register_save_type[i ] == Op_RegF && is_save_on_entry(i-1) ) { - ret_rms [ ret_edge_cnt] = RegMask::Empty; - reth_rms [ reth_edge_cnt] = RegMask::Empty; - tail_call_rms[tail_call_edge_cnt] = RegMask::Empty; - tail_jump_rms[tail_jump_edge_cnt] = RegMask::Empty; - forw_exc_rms [ forw_exc_edge_cnt] = RegMask::Empty; - halt_rms [ halt_edge_cnt] = RegMask::Empty; + ret_rms [ ret_edge_cnt].assignFrom(RegMask::EMPTY); + reth_rms [ reth_edge_cnt].assignFrom(RegMask::EMPTY); + tail_call_rms[tail_call_edge_cnt].assignFrom(RegMask::EMPTY); + tail_jump_rms[tail_jump_edge_cnt].assignFrom(RegMask::EMPTY); + forw_exc_rms [ forw_exc_edge_cnt].assignFrom(RegMask::EMPTY); + halt_rms [ halt_edge_cnt].assignFrom(RegMask::EMPTY); mproj = C->top(); } // Is this a RegI low half of a RegL? Double up 2 adjacent RegI's @@ -830,12 +831,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 } @@ -843,12 +844,12 @@ void Matcher::Fixup_Save_On_Entry( ) { _register_save_type[i-1] == Op_RegI && _register_save_type[i ] == Op_RegI && is_save_on_entry(i-1) ) { - ret_rms [ ret_edge_cnt] = RegMask::Empty; - reth_rms [ reth_edge_cnt] = RegMask::Empty; - tail_call_rms[tail_call_edge_cnt] = RegMask::Empty; - tail_jump_rms[tail_jump_edge_cnt] = RegMask::Empty; - forw_exc_rms [ forw_exc_edge_cnt] = RegMask::Empty; - halt_rms [ halt_edge_cnt] = RegMask::Empty; + ret_rms [ ret_edge_cnt].assignFrom(RegMask::EMPTY); + reth_rms [ reth_edge_cnt].assignFrom(RegMask::EMPTY); + tail_call_rms[tail_call_edge_cnt].assignFrom(RegMask::EMPTY); + tail_jump_rms[tail_jump_edge_cnt].assignFrom(RegMask::EMPTY); + forw_exc_rms [ forw_exc_edge_cnt].assignFrom(RegMask::EMPTY); + halt_rms [ halt_edge_cnt].assignFrom(RegMask::EMPTY); mproj = C->top(); } else { // Make a projection for it off the Start @@ -875,34 +876,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); } } @@ -1240,8 +1241,8 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { } // Do all the pre-defined non-Empty register masks - msfpt->_in_rms[TypeFunc::ReturnAdr] = _return_addr_mask; - msfpt->_in_rms[TypeFunc::FramePtr ] = c_frame_ptr_mask; + msfpt->_in_rms[TypeFunc::ReturnAdr].assignFrom(_return_addr_mask); + msfpt->_in_rms[TypeFunc::FramePtr ].assignFrom(c_frame_ptr_mask); // Place first outgoing argument can possibly be put. OptoReg::Name begin_out_arg_area = OptoReg::add(_new_SP, C->out_preserve_stack_slots()); @@ -1315,17 +1316,17 @@ 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 } @@ -1342,11 +1343,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()->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 0b609b70ab5..e4396b423ac 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -408,14 +408,14 @@ public: 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 2080b7cbeb5..9187ef1a361 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -4325,7 +4325,7 @@ Node *MemBarNode::match( const ProjNode *proj, const Matcher *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; @@ -4572,7 +4572,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) { @@ -5784,7 +5784,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/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index 1e757c2be81..280781f686b 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -202,14 +202,6 @@ const Type* MulNode::Value(PhaseGVN* phase) const { if( t1 == Type::BOTTOM || t2 == Type::BOTTOM ) return bottom_type(); -#if defined(IA32) - // Can't trust native compilers to properly fold strict double - // multiplication with round-to-zero on this platform. - if (op == Op_MulD) { - return TypeD::DOUBLE; - } -#endif - return mul_ring(t1,t2); // Local flavor of type multiplication } @@ -1088,6 +1080,19 @@ Node* LShiftNode::IdealIL(PhaseGVN* phase, bool can_reshape, BasicType bt) { } } } + // Check for "(con0 - X) << con1" + // Transform is legal, but check for profit. Avoid breaking 'i2s' + // and 'i2b' patterns which typically fold into 'StoreC/StoreB'. + if (add1_op == Op_Sub(bt) && (bt != T_INT || con < 16)) { // Left input is a sub? + // Left input is a sub from a constant? + const TypeInteger* t11 = phase->type(add1->in(1))->isa_integer(bt); + if (t11 != nullptr && t11->is_con()) { + // Compute X << con0 + Node* lsh = phase->transform(LShiftNode::make(add1->in(2), in(2), bt)); + // Compute (con1<integercon(java_shift_left(t11->get_con_as_long(bt), con, bt), bt), lsh, bt); + } + } // Check for "(x >> C1) << C2" if (add1_op == Op_RShift(bt) || add1_op == Op_URShift(bt)) { diff --git a/src/hotspot/share/opto/multnode.cpp b/src/hotspot/share/opto/multnode.cpp index f429d5daac0..4d8d1f4246c 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 ) { 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 cca98bd8aba..497d0d1aeb0 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -2800,12 +2800,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/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index 8b44d8b3491..cb8073144a5 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -2779,11 +2779,13 @@ void Parse::do_one_bytecode() { if (C->should_print_igv(perBytecode)) { IdealGraphPrinter* printer = C->igv_printer(); char buffer[256]; - jio_snprintf(buffer, sizeof(buffer), "Bytecode %d: %s, map: %d", bci(), Bytecodes::name(bc()), map() == nullptr ? -1 : map()->_idx); + jio_snprintf(buffer, sizeof(buffer), "Bytecode %d: %s", bci(), Bytecodes::name(bc())); bool old = printer->traverse_outs(); printer->set_traverse_outs(true); + printer->set_parse(this); printer->print_graph(buffer); printer->set_traverse_outs(old); + printer->set_parse(nullptr); } #endif } 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 @@ public: 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 @@ public: } // 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 @@ public: 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 @@ public: } // 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 @@ public: } // 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 @@ public: } // 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 @@ public: } // 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 @@ public: 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 @@ public: 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 @@ public: } // 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 @@ public: // 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 @@ public: 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 @@ public: 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/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index 9de9fe5da03..32acbe57951 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -857,8 +857,9 @@ static const TypeFunc* make_jfr_write_checkpoint_Type() { const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms, fields); // create result type (range) - fields = TypeTuple::fields(0); - const TypeTuple *range = TypeTuple::make(TypeFunc::Parms, fields); + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms] = TypeInstPtr::BOTTOM; + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms + 1, fields); return TypeFunc::make(domain, range); } diff --git a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp index 76e69fd9d36..eb2b49311cf 100644 --- a/src/hotspot/share/opto/runtime.hpp +++ b/src/hotspot/share/opto/runtime.hpp @@ -48,6 +48,12 @@ // it calls the C++ code "xxx_C". The generated nmethod is saved in the // CodeCache. Exception handlers use the nmethod to get the callee-save // register OopMaps. +// +// Please ensure the return type of the runtime call matches its signature, +// even if the return value is unused. This is crucial for correct handling +// of runtime calls that return an oop and may trigger deoptimization +// on return. See rematerialize_objects() in deoptimization.cpp. + class CallInfo; // diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp index bede04c6b2c..425c0c9e99d 100644 --- a/src/hotspot/share/opto/split_if.cpp +++ b/src/hotspot/share/opto/split_if.cpp @@ -655,7 +655,7 @@ void PhaseIdealLoop::do_split_if(Node* iff, RegionNode** new_false_region, Regio // Replace in the graph with lazy-update mechanism new_iff->set_req(0, new_iff); // hook self so it does not go dead - lazy_replace(ifp, ifpx); + replace_node_and_forward_ctrl(ifp, ifpx); new_iff->set_req(0, region); // Record bits for later xforms @@ -669,9 +669,10 @@ void PhaseIdealLoop::do_split_if(Node* iff, RegionNode** new_false_region, Regio } _igvn.remove_dead_node(new_iff); // Lazy replace IDOM info with the region's dominator - lazy_replace(iff, region_dom); - lazy_update(region, region_dom); // idom must be update before handle_uses - region->set_req(0, nullptr); // Break the self-cycle. Required for lazy_update to work on region + replace_node_and_forward_ctrl(iff, region_dom); + // Break the self-cycle. Required for forward_ctrl to work on region. + region->set_req(0, nullptr); + forward_ctrl(region, region_dom); // idom must be updated before handle_use // Now make the original merge point go dead, by handling all its uses. small_cache region_cache; diff --git a/src/hotspot/share/opto/subnode.hpp b/src/hotspot/share/opto/subnode.hpp index 5acf31b45c4..2c3d9cfd35e 100644 --- a/src/hotspot/share/opto/subnode.hpp +++ b/src/hotspot/share/opto/subnode.hpp @@ -331,6 +331,7 @@ struct BoolTest { mask negate( ) const { return negate_mask(_test); } // Return the negative mask for the given mask, for both signed and unsigned comparison. static mask negate_mask(mask btm) { return mask(btm ^ 4); } + static mask unsigned_mask(mask btm) { return mask(btm | unsigned_compare); } bool is_canonical( ) const { return (_test == BoolTest::ne || _test == BoolTest::lt || _test == BoolTest::le || _test == BoolTest::overflow); } bool is_less( ) const { return _test == BoolTest::lt || _test == BoolTest::le; } bool is_greater( ) const { return _test == BoolTest::gt || _test == BoolTest::ge; } diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index 6ab1ff37de9..cc4a94aa054 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -1698,7 +1698,9 @@ VTransformBoolTest PackSet::get_bool_test(const Node_List* bool_pack) const { CmpNode* cmp0 = bol->in(1)->as_Cmp(); assert(get_pack(cmp0) != nullptr, "Bool must have matching Cmp pack"); - if (cmp0->Opcode() == Op_CmpF || cmp0->Opcode() == Op_CmpD) { + switch (cmp0->Opcode()) { + case Op_CmpF: + case Op_CmpD: // If we have a Float or Double comparison, we must be careful with // handling NaN's correctly. CmpF and CmpD have a return code, as // they are based on the java bytecodes fcmpl/dcmpl: @@ -1742,7 +1744,24 @@ VTransformBoolTest PackSet::get_bool_test(const Node_List* bool_pack) const { mask = bol->_test.negate(); is_negated = true; } - } + break; + case Op_CmpU: + case Op_CmpUL: + // When we have CmpU->Bool, the mask of the Bool has no unsigned-ness information, + // but the mask is implicitly unsigned only because of the CmpU. Since we will replace + // the CmpU->Bool with a single VectorMaskCmp, we need to now make the unsigned-ness + // explicit. + mask = BoolTest::unsigned_mask(mask); + break; + case Op_CmpI: + case Op_CmpL: + // The mask of signed int/long scalar comparisons has the same semantics + // as the mask for vector elementwise int/long comparison with VectorMaskCmp. + break; + default: + // Other Cmp ops are not expected to get here. + ShouldNotReachHere(); + } // switch return VTransformBoolTest(mask, is_negated); } diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 6b464ae8e3e..a868f6337e2 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -825,13 +825,11 @@ JVM_ENTRY(jclass, JVM_FindClassFromClass(JNIEnv *env, const char *name, SystemDictionary::class_name_symbol(name, vmSymbols::java_lang_ClassNotFoundException(), CHECK_NULL); oop from_class_oop = JNIHandles::resolve(from); - Klass* from_class = (from_class_oop == nullptr) - ? (Klass*)nullptr - : java_lang_Class::as_Klass(from_class_oop); - oop class_loader = nullptr; - if (from_class != nullptr) { - class_loader = from_class->class_loader(); - } + assert(from_class_oop != nullptr, "must be"); + Klass* from_class = java_lang_Class::as_Klass(from_class_oop); + assert(from_class != nullptr, "must be"); + oop class_loader = from_class->class_loader(); + Handle h_loader(THREAD, class_loader); jclass result = find_class_from_class_loader(env, h_name, init, h_loader, true, thread); 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/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp index 873470db17d..2b44c069dc5 100644 --- a/src/hotspot/share/prims/jvmtiEventController.cpp +++ b/src/hotspot/share/prims/jvmtiEventController.cpp @@ -495,6 +495,10 @@ JvmtiEventControllerPrivate::recompute_env_enabled(JvmtiEnvBase* env) { break; } + if (JvmtiEventController::is_execution_finished()) { + now_enabled &= VM_DEATH_BIT; + } + // Set/reset the event enabled under the tagmap lock. set_enabled_events_with_lock(env, now_enabled); @@ -537,6 +541,10 @@ JvmtiEventControllerPrivate::recompute_env_thread_enabled(JvmtiEnvThreadState* e break; } + if (JvmtiEventController::is_execution_finished()) { + now_enabled &= VM_DEATH_BIT; + } + // if anything changed do update if (now_enabled != was_enabled) { @@ -1047,7 +1055,7 @@ JvmtiEventControllerPrivate::vm_init() { void JvmtiEventControllerPrivate::vm_death() { - // events are disabled (phase has changed) + // events are disabled, see JvmtiEventController::_execution_finished JvmtiEventControllerPrivate::recompute_enabled(); } @@ -1059,6 +1067,9 @@ JvmtiEventControllerPrivate::vm_death() { JvmtiEventEnabled JvmtiEventController::_universal_global_event_enabled; +volatile bool JvmtiEventController::_execution_finished = false; +volatile int JvmtiEventController::_in_callback_count = 0; + bool JvmtiEventController::is_global_event(jvmtiEvent event_type) { assert(is_valid_event_type(event_type), "invalid event type"); @@ -1207,8 +1218,23 @@ JvmtiEventController::vm_init() { void JvmtiEventController::vm_death() { + // No new event callbacks except vm_death can be called after this point. + AtomicAccess::store(&_execution_finished, true); if (JvmtiEnvBase::environments_might_exist()) { MutexLocker mu(JvmtiThreadState_lock); JvmtiEventControllerPrivate::vm_death(); } -} + + // Some events might be still in callback for daemons and VM internal threads. + const double start = os::elapsedTime(); + const double max_wait_time = 60; + // The first time we see the callback count reach zero we know all actual + // callbacks are complete. The count could rise again, but those "callbacks" + // will immediately see `execution_finished()` and return (dropping the count). + while (in_callback_count() > 0) { + os::naked_short_sleep(100); + if (os::elapsedTime() - start > max_wait_time) { + break; + } + } +} \ No newline at end of file diff --git a/src/hotspot/share/prims/jvmtiEventController.hpp b/src/hotspot/share/prims/jvmtiEventController.hpp index 3949f3a7b7d..bd8bf887924 100644 --- a/src/hotspot/share/prims/jvmtiEventController.hpp +++ b/src/hotspot/share/prims/jvmtiEventController.hpp @@ -197,6 +197,11 @@ private: // for all environments, global array indexed by jvmtiEvent static JvmtiEventEnabled _universal_global_event_enabled; + // These fields are used to synchronize stop posting events and + // wait until already executing callbacks are finished. + volatile static bool _execution_finished; + volatile static int _in_callback_count; + public: static bool is_enabled(jvmtiEvent event_type); @@ -245,6 +250,10 @@ public: static void vm_start(); static void vm_init(); static void vm_death(); + static bool is_execution_finished(); + static void inc_in_callback_count(); + static void dec_in_callback_count(); + static int in_callback_count(); }; #endif // SHARE_PRIMS_JVMTIEVENTCONTROLLER_HPP diff --git a/src/hotspot/share/prims/jvmtiEventController.inline.hpp b/src/hotspot/share/prims/jvmtiEventController.inline.hpp index dcebd40b4b5..c5dbdcc5f8e 100644 --- a/src/hotspot/share/prims/jvmtiEventController.inline.hpp +++ b/src/hotspot/share/prims/jvmtiEventController.inline.hpp @@ -108,4 +108,22 @@ inline bool JvmtiEventController::is_enabled(jvmtiEvent event_type) { return _universal_global_event_enabled.is_enabled(event_type); } +inline bool JvmtiEventController::is_execution_finished() { + return AtomicAccess::load(&_execution_finished); +} + +inline void JvmtiEventController::inc_in_callback_count() { + AtomicAccess::inc(&_in_callback_count); +} + +inline void JvmtiEventController::dec_in_callback_count() { + AtomicAccess::dec(&_in_callback_count); +} + +inline int JvmtiEventController::in_callback_count() { + int result = AtomicAccess::load(&_in_callback_count); + assert(result >= 0, "Should be positive"); + return result; +} + #endif // SHARE_PRIMS_JVMTIEVENTCONTROLLER_INLINE_HPP diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index 0884fce2ff7..b58a88dcbb5 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -97,7 +97,12 @@ public: JvmtiJavaThreadEventTransition(JavaThread *thread) : _rm(), _transition(thread), - _hm(thread) {}; + _hm(thread) { + JvmtiEventController::inc_in_callback_count(); + }; + ~JvmtiJavaThreadEventTransition() { + JvmtiEventController::dec_in_callback_count(); + } }; // For JavaThreads which are not in _thread_in_vm state @@ -111,6 +116,7 @@ private: public: JvmtiThreadEventTransition(Thread *thread) : _rm(), _hm(thread) { + JvmtiEventController::inc_in_callback_count(); if (thread->is_Java_thread()) { _jthread = JavaThread::cast(thread); _saved_state = _jthread->thread_state(); @@ -125,11 +131,26 @@ public: } ~JvmtiThreadEventTransition() { - if (_jthread != nullptr) + if (_jthread != nullptr) { ThreadStateTransition::transition_from_native(_jthread, _saved_state); + } + JvmtiEventController::dec_in_callback_count(); } }; +// The JVMTI_...__BLOCK are used to ensure that vm_death is the last posted event. +// The callbacks are not executed after _execution_finished is set to true +// and the _in_callback_count contains the number of callbacks still in progress. +#define JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) \ + JvmtiJavaThreadEventTransition jet(thread); \ + if (JvmtiEventController::is_execution_finished()) {\ + return; \ + } +#define JVMTI_THREAD_EVENT_CALLBACK_BLOCK(thread) \ + JvmtiThreadEventTransition jet(thread); \ + if (JvmtiEventController::is_execution_finished()) { \ + return; \ + } /////////////////////////////////////////////////////////////// // @@ -662,7 +683,7 @@ void JvmtiExport::post_early_vm_start() { EVT_TRACE(JVMTI_EVENT_VM_START, ("Evt Early VM start event sent" )); JavaThread *thread = JavaThread::current(); JvmtiThreadEventMark jem(thread); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventVMStart callback = env->callbacks()->VMStart; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env()); @@ -691,7 +712,7 @@ void JvmtiExport::post_vm_start() { EVT_TRACE(JVMTI_EVENT_VM_START, ("Evt VM start event sent" )); JvmtiThreadEventMark jem(thread); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventVMStart callback = env->callbacks()->VMStart; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env()); @@ -741,7 +762,7 @@ void JvmtiExport::post_vm_initialized() { JavaThread *thread = JavaThread::current(); JvmtiThreadEventMark jem(thread); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventVMInit callback = env->callbacks()->VMInit; if (callback != nullptr) { // We map the JvmtiEnv to its Agent to measure when and for how long @@ -769,6 +790,11 @@ void JvmtiExport::post_vm_death() { JvmtiTagMap::flush_all_object_free_events(); + // It is needed to disable event generation before setting DEAD phase and wait + // until already executing events are finished. + // The VM_DEATH should be the last posted event. + JvmtiEventController::vm_death(); + JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != nullptr; env = it.next(env)) { if (env->is_enabled(JVMTI_EVENT_VM_DEATH)) { @@ -776,6 +802,7 @@ void JvmtiExport::post_vm_death() { JavaThread *thread = JavaThread::current(); JvmtiEventMark jem(thread); + // JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK must not be used here JvmtiJavaThreadEventTransition jet(thread); jvmtiEventVMDeath callback = env->callbacks()->VMDeath; if (callback != nullptr) { @@ -785,7 +812,6 @@ void JvmtiExport::post_vm_death() { } JvmtiEnvBase::set_phase(JVMTI_PHASE_DEAD); - JvmtiEventController::vm_death(); } char** @@ -967,7 +993,7 @@ class JvmtiClassFileLoadHookPoster : public StackObj { JvmtiClassFileLoadEventMark jem(_thread, _h_name, _class_loader, _h_protection_domain, _class_being_redefined); - JvmtiJavaThreadEventTransition jet(_thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(_thread) jvmtiEventClassFileLoadHook callback = env->callbacks()->ClassFileLoadHook; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), @@ -1177,7 +1203,7 @@ void JvmtiExport::post_compiled_method_unload( ResourceMark rm(thread); JvmtiEventMark jem(thread); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventCompiledMethodUnload callback = env->callbacks()->CompiledMethodUnload; if (callback != nullptr) { (*callback)(env->jvmti_external(), method, code_begin); @@ -1219,7 +1245,7 @@ void JvmtiExport::post_raw_breakpoint(JavaThread *thread, Method* method, addres JvmtiEnv *env = ets->get_env(); JvmtiLocationEventMark jem(thread, mh, location); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventBreakpoint callback = env->callbacks()->Breakpoint; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), @@ -1360,7 +1386,7 @@ void JvmtiExport::post_class_load(JavaThread *thread, Klass* klass) { JvmtiTrace::safe_get_thread_name(thread), klass==nullptr? "null" : klass->external_name() )); JvmtiClassEventMark jem(thread, klass); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventClassLoad callback = env->callbacks()->ClassLoad; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), jem.jni_class()); @@ -1401,7 +1427,7 @@ void JvmtiExport::post_class_prepare(JavaThread *thread, Klass* klass) { JvmtiTrace::safe_get_thread_name(thread), klass==nullptr? "null" : klass->external_name() )); JvmtiClassEventMark jem(thread, klass); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventClassPrepare callback = env->callbacks()->ClassPrepare; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), jem.jni_class()); @@ -1444,7 +1470,7 @@ void JvmtiExport::post_class_unload_internal(const char* name) { EVT_TRACE(EXT_EVENT_CLASS_UNLOAD, ("[?] Evt Class Unload sent %s", name)); JvmtiEventMark jem(thread); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiExtensionEvent callback = env->ext_callbacks()->ClassUnload; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), name); @@ -1490,7 +1516,7 @@ void JvmtiExport::post_thread_start(JavaThread *thread) { JvmtiTrace::safe_get_thread_name(thread) )); JvmtiVirtualThreadEventMark jem(thread); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventThreadStart callback = env->callbacks()->ThreadStart; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread()); @@ -1538,7 +1564,7 @@ void JvmtiExport::post_thread_end(JavaThread *thread) { JvmtiTrace::safe_get_thread_name(thread) )); JvmtiVirtualThreadEventMark jem(thread); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventThreadEnd callback = env->callbacks()->ThreadEnd; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread()); @@ -1568,7 +1594,7 @@ void JvmtiExport::post_vthread_start(jobject vthread) { EVT_TRACE(JVMTI_EVENT_VIRTUAL_THREAD_START, ("[%p] Evt Virtual Thread Start event sent", vthread)); JvmtiVirtualThreadEventMark jem(thread); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventVirtualThreadStart callback = env->callbacks()->VirtualThreadStart; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread()); @@ -1604,7 +1630,7 @@ void JvmtiExport::post_vthread_end(jobject vthread) { EVT_TRACE(JVMTI_EVENT_VIRTUAL_THREAD_END, ("[%p] Evt Virtual Thread End event sent", vthread)); JvmtiVirtualThreadEventMark jem(thread); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventVirtualThreadEnd callback = env->callbacks()->VirtualThreadEnd; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), vthread); @@ -1639,7 +1665,7 @@ void JvmtiExport::post_vthread_mount(jobject vthread) { EVT_TRACE(EXT_EVENT_VIRTUAL_THREAD_MOUNT, ("[%p] Evt Virtual Thread Mount event sent", vthread)); JvmtiVirtualThreadEventMark jem(thread); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiExtensionEvent callback = env->ext_callbacks()->VirtualThreadMount; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread()); @@ -1674,7 +1700,7 @@ void JvmtiExport::post_vthread_unmount(jobject vthread) { EVT_TRACE(EXT_EVENT_VIRTUAL_THREAD_UNMOUNT, ("[%p] Evt Virtual Thread Unmount event sent", vthread)); JvmtiVirtualThreadEventMark jem(thread); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiExtensionEvent callback = env->ext_callbacks()->VirtualThreadUnmount; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread()); @@ -1787,7 +1813,7 @@ void JvmtiExport::post_resource_exhausted(jint resource_exhausted_flags, const c EVT_TRACE(JVMTI_EVENT_RESOURCE_EXHAUSTED, ("Evt resource exhausted event sent" )); JvmtiThreadEventMark jem(thread); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventResourceExhausted callback = env->callbacks()->ResourceExhausted; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), @@ -1827,7 +1853,7 @@ void JvmtiExport::post_method_entry(JavaThread *thread, Method* method, frame cu JvmtiEnv *env = ets->get_env(); JvmtiMethodEventMark jem(thread, mh); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventMethodEntry callback = env->callbacks()->MethodEntry; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), jem.jni_methodID()); @@ -1914,7 +1940,7 @@ void JvmtiExport::post_method_exit_inner(JavaThread* thread, JvmtiEnv *env = ets->get_env(); JvmtiMethodEventMark jem(thread, mh); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventMethodExit callback = env->callbacks()->MethodExit; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), @@ -1941,7 +1967,7 @@ void JvmtiExport::post_method_exit_inner(JavaThread* thread, // we also need to issue a frame pop event for this frame JvmtiEnv *env = ets->get_env(); JvmtiMethodEventMark jem(thread, mh); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventFramePop callback = env->callbacks()->FramePop; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), @@ -1989,7 +2015,7 @@ void JvmtiExport::post_single_step(JavaThread *thread, Method* method, address l JvmtiEnv *env = ets->get_env(); JvmtiLocationEventMark jem(thread, mh, location); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventSingleStep callback = env->callbacks()->SingleStep; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), @@ -2077,7 +2103,7 @@ void JvmtiExport::post_exception_throw(JavaThread *thread, Method* method, addre catch_jmethodID = jem.to_jmethodID(current_mh); } - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventException callback = env->callbacks()->Exception; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), @@ -2152,7 +2178,7 @@ void JvmtiExport::notice_unwind_due_to_exception(JavaThread *thread, Method* met JvmtiEnv *env = ets->get_env(); JvmtiExceptionEventMark jem(thread, mh, location, exception_handle); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventExceptionCatch callback = env->callbacks()->ExceptionCatch; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), @@ -2238,7 +2264,7 @@ void JvmtiExport::post_field_access(JavaThread *thread, Method* method, JvmtiLocationEventMark jem(thread, mh, location); jclass field_jclass = jem.to_jclass(field_klass); jobject field_jobject = jem.to_jobject(object()); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventFieldAccess callback = env->callbacks()->FieldAccess; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), @@ -2396,7 +2422,7 @@ void JvmtiExport::post_field_modification(JavaThread *thread, Method* method, JvmtiLocationEventMark jem(thread, mh, location); jclass field_jclass = jem.to_jclass(field_klass); jobject field_jobject = jem.to_jobject(object()); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventFieldModification callback = env->callbacks()->FieldModification; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), @@ -2428,7 +2454,7 @@ void JvmtiExport::post_native_method_bind(Method* method, address* function_ptr) JvmtiTrace::safe_get_thread_name(thread) )); JvmtiMethodEventMark jem(thread, mh); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) JNIEnv* jni_env = (env->phase() == JVMTI_PHASE_PRIMORDIAL) ? nullptr : jem.jni_env(); jvmtiEventNativeMethodBind callback = env->callbacks()->NativeMethodBind; if (callback != nullptr) { @@ -2525,7 +2551,7 @@ void JvmtiExport::post_compiled_method_load(JvmtiEnv* env, nmethod *nm) { jvmtiCompiledMethodLoadInlineRecord* inlinerecord = create_inline_record(nm); // Pass inlining information through the void pointer JvmtiCompiledMethodLoadEventMark jem(thread, nm, inlinerecord); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) (*callback)(env->jvmti_external(), jem.jni_methodID(), jem.code_size(), jem.code_data(), jem.map_length(), jem.map(), jem.compile_info()); @@ -2552,7 +2578,7 @@ void JvmtiExport::post_dynamic_code_generated_internal(const char *name, const v ("[%s] dynamic code generated event sent for %s", JvmtiTrace::safe_get_thread_name(thread), name)); JvmtiEventMark jem(thread); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jint length = (jint)pointer_delta(code_end, code_begin, sizeof(char)); jvmtiEventDynamicCodeGenerated callback = env->callbacks()->DynamicCodeGenerated; if (callback != nullptr) { @@ -2594,7 +2620,7 @@ void JvmtiExport::post_dynamic_code_generated(JvmtiEnv* env, const char *name, ("[%s] dynamic code generated event sent for %s", JvmtiTrace::safe_get_thread_name(thread), name)); JvmtiEventMark jem(thread); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jint length = (jint)pointer_delta(code_end, code_begin, sizeof(char)); jvmtiEventDynamicCodeGenerated callback = env->callbacks()->DynamicCodeGenerated; if (callback != nullptr) { @@ -2678,7 +2704,7 @@ void JvmtiExport::post_garbage_collection_finish() { EVT_TRACE(JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, ("[%s] garbage collection finish event sent", JvmtiTrace::safe_get_thread_name(thread))); - JvmtiThreadEventTransition jet(thread); + JVMTI_THREAD_EVENT_CALLBACK_BLOCK(thread) // JNIEnv is null here because this event is posted from VM Thread jvmtiEventGarbageCollectionFinish callback = env->callbacks()->GarbageCollectionFinish; if (callback != nullptr) { @@ -2699,7 +2725,7 @@ void JvmtiExport::post_garbage_collection_start() { EVT_TRACE(JVMTI_EVENT_GARBAGE_COLLECTION_START, ("[%s] garbage collection start event sent", JvmtiTrace::safe_get_thread_name(thread))); - JvmtiThreadEventTransition jet(thread); + JVMTI_THREAD_EVENT_CALLBACK_BLOCK(thread) // JNIEnv is null here because this event is posted from VM Thread jvmtiEventGarbageCollectionStart callback = env->callbacks()->GarbageCollectionStart; if (callback != nullptr) { @@ -2720,7 +2746,7 @@ void JvmtiExport::post_data_dump() { EVT_TRACE(JVMTI_EVENT_DATA_DUMP_REQUEST, ("[%s] data dump request event sent", JvmtiTrace::safe_get_thread_name(thread))); - JvmtiThreadEventTransition jet(thread); + JVMTI_THREAD_EVENT_CALLBACK_BLOCK(thread) // JNIEnv is null here because this event is posted from VM Thread jvmtiEventDataDumpRequest callback = env->callbacks()->DataDumpRequest; if (callback != nullptr) { @@ -2754,7 +2780,7 @@ void JvmtiExport::post_monitor_contended_enter(JavaThread *thread, ObjectMonitor JvmtiTrace::safe_get_thread_name(thread))); JvmtiMonitorEventMark jem(thread, h()); JvmtiEnv *env = ets->get_env(); - JvmtiThreadEventTransition jet(thread); + JVMTI_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventMonitorContendedEnter callback = env->callbacks()->MonitorContendedEnter; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), jem.jni_object()); @@ -2788,7 +2814,7 @@ void JvmtiExport::post_monitor_contended_entered(JavaThread *thread, ObjectMonit JvmtiTrace::safe_get_thread_name(thread))); JvmtiMonitorEventMark jem(thread, h()); JvmtiEnv *env = ets->get_env(); - JvmtiThreadEventTransition jet(thread); + JVMTI_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventMonitorContendedEntered callback = env->callbacks()->MonitorContendedEntered; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), jem.jni_object()); @@ -2821,7 +2847,7 @@ void JvmtiExport::post_monitor_wait(JavaThread *thread, oop object, JvmtiTrace::safe_get_thread_name(thread))); JvmtiMonitorEventMark jem(thread, h()); JvmtiEnv *env = ets->get_env(); - JvmtiThreadEventTransition jet(thread); + JVMTI_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventMonitorWait callback = env->callbacks()->MonitorWait; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), @@ -2855,7 +2881,7 @@ void JvmtiExport::post_monitor_waited(JavaThread *thread, ObjectMonitor *obj_mnt JvmtiTrace::safe_get_thread_name(thread))); JvmtiMonitorEventMark jem(thread, h()); JvmtiEnv *env = ets->get_env(); - JvmtiThreadEventTransition jet(thread); + JVMTI_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventMonitorWaited callback = env->callbacks()->MonitorWaited; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), @@ -2898,7 +2924,7 @@ void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) { object==nullptr? "null" : object->klass()->external_name())); JvmtiObjectAllocEventMark jem(thread, h()); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventVMObjectAlloc callback = env->callbacks()->VMObjectAlloc; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), @@ -2936,7 +2962,7 @@ void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) { JvmtiEnv *env = ets->get_env(); JvmtiObjectAllocEventMark jem(thread, h()); - JvmtiJavaThreadEventTransition jet(thread); + JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread) jvmtiEventSampledObjectAlloc callback = env->callbacks()->SampledObjectAlloc; if (callback != nullptr) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index b4d100341e0..92f5356235a 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -509,6 +509,14 @@ WB_ENTRY(jboolean, WB_ConcurrentGCRunTo(JNIEnv* env, jobject o, jobject at)) return ConcurrentGCBreakpoints::run_to(c_name); WB_END +WB_ENTRY(jboolean, WB_HasExternalSymbolsStripped(JNIEnv* env, jobject o)) +#if defined(HAS_STRIPPED_DEBUGINFO) + return true; +#else + return false; +#endif +WB_END + #if INCLUDE_G1GC WB_ENTRY(jboolean, WB_G1IsHumongous(JNIEnv* env, jobject o, jobject obj)) @@ -2813,6 +2821,7 @@ static JNINativeMethod methods[] = { {CC"getVMLargePageSize", CC"()J", (void*)&WB_GetVMLargePageSize}, {CC"getHeapSpaceAlignment", CC"()J", (void*)&WB_GetHeapSpaceAlignment}, {CC"getHeapAlignment", CC"()J", (void*)&WB_GetHeapAlignment}, + {CC"hasExternalSymbolsStripped", CC"()Z", (void*)&WB_HasExternalSymbolsStripped}, {CC"countAliveClasses0", CC"(Ljava/lang/String;)I", (void*)&WB_CountAliveClasses }, {CC"getSymbolRefcount", CC"(Ljava/lang/String;)I", (void*)&WB_GetSymbolRefcount }, {CC"parseCommandLine0", diff --git a/src/hotspot/share/runtime/abstract_vm_version.cpp b/src/hotspot/share/runtime/abstract_vm_version.cpp index 7d460d83e1b..3860307f8df 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.cpp +++ b/src/hotspot/share/runtime/abstract_vm_version.cpp @@ -204,7 +204,6 @@ const char* Abstract_VM_Version::vm_release() { #else #define CPU AARCH64_ONLY("aarch64") \ AMD64_ONLY("amd64") \ - IA32_ONLY("x86") \ S390_ONLY("s390") \ RISCV64_ONLY("riscv64") #endif // !ZERO diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 0d9973a1b09..8f8fa6f2eee 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -536,6 +536,7 @@ static SpecialFlag const special_jvm_flags[] = { { "ParallelRefProcEnabled", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "ParallelRefProcBalancingEnabled", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "PSChunkLargeArrays", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, + { "MaxRAM", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, // --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in: { "CreateMinidumpOnCrash", JDK_Version::jdk(9), JDK_Version::undefined(), JDK_Version::undefined() }, @@ -545,8 +546,6 @@ static SpecialFlag const special_jvm_flags[] = { { "UseOprofile", JDK_Version::jdk(25), JDK_Version::jdk(26), JDK_Version::jdk(27) }, #endif { "MetaspaceReclaimPolicy", JDK_Version::undefined(), JDK_Version::jdk(21), JDK_Version::undefined() }, - { "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) @@ -1513,38 +1512,28 @@ static size_t clamp_by_size_t_max(uint64_t value) { } void Arguments::set_heap_size() { - uint64_t physical_memory; - // Check if the user has configured any limit on the amount of RAM we may use. bool has_ram_limit = !FLAG_IS_DEFAULT(MaxRAMPercentage) || !FLAG_IS_DEFAULT(MinRAMPercentage) || !FLAG_IS_DEFAULT(InitialRAMPercentage) || !FLAG_IS_DEFAULT(MaxRAM); - if (has_ram_limit) { - if (!FLAG_IS_DEFAULT(MaxRAM)) { - // The user has configured MaxRAM, use that instead of physical memory - // reported by the OS. - physical_memory = MaxRAM; + if (FLAG_IS_DEFAULT(MaxRAM)) { + if (CompilerConfig::should_set_client_emulation_mode_flags()) { + // Limit the available memory if client emulation mode is enabled. + FLAG_SET_ERGO(MaxRAM, 1ULL*G); } else { - // The user has configured a limit, make sure MaxRAM reflects the physical - // memory limit that heap sizing takes into account. - physical_memory = os::physical_memory(); - FLAG_SET_ERGO(MaxRAM, physical_memory); + // Use the available physical memory on the system. + FLAG_SET_ERGO(MaxRAM, os::physical_memory()); } - } else { - // If the user did not specify any limit, choose the lowest of the available - // physical memory and MaxRAM. MaxRAM is typically set to 128GB on 64-bit - // architecture. - physical_memory = MIN2(os::physical_memory(), MaxRAM); } // If the maximum heap size has not been set with -Xmx, then set it as // fraction of the size of physical memory, respecting the maximum and // minimum sizes of the heap. if (FLAG_IS_DEFAULT(MaxHeapSize)) { - uint64_t min_memory = (uint64_t)(((double)physical_memory * MinRAMPercentage) / 100); - uint64_t max_memory = (uint64_t)(((double)physical_memory * MaxRAMPercentage) / 100); + uint64_t min_memory = (uint64_t)(((double)MaxRAM * MinRAMPercentage) / 100); + uint64_t max_memory = (uint64_t)(((double)MaxRAM * MaxRAMPercentage) / 100); const size_t reasonable_min = clamp_by_size_t_max(min_memory); size_t reasonable_max = clamp_by_size_t_max(max_memory); @@ -1631,7 +1620,7 @@ void Arguments::set_heap_size() { reasonable_minimum = limit_heap_by_allocatable_memory(reasonable_minimum); if (InitialHeapSize == 0) { - uint64_t initial_memory = (uint64_t)(((double)physical_memory * InitialRAMPercentage) / 100); + uint64_t initial_memory = (uint64_t)(((double)MaxRAM * InitialRAMPercentage) / 100); size_t reasonable_initial = clamp_by_size_t_max(initial_memory); reasonable_initial = limit_heap_by_allocatable_memory(reasonable_initial); diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index 853c6554022..daafcaea61b 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -1804,10 +1804,11 @@ void Deoptimization::deoptimize(JavaThread* thread, frame fr, DeoptReason reason deoptimize_single_frame(thread, fr, reason); } -#if INCLUDE_JVMCI -address Deoptimization::deoptimize_for_missing_exception_handler(nmethod* nm) { +address Deoptimization::deoptimize_for_missing_exception_handler(nmethod* nm, bool make_not_entrant) { // there is no exception handler for this pc => deoptimize - nm->make_not_entrant(nmethod::InvalidationReason::MISSING_EXCEPTION_HANDLER); + if (make_not_entrant) { + nm->make_not_entrant(nmethod::InvalidationReason::MISSING_EXCEPTION_HANDLER); + } // Use Deoptimization::deoptimize for all of its side-effects: // gathering traps statistics, logging... @@ -1821,6 +1822,15 @@ address Deoptimization::deoptimize_for_missing_exception_handler(nmethod* nm) { frame runtime_frame = thread->last_frame(); frame caller_frame = runtime_frame.sender(®_map); assert(caller_frame.cb()->as_nmethod_or_null() == nm, "expect top frame compiled method"); + + Deoptimization::deoptimize(thread, caller_frame, Deoptimization::Reason_not_compiled_exception_handler); + + if (!nm->is_compiled_by_jvmci()) { + return SharedRuntime::deopt_blob()->unpack_with_exception_in_tls(); + } + +#if INCLUDE_JVMCI + // JVMCI support vframe* vf = vframe::new_vframe(&caller_frame, ®_map, thread); compiledVFrame* cvf = compiledVFrame::cast(vf); ScopeDesc* imm_scope = cvf->scope(); @@ -1836,16 +1846,15 @@ address Deoptimization::deoptimize_for_missing_exception_handler(nmethod* nm) { } } - Deoptimization::deoptimize(thread, caller_frame, Deoptimization::Reason_not_compiled_exception_handler); MethodData* trap_mdo = get_method_data(thread, methodHandle(thread, nm->method()), true); if (trap_mdo != nullptr) { trap_mdo->inc_trap_count(Deoptimization::Reason_not_compiled_exception_handler); } +#endif return SharedRuntime::deopt_blob()->unpack_with_exception_in_tls(); } -#endif void Deoptimization::deoptimize_frame_internal(JavaThread* thread, intptr_t* id, DeoptReason reason) { assert(thread == Thread::current() || @@ -2748,10 +2757,10 @@ const char* Deoptimization::_trap_reason_name[] = { "unstable_if", "unstable_fused_if", "receiver_constraint", + "not_compiled_exception_handler", "short_running_loop" JVMCI_ONLY("_or_aliasing"), #if INCLUDE_JVMCI "transfer_to_interpreter", - "not_compiled_exception_handler", "unresolved", "jsr_mismatch", #endif diff --git a/src/hotspot/share/runtime/deoptimization.hpp b/src/hotspot/share/runtime/deoptimization.hpp index 5d97e2056ad..d168d9c8af6 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/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 513edaf6588..238517197b2 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -502,7 +502,7 @@ const int ObjectAlignmentInBytes = 8; "If > 0, provokes an error after VM initialization; the value " \ "determines which error to provoke. See controlled_crash() " \ "in vmError.cpp.") \ - range(0, 17) \ + range(0, 18) \ \ develop(uint, TestCrashInErrorHandler, 0, \ "If > 0, provokes an error inside VM error handler (a secondary " \ diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 142324fec7a..f6d569b1b7a 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -1863,10 +1863,10 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // could be profitable. // // TODO-FIXME: change the following logic to a loop of the form - // while (!timeout && !interrupted && _notified == 0) park() + // while (!timeout && !interrupted && node.TState == TS_WAIT) park() int ret = OS_OK; - int WasNotified = 0; + bool was_notified = false; // Need to check interrupt state whilst still _thread_in_vm bool interrupted = interruptible && current->is_interrupted(false); @@ -1882,7 +1882,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { ThreadBlockInVMPreprocess tbivs(current, csos, true /* allow_suspend */); if (interrupted || HAS_PENDING_EXCEPTION) { // Intentionally empty - } else if (!node._notified) { + } else if (node.TState == ObjectWaiter::TS_WAIT) { if (millis <= 0) { current->_ParkEvent->park(); } else { @@ -1910,7 +1910,6 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { Thread::SpinAcquire(&_wait_set_lock); if (node.TState == ObjectWaiter::TS_WAIT) { dequeue_specific_waiter(&node); // unlink from wait_set - assert(!node._notified, "invariant"); node.TState = ObjectWaiter::TS_RUN; } Thread::SpinRelease(&_wait_set_lock); @@ -1923,7 +1922,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { guarantee(node.TState != ObjectWaiter::TS_WAIT, "invariant"); OrderAccess::loadload(); if (has_successor(current)) clear_successor(); - WasNotified = node._notified; + was_notified = node.TState == ObjectWaiter::TS_ENTER; // Reentry phase -- reacquire the monitor. // re-enter contended monitor after object.wait(). @@ -1936,7 +1935,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { if (JvmtiExport::should_post_monitor_waited()) { JvmtiExport::post_monitor_waited(current, this, ret == OS_TIMEOUT); - if (node._notified && has_successor(current)) { + if (was_notified && has_successor(current)) { // In this part of the monitor wait-notify-reenter protocol it // is possible (and normal) for another thread to do a fastpath // monitor enter-exit while this thread is still trying to get @@ -2003,7 +2002,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { } // check if the notification happened - if (!WasNotified) { + if (!was_notified) { // no, it could be timeout or Thread.interrupt() or both // check for interrupt event, otherwise it is timeout if (interruptible && current->is_interrupted(true) && !HAS_PENDING_EXCEPTION) { @@ -2026,7 +2025,6 @@ bool ObjectMonitor::notify_internal(JavaThread* current) { ObjectWaiter* iterator = dequeue_waiter(); if (iterator != nullptr) { guarantee(iterator->TState == ObjectWaiter::TS_WAIT, "invariant"); - guarantee(!iterator->_notified, "invariant"); if (iterator->is_vthread()) { oop vthread = iterator->vthread(); @@ -2048,7 +2046,6 @@ bool ObjectMonitor::notify_internal(JavaThread* current) { inc_unmounted_vthreads(); } - iterator->_notified = true; iterator->_notifier_tid = JFR_THREAD_ID(current); did_notify = true; add_to_entry_list(current, iterator); @@ -2210,7 +2207,6 @@ bool ObjectMonitor::vthread_wait_reenter(JavaThread* current, ObjectWaiter* node Thread::SpinAcquire(&_wait_set_lock); if (node->TState == ObjectWaiter::TS_WAIT) { dequeue_specific_waiter(node); // unlink from wait_set - assert(!node->_notified, "invariant"); node->TState = ObjectWaiter::TS_RUN; } Thread::SpinRelease(&_wait_set_lock); @@ -2516,7 +2512,6 @@ ObjectWaiter::ObjectWaiter(JavaThread* current) { _notifier_tid = 0; _recursions = 0; TState = TS_RUN; - _notified = false; _is_wait = false; _at_reenter = false; _interrupted = false; diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index 058e0317ec1..77919d99955 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -52,7 +52,6 @@ class ObjectWaiter : public CHeapObj { uint64_t _notifier_tid; int _recursions; volatile TStates TState; - volatile bool _notified; bool _is_wait; bool _at_reenter; bool _interrupted; @@ -67,9 +66,8 @@ class ObjectWaiter : public CHeapObj { uint8_t state() const { return TState; } ObjectMonitor* monitor() const { return _monitor; } bool is_wait() const { return _is_wait; } - bool notified() const { return _notified; } bool at_reenter() const { return _at_reenter; } - bool at_monitorenter() const { return !_is_wait || _at_reenter || _notified; } + bool at_monitorenter() const { return !_is_wait || TState != TS_WAIT; } oop vthread() const; void wait_reenter_begin(ObjectMonitor *mon); void wait_reenter_end(ObjectMonitor *mon); 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/reflection.cpp b/src/hotspot/share/runtime/reflection.cpp index 7728643c640..d6c10c5c70c 100644 --- a/src/hotspot/share/runtime/reflection.cpp +++ b/src/hotspot/share/runtime/reflection.cpp @@ -546,7 +546,7 @@ char* Reflection::verify_class_access_msg(const Klass* current_class, size_t len = 100 + strlen(current_class_name) + 2*strlen(module_from_name) + strlen(new_class_name) + 2*strlen(module_to_name); msg = NEW_RESOURCE_ARRAY(char, len); - jio_snprintf(msg, len - 1, + jio_snprintf(msg, len, "class %s (in module %s) cannot access class %s (in module %s) because module %s does not read module %s", current_class_name, module_from_name, new_class_name, module_to_name, module_from_name, module_to_name); @@ -557,7 +557,7 @@ char* Reflection::verify_class_access_msg(const Klass* current_class, size_t len = 160 + strlen(current_class_name) + 2*strlen(module_from_name) + strlen(new_class_name) + 2*sizeof(uintx); msg = NEW_RESOURCE_ARRAY(char, len); - jio_snprintf(msg, len - 1, + jio_snprintf(msg, len, "class %s (in module %s) cannot access class %s (in unnamed module @0x%zx) because module %s does not read unnamed module @0x%zx", current_class_name, module_from_name, new_class_name, uintx(identity_hash), module_from_name, uintx(identity_hash)); @@ -573,7 +573,7 @@ char* Reflection::verify_class_access_msg(const Klass* current_class, size_t len = 118 + strlen(current_class_name) + 2*strlen(module_from_name) + strlen(new_class_name) + 2*strlen(module_to_name) + strlen(package_name); msg = NEW_RESOURCE_ARRAY(char, len); - jio_snprintf(msg, len - 1, + jio_snprintf(msg, len, "class %s (in module %s) cannot access class %s (in module %s) because module %s does not export %s to module %s", current_class_name, module_from_name, new_class_name, module_to_name, module_to_name, package_name, module_from_name); @@ -584,7 +584,7 @@ char* Reflection::verify_class_access_msg(const Klass* current_class, size_t len = 170 + strlen(current_class_name) + strlen(new_class_name) + 2*strlen(module_to_name) + strlen(package_name) + 2*sizeof(uintx); msg = NEW_RESOURCE_ARRAY(char, len); - jio_snprintf(msg, len - 1, + jio_snprintf(msg, len, "class %s (in unnamed module @0x%zx) cannot access class %s (in module %s) because module %s does not export %s to unnamed module @0x%zx", current_class_name, uintx(identity_hash), new_class_name, module_to_name, module_to_name, package_name, uintx(identity_hash)); 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 efc47dd11c6..35bc3f5f1be 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -791,7 +791,8 @@ address SharedRuntime::compute_compiled_exc_handler(nmethod* nm, address ret_pc, if (t != nullptr) { return nm->code_begin() + t->pco(); } else { - return Deoptimization::deoptimize_for_missing_exception_handler(nm); + bool make_not_entrant = true; + return Deoptimization::deoptimize_for_missing_exception_handler(nm, make_not_entrant); } } #endif // INCLUDE_JVMCI @@ -847,6 +848,15 @@ address SharedRuntime::compute_compiled_exc_handler(nmethod* nm, address ret_pc, ExceptionHandlerTable table(nm); HandlerTableEntry *t = table.entry_for(catch_pco, handler_bci, scope_depth); + + // If the compiler did not anticipate a recursive exception, resulting in an exception + // thrown from the catch bci, then the compiled exception handler might be missing. + // This is rare. Just deoptimize and let the interpreter handle it. + if (t == nullptr && recursive_exception_occurred) { + bool make_not_entrant = false; + return Deoptimization::deoptimize_for_missing_exception_handler(nm, make_not_entrant); + } + if (t == nullptr && (nm->is_compiled_by_c1() || handler_bci != -1)) { // Allow abbreviated catch tables. The idea is to allow a method // to materialize its exceptions without committing to the exact diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp index 93cd92b3a32..f4ff51504f0 100644 --- a/src/hotspot/share/runtime/sharedRuntime.hpp +++ b/src/hotspot/share/runtime/sharedRuntime.hpp @@ -384,10 +384,6 @@ class SharedRuntime: AllStatic { // deopt blob static void generate_deopt_blob(void); - static bool handle_ic_miss_helper_internal(Handle receiver, nmethod* caller_nm, const frame& caller_frame, - methodHandle callee_method, Bytecodes::Code bc, CallInfo& call_info, - bool& needs_ic_stub_refill, TRAPS); - public: static DeoptimizationBlob* deopt_blob(void) { return _deopt_blob; } @@ -549,7 +545,6 @@ class SharedRuntime: AllStatic { // A compiled caller has just called the interpreter, but compiled code // exists. Patch the caller so he no longer calls into the interpreter. static void fixup_callers_callsite(Method* moop, address ret_pc); - static bool should_fixup_call_destination(address destination, address entry_point, address caller_pc, Method* moop, CodeBlob* cb); // Slow-path Locking and Unlocking static void complete_monitor_locking_C(oopDesc* obj, BasicLock* lock, JavaThread* current); diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index ffe1a86cda5..299905ff0a2 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -97,6 +97,7 @@ #include "runtime/trimNativeHeap.hpp" #include "runtime/vm_version.hpp" #include "runtime/vmOperations.hpp" +#include "sanitizers/address.hpp" #include "services/attachListener.hpp" #include "services/management.hpp" #include "services/threadIdTable.hpp" @@ -343,6 +344,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. @@ -697,6 +703,10 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { // No more stub generation allowed after that point. StubCodeDesc::freeze(); +#ifdef ADDRESS_SANITIZER + Asan::initialize(); +#endif + // Set flag that basic initialization has completed. Used by exceptions and various // debug stuff, that does not work until all basic classes have been initialized. set_init_completed(); @@ -742,6 +752,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(); @@ -774,7 +788,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. @@ -793,7 +807,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 8dc4b660f91..85f921ef3e3 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1581,8 +1581,8 @@ declare_constant(Deoptimization::Reason_unstable_if) \ declare_constant(Deoptimization::Reason_unstable_fused_if) \ declare_constant(Deoptimization::Reason_receiver_constraint) \ + declare_constant(Deoptimization::Reason_not_compiled_exception_handler) \ NOT_ZERO(JVMCI_ONLY(declare_constant(Deoptimization::Reason_transfer_to_interpreter))) \ - NOT_ZERO(JVMCI_ONLY(declare_constant(Deoptimization::Reason_not_compiled_exception_handler))) \ NOT_ZERO(JVMCI_ONLY(declare_constant(Deoptimization::Reason_unresolved))) \ NOT_ZERO(JVMCI_ONLY(declare_constant(Deoptimization::Reason_jsr_mismatch))) \ declare_constant(Deoptimization::Reason_tenured) \ diff --git a/src/hotspot/share/sanitizers/address.cpp b/src/hotspot/share/sanitizers/address.cpp new file mode 100644 index 00000000000..b050039b1b8 --- /dev/null +++ b/src/hotspot/share/sanitizers/address.cpp @@ -0,0 +1,120 @@ +/* + * 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 + * 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. + * + */ + +#ifdef ADDRESS_SANITIZER + +#include "logging/log.hpp" +#include "sanitizers/address.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/vmError.hpp" + +#include +#include + +typedef void (*callback_setter_t) (void (*callback)(const char *)); +static callback_setter_t g_callback_setter = nullptr; +static const char* g_report = nullptr; + +extern "C" void asan_error_callback(const char* report_text) { + // Please keep things very short and simple here and use as little + // as possible of any hotspot infrastructure. However shaky the JVM, + // we should always at least get the ASAN report on stderr. + + // Note: this is threadsafe since ASAN synchronizes error reports + g_report = report_text; + + // First, print off the bare error to stderr + fprintf(stderr, "JVM caught ASAN Error\n"); + fprintf(stderr, "%s\n", report_text); + + // Then, let normal JVM error handling run its due course. + fatal("ASAN Error"); +} + +void Asan::initialize() { + + // For documentation of __asan_set_error_report_callback() see asan_interface.h . + g_callback_setter = (callback_setter_t) dlsym(RTLD_DEFAULT, "__asan_set_error_report_callback"); + if (g_callback_setter == nullptr) { + log_info(asan)("*** Failed to install JVM callback for ASAN. ASAN errors will not generate hs-err files. ***"); + return; + } + + g_callback_setter(asan_error_callback); + log_info(asan)("JVM callback for ASAN errors successfully installed"); + + // Controlling core dump behavior: + // + // In hotspot, CreateCoredumpOnCrash decides whether to create a core dump (on Posix, whether to + // end the process with abort(3) or exit(3)). + // + // Core generation in the default ASAN reporter is controlled by two options: + // - "abort_on_error=0" (default) - end with exit(3), "abort_on_error=1" end with abort(3) + // - "disable_coredump=1" (default) disables cores by imposing a near-zero core soft limit. + // By default both options are set to prevent cores. That default makes sense since ASAN cores + // can get very large (due to the shadow map) and very numerous (ASAN is typically ran for + // large-scale integration tests, not targeted micro-tests). + // + // In hotspot ASAN builds, we replace the default ASAN reporter. The soft limit imposed by + // "disable_coredump=1" is still in effect. But "abort_on_error" is not honored. Since we'd + // like to exhibit exactly the same behavior as the standard ASAN error reporter, we disable + // core files if ASAN would inhibit them (we just switch off CreateCoredumpOnCrash). + // + // Thus: + // abort_on_error disable_coredump core file? + // 0 0 No (enforced by ergo-setting CreateCoredumpOnCrash=0) + // (*) 0 1 No (enforced by ASAN-imposed soft limit) + // 1 0 Yes, unless -XX:-CreateCoredumpOnCrash set on command line + // 1 1 No (enforced by ASAN-imposed soft limit) + // (*) is the default if no ASAN options are specified. + + const char* const asan_options = getenv("ASAN_OPTIONS"); + const bool asan_inhibits_cores = (asan_options == nullptr) || + (::strstr(asan_options, "abort_on_error=1") == nullptr) || + (::strstr(asan_options, "disable_coredump=0") == nullptr); + if (asan_inhibits_cores) { + if (CreateCoredumpOnCrash) { + log_info(asan)("CreateCoredumpOnCrash overruled by%s asan options. Core generation disabled.", + asan_options != nullptr ? "" : " default setting for"); + log_info(asan)("Use 'ASAN_OPTIONS=abort_on_error=1:disable_coredump=0:unmap_shadow_on_exit=1' " + "to enable core generation."); + } + FLAG_SET_ERGO(CreateCoredumpOnCrash, false); + } +} + +bool Asan::had_error() { + return g_report != nullptr; +} + +void Asan::report(outputStream* st) { + if (had_error()) { + // Use raw print here to avoid truncation. + st->print_raw(g_report); + st->cr(); + st->cr(); + } +} + +#endif // ADDRESS_SANITIZER diff --git a/src/hotspot/share/sanitizers/address.hpp b/src/hotspot/share/sanitizers/address.hpp index 5186053f1c9..109aa59dac0 100644 --- a/src/hotspot/share/sanitizers/address.hpp +++ b/src/hotspot/share/sanitizers/address.hpp @@ -26,6 +26,8 @@ #define SHARE_SANITIZERS_ADDRESS_HPP #ifdef ADDRESS_SANITIZER +#include "memory/allStatic.hpp" + #include #endif @@ -74,4 +76,14 @@ } while (false) #endif +class outputStream; + +#ifdef ADDRESS_SANITIZER +struct Asan : public AllStatic { + static void initialize(); + static bool had_error(); + static void report(outputStream* st); +}; +#endif + #endif // SHARE_SANITIZERS_ADDRESS_HPP diff --git a/src/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp index 001d89a5aef..2364b0ce4cd 100644 --- a/src/hotspot/share/services/diagnosticCommand.hpp +++ b/src/hotspot/share/services/diagnosticCommand.hpp @@ -356,7 +356,9 @@ public: 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."; @@ -768,7 +770,8 @@ public: 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/hotspot/share/services/diagnosticFramework.cpp b/src/hotspot/share/services/diagnosticFramework.cpp index 7cf41750f5d..c37fe7b4e1e 100644 --- a/src/hotspot/share/services/diagnosticFramework.cpp +++ b/src/hotspot/share/services/diagnosticFramework.cpp @@ -208,7 +208,7 @@ void DCmdParser::parse(CmdLine* line, char delim, TRAPS) { strncpy(argbuf, iter.key_addr(), len); argbuf[len] = '\0'; - jio_snprintf(buf, buflen - 1, "Unknown argument '%s' in diagnostic command.", argbuf); + jio_snprintf(buf, buflen, "Unknown argument '%s' in diagnostic command.", argbuf); THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), buf); } @@ -236,7 +236,7 @@ void DCmdParser::check(TRAPS) { GenDCmdArgument* arg = _arguments_list; while (arg != nullptr) { if (arg->is_mandatory() && !arg->has_value()) { - jio_snprintf(buf, buflen - 1, "The argument '%s' is mandatory.", arg->name()); + jio_snprintf(buf, buflen, "The argument '%s' is mandatory.", arg->name()); THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), buf); } arg = arg->next(); @@ -244,7 +244,7 @@ void DCmdParser::check(TRAPS) { arg = _options; while (arg != nullptr) { if (arg->is_mandatory() && !arg->has_value()) { - jio_snprintf(buf, buflen - 1, "The option '%s' is mandatory.", arg->name()); + jio_snprintf(buf, buflen, "The option '%s' is mandatory.", arg->name()); THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), buf); } arg = arg->next(); diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index 6d8a82b6798..89c0a1ebc08 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -664,7 +664,6 @@ void help() { tty->print_cr(" pns(void* sp, void* fp, void* pc) - print native (i.e. mixed) stack trace, e.g."); #ifdef LINUX AMD64_ONLY( tty->print_cr(" pns($sp, $rbp, $pc) on Linux/amd64")); - IA32_ONLY( tty->print_cr(" pns($sp, $ebp, $pc) on Linux/x86")); AARCH64_ONLY(tty->print_cr(" pns($sp, $fp, $pc) on Linux/AArch64")); RISCV_ONLY( tty->print_cr(" pns($sp, $fp, $pc) on Linux/RISC-V")); PPC64_ONLY( tty->print_cr(" pns($sp, 0, $pc) on Linux/ppc64")); diff --git a/src/hotspot/share/utilities/macros.hpp b/src/hotspot/share/utilities/macros.hpp index a5caf316aa3..1d2e651d674 100644 --- a/src/hotspot/share/utilities/macros.hpp +++ b/src/hotspot/share/utilities/macros.hpp @@ -452,7 +452,7 @@ #define NOT_ZERO_RETURN #endif -#if defined(IA32) || defined(AMD64) +#if defined(AMD64) #define X86 #define X86_ONLY(code) code #define NOT_X86(code) @@ -462,14 +462,6 @@ #define NOT_X86(code) code #endif -#ifdef IA32 -#define IA32_ONLY(code) code -#define NOT_IA32(code) -#else -#define IA32_ONLY(code) -#define NOT_IA32(code) code -#endif - #ifdef AMD64 #define AMD64_ONLY(code) code #define NOT_AMD64(code) diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 0fbd8ed4259..e0cbb60c744 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -60,6 +60,7 @@ #include "runtime/vm_version.hpp" #include "runtime/vmOperations.hpp" #include "runtime/vmThread.hpp" +#include "sanitizers/address.hpp" #include "sanitizers/ub.hpp" #include "utilities/debug.hpp" #include "utilities/decoder.hpp" @@ -910,7 +911,16 @@ void VMError::report(outputStream* st, bool _verbose) { STEP_IF("printing date and time", _verbose) os::print_date_and_time(st, buf, sizeof(buf)); - STEP_IF("printing thread", _verbose) +#ifdef ADDRESS_SANITIZER + STEP_IF("printing ASAN error information", _verbose && Asan::had_error()) + st->cr(); + st->print_cr("------------------ A S A N ----------------"); + st->cr(); + Asan::report(st); + st->cr(); +#endif // ADDRESS_SANITIZER + + STEP_IF("printing thread", _verbose) st->cr(); st->print_cr("--------------- T H R E A D ---------------"); st->cr(); @@ -2186,6 +2196,14 @@ void VMError::controlled_crash(int how) { fatal("Force crash with a nested ThreadsListHandle."); } } + case 18: { + // Trigger an error that should cause ASAN to report a double free or use-after-free. + // Please note that this is not 100% bullet-proof since it assumes that this block + // is not immediately repurposed by some other thread after free. + void* const p = os::malloc(4096, mtTest); + os::free(p); + os::free(p); + } default: // If another number is given, give a generic crash. fatal("Crashing with number %d", how); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java b/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java index 12359cba7d1..329f367717a 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, 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 @@ -49,7 +49,7 @@ import java.util.Arrays; * * @author Valerie Peng * - * @see AESCrypt + * @see AES_Crypt * @see CipherBlockChaining * @see ElectronicCodeBook * @see CipherFeedback @@ -174,7 +174,7 @@ class AESCipher extends CipherSpi { * PKCS5Padding. */ protected AESCipher(int keySize) { - core = new CipherCore(new AESCrypt(), AESConstants.AES_BLOCK_SIZE); + core = new CipherCore(new AES_Crypt(), AESConstants.AES_BLOCK_SIZE); fixedKeySize = keySize; } @@ -504,7 +504,7 @@ class AESCipher extends CipherSpi { protected int engineGetKeySize(Key key) throws InvalidKeyException { byte[] encoded = key.getEncoded(); Arrays.fill(encoded, (byte)0); - if (!AESCrypt.isKeySizeValid(encoded.length)) { + if (!AES_Crypt.isKeySizeValid(encoded.length)) { throw new InvalidKeyException("Invalid AES key length: " + encoded.length + " bytes"); } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/AESCrypt.java b/src/java.base/share/classes/com/sun/crypto/provider/AESCrypt.java deleted file mode 100644 index 9bbc8c16764..00000000000 --- a/src/java.base/share/classes/com/sun/crypto/provider/AESCrypt.java +++ /dev/null @@ -1,1437 +0,0 @@ -/* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. 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. - */ - -/* $Id: Rijndael.java,v 1.6 2000/02/10 01:31:41 gelderen Exp $ - * - * Copyright (C) 1995-2000 The Cryptix Foundation Limited. - * All rights reserved. - * - * Use, modification, copying and distribution of this softwareas is subject - * the terms and conditions of the Cryptix General Licence. You should have - * received a copy of the Cryptix General Licence along with this library; - * if not, you can download a copy from http://www.cryptix.org/ . - */ - -package com.sun.crypto.provider; - -import java.security.InvalidKeyException; -import java.security.MessageDigest; -import java.util.Arrays; - -import jdk.internal.vm.annotation.IntrinsicCandidate; - -/** - * Rijndael --pronounced Reindaal-- is a symmetric cipher with a 128-bit - * block size and variable key-size (128-, 192- and 256-bit). - *

- * Rijndael was designed by Vincent - * Rijmen and Joan Daemen. - */ -final class AESCrypt extends SymmetricCipher implements AESConstants { - // - // Pre-computed tables, which are copied or derived from FIPS 197. - // - - // the pre-computed substitution table (S-box), 256 bytes - private static final byte[] S = { - (byte)0x63, (byte)0x7C, (byte)0x77, (byte)0x7B, - (byte)0xF2, (byte)0x6B, (byte)0x6F, (byte)0xC5, - (byte)0x30, (byte)0x01, (byte)0x67, (byte)0x2B, - (byte)0xFE, (byte)0xD7, (byte)0xAB, (byte)0x76, - (byte)0xCA, (byte)0x82, (byte)0xC9, (byte)0x7D, - (byte)0xFA, (byte)0x59, (byte)0x47, (byte)0xF0, - (byte)0xAD, (byte)0xD4, (byte)0xA2, (byte)0xAF, - (byte)0x9C, (byte)0xA4, (byte)0x72, (byte)0xC0, - (byte)0xB7, (byte)0xFD, (byte)0x93, (byte)0x26, - (byte)0x36, (byte)0x3F, (byte)0xF7, (byte)0xCC, - (byte)0x34, (byte)0xA5, (byte)0xE5, (byte)0xF1, - (byte)0x71, (byte)0xD8, (byte)0x31, (byte)0x15, - (byte)0x04, (byte)0xC7, (byte)0x23, (byte)0xC3, - (byte)0x18, (byte)0x96, (byte)0x05, (byte)0x9A, - (byte)0x07, (byte)0x12, (byte)0x80, (byte)0xE2, - (byte)0xEB, (byte)0x27, (byte)0xB2, (byte)0x75, - (byte)0x09, (byte)0x83, (byte)0x2C, (byte)0x1A, - (byte)0x1B, (byte)0x6E, (byte)0x5A, (byte)0xA0, - (byte)0x52, (byte)0x3B, (byte)0xD6, (byte)0xB3, - (byte)0x29, (byte)0xE3, (byte)0x2F, (byte)0x84, - (byte)0x53, (byte)0xD1, (byte)0x00, (byte)0xED, - (byte)0x20, (byte)0xFC, (byte)0xB1, (byte)0x5B, - (byte)0x6A, (byte)0xCB, (byte)0xBE, (byte)0x39, - (byte)0x4A, (byte)0x4C, (byte)0x58, (byte)0xCF, - (byte)0xD0, (byte)0xEF, (byte)0xAA, (byte)0xFB, - (byte)0x43, (byte)0x4D, (byte)0x33, (byte)0x85, - (byte)0x45, (byte)0xF9, (byte)0x02, (byte)0x7F, - (byte)0x50, (byte)0x3C, (byte)0x9F, (byte)0xA8, - (byte)0x51, (byte)0xA3, (byte)0x40, (byte)0x8F, - (byte)0x92, (byte)0x9D, (byte)0x38, (byte)0xF5, - (byte)0xBC, (byte)0xB6, (byte)0xDA, (byte)0x21, - (byte)0x10, (byte)0xFF, (byte)0xF3, (byte)0xD2, - (byte)0xCD, (byte)0x0C, (byte)0x13, (byte)0xEC, - (byte)0x5F, (byte)0x97, (byte)0x44, (byte)0x17, - (byte)0xC4, (byte)0xA7, (byte)0x7E, (byte)0x3D, - (byte)0x64, (byte)0x5D, (byte)0x19, (byte)0x73, - (byte)0x60, (byte)0x81, (byte)0x4F, (byte)0xDC, - (byte)0x22, (byte)0x2A, (byte)0x90, (byte)0x88, - (byte)0x46, (byte)0xEE, (byte)0xB8, (byte)0x14, - (byte)0xDE, (byte)0x5E, (byte)0x0B, (byte)0xDB, - (byte)0xE0, (byte)0x32, (byte)0x3A, (byte)0x0A, - (byte)0x49, (byte)0x06, (byte)0x24, (byte)0x5C, - (byte)0xC2, (byte)0xD3, (byte)0xAC, (byte)0x62, - (byte)0x91, (byte)0x95, (byte)0xE4, (byte)0x79, - (byte)0xE7, (byte)0xC8, (byte)0x37, (byte)0x6D, - (byte)0x8D, (byte)0xD5, (byte)0x4E, (byte)0xA9, - (byte)0x6C, (byte)0x56, (byte)0xF4, (byte)0xEA, - (byte)0x65, (byte)0x7A, (byte)0xAE, (byte)0x08, - (byte)0xBA, (byte)0x78, (byte)0x25, (byte)0x2E, - (byte)0x1C, (byte)0xA6, (byte)0xB4, (byte)0xC6, - (byte)0xE8, (byte)0xDD, (byte)0x74, (byte)0x1F, - (byte)0x4B, (byte)0xBD, (byte)0x8B, (byte)0x8A, - (byte)0x70, (byte)0x3E, (byte)0xB5, (byte)0x66, - (byte)0x48, (byte)0x03, (byte)0xF6, (byte)0x0E, - (byte)0x61, (byte)0x35, (byte)0x57, (byte)0xB9, - (byte)0x86, (byte)0xC1, (byte)0x1D, (byte)0x9E, - (byte)0xE1, (byte)0xF8, (byte)0x98, (byte)0x11, - (byte)0x69, (byte)0xD9, (byte)0x8E, (byte)0x94, - (byte)0x9B, (byte)0x1E, (byte)0x87, (byte)0xE9, - (byte)0xCE, (byte)0x55, (byte)0x28, (byte)0xDF, - (byte)0x8C, (byte)0xA1, (byte)0x89, (byte)0x0D, - (byte)0xBF, (byte)0xE6, (byte)0x42, (byte)0x68, - (byte)0x41, (byte)0x99, (byte)0x2D, (byte)0x0F, - (byte)0xB0, (byte)0x54, (byte)0xBB, (byte)0x16, - }; - - // the pre-computed substitution table (inverse S-box), 256 bytes - private static final byte[] Si = { - (byte)0x52, (byte)0x09, (byte)0x6A, (byte)0xD5, - (byte)0x30, (byte)0x36, (byte)0xA5, (byte)0x38, - (byte)0xBF, (byte)0x40, (byte)0xA3, (byte)0x9E, - (byte)0x81, (byte)0xF3, (byte)0xD7, (byte)0xFB, - (byte)0x7C, (byte)0xE3, (byte)0x39, (byte)0x82, - (byte)0x9B, (byte)0x2F, (byte)0xFF, (byte)0x87, - (byte)0x34, (byte)0x8E, (byte)0x43, (byte)0x44, - (byte)0xC4, (byte)0xDE, (byte)0xE9, (byte)0xCB, - (byte)0x54, (byte)0x7B, (byte)0x94, (byte)0x32, - (byte)0xA6, (byte)0xC2, (byte)0x23, (byte)0x3D, - (byte)0xEE, (byte)0x4C, (byte)0x95, (byte)0x0B, - (byte)0x42, (byte)0xFA, (byte)0xC3, (byte)0x4E, - (byte)0x08, (byte)0x2E, (byte)0xA1, (byte)0x66, - (byte)0x28, (byte)0xD9, (byte)0x24, (byte)0xB2, - (byte)0x76, (byte)0x5B, (byte)0xA2, (byte)0x49, - (byte)0x6D, (byte)0x8B, (byte)0xD1, (byte)0x25, - (byte)0x72, (byte)0xF8, (byte)0xF6, (byte)0x64, - (byte)0x86, (byte)0x68, (byte)0x98, (byte)0x16, - (byte)0xD4, (byte)0xA4, (byte)0x5C, (byte)0xCC, - (byte)0x5D, (byte)0x65, (byte)0xB6, (byte)0x92, - (byte)0x6C, (byte)0x70, (byte)0x48, (byte)0x50, - (byte)0xFD, (byte)0xED, (byte)0xB9, (byte)0xDA, - (byte)0x5E, (byte)0x15, (byte)0x46, (byte)0x57, - (byte)0xA7, (byte)0x8D, (byte)0x9D, (byte)0x84, - (byte)0x90, (byte)0xD8, (byte)0xAB, (byte)0x00, - (byte)0x8C, (byte)0xBC, (byte)0xD3, (byte)0x0A, - (byte)0xF7, (byte)0xE4, (byte)0x58, (byte)0x05, - (byte)0xB8, (byte)0xB3, (byte)0x45, (byte)0x06, - (byte)0xD0, (byte)0x2C, (byte)0x1E, (byte)0x8F, - (byte)0xCA, (byte)0x3F, (byte)0x0F, (byte)0x02, - (byte)0xC1, (byte)0xAF, (byte)0xBD, (byte)0x03, - (byte)0x01, (byte)0x13, (byte)0x8A, (byte)0x6B, - (byte)0x3A, (byte)0x91, (byte)0x11, (byte)0x41, - (byte)0x4F, (byte)0x67, (byte)0xDC, (byte)0xEA, - (byte)0x97, (byte)0xF2, (byte)0xCF, (byte)0xCE, - (byte)0xF0, (byte)0xB4, (byte)0xE6, (byte)0x73, - (byte)0x96, (byte)0xAC, (byte)0x74, (byte)0x22, - (byte)0xE7, (byte)0xAD, (byte)0x35, (byte)0x85, - (byte)0xE2, (byte)0xF9, (byte)0x37, (byte)0xE8, - (byte)0x1C, (byte)0x75, (byte)0xDF, (byte)0x6E, - (byte)0x47, (byte)0xF1, (byte)0x1A, (byte)0x71, - (byte)0x1D, (byte)0x29, (byte)0xC5, (byte)0x89, - (byte)0x6F, (byte)0xB7, (byte)0x62, (byte)0x0E, - (byte)0xAA, (byte)0x18, (byte)0xBE, (byte)0x1B, - (byte)0xFC, (byte)0x56, (byte)0x3E, (byte)0x4B, - (byte)0xC6, (byte)0xD2, (byte)0x79, (byte)0x20, - (byte)0x9A, (byte)0xDB, (byte)0xC0, (byte)0xFE, - (byte)0x78, (byte)0xCD, (byte)0x5A, (byte)0xF4, - (byte)0x1F, (byte)0xDD, (byte)0xA8, (byte)0x33, - (byte)0x88, (byte)0x07, (byte)0xC7, (byte)0x31, - (byte)0xB1, (byte)0x12, (byte)0x10, (byte)0x59, - (byte)0x27, (byte)0x80, (byte)0xEC, (byte)0x5F, - (byte)0x60, (byte)0x51, (byte)0x7F, (byte)0xA9, - (byte)0x19, (byte)0xB5, (byte)0x4A, (byte)0x0D, - (byte)0x2D, (byte)0xE5, (byte)0x7A, (byte)0x9F, - (byte)0x93, (byte)0xC9, (byte)0x9C, (byte)0xEF, - (byte)0xA0, (byte)0xE0, (byte)0x3B, (byte)0x4D, - (byte)0xAE, (byte)0x2A, (byte)0xF5, (byte)0xB0, - (byte)0xC8, (byte)0xEB, (byte)0xBB, (byte)0x3C, - (byte)0x83, (byte)0x53, (byte)0x99, (byte)0x61, - (byte)0x17, (byte)0x2B, (byte)0x04, (byte)0x7E, - (byte)0xBA, (byte)0x77, (byte)0xD6, (byte)0x26, - (byte)0xE1, (byte)0x69, (byte)0x14, (byte)0x63, - (byte)0x55, (byte)0x21, (byte)0x0C, (byte)0x7D, - }; - - // pre-computed tables (T-box) - private static final int[] T1 = { - 0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, - 0xFFF2F20D, 0xD66B6BBD, 0xDE6F6FB1, 0x91C5C554, - 0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D, - 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A, - 0x8FCACA45, 0x1F82829D, 0x89C9C940, 0xFA7D7D87, - 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B, - 0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, - 0x239C9CBF, 0x53A4A4F7, 0xE4727296, 0x9BC0C05B, - 0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A, - 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F, - 0x6834345C, 0x51A5A5F4, 0xD1E5E534, 0xF9F1F108, - 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F, - 0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, - 0x30181828, 0x379696A1, 0x0A05050F, 0x2F9A9AB5, - 0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, - 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F, - 0x1209091B, 0x1D83839E, 0x582C2C74, 0x341A1A2E, - 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB, - 0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, - 0x5229297B, 0xDDE3E33E, 0x5E2F2F71, 0x13848497, - 0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, - 0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED, - 0xD46A6ABE, 0x8DCBCB46, 0x67BEBED9, 0x7239394B, - 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A, - 0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, - 0x864343C5, 0x9A4D4DD7, 0x66333355, 0x11858594, - 0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81, - 0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3, - 0xA25151F3, 0x5DA3A3FE, 0x804040C0, 0x058F8F8A, - 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504, - 0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, - 0x20101030, 0xE5FFFF1A, 0xFDF3F30E, 0xBFD2D26D, - 0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F, - 0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739, - 0x93C4C457, 0x55A7A7F2, 0xFC7E7E82, 0x7A3D3D47, - 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395, - 0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, - 0x44222266, 0x542A2A7E, 0x3B9090AB, 0x0B888883, - 0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C, - 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76, - 0xDBE0E03B, 0x64323256, 0x743A3A4E, 0x140A0A1E, - 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4, - 0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, - 0x399191A8, 0x319595A4, 0xD3E4E437, 0xF279798B, - 0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, - 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0, - 0xD86C6CB4, 0xAC5656FA, 0xF3F4F407, 0xCFEAEA25, - 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818, - 0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, - 0x381C1C24, 0x57A6A6F1, 0x73B4B4C7, 0x97C6C651, - 0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, - 0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85, - 0xE0707090, 0x7C3E3E42, 0x71B5B5C4, 0xCC6666AA, - 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12, - 0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, - 0x17868691, 0x99C1C158, 0x3A1D1D27, 0x279E9EB9, - 0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133, - 0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7, - 0x2D9B9BB6, 0x3C1E1E22, 0x15878792, 0xC9E9E920, - 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A, - 0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, - 0x65BFBFDA, 0xD7E6E631, 0x844242C6, 0xD06868B8, - 0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11, - 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A, - }; - - private static final int[] T2 = { - 0xA5C66363, 0x84F87C7C, 0x99EE7777, 0x8DF67B7B, - 0x0DFFF2F2, 0xBDD66B6B, 0xB1DE6F6F, 0x5491C5C5, - 0x50603030, 0x03020101, 0xA9CE6767, 0x7D562B2B, - 0x19E7FEFE, 0x62B5D7D7, 0xE64DABAB, 0x9AEC7676, - 0x458FCACA, 0x9D1F8282, 0x4089C9C9, 0x87FA7D7D, - 0x15EFFAFA, 0xEBB25959, 0xC98E4747, 0x0BFBF0F0, - 0xEC41ADAD, 0x67B3D4D4, 0xFD5FA2A2, 0xEA45AFAF, - 0xBF239C9C, 0xF753A4A4, 0x96E47272, 0x5B9BC0C0, - 0xC275B7B7, 0x1CE1FDFD, 0xAE3D9393, 0x6A4C2626, - 0x5A6C3636, 0x417E3F3F, 0x02F5F7F7, 0x4F83CCCC, - 0x5C683434, 0xF451A5A5, 0x34D1E5E5, 0x08F9F1F1, - 0x93E27171, 0x73ABD8D8, 0x53623131, 0x3F2A1515, - 0x0C080404, 0x5295C7C7, 0x65462323, 0x5E9DC3C3, - 0x28301818, 0xA1379696, 0x0F0A0505, 0xB52F9A9A, - 0x090E0707, 0x36241212, 0x9B1B8080, 0x3DDFE2E2, - 0x26CDEBEB, 0x694E2727, 0xCD7FB2B2, 0x9FEA7575, - 0x1B120909, 0x9E1D8383, 0x74582C2C, 0x2E341A1A, - 0x2D361B1B, 0xB2DC6E6E, 0xEEB45A5A, 0xFB5BA0A0, - 0xF6A45252, 0x4D763B3B, 0x61B7D6D6, 0xCE7DB3B3, - 0x7B522929, 0x3EDDE3E3, 0x715E2F2F, 0x97138484, - 0xF5A65353, 0x68B9D1D1, 0x00000000, 0x2CC1EDED, - 0x60402020, 0x1FE3FCFC, 0xC879B1B1, 0xEDB65B5B, - 0xBED46A6A, 0x468DCBCB, 0xD967BEBE, 0x4B723939, - 0xDE944A4A, 0xD4984C4C, 0xE8B05858, 0x4A85CFCF, - 0x6BBBD0D0, 0x2AC5EFEF, 0xE54FAAAA, 0x16EDFBFB, - 0xC5864343, 0xD79A4D4D, 0x55663333, 0x94118585, - 0xCF8A4545, 0x10E9F9F9, 0x06040202, 0x81FE7F7F, - 0xF0A05050, 0x44783C3C, 0xBA259F9F, 0xE34BA8A8, - 0xF3A25151, 0xFE5DA3A3, 0xC0804040, 0x8A058F8F, - 0xAD3F9292, 0xBC219D9D, 0x48703838, 0x04F1F5F5, - 0xDF63BCBC, 0xC177B6B6, 0x75AFDADA, 0x63422121, - 0x30201010, 0x1AE5FFFF, 0x0EFDF3F3, 0x6DBFD2D2, - 0x4C81CDCD, 0x14180C0C, 0x35261313, 0x2FC3ECEC, - 0xE1BE5F5F, 0xA2359797, 0xCC884444, 0x392E1717, - 0x5793C4C4, 0xF255A7A7, 0x82FC7E7E, 0x477A3D3D, - 0xACC86464, 0xE7BA5D5D, 0x2B321919, 0x95E67373, - 0xA0C06060, 0x98198181, 0xD19E4F4F, 0x7FA3DCDC, - 0x66442222, 0x7E542A2A, 0xAB3B9090, 0x830B8888, - 0xCA8C4646, 0x29C7EEEE, 0xD36BB8B8, 0x3C281414, - 0x79A7DEDE, 0xE2BC5E5E, 0x1D160B0B, 0x76ADDBDB, - 0x3BDBE0E0, 0x56643232, 0x4E743A3A, 0x1E140A0A, - 0xDB924949, 0x0A0C0606, 0x6C482424, 0xE4B85C5C, - 0x5D9FC2C2, 0x6EBDD3D3, 0xEF43ACAC, 0xA6C46262, - 0xA8399191, 0xA4319595, 0x37D3E4E4, 0x8BF27979, - 0x32D5E7E7, 0x438BC8C8, 0x596E3737, 0xB7DA6D6D, - 0x8C018D8D, 0x64B1D5D5, 0xD29C4E4E, 0xE049A9A9, - 0xB4D86C6C, 0xFAAC5656, 0x07F3F4F4, 0x25CFEAEA, - 0xAFCA6565, 0x8EF47A7A, 0xE947AEAE, 0x18100808, - 0xD56FBABA, 0x88F07878, 0x6F4A2525, 0x725C2E2E, - 0x24381C1C, 0xF157A6A6, 0xC773B4B4, 0x5197C6C6, - 0x23CBE8E8, 0x7CA1DDDD, 0x9CE87474, 0x213E1F1F, - 0xDD964B4B, 0xDC61BDBD, 0x860D8B8B, 0x850F8A8A, - 0x90E07070, 0x427C3E3E, 0xC471B5B5, 0xAACC6666, - 0xD8904848, 0x05060303, 0x01F7F6F6, 0x121C0E0E, - 0xA3C26161, 0x5F6A3535, 0xF9AE5757, 0xD069B9B9, - 0x91178686, 0x5899C1C1, 0x273A1D1D, 0xB9279E9E, - 0x38D9E1E1, 0x13EBF8F8, 0xB32B9898, 0x33221111, - 0xBBD26969, 0x70A9D9D9, 0x89078E8E, 0xA7339494, - 0xB62D9B9B, 0x223C1E1E, 0x92158787, 0x20C9E9E9, - 0x4987CECE, 0xFFAA5555, 0x78502828, 0x7AA5DFDF, - 0x8F038C8C, 0xF859A1A1, 0x80098989, 0x171A0D0D, - 0xDA65BFBF, 0x31D7E6E6, 0xC6844242, 0xB8D06868, - 0xC3824141, 0xB0299999, 0x775A2D2D, 0x111E0F0F, - 0xCB7BB0B0, 0xFCA85454, 0xD66DBBBB, 0x3A2C1616, - }; - - private static final int[] T3 = { - 0x63A5C663, 0x7C84F87C, 0x7799EE77, 0x7B8DF67B, - 0xF20DFFF2, 0x6BBDD66B, 0x6FB1DE6F, 0xC55491C5, - 0x30506030, 0x01030201, 0x67A9CE67, 0x2B7D562B, - 0xFE19E7FE, 0xD762B5D7, 0xABE64DAB, 0x769AEC76, - 0xCA458FCA, 0x829D1F82, 0xC94089C9, 0x7D87FA7D, - 0xFA15EFFA, 0x59EBB259, 0x47C98E47, 0xF00BFBF0, - 0xADEC41AD, 0xD467B3D4, 0xA2FD5FA2, 0xAFEA45AF, - 0x9CBF239C, 0xA4F753A4, 0x7296E472, 0xC05B9BC0, - 0xB7C275B7, 0xFD1CE1FD, 0x93AE3D93, 0x266A4C26, - 0x365A6C36, 0x3F417E3F, 0xF702F5F7, 0xCC4F83CC, - 0x345C6834, 0xA5F451A5, 0xE534D1E5, 0xF108F9F1, - 0x7193E271, 0xD873ABD8, 0x31536231, 0x153F2A15, - 0x040C0804, 0xC75295C7, 0x23654623, 0xC35E9DC3, - 0x18283018, 0x96A13796, 0x050F0A05, 0x9AB52F9A, - 0x07090E07, 0x12362412, 0x809B1B80, 0xE23DDFE2, - 0xEB26CDEB, 0x27694E27, 0xB2CD7FB2, 0x759FEA75, - 0x091B1209, 0x839E1D83, 0x2C74582C, 0x1A2E341A, - 0x1B2D361B, 0x6EB2DC6E, 0x5AEEB45A, 0xA0FB5BA0, - 0x52F6A452, 0x3B4D763B, 0xD661B7D6, 0xB3CE7DB3, - 0x297B5229, 0xE33EDDE3, 0x2F715E2F, 0x84971384, - 0x53F5A653, 0xD168B9D1, 0x00000000, 0xED2CC1ED, - 0x20604020, 0xFC1FE3FC, 0xB1C879B1, 0x5BEDB65B, - 0x6ABED46A, 0xCB468DCB, 0xBED967BE, 0x394B7239, - 0x4ADE944A, 0x4CD4984C, 0x58E8B058, 0xCF4A85CF, - 0xD06BBBD0, 0xEF2AC5EF, 0xAAE54FAA, 0xFB16EDFB, - 0x43C58643, 0x4DD79A4D, 0x33556633, 0x85941185, - 0x45CF8A45, 0xF910E9F9, 0x02060402, 0x7F81FE7F, - 0x50F0A050, 0x3C44783C, 0x9FBA259F, 0xA8E34BA8, - 0x51F3A251, 0xA3FE5DA3, 0x40C08040, 0x8F8A058F, - 0x92AD3F92, 0x9DBC219D, 0x38487038, 0xF504F1F5, - 0xBCDF63BC, 0xB6C177B6, 0xDA75AFDA, 0x21634221, - 0x10302010, 0xFF1AE5FF, 0xF30EFDF3, 0xD26DBFD2, - 0xCD4C81CD, 0x0C14180C, 0x13352613, 0xEC2FC3EC, - 0x5FE1BE5F, 0x97A23597, 0x44CC8844, 0x17392E17, - 0xC45793C4, 0xA7F255A7, 0x7E82FC7E, 0x3D477A3D, - 0x64ACC864, 0x5DE7BA5D, 0x192B3219, 0x7395E673, - 0x60A0C060, 0x81981981, 0x4FD19E4F, 0xDC7FA3DC, - 0x22664422, 0x2A7E542A, 0x90AB3B90, 0x88830B88, - 0x46CA8C46, 0xEE29C7EE, 0xB8D36BB8, 0x143C2814, - 0xDE79A7DE, 0x5EE2BC5E, 0x0B1D160B, 0xDB76ADDB, - 0xE03BDBE0, 0x32566432, 0x3A4E743A, 0x0A1E140A, - 0x49DB9249, 0x060A0C06, 0x246C4824, 0x5CE4B85C, - 0xC25D9FC2, 0xD36EBDD3, 0xACEF43AC, 0x62A6C462, - 0x91A83991, 0x95A43195, 0xE437D3E4, 0x798BF279, - 0xE732D5E7, 0xC8438BC8, 0x37596E37, 0x6DB7DA6D, - 0x8D8C018D, 0xD564B1D5, 0x4ED29C4E, 0xA9E049A9, - 0x6CB4D86C, 0x56FAAC56, 0xF407F3F4, 0xEA25CFEA, - 0x65AFCA65, 0x7A8EF47A, 0xAEE947AE, 0x08181008, - 0xBAD56FBA, 0x7888F078, 0x256F4A25, 0x2E725C2E, - 0x1C24381C, 0xA6F157A6, 0xB4C773B4, 0xC65197C6, - 0xE823CBE8, 0xDD7CA1DD, 0x749CE874, 0x1F213E1F, - 0x4BDD964B, 0xBDDC61BD, 0x8B860D8B, 0x8A850F8A, - 0x7090E070, 0x3E427C3E, 0xB5C471B5, 0x66AACC66, - 0x48D89048, 0x03050603, 0xF601F7F6, 0x0E121C0E, - 0x61A3C261, 0x355F6A35, 0x57F9AE57, 0xB9D069B9, - 0x86911786, 0xC15899C1, 0x1D273A1D, 0x9EB9279E, - 0xE138D9E1, 0xF813EBF8, 0x98B32B98, 0x11332211, - 0x69BBD269, 0xD970A9D9, 0x8E89078E, 0x94A73394, - 0x9BB62D9B, 0x1E223C1E, 0x87921587, 0xE920C9E9, - 0xCE4987CE, 0x55FFAA55, 0x28785028, 0xDF7AA5DF, - 0x8C8F038C, 0xA1F859A1, 0x89800989, 0x0D171A0D, - 0xBFDA65BF, 0xE631D7E6, 0x42C68442, 0x68B8D068, - 0x41C38241, 0x99B02999, 0x2D775A2D, 0x0F111E0F, - 0xB0CB7BB0, 0x54FCA854, 0xBBD66DBB, 0x163A2C16, - }; - - private static final int[] T4 = { - 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, - 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, - 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, - 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, - 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, - 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, - 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, - 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, - 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, - 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, - 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, - 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, - 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, - 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, - 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, - 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, - 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, - 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, - 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, - 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, - 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, - 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, - 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, - 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, - 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, - 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, - 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, - 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, - 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, - 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, - 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, - 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, - 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, - 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, - 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, - 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, - 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, - 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, - 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, - 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, - 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, - 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, - 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, - 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, - 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, - 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, - 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, - 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, - 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, - 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, - 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, - 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, - 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, - 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, - 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, - 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, - 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, - 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, - 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, - 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, - 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, - 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, - 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, - 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C, - }; - - - // pre-computed inverse tables (inverse T-box) - private static final int[] T5 = { - 0x51F4A750, 0x7E416553, 0x1A17A4C3, 0x3A275E96, - 0x3BAB6BCB, 0x1F9D45F1, 0xACFA58AB, 0x4BE30393, - 0x2030FA55, 0xAD766DF6, 0x88CC7691, 0xF5024C25, - 0x4FE5D7FC, 0xC52ACBD7, 0x26354480, 0xB562A38F, - 0xDEB15A49, 0x25BA1B67, 0x45EA0E98, 0x5DFEC0E1, - 0xC32F7502, 0x814CF012, 0x8D4697A3, 0x6BD3F9C6, - 0x038F5FE7, 0x15929C95, 0xBF6D7AEB, 0x955259DA, - 0xD4BE832D, 0x587421D3, 0x49E06929, 0x8EC9C844, - 0x75C2896A, 0xF48E7978, 0x99583E6B, 0x27B971DD, - 0xBEE14FB6, 0xF088AD17, 0xC920AC66, 0x7DCE3AB4, - 0x63DF4A18, 0xE51A3182, 0x97513360, 0x62537F45, - 0xB16477E0, 0xBB6BAE84, 0xFE81A01C, 0xF9082B94, - 0x70486858, 0x8F45FD19, 0x94DE6C87, 0x527BF8B7, - 0xAB73D323, 0x724B02E2, 0xE31F8F57, 0x6655AB2A, - 0xB2EB2807, 0x2FB5C203, 0x86C57B9A, 0xD33708A5, - 0x302887F2, 0x23BFA5B2, 0x02036ABA, 0xED16825C, - 0x8ACF1C2B, 0xA779B492, 0xF307F2F0, 0x4E69E2A1, - 0x65DAF4CD, 0x0605BED5, 0xD134621F, 0xC4A6FE8A, - 0x342E539D, 0xA2F355A0, 0x058AE132, 0xA4F6EB75, - 0x0B83EC39, 0x4060EFAA, 0x5E719F06, 0xBD6E1051, - 0x3E218AF9, 0x96DD063D, 0xDD3E05AE, 0x4DE6BD46, - 0x91548DB5, 0x71C45D05, 0x0406D46F, 0x605015FF, - 0x1998FB24, 0xD6BDE997, 0x894043CC, 0x67D99E77, - 0xB0E842BD, 0x07898B88, 0xE7195B38, 0x79C8EEDB, - 0xA17C0A47, 0x7C420FE9, 0xF8841EC9, 0x00000000, - 0x09808683, 0x322BED48, 0x1E1170AC, 0x6C5A724E, - 0xFD0EFFFB, 0x0F853856, 0x3DAED51E, 0x362D3927, - 0x0A0FD964, 0x685CA621, 0x9B5B54D1, 0x24362E3A, - 0x0C0A67B1, 0x9357E70F, 0xB4EE96D2, 0x1B9B919E, - 0x80C0C54F, 0x61DC20A2, 0x5A774B69, 0x1C121A16, - 0xE293BA0A, 0xC0A02AE5, 0x3C22E043, 0x121B171D, - 0x0E090D0B, 0xF28BC7AD, 0x2DB6A8B9, 0x141EA9C8, - 0x57F11985, 0xAF75074C, 0xEE99DDBB, 0xA37F60FD, - 0xF701269F, 0x5C72F5BC, 0x44663BC5, 0x5BFB7E34, - 0x8B432976, 0xCB23C6DC, 0xB6EDFC68, 0xB8E4F163, - 0xD731DCCA, 0x42638510, 0x13972240, 0x84C61120, - 0x854A247D, 0xD2BB3DF8, 0xAEF93211, 0xC729A16D, - 0x1D9E2F4B, 0xDCB230F3, 0x0D8652EC, 0x77C1E3D0, - 0x2BB3166C, 0xA970B999, 0x119448FA, 0x47E96422, - 0xA8FC8CC4, 0xA0F03F1A, 0x567D2CD8, 0x223390EF, - 0x87494EC7, 0xD938D1C1, 0x8CCAA2FE, 0x98D40B36, - 0xA6F581CF, 0xA57ADE28, 0xDAB78E26, 0x3FADBFA4, - 0x2C3A9DE4, 0x5078920D, 0x6A5FCC9B, 0x547E4662, - 0xF68D13C2, 0x90D8B8E8, 0x2E39F75E, 0x82C3AFF5, - 0x9F5D80BE, 0x69D0937C, 0x6FD52DA9, 0xCF2512B3, - 0xC8AC993B, 0x10187DA7, 0xE89C636E, 0xDB3BBB7B, - 0xCD267809, 0x6E5918F4, 0xEC9AB701, 0x834F9AA8, - 0xE6956E65, 0xAAFFE67E, 0x21BCCF08, 0xEF15E8E6, - 0xBAE79BD9, 0x4A6F36CE, 0xEA9F09D4, 0x29B07CD6, - 0x31A4B2AF, 0x2A3F2331, 0xC6A59430, 0x35A266C0, - 0x744EBC37, 0xFC82CAA6, 0xE090D0B0, 0x33A7D815, - 0xF104984A, 0x41ECDAF7, 0x7FCD500E, 0x1791F62F, - 0x764DD68D, 0x43EFB04D, 0xCCAA4D54, 0xE49604DF, - 0x9ED1B5E3, 0x4C6A881B, 0xC12C1FB8, 0x4665517F, - 0x9D5EEA04, 0x018C355D, 0xFA877473, 0xFB0B412E, - 0xB3671D5A, 0x92DBD252, 0xE9105633, 0x6DD64713, - 0x9AD7618C, 0x37A10C7A, 0x59F8148E, 0xEB133C89, - 0xCEA927EE, 0xB761C935, 0xE11CE5ED, 0x7A47B13C, - 0x9CD2DF59, 0x55F2733F, 0x1814CE79, 0x73C737BF, - 0x53F7CDEA, 0x5FFDAA5B, 0xDF3D6F14, 0x7844DB86, - 0xCAAFF381, 0xB968C43E, 0x3824342C, 0xC2A3405F, - 0x161DC372, 0xBCE2250C, 0x283C498B, 0xFF0D9541, - 0x39A80171, 0x080CB3DE, 0xD8B4E49C, 0x6456C190, - 0x7BCB8461, 0xD532B670, 0x486C5C74, 0xD0B85742, - }; - - private static final int[] T6 = { - 0x5051F4A7, 0x537E4165, 0xC31A17A4, 0x963A275E, - 0xCB3BAB6B, 0xF11F9D45, 0xABACFA58, 0x934BE303, - 0x552030FA, 0xF6AD766D, 0x9188CC76, 0x25F5024C, - 0xFC4FE5D7, 0xD7C52ACB, 0x80263544, 0x8FB562A3, - 0x49DEB15A, 0x6725BA1B, 0x9845EA0E, 0xE15DFEC0, - 0x02C32F75, 0x12814CF0, 0xA38D4697, 0xC66BD3F9, - 0xE7038F5F, 0x9515929C, 0xEBBF6D7A, 0xDA955259, - 0x2DD4BE83, 0xD3587421, 0x2949E069, 0x448EC9C8, - 0x6A75C289, 0x78F48E79, 0x6B99583E, 0xDD27B971, - 0xB6BEE14F, 0x17F088AD, 0x66C920AC, 0xB47DCE3A, - 0x1863DF4A, 0x82E51A31, 0x60975133, 0x4562537F, - 0xE0B16477, 0x84BB6BAE, 0x1CFE81A0, 0x94F9082B, - 0x58704868, 0x198F45FD, 0x8794DE6C, 0xB7527BF8, - 0x23AB73D3, 0xE2724B02, 0x57E31F8F, 0x2A6655AB, - 0x07B2EB28, 0x032FB5C2, 0x9A86C57B, 0xA5D33708, - 0xF2302887, 0xB223BFA5, 0xBA02036A, 0x5CED1682, - 0x2B8ACF1C, 0x92A779B4, 0xF0F307F2, 0xA14E69E2, - 0xCD65DAF4, 0xD50605BE, 0x1FD13462, 0x8AC4A6FE, - 0x9D342E53, 0xA0A2F355, 0x32058AE1, 0x75A4F6EB, - 0x390B83EC, 0xAA4060EF, 0x065E719F, 0x51BD6E10, - 0xF93E218A, 0x3D96DD06, 0xAEDD3E05, 0x464DE6BD, - 0xB591548D, 0x0571C45D, 0x6F0406D4, 0xFF605015, - 0x241998FB, 0x97D6BDE9, 0xCC894043, 0x7767D99E, - 0xBDB0E842, 0x8807898B, 0x38E7195B, 0xDB79C8EE, - 0x47A17C0A, 0xE97C420F, 0xC9F8841E, 0x00000000, - 0x83098086, 0x48322BED, 0xAC1E1170, 0x4E6C5A72, - 0xFBFD0EFF, 0x560F8538, 0x1E3DAED5, 0x27362D39, - 0x640A0FD9, 0x21685CA6, 0xD19B5B54, 0x3A24362E, - 0xB10C0A67, 0x0F9357E7, 0xD2B4EE96, 0x9E1B9B91, - 0x4F80C0C5, 0xA261DC20, 0x695A774B, 0x161C121A, - 0x0AE293BA, 0xE5C0A02A, 0x433C22E0, 0x1D121B17, - 0x0B0E090D, 0xADF28BC7, 0xB92DB6A8, 0xC8141EA9, - 0x8557F119, 0x4CAF7507, 0xBBEE99DD, 0xFDA37F60, - 0x9FF70126, 0xBC5C72F5, 0xC544663B, 0x345BFB7E, - 0x768B4329, 0xDCCB23C6, 0x68B6EDFC, 0x63B8E4F1, - 0xCAD731DC, 0x10426385, 0x40139722, 0x2084C611, - 0x7D854A24, 0xF8D2BB3D, 0x11AEF932, 0x6DC729A1, - 0x4B1D9E2F, 0xF3DCB230, 0xEC0D8652, 0xD077C1E3, - 0x6C2BB316, 0x99A970B9, 0xFA119448, 0x2247E964, - 0xC4A8FC8C, 0x1AA0F03F, 0xD8567D2C, 0xEF223390, - 0xC787494E, 0xC1D938D1, 0xFE8CCAA2, 0x3698D40B, - 0xCFA6F581, 0x28A57ADE, 0x26DAB78E, 0xA43FADBF, - 0xE42C3A9D, 0x0D507892, 0x9B6A5FCC, 0x62547E46, - 0xC2F68D13, 0xE890D8B8, 0x5E2E39F7, 0xF582C3AF, - 0xBE9F5D80, 0x7C69D093, 0xA96FD52D, 0xB3CF2512, - 0x3BC8AC99, 0xA710187D, 0x6EE89C63, 0x7BDB3BBB, - 0x09CD2678, 0xF46E5918, 0x01EC9AB7, 0xA8834F9A, - 0x65E6956E, 0x7EAAFFE6, 0x0821BCCF, 0xE6EF15E8, - 0xD9BAE79B, 0xCE4A6F36, 0xD4EA9F09, 0xD629B07C, - 0xAF31A4B2, 0x312A3F23, 0x30C6A594, 0xC035A266, - 0x37744EBC, 0xA6FC82CA, 0xB0E090D0, 0x1533A7D8, - 0x4AF10498, 0xF741ECDA, 0x0E7FCD50, 0x2F1791F6, - 0x8D764DD6, 0x4D43EFB0, 0x54CCAA4D, 0xDFE49604, - 0xE39ED1B5, 0x1B4C6A88, 0xB8C12C1F, 0x7F466551, - 0x049D5EEA, 0x5D018C35, 0x73FA8774, 0x2EFB0B41, - 0x5AB3671D, 0x5292DBD2, 0x33E91056, 0x136DD647, - 0x8C9AD761, 0x7A37A10C, 0x8E59F814, 0x89EB133C, - 0xEECEA927, 0x35B761C9, 0xEDE11CE5, 0x3C7A47B1, - 0x599CD2DF, 0x3F55F273, 0x791814CE, 0xBF73C737, - 0xEA53F7CD, 0x5B5FFDAA, 0x14DF3D6F, 0x867844DB, - 0x81CAAFF3, 0x3EB968C4, 0x2C382434, 0x5FC2A340, - 0x72161DC3, 0x0CBCE225, 0x8B283C49, 0x41FF0D95, - 0x7139A801, 0xDE080CB3, 0x9CD8B4E4, 0x906456C1, - 0x617BCB84, 0x70D532B6, 0x74486C5C, 0x42D0B857, - }; - - private static final int[] T7 = { - 0xA75051F4, 0x65537E41, 0xA4C31A17, 0x5E963A27, - 0x6BCB3BAB, 0x45F11F9D, 0x58ABACFA, 0x03934BE3, - 0xFA552030, 0x6DF6AD76, 0x769188CC, 0x4C25F502, - 0xD7FC4FE5, 0xCBD7C52A, 0x44802635, 0xA38FB562, - 0x5A49DEB1, 0x1B6725BA, 0x0E9845EA, 0xC0E15DFE, - 0x7502C32F, 0xF012814C, 0x97A38D46, 0xF9C66BD3, - 0x5FE7038F, 0x9C951592, 0x7AEBBF6D, 0x59DA9552, - 0x832DD4BE, 0x21D35874, 0x692949E0, 0xC8448EC9, - 0x896A75C2, 0x7978F48E, 0x3E6B9958, 0x71DD27B9, - 0x4FB6BEE1, 0xAD17F088, 0xAC66C920, 0x3AB47DCE, - 0x4A1863DF, 0x3182E51A, 0x33609751, 0x7F456253, - 0x77E0B164, 0xAE84BB6B, 0xA01CFE81, 0x2B94F908, - 0x68587048, 0xFD198F45, 0x6C8794DE, 0xF8B7527B, - 0xD323AB73, 0x02E2724B, 0x8F57E31F, 0xAB2A6655, - 0x2807B2EB, 0xC2032FB5, 0x7B9A86C5, 0x08A5D337, - 0x87F23028, 0xA5B223BF, 0x6ABA0203, 0x825CED16, - 0x1C2B8ACF, 0xB492A779, 0xF2F0F307, 0xE2A14E69, - 0xF4CD65DA, 0xBED50605, 0x621FD134, 0xFE8AC4A6, - 0x539D342E, 0x55A0A2F3, 0xE132058A, 0xEB75A4F6, - 0xEC390B83, 0xEFAA4060, 0x9F065E71, 0x1051BD6E, - 0x8AF93E21, 0x063D96DD, 0x05AEDD3E, 0xBD464DE6, - 0x8DB59154, 0x5D0571C4, 0xD46F0406, 0x15FF6050, - 0xFB241998, 0xE997D6BD, 0x43CC8940, 0x9E7767D9, - 0x42BDB0E8, 0x8B880789, 0x5B38E719, 0xEEDB79C8, - 0x0A47A17C, 0x0FE97C42, 0x1EC9F884, 0x00000000, - 0x86830980, 0xED48322B, 0x70AC1E11, 0x724E6C5A, - 0xFFFBFD0E, 0x38560F85, 0xD51E3DAE, 0x3927362D, - 0xD9640A0F, 0xA621685C, 0x54D19B5B, 0x2E3A2436, - 0x67B10C0A, 0xE70F9357, 0x96D2B4EE, 0x919E1B9B, - 0xC54F80C0, 0x20A261DC, 0x4B695A77, 0x1A161C12, - 0xBA0AE293, 0x2AE5C0A0, 0xE0433C22, 0x171D121B, - 0x0D0B0E09, 0xC7ADF28B, 0xA8B92DB6, 0xA9C8141E, - 0x198557F1, 0x074CAF75, 0xDDBBEE99, 0x60FDA37F, - 0x269FF701, 0xF5BC5C72, 0x3BC54466, 0x7E345BFB, - 0x29768B43, 0xC6DCCB23, 0xFC68B6ED, 0xF163B8E4, - 0xDCCAD731, 0x85104263, 0x22401397, 0x112084C6, - 0x247D854A, 0x3DF8D2BB, 0x3211AEF9, 0xA16DC729, - 0x2F4B1D9E, 0x30F3DCB2, 0x52EC0D86, 0xE3D077C1, - 0x166C2BB3, 0xB999A970, 0x48FA1194, 0x642247E9, - 0x8CC4A8FC, 0x3F1AA0F0, 0x2CD8567D, 0x90EF2233, - 0x4EC78749, 0xD1C1D938, 0xA2FE8CCA, 0x0B3698D4, - 0x81CFA6F5, 0xDE28A57A, 0x8E26DAB7, 0xBFA43FAD, - 0x9DE42C3A, 0x920D5078, 0xCC9B6A5F, 0x4662547E, - 0x13C2F68D, 0xB8E890D8, 0xF75E2E39, 0xAFF582C3, - 0x80BE9F5D, 0x937C69D0, 0x2DA96FD5, 0x12B3CF25, - 0x993BC8AC, 0x7DA71018, 0x636EE89C, 0xBB7BDB3B, - 0x7809CD26, 0x18F46E59, 0xB701EC9A, 0x9AA8834F, - 0x6E65E695, 0xE67EAAFF, 0xCF0821BC, 0xE8E6EF15, - 0x9BD9BAE7, 0x36CE4A6F, 0x09D4EA9F, 0x7CD629B0, - 0xB2AF31A4, 0x23312A3F, 0x9430C6A5, 0x66C035A2, - 0xBC37744E, 0xCAA6FC82, 0xD0B0E090, 0xD81533A7, - 0x984AF104, 0xDAF741EC, 0x500E7FCD, 0xF62F1791, - 0xD68D764D, 0xB04D43EF, 0x4D54CCAA, 0x04DFE496, - 0xB5E39ED1, 0x881B4C6A, 0x1FB8C12C, 0x517F4665, - 0xEA049D5E, 0x355D018C, 0x7473FA87, 0x412EFB0B, - 0x1D5AB367, 0xD25292DB, 0x5633E910, 0x47136DD6, - 0x618C9AD7, 0x0C7A37A1, 0x148E59F8, 0x3C89EB13, - 0x27EECEA9, 0xC935B761, 0xE5EDE11C, 0xB13C7A47, - 0xDF599CD2, 0x733F55F2, 0xCE791814, 0x37BF73C7, - 0xCDEA53F7, 0xAA5B5FFD, 0x6F14DF3D, 0xDB867844, - 0xF381CAAF, 0xC43EB968, 0x342C3824, 0x405FC2A3, - 0xC372161D, 0x250CBCE2, 0x498B283C, 0x9541FF0D, - 0x017139A8, 0xB3DE080C, 0xE49CD8B4, 0xC1906456, - 0x84617BCB, 0xB670D532, 0x5C74486C, 0x5742D0B8, - }; - - private static final int[] T8 = { - 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, - 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B, - 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, - 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5, - 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, - 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B, - 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, - 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E, - 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, - 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D, - 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, - 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9, - 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, - 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66, - 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, - 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED, - 0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, - 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4, - 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, - 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD, - 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, - 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60, - 0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, - 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79, - 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, - 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C, - 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, - 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24, - 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, - 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C, - 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, - 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814, - 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, - 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B, - 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, - 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084, - 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, - 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077, - 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, - 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22, - 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, - 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F, - 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, - 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582, - 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, - 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB, - 0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, - 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF, - 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, - 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035, - 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, - 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17, - 0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, - 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46, - 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, - 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D, - 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, - 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A, - 0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, - 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678, - 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, - 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF, - 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, - 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0, - }; - - private static final int[] U1 = { - 0x00000000, 0x0E090D0B, 0x1C121A16, 0x121B171D, - 0x3824342C, 0x362D3927, 0x24362E3A, 0x2A3F2331, - 0x70486858, 0x7E416553, 0x6C5A724E, 0x62537F45, - 0x486C5C74, 0x4665517F, 0x547E4662, 0x5A774B69, - 0xE090D0B0, 0xEE99DDBB, 0xFC82CAA6, 0xF28BC7AD, - 0xD8B4E49C, 0xD6BDE997, 0xC4A6FE8A, 0xCAAFF381, - 0x90D8B8E8, 0x9ED1B5E3, 0x8CCAA2FE, 0x82C3AFF5, - 0xA8FC8CC4, 0xA6F581CF, 0xB4EE96D2, 0xBAE79BD9, - 0xDB3BBB7B, 0xD532B670, 0xC729A16D, 0xC920AC66, - 0xE31F8F57, 0xED16825C, 0xFF0D9541, 0xF104984A, - 0xAB73D323, 0xA57ADE28, 0xB761C935, 0xB968C43E, - 0x9357E70F, 0x9D5EEA04, 0x8F45FD19, 0x814CF012, - 0x3BAB6BCB, 0x35A266C0, 0x27B971DD, 0x29B07CD6, - 0x038F5FE7, 0x0D8652EC, 0x1F9D45F1, 0x119448FA, - 0x4BE30393, 0x45EA0E98, 0x57F11985, 0x59F8148E, - 0x73C737BF, 0x7DCE3AB4, 0x6FD52DA9, 0x61DC20A2, - 0xAD766DF6, 0xA37F60FD, 0xB16477E0, 0xBF6D7AEB, - 0x955259DA, 0x9B5B54D1, 0x894043CC, 0x87494EC7, - 0xDD3E05AE, 0xD33708A5, 0xC12C1FB8, 0xCF2512B3, - 0xE51A3182, 0xEB133C89, 0xF9082B94, 0xF701269F, - 0x4DE6BD46, 0x43EFB04D, 0x51F4A750, 0x5FFDAA5B, - 0x75C2896A, 0x7BCB8461, 0x69D0937C, 0x67D99E77, - 0x3DAED51E, 0x33A7D815, 0x21BCCF08, 0x2FB5C203, - 0x058AE132, 0x0B83EC39, 0x1998FB24, 0x1791F62F, - 0x764DD68D, 0x7844DB86, 0x6A5FCC9B, 0x6456C190, - 0x4E69E2A1, 0x4060EFAA, 0x527BF8B7, 0x5C72F5BC, - 0x0605BED5, 0x080CB3DE, 0x1A17A4C3, 0x141EA9C8, - 0x3E218AF9, 0x302887F2, 0x223390EF, 0x2C3A9DE4, - 0x96DD063D, 0x98D40B36, 0x8ACF1C2B, 0x84C61120, - 0xAEF93211, 0xA0F03F1A, 0xB2EB2807, 0xBCE2250C, - 0xE6956E65, 0xE89C636E, 0xFA877473, 0xF48E7978, - 0xDEB15A49, 0xD0B85742, 0xC2A3405F, 0xCCAA4D54, - 0x41ECDAF7, 0x4FE5D7FC, 0x5DFEC0E1, 0x53F7CDEA, - 0x79C8EEDB, 0x77C1E3D0, 0x65DAF4CD, 0x6BD3F9C6, - 0x31A4B2AF, 0x3FADBFA4, 0x2DB6A8B9, 0x23BFA5B2, - 0x09808683, 0x07898B88, 0x15929C95, 0x1B9B919E, - 0xA17C0A47, 0xAF75074C, 0xBD6E1051, 0xB3671D5A, - 0x99583E6B, 0x97513360, 0x854A247D, 0x8B432976, - 0xD134621F, 0xDF3D6F14, 0xCD267809, 0xC32F7502, - 0xE9105633, 0xE7195B38, 0xF5024C25, 0xFB0B412E, - 0x9AD7618C, 0x94DE6C87, 0x86C57B9A, 0x88CC7691, - 0xA2F355A0, 0xACFA58AB, 0xBEE14FB6, 0xB0E842BD, - 0xEA9F09D4, 0xE49604DF, 0xF68D13C2, 0xF8841EC9, - 0xD2BB3DF8, 0xDCB230F3, 0xCEA927EE, 0xC0A02AE5, - 0x7A47B13C, 0x744EBC37, 0x6655AB2A, 0x685CA621, - 0x42638510, 0x4C6A881B, 0x5E719F06, 0x5078920D, - 0x0A0FD964, 0x0406D46F, 0x161DC372, 0x1814CE79, - 0x322BED48, 0x3C22E043, 0x2E39F75E, 0x2030FA55, - 0xEC9AB701, 0xE293BA0A, 0xF088AD17, 0xFE81A01C, - 0xD4BE832D, 0xDAB78E26, 0xC8AC993B, 0xC6A59430, - 0x9CD2DF59, 0x92DBD252, 0x80C0C54F, 0x8EC9C844, - 0xA4F6EB75, 0xAAFFE67E, 0xB8E4F163, 0xB6EDFC68, - 0x0C0A67B1, 0x02036ABA, 0x10187DA7, 0x1E1170AC, - 0x342E539D, 0x3A275E96, 0x283C498B, 0x26354480, - 0x7C420FE9, 0x724B02E2, 0x605015FF, 0x6E5918F4, - 0x44663BC5, 0x4A6F36CE, 0x587421D3, 0x567D2CD8, - 0x37A10C7A, 0x39A80171, 0x2BB3166C, 0x25BA1B67, - 0x0F853856, 0x018C355D, 0x13972240, 0x1D9E2F4B, - 0x47E96422, 0x49E06929, 0x5BFB7E34, 0x55F2733F, - 0x7FCD500E, 0x71C45D05, 0x63DF4A18, 0x6DD64713, - 0xD731DCCA, 0xD938D1C1, 0xCB23C6DC, 0xC52ACBD7, - 0xEF15E8E6, 0xE11CE5ED, 0xF307F2F0, 0xFD0EFFFB, - 0xA779B492, 0xA970B999, 0xBB6BAE84, 0xB562A38F, - 0x9F5D80BE, 0x91548DB5, 0x834F9AA8, 0x8D4697A3, - }; - - private static final int[] U2 = { - 0x00000000, 0x0B0E090D, 0x161C121A, 0x1D121B17, - 0x2C382434, 0x27362D39, 0x3A24362E, 0x312A3F23, - 0x58704868, 0x537E4165, 0x4E6C5A72, 0x4562537F, - 0x74486C5C, 0x7F466551, 0x62547E46, 0x695A774B, - 0xB0E090D0, 0xBBEE99DD, 0xA6FC82CA, 0xADF28BC7, - 0x9CD8B4E4, 0x97D6BDE9, 0x8AC4A6FE, 0x81CAAFF3, - 0xE890D8B8, 0xE39ED1B5, 0xFE8CCAA2, 0xF582C3AF, - 0xC4A8FC8C, 0xCFA6F581, 0xD2B4EE96, 0xD9BAE79B, - 0x7BDB3BBB, 0x70D532B6, 0x6DC729A1, 0x66C920AC, - 0x57E31F8F, 0x5CED1682, 0x41FF0D95, 0x4AF10498, - 0x23AB73D3, 0x28A57ADE, 0x35B761C9, 0x3EB968C4, - 0x0F9357E7, 0x049D5EEA, 0x198F45FD, 0x12814CF0, - 0xCB3BAB6B, 0xC035A266, 0xDD27B971, 0xD629B07C, - 0xE7038F5F, 0xEC0D8652, 0xF11F9D45, 0xFA119448, - 0x934BE303, 0x9845EA0E, 0x8557F119, 0x8E59F814, - 0xBF73C737, 0xB47DCE3A, 0xA96FD52D, 0xA261DC20, - 0xF6AD766D, 0xFDA37F60, 0xE0B16477, 0xEBBF6D7A, - 0xDA955259, 0xD19B5B54, 0xCC894043, 0xC787494E, - 0xAEDD3E05, 0xA5D33708, 0xB8C12C1F, 0xB3CF2512, - 0x82E51A31, 0x89EB133C, 0x94F9082B, 0x9FF70126, - 0x464DE6BD, 0x4D43EFB0, 0x5051F4A7, 0x5B5FFDAA, - 0x6A75C289, 0x617BCB84, 0x7C69D093, 0x7767D99E, - 0x1E3DAED5, 0x1533A7D8, 0x0821BCCF, 0x032FB5C2, - 0x32058AE1, 0x390B83EC, 0x241998FB, 0x2F1791F6, - 0x8D764DD6, 0x867844DB, 0x9B6A5FCC, 0x906456C1, - 0xA14E69E2, 0xAA4060EF, 0xB7527BF8, 0xBC5C72F5, - 0xD50605BE, 0xDE080CB3, 0xC31A17A4, 0xC8141EA9, - 0xF93E218A, 0xF2302887, 0xEF223390, 0xE42C3A9D, - 0x3D96DD06, 0x3698D40B, 0x2B8ACF1C, 0x2084C611, - 0x11AEF932, 0x1AA0F03F, 0x07B2EB28, 0x0CBCE225, - 0x65E6956E, 0x6EE89C63, 0x73FA8774, 0x78F48E79, - 0x49DEB15A, 0x42D0B857, 0x5FC2A340, 0x54CCAA4D, - 0xF741ECDA, 0xFC4FE5D7, 0xE15DFEC0, 0xEA53F7CD, - 0xDB79C8EE, 0xD077C1E3, 0xCD65DAF4, 0xC66BD3F9, - 0xAF31A4B2, 0xA43FADBF, 0xB92DB6A8, 0xB223BFA5, - 0x83098086, 0x8807898B, 0x9515929C, 0x9E1B9B91, - 0x47A17C0A, 0x4CAF7507, 0x51BD6E10, 0x5AB3671D, - 0x6B99583E, 0x60975133, 0x7D854A24, 0x768B4329, - 0x1FD13462, 0x14DF3D6F, 0x09CD2678, 0x02C32F75, - 0x33E91056, 0x38E7195B, 0x25F5024C, 0x2EFB0B41, - 0x8C9AD761, 0x8794DE6C, 0x9A86C57B, 0x9188CC76, - 0xA0A2F355, 0xABACFA58, 0xB6BEE14F, 0xBDB0E842, - 0xD4EA9F09, 0xDFE49604, 0xC2F68D13, 0xC9F8841E, - 0xF8D2BB3D, 0xF3DCB230, 0xEECEA927, 0xE5C0A02A, - 0x3C7A47B1, 0x37744EBC, 0x2A6655AB, 0x21685CA6, - 0x10426385, 0x1B4C6A88, 0x065E719F, 0x0D507892, - 0x640A0FD9, 0x6F0406D4, 0x72161DC3, 0x791814CE, - 0x48322BED, 0x433C22E0, 0x5E2E39F7, 0x552030FA, - 0x01EC9AB7, 0x0AE293BA, 0x17F088AD, 0x1CFE81A0, - 0x2DD4BE83, 0x26DAB78E, 0x3BC8AC99, 0x30C6A594, - 0x599CD2DF, 0x5292DBD2, 0x4F80C0C5, 0x448EC9C8, - 0x75A4F6EB, 0x7EAAFFE6, 0x63B8E4F1, 0x68B6EDFC, - 0xB10C0A67, 0xBA02036A, 0xA710187D, 0xAC1E1170, - 0x9D342E53, 0x963A275E, 0x8B283C49, 0x80263544, - 0xE97C420F, 0xE2724B02, 0xFF605015, 0xF46E5918, - 0xC544663B, 0xCE4A6F36, 0xD3587421, 0xD8567D2C, - 0x7A37A10C, 0x7139A801, 0x6C2BB316, 0x6725BA1B, - 0x560F8538, 0x5D018C35, 0x40139722, 0x4B1D9E2F, - 0x2247E964, 0x2949E069, 0x345BFB7E, 0x3F55F273, - 0x0E7FCD50, 0x0571C45D, 0x1863DF4A, 0x136DD647, - 0xCAD731DC, 0xC1D938D1, 0xDCCB23C6, 0xD7C52ACB, - 0xE6EF15E8, 0xEDE11CE5, 0xF0F307F2, 0xFBFD0EFF, - 0x92A779B4, 0x99A970B9, 0x84BB6BAE, 0x8FB562A3, - 0xBE9F5D80, 0xB591548D, 0xA8834F9A, 0xA38D4697, - }; - - private static final int[] U3 = { - 0x00000000, 0x0D0B0E09, 0x1A161C12, 0x171D121B, - 0x342C3824, 0x3927362D, 0x2E3A2436, 0x23312A3F, - 0x68587048, 0x65537E41, 0x724E6C5A, 0x7F456253, - 0x5C74486C, 0x517F4665, 0x4662547E, 0x4B695A77, - 0xD0B0E090, 0xDDBBEE99, 0xCAA6FC82, 0xC7ADF28B, - 0xE49CD8B4, 0xE997D6BD, 0xFE8AC4A6, 0xF381CAAF, - 0xB8E890D8, 0xB5E39ED1, 0xA2FE8CCA, 0xAFF582C3, - 0x8CC4A8FC, 0x81CFA6F5, 0x96D2B4EE, 0x9BD9BAE7, - 0xBB7BDB3B, 0xB670D532, 0xA16DC729, 0xAC66C920, - 0x8F57E31F, 0x825CED16, 0x9541FF0D, 0x984AF104, - 0xD323AB73, 0xDE28A57A, 0xC935B761, 0xC43EB968, - 0xE70F9357, 0xEA049D5E, 0xFD198F45, 0xF012814C, - 0x6BCB3BAB, 0x66C035A2, 0x71DD27B9, 0x7CD629B0, - 0x5FE7038F, 0x52EC0D86, 0x45F11F9D, 0x48FA1194, - 0x03934BE3, 0x0E9845EA, 0x198557F1, 0x148E59F8, - 0x37BF73C7, 0x3AB47DCE, 0x2DA96FD5, 0x20A261DC, - 0x6DF6AD76, 0x60FDA37F, 0x77E0B164, 0x7AEBBF6D, - 0x59DA9552, 0x54D19B5B, 0x43CC8940, 0x4EC78749, - 0x05AEDD3E, 0x08A5D337, 0x1FB8C12C, 0x12B3CF25, - 0x3182E51A, 0x3C89EB13, 0x2B94F908, 0x269FF701, - 0xBD464DE6, 0xB04D43EF, 0xA75051F4, 0xAA5B5FFD, - 0x896A75C2, 0x84617BCB, 0x937C69D0, 0x9E7767D9, - 0xD51E3DAE, 0xD81533A7, 0xCF0821BC, 0xC2032FB5, - 0xE132058A, 0xEC390B83, 0xFB241998, 0xF62F1791, - 0xD68D764D, 0xDB867844, 0xCC9B6A5F, 0xC1906456, - 0xE2A14E69, 0xEFAA4060, 0xF8B7527B, 0xF5BC5C72, - 0xBED50605, 0xB3DE080C, 0xA4C31A17, 0xA9C8141E, - 0x8AF93E21, 0x87F23028, 0x90EF2233, 0x9DE42C3A, - 0x063D96DD, 0x0B3698D4, 0x1C2B8ACF, 0x112084C6, - 0x3211AEF9, 0x3F1AA0F0, 0x2807B2EB, 0x250CBCE2, - 0x6E65E695, 0x636EE89C, 0x7473FA87, 0x7978F48E, - 0x5A49DEB1, 0x5742D0B8, 0x405FC2A3, 0x4D54CCAA, - 0xDAF741EC, 0xD7FC4FE5, 0xC0E15DFE, 0xCDEA53F7, - 0xEEDB79C8, 0xE3D077C1, 0xF4CD65DA, 0xF9C66BD3, - 0xB2AF31A4, 0xBFA43FAD, 0xA8B92DB6, 0xA5B223BF, - 0x86830980, 0x8B880789, 0x9C951592, 0x919E1B9B, - 0x0A47A17C, 0x074CAF75, 0x1051BD6E, 0x1D5AB367, - 0x3E6B9958, 0x33609751, 0x247D854A, 0x29768B43, - 0x621FD134, 0x6F14DF3D, 0x7809CD26, 0x7502C32F, - 0x5633E910, 0x5B38E719, 0x4C25F502, 0x412EFB0B, - 0x618C9AD7, 0x6C8794DE, 0x7B9A86C5, 0x769188CC, - 0x55A0A2F3, 0x58ABACFA, 0x4FB6BEE1, 0x42BDB0E8, - 0x09D4EA9F, 0x04DFE496, 0x13C2F68D, 0x1EC9F884, - 0x3DF8D2BB, 0x30F3DCB2, 0x27EECEA9, 0x2AE5C0A0, - 0xB13C7A47, 0xBC37744E, 0xAB2A6655, 0xA621685C, - 0x85104263, 0x881B4C6A, 0x9F065E71, 0x920D5078, - 0xD9640A0F, 0xD46F0406, 0xC372161D, 0xCE791814, - 0xED48322B, 0xE0433C22, 0xF75E2E39, 0xFA552030, - 0xB701EC9A, 0xBA0AE293, 0xAD17F088, 0xA01CFE81, - 0x832DD4BE, 0x8E26DAB7, 0x993BC8AC, 0x9430C6A5, - 0xDF599CD2, 0xD25292DB, 0xC54F80C0, 0xC8448EC9, - 0xEB75A4F6, 0xE67EAAFF, 0xF163B8E4, 0xFC68B6ED, - 0x67B10C0A, 0x6ABA0203, 0x7DA71018, 0x70AC1E11, - 0x539D342E, 0x5E963A27, 0x498B283C, 0x44802635, - 0x0FE97C42, 0x02E2724B, 0x15FF6050, 0x18F46E59, - 0x3BC54466, 0x36CE4A6F, 0x21D35874, 0x2CD8567D, - 0x0C7A37A1, 0x017139A8, 0x166C2BB3, 0x1B6725BA, - 0x38560F85, 0x355D018C, 0x22401397, 0x2F4B1D9E, - 0x642247E9, 0x692949E0, 0x7E345BFB, 0x733F55F2, - 0x500E7FCD, 0x5D0571C4, 0x4A1863DF, 0x47136DD6, - 0xDCCAD731, 0xD1C1D938, 0xC6DCCB23, 0xCBD7C52A, - 0xE8E6EF15, 0xE5EDE11C, 0xF2F0F307, 0xFFFBFD0E, - 0xB492A779, 0xB999A970, 0xAE84BB6B, 0xA38FB562, - 0x80BE9F5D, 0x8DB59154, 0x9AA8834F, 0x97A38D46, - }; - - private static final int[] U4 = { - 0x00000000, 0x090D0B0E, 0x121A161C, 0x1B171D12, - 0x24342C38, 0x2D392736, 0x362E3A24, 0x3F23312A, - 0x48685870, 0x4165537E, 0x5A724E6C, 0x537F4562, - 0x6C5C7448, 0x65517F46, 0x7E466254, 0x774B695A, - 0x90D0B0E0, 0x99DDBBEE, 0x82CAA6FC, 0x8BC7ADF2, - 0xB4E49CD8, 0xBDE997D6, 0xA6FE8AC4, 0xAFF381CA, - 0xD8B8E890, 0xD1B5E39E, 0xCAA2FE8C, 0xC3AFF582, - 0xFC8CC4A8, 0xF581CFA6, 0xEE96D2B4, 0xE79BD9BA, - 0x3BBB7BDB, 0x32B670D5, 0x29A16DC7, 0x20AC66C9, - 0x1F8F57E3, 0x16825CED, 0x0D9541FF, 0x04984AF1, - 0x73D323AB, 0x7ADE28A5, 0x61C935B7, 0x68C43EB9, - 0x57E70F93, 0x5EEA049D, 0x45FD198F, 0x4CF01281, - 0xAB6BCB3B, 0xA266C035, 0xB971DD27, 0xB07CD629, - 0x8F5FE703, 0x8652EC0D, 0x9D45F11F, 0x9448FA11, - 0xE303934B, 0xEA0E9845, 0xF1198557, 0xF8148E59, - 0xC737BF73, 0xCE3AB47D, 0xD52DA96F, 0xDC20A261, - 0x766DF6AD, 0x7F60FDA3, 0x6477E0B1, 0x6D7AEBBF, - 0x5259DA95, 0x5B54D19B, 0x4043CC89, 0x494EC787, - 0x3E05AEDD, 0x3708A5D3, 0x2C1FB8C1, 0x2512B3CF, - 0x1A3182E5, 0x133C89EB, 0x082B94F9, 0x01269FF7, - 0xE6BD464D, 0xEFB04D43, 0xF4A75051, 0xFDAA5B5F, - 0xC2896A75, 0xCB84617B, 0xD0937C69, 0xD99E7767, - 0xAED51E3D, 0xA7D81533, 0xBCCF0821, 0xB5C2032F, - 0x8AE13205, 0x83EC390B, 0x98FB2419, 0x91F62F17, - 0x4DD68D76, 0x44DB8678, 0x5FCC9B6A, 0x56C19064, - 0x69E2A14E, 0x60EFAA40, 0x7BF8B752, 0x72F5BC5C, - 0x05BED506, 0x0CB3DE08, 0x17A4C31A, 0x1EA9C814, - 0x218AF93E, 0x2887F230, 0x3390EF22, 0x3A9DE42C, - 0xDD063D96, 0xD40B3698, 0xCF1C2B8A, 0xC6112084, - 0xF93211AE, 0xF03F1AA0, 0xEB2807B2, 0xE2250CBC, - 0x956E65E6, 0x9C636EE8, 0x877473FA, 0x8E7978F4, - 0xB15A49DE, 0xB85742D0, 0xA3405FC2, 0xAA4D54CC, - 0xECDAF741, 0xE5D7FC4F, 0xFEC0E15D, 0xF7CDEA53, - 0xC8EEDB79, 0xC1E3D077, 0xDAF4CD65, 0xD3F9C66B, - 0xA4B2AF31, 0xADBFA43F, 0xB6A8B92D, 0xBFA5B223, - 0x80868309, 0x898B8807, 0x929C9515, 0x9B919E1B, - 0x7C0A47A1, 0x75074CAF, 0x6E1051BD, 0x671D5AB3, - 0x583E6B99, 0x51336097, 0x4A247D85, 0x4329768B, - 0x34621FD1, 0x3D6F14DF, 0x267809CD, 0x2F7502C3, - 0x105633E9, 0x195B38E7, 0x024C25F5, 0x0B412EFB, - 0xD7618C9A, 0xDE6C8794, 0xC57B9A86, 0xCC769188, - 0xF355A0A2, 0xFA58ABAC, 0xE14FB6BE, 0xE842BDB0, - 0x9F09D4EA, 0x9604DFE4, 0x8D13C2F6, 0x841EC9F8, - 0xBB3DF8D2, 0xB230F3DC, 0xA927EECE, 0xA02AE5C0, - 0x47B13C7A, 0x4EBC3774, 0x55AB2A66, 0x5CA62168, - 0x63851042, 0x6A881B4C, 0x719F065E, 0x78920D50, - 0x0FD9640A, 0x06D46F04, 0x1DC37216, 0x14CE7918, - 0x2BED4832, 0x22E0433C, 0x39F75E2E, 0x30FA5520, - 0x9AB701EC, 0x93BA0AE2, 0x88AD17F0, 0x81A01CFE, - 0xBE832DD4, 0xB78E26DA, 0xAC993BC8, 0xA59430C6, - 0xD2DF599C, 0xDBD25292, 0xC0C54F80, 0xC9C8448E, - 0xF6EB75A4, 0xFFE67EAA, 0xE4F163B8, 0xEDFC68B6, - 0x0A67B10C, 0x036ABA02, 0x187DA710, 0x1170AC1E, - 0x2E539D34, 0x275E963A, 0x3C498B28, 0x35448026, - 0x420FE97C, 0x4B02E272, 0x5015FF60, 0x5918F46E, - 0x663BC544, 0x6F36CE4A, 0x7421D358, 0x7D2CD856, - 0xA10C7A37, 0xA8017139, 0xB3166C2B, 0xBA1B6725, - 0x8538560F, 0x8C355D01, 0x97224013, 0x9E2F4B1D, - 0xE9642247, 0xE0692949, 0xFB7E345B, 0xF2733F55, - 0xCD500E7F, 0xC45D0571, 0xDF4A1863, 0xD647136D, - 0x31DCCAD7, 0x38D1C1D9, 0x23C6DCCB, 0x2ACBD7C5, - 0x15E8E6EF, 0x1CE5EDE1, 0x07F2F0F3, 0x0EFFFBFD, - 0x79B492A7, 0x70B999A9, 0x6BAE84BB, 0x62A38FB5, - 0x5D80BE9F, 0x548DB591, 0x4F9AA883, 0x4697A38D, - }; - - private static final int[] rcon = { - 0x00000001, 0x00000002, 0x00000004, 0x00000008, - 0x00000010, 0x00000020, 0x00000040, 0x00000080, - 0x0000001B, 0x00000036, 0x0000006C, 0x000000D8, - 0x000000AB, 0x0000004D, 0x0000009A, 0x0000002F, - 0x0000005E, 0x000000BC, 0x00000063, 0x000000C6, - 0x00000097, 0x00000035, 0x0000006A, 0x000000D4, - 0x000000B3, 0x0000007D, 0x000000FA, 0x000000EF, - 0x000000C5, 0x00000091, - }; - - private boolean ROUNDS_12 = false; - private boolean ROUNDS_14 = false; - - /** Session and Sub keys */ - private int[][] sessionK = null; - private int[] K = null; - - /** Cipher encryption/decryption key */ - // skip re-generating Session and Sub keys if the cipher key is - // the same - private byte[] lastKey = null; - - /** ROUNDS * 4 */ - private int limit = 0; - - AESCrypt() { - // empty - } - - /** - * Returns this cipher's block size. - * - * @return this cipher's block size - */ - int getBlockSize() { - return AES_BLOCK_SIZE; - } - - void init(boolean decrypting, String algorithm, byte[] key) - throws InvalidKeyException { - if (!algorithm.equalsIgnoreCase("AES") - && !algorithm.equalsIgnoreCase("Rijndael")) { - throw new InvalidKeyException - ("Wrong algorithm: AES or Rijndael required"); - } - - if (key == null) { // Unlikely, but just double check it. - throw new InvalidKeyException("Empty key"); - } - - if (!MessageDigest.isEqual(key, lastKey)) { - // re-generate session key 'sessionK' when cipher key changes - makeSessionKey(key); - if (lastKey != null) { - Arrays.fill(lastKey, (byte)0); - } - lastKey = key.clone(); // save cipher key - } - - // set sub key to the corresponding session Key - this.K = sessionK[(decrypting? 1:0)]; - } - - // check if the specified length (in bytes) is a valid keysize for AES - static boolean isKeySizeValid(int len) { - for (int aesKeysize : AES_KEYSIZES) { - if (len == aesKeysize) { - return true; - } - } - return false; - } - - /** - * Encrypt exactly one block of plaintext. - */ - void encryptBlock(byte[] in, int inOffset, - byte[] out, int outOffset) { - // Array bound checks are done in caller code, i.e. - // FeedbackCipher.encrypt/decrypt(...) to improve performance. - implEncryptBlock(in, inOffset, out, outOffset); - } - - // Encryption operation. Possibly replaced with a compiler intrinsic. - @IntrinsicCandidate - private void implEncryptBlock(byte[] in, int inOffset, - byte[] out, int outOffset) - { - int keyOffset = 0; - int t0 = ((in[inOffset++] ) << 24 | - (in[inOffset++] & 0xFF) << 16 | - (in[inOffset++] & 0xFF) << 8 | - (in[inOffset++] & 0xFF) ) ^ K[keyOffset++]; - int t1 = ((in[inOffset++] ) << 24 | - (in[inOffset++] & 0xFF) << 16 | - (in[inOffset++] & 0xFF) << 8 | - (in[inOffset++] & 0xFF) ) ^ K[keyOffset++]; - int t2 = ((in[inOffset++] ) << 24 | - (in[inOffset++] & 0xFF) << 16 | - (in[inOffset++] & 0xFF) << 8 | - (in[inOffset++] & 0xFF) ) ^ K[keyOffset++]; - int t3 = ((in[inOffset++] ) << 24 | - (in[inOffset++] & 0xFF) << 16 | - (in[inOffset++] & 0xFF) << 8 | - (in[inOffset] & 0xFF) ) ^ K[keyOffset++]; - - // apply round transforms - while( keyOffset < limit ) - { - int a0, a1, a2; - a0 = T1[(t0 >>> 24) ] ^ - T2[(t1 >>> 16) & 0xFF] ^ - T3[(t2 >>> 8) & 0xFF] ^ - T4[(t3 ) & 0xFF] ^ K[keyOffset++]; - a1 = T1[(t1 >>> 24) ] ^ - T2[(t2 >>> 16) & 0xFF] ^ - T3[(t3 >>> 8) & 0xFF] ^ - T4[(t0 ) & 0xFF] ^ K[keyOffset++]; - a2 = T1[(t2 >>> 24) ] ^ - T2[(t3 >>> 16) & 0xFF] ^ - T3[(t0 >>> 8) & 0xFF] ^ - T4[(t1 ) & 0xFF] ^ K[keyOffset++]; - t3 = T1[(t3 >>> 24) ] ^ - T2[(t0 >>> 16) & 0xFF] ^ - T3[(t1 >>> 8) & 0xFF] ^ - T4[(t2 ) & 0xFF] ^ K[keyOffset++]; - t0 = a0; t1 = a1; t2 = a2; - } - - // last round is special - int tt = K[keyOffset++]; - out[outOffset++] = (byte)(S[(t0 >>> 24) ] ^ (tt >>> 24)); - out[outOffset++] = (byte)(S[(t1 >>> 16) & 0xFF] ^ (tt >>> 16)); - out[outOffset++] = (byte)(S[(t2 >>> 8) & 0xFF] ^ (tt >>> 8)); - out[outOffset++] = (byte)(S[(t3 ) & 0xFF] ^ (tt )); - tt = K[keyOffset++]; - out[outOffset++] = (byte)(S[(t1 >>> 24) ] ^ (tt >>> 24)); - out[outOffset++] = (byte)(S[(t2 >>> 16) & 0xFF] ^ (tt >>> 16)); - out[outOffset++] = (byte)(S[(t3 >>> 8) & 0xFF] ^ (tt >>> 8)); - out[outOffset++] = (byte)(S[(t0 ) & 0xFF] ^ (tt )); - tt = K[keyOffset++]; - out[outOffset++] = (byte)(S[(t2 >>> 24) ] ^ (tt >>> 24)); - out[outOffset++] = (byte)(S[(t3 >>> 16) & 0xFF] ^ (tt >>> 16)); - out[outOffset++] = (byte)(S[(t0 >>> 8) & 0xFF] ^ (tt >>> 8)); - out[outOffset++] = (byte)(S[(t1 ) & 0xFF] ^ (tt )); - tt = K[keyOffset]; - out[outOffset++] = (byte)(S[(t3 >>> 24) ] ^ (tt >>> 24)); - out[outOffset++] = (byte)(S[(t0 >>> 16) & 0xFF] ^ (tt >>> 16)); - out[outOffset++] = (byte)(S[(t1 >>> 8) & 0xFF] ^ (tt >>> 8)); - out[outOffset ] = (byte)(S[(t2 ) & 0xFF] ^ (tt )); - } - - /** - * Decrypt exactly one block of plaintext. - */ - void decryptBlock(byte[] in, int inOffset, - byte[] out, int outOffset) { - // Array bound checks are done in caller code, i.e. - // FeedbackCipher.encrypt/decrypt(...) to improve performance. - implDecryptBlock(in, inOffset, out, outOffset); - } - - // Decrypt operation. Possibly replaced with a compiler intrinsic. - @IntrinsicCandidate - private void implDecryptBlock(byte[] in, int inOffset, - byte[] out, int outOffset) - { - int keyOffset = 4; - int t0 = ((in[inOffset++] ) << 24 | - (in[inOffset++] & 0xFF) << 16 | - (in[inOffset++] & 0xFF) << 8 | - (in[inOffset++] & 0xFF) ) ^ K[keyOffset++]; - int t1 = ((in[inOffset++] ) << 24 | - (in[inOffset++] & 0xFF) << 16 | - (in[inOffset++] & 0xFF) << 8 | - (in[inOffset++] & 0xFF) ) ^ K[keyOffset++]; - int t2 = ((in[inOffset++] ) << 24 | - (in[inOffset++] & 0xFF) << 16 | - (in[inOffset++] & 0xFF) << 8 | - (in[inOffset++] & 0xFF) ) ^ K[keyOffset++]; - int t3 = ((in[inOffset++] ) << 24 | - (in[inOffset++] & 0xFF) << 16 | - (in[inOffset++] & 0xFF) << 8 | - (in[inOffset ] & 0xFF) ) ^ K[keyOffset++]; - - int a0, a1, a2; - if(ROUNDS_12) - { - a0 = T5[(t0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^ - T7[(t2>>> 8)&0xFF] ^ T8[(t1 )&0xFF] ^ K[keyOffset++]; - a1 = T5[(t1>>>24) ] ^ T6[(t0>>>16)&0xFF] ^ - T7[(t3>>> 8)&0xFF] ^ T8[(t2 )&0xFF] ^ K[keyOffset++]; - a2 = T5[(t2>>>24) ] ^ T6[(t1>>>16)&0xFF] ^ - T7[(t0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++]; - t3 = T5[(t3>>>24) ] ^ T6[(t2>>>16)&0xFF] ^ - T7[(t1>>> 8)&0xFF] ^ T8[(t0 )&0xFF] ^ K[keyOffset++]; - t0 = T5[(a0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^ - T7[(a2>>> 8)&0xFF] ^ T8[(a1 )&0xFF] ^ K[keyOffset++]; - t1 = T5[(a1>>>24) ] ^ T6[(a0>>>16)&0xFF] ^ - T7[(t3>>> 8)&0xFF] ^ T8[(a2 )&0xFF] ^ K[keyOffset++]; - t2 = T5[(a2>>>24) ] ^ T6[(a1>>>16)&0xFF] ^ - T7[(a0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++]; - t3 = T5[(t3>>>24) ] ^ T6[(a2>>>16)&0xFF] ^ - T7[(a1>>> 8)&0xFF] ^ T8[(a0 )&0xFF] ^ K[keyOffset++]; - - if(ROUNDS_14) - { - a0 = T5[(t0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^ - T7[(t2>>> 8)&0xFF] ^ T8[(t1 )&0xFF] ^ K[keyOffset++]; - a1 = T5[(t1>>>24) ] ^ T6[(t0>>>16)&0xFF] ^ - T7[(t3>>> 8)&0xFF] ^ T8[(t2 )&0xFF] ^ K[keyOffset++]; - a2 = T5[(t2>>>24) ] ^ T6[(t1>>>16)&0xFF] ^ - T7[(t0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++]; - t3 = T5[(t3>>>24) ] ^ T6[(t2>>>16)&0xFF] ^ - T7[(t1>>> 8)&0xFF] ^ T8[(t0 )&0xFF] ^ K[keyOffset++]; - t0 = T5[(a0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^ - T7[(a2>>> 8)&0xFF] ^ T8[(a1 )&0xFF] ^ K[keyOffset++]; - t1 = T5[(a1>>>24) ] ^ T6[(a0>>>16)&0xFF] ^ - T7[(t3>>> 8)&0xFF] ^ T8[(a2 )&0xFF] ^ K[keyOffset++]; - t2 = T5[(a2>>>24) ] ^ T6[(a1>>>16)&0xFF] ^ - T7[(a0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++]; - t3 = T5[(t3>>>24) ] ^ T6[(a2>>>16)&0xFF] ^ - T7[(a1>>> 8)&0xFF] ^ T8[(a0 )&0xFF] ^ K[keyOffset++]; - } - } - a0 = T5[(t0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^ - T7[(t2>>> 8)&0xFF] ^ T8[(t1 )&0xFF] ^ K[keyOffset++]; - a1 = T5[(t1>>>24) ] ^ T6[(t0>>>16)&0xFF] ^ - T7[(t3>>> 8)&0xFF] ^ T8[(t2 )&0xFF] ^ K[keyOffset++]; - a2 = T5[(t2>>>24) ] ^ T6[(t1>>>16)&0xFF] ^ - T7[(t0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++]; - t3 = T5[(t3>>>24) ] ^ T6[(t2>>>16)&0xFF] ^ - T7[(t1>>> 8)&0xFF] ^ T8[(t0 )&0xFF] ^ K[keyOffset++]; - t0 = T5[(a0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^ - T7[(a2>>> 8)&0xFF] ^ T8[(a1 )&0xFF] ^ K[keyOffset++]; - t1 = T5[(a1>>>24) ] ^ T6[(a0>>>16)&0xFF] ^ - T7[(t3>>> 8)&0xFF] ^ T8[(a2 )&0xFF] ^ K[keyOffset++]; - t2 = T5[(a2>>>24) ] ^ T6[(a1>>>16)&0xFF] ^ - T7[(a0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++]; - t3 = T5[(t3>>>24) ] ^ T6[(a2>>>16)&0xFF] ^ - T7[(a1>>> 8)&0xFF] ^ T8[(a0 )&0xFF] ^ K[keyOffset++]; - a0 = T5[(t0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^ - T7[(t2>>> 8)&0xFF] ^ T8[(t1 )&0xFF] ^ K[keyOffset++]; - a1 = T5[(t1>>>24) ] ^ T6[(t0>>>16)&0xFF] ^ - T7[(t3>>> 8)&0xFF] ^ T8[(t2 )&0xFF] ^ K[keyOffset++]; - a2 = T5[(t2>>>24) ] ^ T6[(t1>>>16)&0xFF] ^ - T7[(t0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++]; - t3 = T5[(t3>>>24) ] ^ T6[(t2>>>16)&0xFF] ^ - T7[(t1>>> 8)&0xFF] ^ T8[(t0 )&0xFF] ^ K[keyOffset++]; - t0 = T5[(a0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^ - T7[(a2>>> 8)&0xFF] ^ T8[(a1 )&0xFF] ^ K[keyOffset++]; - t1 = T5[(a1>>>24) ] ^ T6[(a0>>>16)&0xFF] ^ - T7[(t3>>> 8)&0xFF] ^ T8[(a2 )&0xFF] ^ K[keyOffset++]; - t2 = T5[(a2>>>24) ] ^ T6[(a1>>>16)&0xFF] ^ - T7[(a0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++]; - t3 = T5[(t3>>>24) ] ^ T6[(a2>>>16)&0xFF] ^ - T7[(a1>>> 8)&0xFF] ^ T8[(a0 )&0xFF] ^ K[keyOffset++]; - a0 = T5[(t0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^ - T7[(t2>>> 8)&0xFF] ^ T8[(t1 )&0xFF] ^ K[keyOffset++]; - a1 = T5[(t1>>>24) ] ^ T6[(t0>>>16)&0xFF] ^ - T7[(t3>>> 8)&0xFF] ^ T8[(t2 )&0xFF] ^ K[keyOffset++]; - a2 = T5[(t2>>>24) ] ^ T6[(t1>>>16)&0xFF] ^ - T7[(t0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++]; - t3 = T5[(t3>>>24) ] ^ T6[(t2>>>16)&0xFF] ^ - T7[(t1>>> 8)&0xFF] ^ T8[(t0 )&0xFF] ^ K[keyOffset++]; - t0 = T5[(a0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^ - T7[(a2>>> 8)&0xFF] ^ T8[(a1 )&0xFF] ^ K[keyOffset++]; - t1 = T5[(a1>>>24) ] ^ T6[(a0>>>16)&0xFF] ^ - T7[(t3>>> 8)&0xFF] ^ T8[(a2 )&0xFF] ^ K[keyOffset++]; - t2 = T5[(a2>>>24) ] ^ T6[(a1>>>16)&0xFF] ^ - T7[(a0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++]; - t3 = T5[(t3>>>24) ] ^ T6[(a2>>>16)&0xFF] ^ - T7[(a1>>> 8)&0xFF] ^ T8[(a0 )&0xFF] ^ K[keyOffset++]; - a0 = T5[(t0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^ - T7[(t2>>> 8)&0xFF] ^ T8[(t1 )&0xFF] ^ K[keyOffset++]; - a1 = T5[(t1>>>24) ] ^ T6[(t0>>>16)&0xFF] ^ - T7[(t3>>> 8)&0xFF] ^ T8[(t2 )&0xFF] ^ K[keyOffset++]; - a2 = T5[(t2>>>24) ] ^ T6[(t1>>>16)&0xFF] ^ - T7[(t0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++]; - t3 = T5[(t3>>>24) ] ^ T6[(t2>>>16)&0xFF] ^ - T7[(t1>>> 8)&0xFF] ^ T8[(t0 )&0xFF] ^ K[keyOffset++]; - t0 = T5[(a0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^ - T7[(a2>>> 8)&0xFF] ^ T8[(a1 )&0xFF] ^ K[keyOffset++]; - t1 = T5[(a1>>>24) ] ^ T6[(a0>>>16)&0xFF] ^ - T7[(t3>>> 8)&0xFF] ^ T8[(a2 )&0xFF] ^ K[keyOffset++]; - t2 = T5[(a2>>>24) ] ^ T6[(a1>>>16)&0xFF] ^ - T7[(a0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++]; - t3 = T5[(t3>>>24) ] ^ T6[(a2>>>16)&0xFF] ^ - T7[(a1>>> 8)&0xFF] ^ T8[(a0 )&0xFF] ^ K[keyOffset++]; - a0 = T5[(t0>>>24) ] ^ T6[(t3>>>16)&0xFF] ^ - T7[(t2>>> 8)&0xFF] ^ T8[(t1 )&0xFF] ^ K[keyOffset++]; - a1 = T5[(t1>>>24) ] ^ T6[(t0>>>16)&0xFF] ^ - T7[(t3>>> 8)&0xFF] ^ T8[(t2 )&0xFF] ^ K[keyOffset++]; - a2 = T5[(t2>>>24) ] ^ T6[(t1>>>16)&0xFF] ^ - T7[(t0>>> 8)&0xFF] ^ T8[(t3 )&0xFF] ^ K[keyOffset++]; - t3 = T5[(t3>>>24) ] ^ T6[(t2>>>16)&0xFF] ^ - T7[(t1>>> 8)&0xFF] ^ T8[(t0 )&0xFF] ^ K[keyOffset]; - - t1 = K[0]; - out[outOffset++] = (byte)(Si[(a0 >>> 24) ] ^ (t1 >>> 24)); - out[outOffset++] = (byte)(Si[(t3 >>> 16) & 0xFF] ^ (t1 >>> 16)); - out[outOffset++] = (byte)(Si[(a2 >>> 8) & 0xFF] ^ (t1 >>> 8)); - out[outOffset++] = (byte)(Si[(a1 ) & 0xFF] ^ (t1 )); - t1 = K[1]; - out[outOffset++] = (byte)(Si[(a1 >>> 24) ] ^ (t1 >>> 24)); - out[outOffset++] = (byte)(Si[(a0 >>> 16) & 0xFF] ^ (t1 >>> 16)); - out[outOffset++] = (byte)(Si[(t3 >>> 8) & 0xFF] ^ (t1 >>> 8)); - out[outOffset++] = (byte)(Si[(a2 ) & 0xFF] ^ (t1 )); - t1 = K[2]; - out[outOffset++] = (byte)(Si[(a2 >>> 24) ] ^ (t1 >>> 24)); - out[outOffset++] = (byte)(Si[(a1 >>> 16) & 0xFF] ^ (t1 >>> 16)); - out[outOffset++] = (byte)(Si[(a0 >>> 8) & 0xFF] ^ (t1 >>> 8)); - out[outOffset++] = (byte)(Si[(t3 ) & 0xFF] ^ (t1 )); - t1 = K[3]; - out[outOffset++] = (byte)(Si[(t3 >>> 24) ] ^ (t1 >>> 24)); - out[outOffset++] = (byte)(Si[(a2 >>> 16) & 0xFF] ^ (t1 >>> 16)); - out[outOffset++] = (byte)(Si[(a1 >>> 8) & 0xFF] ^ (t1 >>> 8)); - out[outOffset ] = (byte)(Si[(a0 ) & 0xFF] ^ (t1 )); - } - - /** - * Expand a user-supplied key material into a session key. - * - * @param k The 128/192/256-bit cipher key to use. - * @exception InvalidKeyException If the key is invalid. - */ - private void makeSessionKey(byte[] k) throws InvalidKeyException { - if (!isKeySizeValid(k.length)) { - throw new InvalidKeyException("Invalid AES key length: " + - k.length + " bytes"); - } - - final int BC = 4; - - int ROUNDS = getRounds(k.length); - int ROUND_KEY_COUNT = (ROUNDS + 1) * BC; - - int[] Ke = new int[ROUND_KEY_COUNT]; // encryption round keys - int[] Kd = new int[ROUND_KEY_COUNT]; // decryption round keys - - int KC = k.length/4; // keylen in 32-bit elements - - int[] tk = new int[KC]; - int i, j; - - // copy user material bytes into temporary ints - for (i = 0, j = 0; i < KC; i++, j+=4) { - tk[i] = (k[j] ) << 24 | - (k[j+1] & 0xFF) << 16 | - (k[j+2] & 0xFF) << 8 | - (k[j+3] & 0xFF); - } - - // copy values into round key arrays - int t = 0; - for (j = 0; (j < KC) && (t < ROUND_KEY_COUNT); j++, t++) { - Ke[t] = tk[j]; - Kd[(ROUNDS - (t / BC))*BC + (t % BC)] = tk[j]; - } - int tt, rconpointer = 0; - while (t < ROUND_KEY_COUNT) { - // extrapolate using phi (the round key evolution function) - tt = tk[KC - 1]; - tk[0] ^= (S[(tt >>> 16) & 0xFF] ) << 24 ^ - (S[(tt >>> 8) & 0xFF] & 0xFF) << 16 ^ - (S[(tt ) & 0xFF] & 0xFF) << 8 ^ - (S[(tt >>> 24) ] & 0xFF) ^ - (rcon[rconpointer++] ) << 24; - if (KC != 8) - for (i = 1, j = 0; i < KC; i++, j++) tk[i] ^= tk[j]; - else { - for (i = 1, j = 0; i < KC / 2; i++, j++) tk[i] ^= tk[j]; - tt = tk[KC / 2 - 1]; - tk[KC / 2] ^= (S[(tt ) & 0xFF] & 0xFF) ^ - (S[(tt >>> 8) & 0xFF] & 0xFF) << 8 ^ - (S[(tt >>> 16) & 0xFF] & 0xFF) << 16 ^ - (S[(tt >>> 24) ] ) << 24; - for (j = KC / 2, i = j + 1; i < KC; i++, j++) tk[i] ^= tk[j]; - } - // copy values into round key arrays - for (j = 0; (j < KC) && (t < ROUND_KEY_COUNT); j++, t++) { - Ke[t] = tk[j]; - Kd[(ROUNDS - (t / BC))*BC + (t % BC)] = tk[j]; - } - } - for (int r = 1; r < ROUNDS; r++) { - // inverse MixColumn where needed - for (j = 0; j < BC; j++) { - int idx = r*BC + j; - tt = Kd[idx]; - Kd[idx] = U1[(tt >>> 24) & 0xFF] ^ - U2[(tt >>> 16) & 0xFF] ^ - U3[(tt >>> 8) & 0xFF] ^ - U4[ tt & 0xFF]; - } - } - - // For decryption round keys, need to rotate right by 4 ints. - // Do that without allocating and zeroing the small buffer. - int KdTail_0 = Kd[Kd.length - 4]; - int KdTail_1 = Kd[Kd.length - 3]; - int KdTail_2 = Kd[Kd.length - 2]; - int KdTail_3 = Kd[Kd.length - 1]; - System.arraycopy(Kd, 0, Kd, 4, Kd.length - 4); - Kd[0] = KdTail_0; - Kd[1] = KdTail_1; - Kd[2] = KdTail_2; - Kd[3] = KdTail_3; - - Arrays.fill(tk, 0); - ROUNDS_12 = (ROUNDS>=12); - ROUNDS_14 = (ROUNDS==14); - limit = ROUNDS*4; - - // store the expanded sub keys into 'sessionK' - if (sessionK != null) { - // erase the previous values in sessionK - Arrays.fill(sessionK[0], 0); - Arrays.fill(sessionK[1], 0); - } else { - sessionK = new int[2][]; - } - sessionK[0] = Ke; - sessionK[1] = Kd; - } - - /** - * Return The number of rounds for a given Rijndael keysize. - * - * @param keySize The size of the user key material in bytes. - * MUST be one of (16, 24, 32). - * @return The number of rounds. - */ - private static int getRounds(int keySize) { - return (keySize >> 2) + 6; - } -} diff --git a/src/java.base/share/classes/com/sun/crypto/provider/AESKeyGenerator.java b/src/java.base/share/classes/com/sun/crypto/provider/AESKeyGenerator.java index 51671fdf25d..915d329d33d 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/AESKeyGenerator.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/AESKeyGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, 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 @@ -91,7 +91,7 @@ public final class AESKeyGenerator extends KeyGeneratorSpi { */ protected void engineInit(int keysize, SecureRandom random) { if (((keysize % 8) != 0) || - (!AESCrypt.isKeySizeValid(keysize/8))) { + (!AES_Crypt.isKeySizeValid(keysize/8))) { throw new InvalidParameterException ("Wrong keysize: must be equal to 128, 192 or 256"); } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrap.java b/src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrap.java index 7ba4420f973..2828183802f 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrap.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrap.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 @@ -50,7 +50,7 @@ class AESKeyWrap extends FeedbackCipher { }; AESKeyWrap() { - super(new AESCrypt()); + super(new AES_Crypt()); } /** diff --git a/src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrapPadded.java b/src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrapPadded.java index 4f25849d850..f6fec405b2f 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrapPadded.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrapPadded.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 @@ -87,7 +87,7 @@ class AESKeyWrapPadded extends FeedbackCipher { } AESKeyWrapPadded() { - super(new AESCrypt()); + super(new AES_Crypt()); } /** diff --git a/src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java b/src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java new file mode 100644 index 00000000000..6e3e6144aff --- /dev/null +++ b/src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java @@ -0,0 +1,1392 @@ +/* + * 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 com.sun.crypto.provider; + +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.util.Arrays; + +import jdk.internal.vm.annotation.IntrinsicCandidate; + +/** + * Implementation of the AES cipher, which is based on the following documents: + * + * @spec https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/aes-development/rijndael-ammended.pdf + * + * @spec https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197-upd1.pdf + * + * https://www.internationaljournalcorner.com/index.php/ijird_ojs/article/view/134688 + */ +final class AES_Crypt extends SymmetricCipher { + + // Number of words in a block + private static final int WB = 4; + // Number of bytes in a word + private static final int BW = 4; + + private static final int AES_128_ROUNDS = 10; + private static final int AES_192_ROUNDS = 12; + private static final int AES_256_ROUNDS = 14; + + private int rounds; + private byte[] prevKey = null; + + // Following two attributes are specific to Intrinsics where sessionK is + // used for PPC64, S390, and RISCV64 architectures, whereas K is used for + // everything else. + private int[][] sessionK = null; + private int[] K = null; + + // Round constant + private static final int[] RCON = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, + 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, + }; + + private static final byte[][] SBOX = { + { (byte)0x63, (byte)0x7C, (byte)0x77, (byte)0x7B, (byte)0xF2, + (byte)0x6B, (byte)0x6F, (byte)0xC5, (byte)0x30, (byte)0x01, + (byte)0x67, (byte)0x2B, (byte)0xFE, (byte)0xD7, (byte)0xAB, + (byte)0x76 }, + { (byte)0xCA, (byte)0x82, (byte)0xC9, (byte)0x7D, (byte)0xFA, + (byte)0x59, (byte)0x47, (byte)0xF0, (byte)0xAD, (byte)0xD4, + (byte)0xA2, (byte)0xAF, (byte)0x9C, (byte)0xA4, (byte)0x72, + (byte)0xC0 }, + { (byte)0xB7, (byte)0xFD, (byte)0x93, (byte)0x26, (byte)0x36, + (byte)0x3F, (byte)0xF7, (byte)0xCC, (byte)0x34, (byte)0xA5, + (byte)0xE5, (byte)0xF1, (byte)0x71, (byte)0xD8, (byte)0x31, + (byte)0x15 }, + { (byte)0x04, (byte)0xC7, (byte)0x23, (byte)0xC3, (byte)0x18, + (byte)0x96, (byte)0x05, (byte)0x9A, (byte)0x07, (byte)0x12, + (byte)0x80, (byte)0xE2, (byte)0xEB, (byte)0x27, (byte)0xB2, + (byte)0x75 }, + { (byte)0x09, (byte)0x83, (byte)0x2C, (byte)0x1A, (byte)0x1B, + (byte)0x6E, (byte)0x5A, (byte)0xA0, (byte)0x52, (byte)0x3B, + (byte)0xD6, (byte)0xB3, (byte)0x29, (byte)0xE3, (byte)0x2F, + (byte)0x84 }, + { (byte)0x53, (byte)0xD1, (byte)0x00, (byte)0xED, (byte)0x20, + (byte)0xFC, (byte)0xB1, (byte)0x5B, (byte)0x6A, (byte)0xCB, + (byte)0xBE, (byte)0x39, (byte)0x4A, (byte)0x4C, (byte)0x58, + (byte)0xCF }, + { (byte)0xD0, (byte)0xEF, (byte)0xAA, (byte)0xFB, (byte)0x43, + (byte)0x4D, (byte)0x33, (byte)0x85, (byte)0x45, (byte)0xF9, + (byte)0x02, (byte)0x7F, (byte)0x50, (byte)0x3C, (byte)0x9F, + (byte)0xA8 }, + { (byte)0x51, (byte)0xA3, (byte)0x40, (byte)0x8F, (byte)0x92, + (byte)0x9D, (byte)0x38, (byte)0xF5, (byte)0xBC, (byte)0xB6, + (byte)0xDA, (byte)0x21, (byte)0x10, (byte)0xFF, (byte)0xF3, + (byte)0xD2 }, + { (byte)0xCD, (byte)0x0C, (byte)0x13, (byte)0xEC, (byte)0x5F, + (byte)0x97, (byte)0x44, (byte)0x17, (byte)0xC4, (byte)0xA7, + (byte)0x7E, (byte)0x3D, (byte)0x64, (byte)0x5D, (byte)0x19, + (byte)0x73 }, + { (byte)0x60, (byte)0x81, (byte)0x4F, (byte)0xDC, (byte)0x22, + (byte)0x2A, (byte)0x90, (byte)0x88, (byte)0x46, (byte)0xEE, + (byte)0xB8, (byte)0x14, (byte)0xDE, (byte)0x5E, (byte)0x0B, + (byte)0xDB }, + { (byte)0xE0, (byte)0x32, (byte)0x3A, (byte)0x0A, (byte)0x49, + (byte)0x06, (byte)0x24, (byte)0x5C, (byte)0xC2, (byte)0xD3, + (byte)0xAC, (byte)0x62, (byte)0x91, (byte)0x95, (byte)0xE4, + (byte)0x79 }, + { (byte)0xE7, (byte)0xC8, (byte)0x37, (byte)0x6D, (byte)0x8D, + (byte)0xD5, (byte)0x4E, (byte)0xA9, (byte)0x6C, (byte)0x56, + (byte)0xF4, (byte)0xEA, (byte)0x65, (byte)0x7A, (byte)0xAE, + (byte)0x08 }, + { (byte)0xBA, (byte)0x78, (byte)0x25, (byte)0x2E, (byte)0x1C, + (byte)0xA6, (byte)0xB4, (byte)0xC6, (byte)0xE8, (byte)0xDD, + (byte)0x74, (byte)0x1F, (byte)0x4B, (byte)0xBD, (byte)0x8B, + (byte)0x8A }, + { (byte)0x70, (byte)0x3E, (byte)0xB5, (byte)0x66, (byte)0x48, + (byte)0x03, (byte)0xF6, (byte)0x0E, (byte)0x61, (byte)0x35, + (byte)0x57, (byte)0xB9, (byte)0x86, (byte)0xC1, (byte)0x1D, + (byte)0x9E }, + { (byte)0xE1, (byte)0xF8, (byte)0x98, (byte)0x11, (byte)0x69, + (byte)0xD9, (byte)0x8E, (byte)0x94, (byte)0x9B, (byte)0x1E, + (byte)0x87, (byte)0xE9, (byte)0xCE, (byte)0x55, (byte)0x28, + (byte)0xDF }, + { (byte)0x8C, (byte)0xA1, (byte)0x89, (byte)0x0D, (byte)0xBF, + (byte)0xE6, (byte)0x42, (byte)0x68, (byte)0x41, (byte)0x99, + (byte)0x2D, (byte)0x0F, (byte)0xB0, (byte)0x54, (byte)0xBB, + (byte)0x16 } + }; + + // Lookup table for row 0 transforms, see section 5.2.1 of original spec. + private static final int[] T0 = { + 0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, + 0xD66B6BBD, 0xDE6F6FB1, 0x91C5C554, 0x60303050, 0x02010103, + 0xCE6767A9, 0x562B2B7D, 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, + 0xEC76769A, 0x8FCACA45, 0x1F82829D, 0x89C9C940, 0xFA7D7D87, + 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B, 0x41ADADEC, + 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7, + 0xE4727296, 0x9BC0C05B, 0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, + 0x4C26266A, 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F, + 0x6834345C, 0x51A5A5F4, 0xD1E5E534, 0xF9F1F108, 0xE2717193, + 0xABD8D873, 0x62313153, 0x2A15153F, 0x0804040C, 0x95C7C752, + 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1, 0x0A05050F, + 0x2F9A9AB5, 0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, + 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F, 0x1209091B, + 0x1D83839E, 0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, + 0xB45A5AEE, 0x5BA0A0FB, 0xA45252F6, 0x763B3B4D, 0xB7D6D661, + 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E, 0x5E2F2F71, 0x13848497, + 0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, 0x40202060, + 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED, 0xD46A6ABE, 0x8DCBCB46, + 0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, + 0x85CFCF4A, 0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, + 0x864343C5, 0x9A4D4DD7, 0x66333355, 0x11858594, 0x8A4545CF, + 0xE9F9F910, 0x04020206, 0xFE7F7F81, 0xA05050F0, 0x783C3C44, + 0x259F9FBA, 0x4BA8A8E3, 0xA25151F3, 0x5DA3A3FE, 0x804040C0, + 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504, + 0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, + 0xE5FFFF1A, 0xFDF3F30E, 0xBFD2D26D, 0x81CDCD4C, 0x180C0C14, + 0x26131335, 0xC3ECEC2F, 0xBE5F5FE1, 0x359797A2, 0x884444CC, + 0x2E171739, 0x93C4C457, 0x55A7A7F2, 0xFC7E7E82, 0x7A3D3D47, + 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395, 0xC06060A0, + 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E, + 0x3B9090AB, 0x0B888883, 0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, + 0x2814143C, 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76, + 0xDBE0E03B, 0x64323256, 0x743A3A4E, 0x140A0A1E, 0x924949DB, + 0x0C06060A, 0x4824246C, 0xB85C5CE4, 0x9FC2C25D, 0xBDD3D36E, + 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4, 0xD3E4E437, + 0xF279798B, 0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, + 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0, 0xD86C6CB4, + 0xAC5656FA, 0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, + 0x47AEAEE9, 0x10080818, 0x6FBABAD5, 0xF0787888, 0x4A25256F, + 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1, 0x73B4B4C7, 0x97C6C651, + 0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, 0x964B4BDD, + 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85, 0xE0707090, 0x7C3E3E42, + 0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, + 0x1C0E0E12, 0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, + 0x17868691, 0x99C1C158, 0x3A1D1D27, 0x279E9EB9, 0xD9E1E138, + 0xEBF8F813, 0x2B9898B3, 0x22111133, 0xD26969BB, 0xA9D9D970, + 0x078E8E89, 0x339494A7, 0x2D9B9BB6, 0x3C1E1E22, 0x15878792, + 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A, + 0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, + 0xD7E6E631, 0x844242C6, 0xD06868B8, 0x824141C3, 0x299999B0, + 0x5A2D2D77, 0x1E0F0F11, 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, + 0x2C16163A, + }; + + // Lookup table for row 1 transforms, see section 5.2.1 of original spec. + private static final int[] T1 = { + 0xA5C66363, 0x84F87C7C, 0x99EE7777, 0x8DF67B7B, 0x0DFFF2F2, + 0xBDD66B6B, 0xB1DE6F6F, 0x5491C5C5, 0x50603030, 0x03020101, + 0xA9CE6767, 0x7D562B2B, 0x19E7FEFE, 0x62B5D7D7, 0xE64DABAB, + 0x9AEC7676, 0x458FCACA, 0x9D1F8282, 0x4089C9C9, 0x87FA7D7D, + 0x15EFFAFA, 0xEBB25959, 0xC98E4747, 0x0BFBF0F0, 0xEC41ADAD, + 0x67B3D4D4, 0xFD5FA2A2, 0xEA45AFAF, 0xBF239C9C, 0xF753A4A4, + 0x96E47272, 0x5B9BC0C0, 0xC275B7B7, 0x1CE1FDFD, 0xAE3D9393, + 0x6A4C2626, 0x5A6C3636, 0x417E3F3F, 0x02F5F7F7, 0x4F83CCCC, + 0x5C683434, 0xF451A5A5, 0x34D1E5E5, 0x08F9F1F1, 0x93E27171, + 0x73ABD8D8, 0x53623131, 0x3F2A1515, 0x0C080404, 0x5295C7C7, + 0x65462323, 0x5E9DC3C3, 0x28301818, 0xA1379696, 0x0F0A0505, + 0xB52F9A9A, 0x090E0707, 0x36241212, 0x9B1B8080, 0x3DDFE2E2, + 0x26CDEBEB, 0x694E2727, 0xCD7FB2B2, 0x9FEA7575, 0x1B120909, + 0x9E1D8383, 0x74582C2C, 0x2E341A1A, 0x2D361B1B, 0xB2DC6E6E, + 0xEEB45A5A, 0xFB5BA0A0, 0xF6A45252, 0x4D763B3B, 0x61B7D6D6, + 0xCE7DB3B3, 0x7B522929, 0x3EDDE3E3, 0x715E2F2F, 0x97138484, + 0xF5A65353, 0x68B9D1D1, 0x00000000, 0x2CC1EDED, 0x60402020, + 0x1FE3FCFC, 0xC879B1B1, 0xEDB65B5B, 0xBED46A6A, 0x468DCBCB, + 0xD967BEBE, 0x4B723939, 0xDE944A4A, 0xD4984C4C, 0xE8B05858, + 0x4A85CFCF, 0x6BBBD0D0, 0x2AC5EFEF, 0xE54FAAAA, 0x16EDFBFB, + 0xC5864343, 0xD79A4D4D, 0x55663333, 0x94118585, 0xCF8A4545, + 0x10E9F9F9, 0x06040202, 0x81FE7F7F, 0xF0A05050, 0x44783C3C, + 0xBA259F9F, 0xE34BA8A8, 0xF3A25151, 0xFE5DA3A3, 0xC0804040, + 0x8A058F8F, 0xAD3F9292, 0xBC219D9D, 0x48703838, 0x04F1F5F5, + 0xDF63BCBC, 0xC177B6B6, 0x75AFDADA, 0x63422121, 0x30201010, + 0x1AE5FFFF, 0x0EFDF3F3, 0x6DBFD2D2, 0x4C81CDCD, 0x14180C0C, + 0x35261313, 0x2FC3ECEC, 0xE1BE5F5F, 0xA2359797, 0xCC884444, + 0x392E1717, 0x5793C4C4, 0xF255A7A7, 0x82FC7E7E, 0x477A3D3D, + 0xACC86464, 0xE7BA5D5D, 0x2B321919, 0x95E67373, 0xA0C06060, + 0x98198181, 0xD19E4F4F, 0x7FA3DCDC, 0x66442222, 0x7E542A2A, + 0xAB3B9090, 0x830B8888, 0xCA8C4646, 0x29C7EEEE, 0xD36BB8B8, + 0x3C281414, 0x79A7DEDE, 0xE2BC5E5E, 0x1D160B0B, 0x76ADDBDB, + 0x3BDBE0E0, 0x56643232, 0x4E743A3A, 0x1E140A0A, 0xDB924949, + 0x0A0C0606, 0x6C482424, 0xE4B85C5C, 0x5D9FC2C2, 0x6EBDD3D3, + 0xEF43ACAC, 0xA6C46262, 0xA8399191, 0xA4319595, 0x37D3E4E4, + 0x8BF27979, 0x32D5E7E7, 0x438BC8C8, 0x596E3737, 0xB7DA6D6D, + 0x8C018D8D, 0x64B1D5D5, 0xD29C4E4E, 0xE049A9A9, 0xB4D86C6C, + 0xFAAC5656, 0x07F3F4F4, 0x25CFEAEA, 0xAFCA6565, 0x8EF47A7A, + 0xE947AEAE, 0x18100808, 0xD56FBABA, 0x88F07878, 0x6F4A2525, + 0x725C2E2E, 0x24381C1C, 0xF157A6A6, 0xC773B4B4, 0x5197C6C6, + 0x23CBE8E8, 0x7CA1DDDD, 0x9CE87474, 0x213E1F1F, 0xDD964B4B, + 0xDC61BDBD, 0x860D8B8B, 0x850F8A8A, 0x90E07070, 0x427C3E3E, + 0xC471B5B5, 0xAACC6666, 0xD8904848, 0x05060303, 0x01F7F6F6, + 0x121C0E0E, 0xA3C26161, 0x5F6A3535, 0xF9AE5757, 0xD069B9B9, + 0x91178686, 0x5899C1C1, 0x273A1D1D, 0xB9279E9E, 0x38D9E1E1, + 0x13EBF8F8, 0xB32B9898, 0x33221111, 0xBBD26969, 0x70A9D9D9, + 0x89078E8E, 0xA7339494, 0xB62D9B9B, 0x223C1E1E, 0x92158787, + 0x20C9E9E9, 0x4987CECE, 0xFFAA5555, 0x78502828, 0x7AA5DFDF, + 0x8F038C8C, 0xF859A1A1, 0x80098989, 0x171A0D0D, 0xDA65BFBF, + 0x31D7E6E6, 0xC6844242, 0xB8D06868, 0xC3824141, 0xB0299999, + 0x775A2D2D, 0x111E0F0F, 0xCB7BB0B0, 0xFCA85454, 0xD66DBBBB, + 0x3A2C1616, + }; + + // Lookup table for row 2 transforms, see section 5.2.1 of original spec. + private static final int[] T2 = { + 0x63A5C663, 0x7C84F87C, 0x7799EE77, 0x7B8DF67B, 0xF20DFFF2, + 0x6BBDD66B, 0x6FB1DE6F, 0xC55491C5, 0x30506030, 0x01030201, + 0x67A9CE67, 0x2B7D562B, 0xFE19E7FE, 0xD762B5D7, 0xABE64DAB, + 0x769AEC76, 0xCA458FCA, 0x829D1F82, 0xC94089C9, 0x7D87FA7D, + 0xFA15EFFA, 0x59EBB259, 0x47C98E47, 0xF00BFBF0, 0xADEC41AD, + 0xD467B3D4, 0xA2FD5FA2, 0xAFEA45AF, 0x9CBF239C, 0xA4F753A4, + 0x7296E472, 0xC05B9BC0, 0xB7C275B7, 0xFD1CE1FD, 0x93AE3D93, + 0x266A4C26, 0x365A6C36, 0x3F417E3F, 0xF702F5F7, 0xCC4F83CC, + 0x345C6834, 0xA5F451A5, 0xE534D1E5, 0xF108F9F1, 0x7193E271, + 0xD873ABD8, 0x31536231, 0x153F2A15, 0x040C0804, 0xC75295C7, + 0x23654623, 0xC35E9DC3, 0x18283018, 0x96A13796, 0x050F0A05, + 0x9AB52F9A, 0x07090E07, 0x12362412, 0x809B1B80, 0xE23DDFE2, + 0xEB26CDEB, 0x27694E27, 0xB2CD7FB2, 0x759FEA75, 0x091B1209, + 0x839E1D83, 0x2C74582C, 0x1A2E341A, 0x1B2D361B, 0x6EB2DC6E, + 0x5AEEB45A, 0xA0FB5BA0, 0x52F6A452, 0x3B4D763B, 0xD661B7D6, + 0xB3CE7DB3, 0x297B5229, 0xE33EDDE3, 0x2F715E2F, 0x84971384, + 0x53F5A653, 0xD168B9D1, 0x00000000, 0xED2CC1ED, 0x20604020, + 0xFC1FE3FC, 0xB1C879B1, 0x5BEDB65B, 0x6ABED46A, 0xCB468DCB, + 0xBED967BE, 0x394B7239, 0x4ADE944A, 0x4CD4984C, 0x58E8B058, + 0xCF4A85CF, 0xD06BBBD0, 0xEF2AC5EF, 0xAAE54FAA, 0xFB16EDFB, + 0x43C58643, 0x4DD79A4D, 0x33556633, 0x85941185, 0x45CF8A45, + 0xF910E9F9, 0x02060402, 0x7F81FE7F, 0x50F0A050, 0x3C44783C, + 0x9FBA259F, 0xA8E34BA8, 0x51F3A251, 0xA3FE5DA3, 0x40C08040, + 0x8F8A058F, 0x92AD3F92, 0x9DBC219D, 0x38487038, 0xF504F1F5, + 0xBCDF63BC, 0xB6C177B6, 0xDA75AFDA, 0x21634221, 0x10302010, + 0xFF1AE5FF, 0xF30EFDF3, 0xD26DBFD2, 0xCD4C81CD, 0x0C14180C, + 0x13352613, 0xEC2FC3EC, 0x5FE1BE5F, 0x97A23597, 0x44CC8844, + 0x17392E17, 0xC45793C4, 0xA7F255A7, 0x7E82FC7E, 0x3D477A3D, + 0x64ACC864, 0x5DE7BA5D, 0x192B3219, 0x7395E673, 0x60A0C060, + 0x81981981, 0x4FD19E4F, 0xDC7FA3DC, 0x22664422, 0x2A7E542A, + 0x90AB3B90, 0x88830B88, 0x46CA8C46, 0xEE29C7EE, 0xB8D36BB8, + 0x143C2814, 0xDE79A7DE, 0x5EE2BC5E, 0x0B1D160B, 0xDB76ADDB, + 0xE03BDBE0, 0x32566432, 0x3A4E743A, 0x0A1E140A, 0x49DB9249, + 0x060A0C06, 0x246C4824, 0x5CE4B85C, 0xC25D9FC2, 0xD36EBDD3, + 0xACEF43AC, 0x62A6C462, 0x91A83991, 0x95A43195, 0xE437D3E4, + 0x798BF279, 0xE732D5E7, 0xC8438BC8, 0x37596E37, 0x6DB7DA6D, + 0x8D8C018D, 0xD564B1D5, 0x4ED29C4E, 0xA9E049A9, 0x6CB4D86C, + 0x56FAAC56, 0xF407F3F4, 0xEA25CFEA, 0x65AFCA65, 0x7A8EF47A, + 0xAEE947AE, 0x08181008, 0xBAD56FBA, 0x7888F078, 0x256F4A25, + 0x2E725C2E, 0x1C24381C, 0xA6F157A6, 0xB4C773B4, 0xC65197C6, + 0xE823CBE8, 0xDD7CA1DD, 0x749CE874, 0x1F213E1F, 0x4BDD964B, + 0xBDDC61BD, 0x8B860D8B, 0x8A850F8A, 0x7090E070, 0x3E427C3E, + 0xB5C471B5, 0x66AACC66, 0x48D89048, 0x03050603, 0xF601F7F6, + 0x0E121C0E, 0x61A3C261, 0x355F6A35, 0x57F9AE57, 0xB9D069B9, + 0x86911786, 0xC15899C1, 0x1D273A1D, 0x9EB9279E, 0xE138D9E1, + 0xF813EBF8, 0x98B32B98, 0x11332211, 0x69BBD269, 0xD970A9D9, + 0x8E89078E, 0x94A73394, 0x9BB62D9B, 0x1E223C1E, 0x87921587, + 0xE920C9E9, 0xCE4987CE, 0x55FFAA55, 0x28785028, 0xDF7AA5DF, + 0x8C8F038C, 0xA1F859A1, 0x89800989, 0x0D171A0D, 0xBFDA65BF, + 0xE631D7E6, 0x42C68442, 0x68B8D068, 0x41C38241, 0x99B02999, + 0x2D775A2D, 0x0F111E0F, 0xB0CB7BB0, 0x54FCA854, 0xBBD66DBB, + 0x163A2C16, + }; + + // Lookup table for row 3 transforms, see section 5.2.1 of original spec. + private static final int[] T3 = { + 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, + 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, 0x30305060, 0x01010302, + 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, + 0x76769AEC, 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, + 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, 0xADADEC41, + 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, + 0x727296E4, 0xC0C05B9B, 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, + 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, + 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, + 0xD8D873AB, 0x31315362, 0x15153F2A, 0x04040C08, 0xC7C75295, + 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, + 0x9A9AB52F, 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, + 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, 0x09091B12, + 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, + 0x5A5AEEB4, 0xA0A0FB5B, 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, + 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, + 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, + 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, 0x6A6ABED4, 0xCBCB468D, + 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, + 0xCFCF4A85, 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, + 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, 0x4545CF8A, + 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, + 0x9F9FBA25, 0xA8A8E34B, 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, + 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, + 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, + 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, 0xCDCD4C81, 0x0C0C1418, + 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, + 0x1717392E, 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, + 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, 0x6060A0C0, + 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, + 0x9090AB3B, 0x8888830B, 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, + 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, + 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, + 0x06060A0C, 0x24246C48, 0x5C5CE4B8, 0xC2C25D9F, 0xD3D36EBD, + 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, + 0x79798BF2, 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, + 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, 0x6C6CB4D8, + 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, + 0xAEAEE947, 0x08081810, 0xBABAD56F, 0x787888F0, 0x25256F4A, + 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, + 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, + 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, 0x707090E0, 0x3E3E427C, + 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, + 0x0E0E121C, 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, + 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, 0xE1E138D9, + 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, + 0x8E8E8907, 0x9494A733, 0x9B9BB62D, 0x1E1E223C, 0x87879215, + 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, + 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, + 0xE6E631D7, 0x4242C684, 0x6868B8D0, 0x4141C382, 0x9999B029, + 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, + 0x16163A2C, + }; + + // Lookup table for row 0 inverse transforms, see section 5.2.1 of original + // spec. + private static final int[] TI0 = { + 0x51F4A750, 0x7E416553, 0x1A17A4C3, 0x3A275E96, 0x3BAB6BCB, + 0x1F9D45F1, 0xACFA58AB, 0x4BE30393, 0x2030FA55, 0xAD766DF6, + 0x88CC7691, 0xF5024C25, 0x4FE5D7FC, 0xC52ACBD7, 0x26354480, + 0xB562A38F, 0xDEB15A49, 0x25BA1B67, 0x45EA0E98, 0x5DFEC0E1, + 0xC32F7502, 0x814CF012, 0x8D4697A3, 0x6BD3F9C6, 0x038F5FE7, + 0x15929C95, 0xBF6D7AEB, 0x955259DA, 0xD4BE832D, 0x587421D3, + 0x49E06929, 0x8EC9C844, 0x75C2896A, 0xF48E7978, 0x99583E6B, + 0x27B971DD, 0xBEE14FB6, 0xF088AD17, 0xC920AC66, 0x7DCE3AB4, + 0x63DF4A18, 0xE51A3182, 0x97513360, 0x62537F45, 0xB16477E0, + 0xBB6BAE84, 0xFE81A01C, 0xF9082B94, 0x70486858, 0x8F45FD19, + 0x94DE6C87, 0x527BF8B7, 0xAB73D323, 0x724B02E2, 0xE31F8F57, + 0x6655AB2A, 0xB2EB2807, 0x2FB5C203, 0x86C57B9A, 0xD33708A5, + 0x302887F2, 0x23BFA5B2, 0x02036ABA, 0xED16825C, 0x8ACF1C2B, + 0xA779B492, 0xF307F2F0, 0x4E69E2A1, 0x65DAF4CD, 0x0605BED5, + 0xD134621F, 0xC4A6FE8A, 0x342E539D, 0xA2F355A0, 0x058AE132, + 0xA4F6EB75, 0x0B83EC39, 0x4060EFAA, 0x5E719F06, 0xBD6E1051, + 0x3E218AF9, 0x96DD063D, 0xDD3E05AE, 0x4DE6BD46, 0x91548DB5, + 0x71C45D05, 0x0406D46F, 0x605015FF, 0x1998FB24, 0xD6BDE997, + 0x894043CC, 0x67D99E77, 0xB0E842BD, 0x07898B88, 0xE7195B38, + 0x79C8EEDB, 0xA17C0A47, 0x7C420FE9, 0xF8841EC9, 0x00000000, + 0x09808683, 0x322BED48, 0x1E1170AC, 0x6C5A724E, 0xFD0EFFFB, + 0x0F853856, 0x3DAED51E, 0x362D3927, 0x0A0FD964, 0x685CA621, + 0x9B5B54D1, 0x24362E3A, 0x0C0A67B1, 0x9357E70F, 0xB4EE96D2, + 0x1B9B919E, 0x80C0C54F, 0x61DC20A2, 0x5A774B69, 0x1C121A16, + 0xE293BA0A, 0xC0A02AE5, 0x3C22E043, 0x121B171D, 0x0E090D0B, + 0xF28BC7AD, 0x2DB6A8B9, 0x141EA9C8, 0x57F11985, 0xAF75074C, + 0xEE99DDBB, 0xA37F60FD, 0xF701269F, 0x5C72F5BC, 0x44663BC5, + 0x5BFB7E34, 0x8B432976, 0xCB23C6DC, 0xB6EDFC68, 0xB8E4F163, + 0xD731DCCA, 0x42638510, 0x13972240, 0x84C61120, 0x854A247D, + 0xD2BB3DF8, 0xAEF93211, 0xC729A16D, 0x1D9E2F4B, 0xDCB230F3, + 0x0D8652EC, 0x77C1E3D0, 0x2BB3166C, 0xA970B999, 0x119448FA, + 0x47E96422, 0xA8FC8CC4, 0xA0F03F1A, 0x567D2CD8, 0x223390EF, + 0x87494EC7, 0xD938D1C1, 0x8CCAA2FE, 0x98D40B36, 0xA6F581CF, + 0xA57ADE28, 0xDAB78E26, 0x3FADBFA4, 0x2C3A9DE4, 0x5078920D, + 0x6A5FCC9B, 0x547E4662, 0xF68D13C2, 0x90D8B8E8, 0x2E39F75E, + 0x82C3AFF5, 0x9F5D80BE, 0x69D0937C, 0x6FD52DA9, 0xCF2512B3, + 0xC8AC993B, 0x10187DA7, 0xE89C636E, 0xDB3BBB7B, 0xCD267809, + 0x6E5918F4, 0xEC9AB701, 0x834F9AA8, 0xE6956E65, 0xAAFFE67E, + 0x21BCCF08, 0xEF15E8E6, 0xBAE79BD9, 0x4A6F36CE, 0xEA9F09D4, + 0x29B07CD6, 0x31A4B2AF, 0x2A3F2331, 0xC6A59430, 0x35A266C0, + 0x744EBC37, 0xFC82CAA6, 0xE090D0B0, 0x33A7D815, 0xF104984A, + 0x41ECDAF7, 0x7FCD500E, 0x1791F62F, 0x764DD68D, 0x43EFB04D, + 0xCCAA4D54, 0xE49604DF, 0x9ED1B5E3, 0x4C6A881B, 0xC12C1FB8, + 0x4665517F, 0x9D5EEA04, 0x018C355D, 0xFA877473, 0xFB0B412E, + 0xB3671D5A, 0x92DBD252, 0xE9105633, 0x6DD64713, 0x9AD7618C, + 0x37A10C7A, 0x59F8148E, 0xEB133C89, 0xCEA927EE, 0xB761C935, + 0xE11CE5ED, 0x7A47B13C, 0x9CD2DF59, 0x55F2733F, 0x1814CE79, + 0x73C737BF, 0x53F7CDEA, 0x5FFDAA5B, 0xDF3D6F14, 0x7844DB86, + 0xCAAFF381, 0xB968C43E, 0x3824342C, 0xC2A3405F, 0x161DC372, + 0xBCE2250C, 0x283C498B, 0xFF0D9541, 0x39A80171, 0x080CB3DE, + 0xD8B4E49C, 0x6456C190, 0x7BCB8461, 0xD532B670, 0x486C5C74, + 0xD0B85742, + }; + + // Lookup table for row 1 inverse transforms, see section 5.2.1 of original + // spec. + private static final int[] TI1 = { + 0x5051F4A7, 0x537E4165, 0xC31A17A4, 0x963A275E, 0xCB3BAB6B, + 0xF11F9D45, 0xABACFA58, 0x934BE303, 0x552030FA, 0xF6AD766D, + 0x9188CC76, 0x25F5024C, 0xFC4FE5D7, 0xD7C52ACB, 0x80263544, + 0x8FB562A3, 0x49DEB15A, 0x6725BA1B, 0x9845EA0E, 0xE15DFEC0, + 0x02C32F75, 0x12814CF0, 0xA38D4697, 0xC66BD3F9, 0xE7038F5F, + 0x9515929C, 0xEBBF6D7A, 0xDA955259, 0x2DD4BE83, 0xD3587421, + 0x2949E069, 0x448EC9C8, 0x6A75C289, 0x78F48E79, 0x6B99583E, + 0xDD27B971, 0xB6BEE14F, 0x17F088AD, 0x66C920AC, 0xB47DCE3A, + 0x1863DF4A, 0x82E51A31, 0x60975133, 0x4562537F, 0xE0B16477, + 0x84BB6BAE, 0x1CFE81A0, 0x94F9082B, 0x58704868, 0x198F45FD, + 0x8794DE6C, 0xB7527BF8, 0x23AB73D3, 0xE2724B02, 0x57E31F8F, + 0x2A6655AB, 0x07B2EB28, 0x032FB5C2, 0x9A86C57B, 0xA5D33708, + 0xF2302887, 0xB223BFA5, 0xBA02036A, 0x5CED1682, 0x2B8ACF1C, + 0x92A779B4, 0xF0F307F2, 0xA14E69E2, 0xCD65DAF4, 0xD50605BE, + 0x1FD13462, 0x8AC4A6FE, 0x9D342E53, 0xA0A2F355, 0x32058AE1, + 0x75A4F6EB, 0x390B83EC, 0xAA4060EF, 0x065E719F, 0x51BD6E10, + 0xF93E218A, 0x3D96DD06, 0xAEDD3E05, 0x464DE6BD, 0xB591548D, + 0x0571C45D, 0x6F0406D4, 0xFF605015, 0x241998FB, 0x97D6BDE9, + 0xCC894043, 0x7767D99E, 0xBDB0E842, 0x8807898B, 0x38E7195B, + 0xDB79C8EE, 0x47A17C0A, 0xE97C420F, 0xC9F8841E, 0x00000000, + 0x83098086, 0x48322BED, 0xAC1E1170, 0x4E6C5A72, 0xFBFD0EFF, + 0x560F8538, 0x1E3DAED5, 0x27362D39, 0x640A0FD9, 0x21685CA6, + 0xD19B5B54, 0x3A24362E, 0xB10C0A67, 0x0F9357E7, 0xD2B4EE96, + 0x9E1B9B91, 0x4F80C0C5, 0xA261DC20, 0x695A774B, 0x161C121A, + 0x0AE293BA, 0xE5C0A02A, 0x433C22E0, 0x1D121B17, 0x0B0E090D, + 0xADF28BC7, 0xB92DB6A8, 0xC8141EA9, 0x8557F119, 0x4CAF7507, + 0xBBEE99DD, 0xFDA37F60, 0x9FF70126, 0xBC5C72F5, 0xC544663B, + 0x345BFB7E, 0x768B4329, 0xDCCB23C6, 0x68B6EDFC, 0x63B8E4F1, + 0xCAD731DC, 0x10426385, 0x40139722, 0x2084C611, 0x7D854A24, + 0xF8D2BB3D, 0x11AEF932, 0x6DC729A1, 0x4B1D9E2F, 0xF3DCB230, + 0xEC0D8652, 0xD077C1E3, 0x6C2BB316, 0x99A970B9, 0xFA119448, + 0x2247E964, 0xC4A8FC8C, 0x1AA0F03F, 0xD8567D2C, 0xEF223390, + 0xC787494E, 0xC1D938D1, 0xFE8CCAA2, 0x3698D40B, 0xCFA6F581, + 0x28A57ADE, 0x26DAB78E, 0xA43FADBF, 0xE42C3A9D, 0x0D507892, + 0x9B6A5FCC, 0x62547E46, 0xC2F68D13, 0xE890D8B8, 0x5E2E39F7, + 0xF582C3AF, 0xBE9F5D80, 0x7C69D093, 0xA96FD52D, 0xB3CF2512, + 0x3BC8AC99, 0xA710187D, 0x6EE89C63, 0x7BDB3BBB, 0x09CD2678, + 0xF46E5918, 0x01EC9AB7, 0xA8834F9A, 0x65E6956E, 0x7EAAFFE6, + 0x0821BCCF, 0xE6EF15E8, 0xD9BAE79B, 0xCE4A6F36, 0xD4EA9F09, + 0xD629B07C, 0xAF31A4B2, 0x312A3F23, 0x30C6A594, 0xC035A266, + 0x37744EBC, 0xA6FC82CA, 0xB0E090D0, 0x1533A7D8, 0x4AF10498, + 0xF741ECDA, 0x0E7FCD50, 0x2F1791F6, 0x8D764DD6, 0x4D43EFB0, + 0x54CCAA4D, 0xDFE49604, 0xE39ED1B5, 0x1B4C6A88, 0xB8C12C1F, + 0x7F466551, 0x049D5EEA, 0x5D018C35, 0x73FA8774, 0x2EFB0B41, + 0x5AB3671D, 0x5292DBD2, 0x33E91056, 0x136DD647, 0x8C9AD761, + 0x7A37A10C, 0x8E59F814, 0x89EB133C, 0xEECEA927, 0x35B761C9, + 0xEDE11CE5, 0x3C7A47B1, 0x599CD2DF, 0x3F55F273, 0x791814CE, + 0xBF73C737, 0xEA53F7CD, 0x5B5FFDAA, 0x14DF3D6F, 0x867844DB, + 0x81CAAFF3, 0x3EB968C4, 0x2C382434, 0x5FC2A340, 0x72161DC3, + 0x0CBCE225, 0x8B283C49, 0x41FF0D95, 0x7139A801, 0xDE080CB3, + 0x9CD8B4E4, 0x906456C1, 0x617BCB84, 0x70D532B6, 0x74486C5C, + 0x42D0B857, + }; + + // Lookup table for row 2 inverse transforms, see section 5.2.1 of original + // spec. + private static final int[] TI2 = { + 0xA75051F4, 0x65537E41, 0xA4C31A17, 0x5E963A27, 0x6BCB3BAB, + 0x45F11F9D, 0x58ABACFA, 0x03934BE3, 0xFA552030, 0x6DF6AD76, + 0x769188CC, 0x4C25F502, 0xD7FC4FE5, 0xCBD7C52A, 0x44802635, + 0xA38FB562, 0x5A49DEB1, 0x1B6725BA, 0x0E9845EA, 0xC0E15DFE, + 0x7502C32F, 0xF012814C, 0x97A38D46, 0xF9C66BD3, 0x5FE7038F, + 0x9C951592, 0x7AEBBF6D, 0x59DA9552, 0x832DD4BE, 0x21D35874, + 0x692949E0, 0xC8448EC9, 0x896A75C2, 0x7978F48E, 0x3E6B9958, + 0x71DD27B9, 0x4FB6BEE1, 0xAD17F088, 0xAC66C920, 0x3AB47DCE, + 0x4A1863DF, 0x3182E51A, 0x33609751, 0x7F456253, 0x77E0B164, + 0xAE84BB6B, 0xA01CFE81, 0x2B94F908, 0x68587048, 0xFD198F45, + 0x6C8794DE, 0xF8B7527B, 0xD323AB73, 0x02E2724B, 0x8F57E31F, + 0xAB2A6655, 0x2807B2EB, 0xC2032FB5, 0x7B9A86C5, 0x08A5D337, + 0x87F23028, 0xA5B223BF, 0x6ABA0203, 0x825CED16, 0x1C2B8ACF, + 0xB492A779, 0xF2F0F307, 0xE2A14E69, 0xF4CD65DA, 0xBED50605, + 0x621FD134, 0xFE8AC4A6, 0x539D342E, 0x55A0A2F3, 0xE132058A, + 0xEB75A4F6, 0xEC390B83, 0xEFAA4060, 0x9F065E71, 0x1051BD6E, + 0x8AF93E21, 0x063D96DD, 0x05AEDD3E, 0xBD464DE6, 0x8DB59154, + 0x5D0571C4, 0xD46F0406, 0x15FF6050, 0xFB241998, 0xE997D6BD, + 0x43CC8940, 0x9E7767D9, 0x42BDB0E8, 0x8B880789, 0x5B38E719, + 0xEEDB79C8, 0x0A47A17C, 0x0FE97C42, 0x1EC9F884, 0x00000000, + 0x86830980, 0xED48322B, 0x70AC1E11, 0x724E6C5A, 0xFFFBFD0E, + 0x38560F85, 0xD51E3DAE, 0x3927362D, 0xD9640A0F, 0xA621685C, + 0x54D19B5B, 0x2E3A2436, 0x67B10C0A, 0xE70F9357, 0x96D2B4EE, + 0x919E1B9B, 0xC54F80C0, 0x20A261DC, 0x4B695A77, 0x1A161C12, + 0xBA0AE293, 0x2AE5C0A0, 0xE0433C22, 0x171D121B, 0x0D0B0E09, + 0xC7ADF28B, 0xA8B92DB6, 0xA9C8141E, 0x198557F1, 0x074CAF75, + 0xDDBBEE99, 0x60FDA37F, 0x269FF701, 0xF5BC5C72, 0x3BC54466, + 0x7E345BFB, 0x29768B43, 0xC6DCCB23, 0xFC68B6ED, 0xF163B8E4, + 0xDCCAD731, 0x85104263, 0x22401397, 0x112084C6, 0x247D854A, + 0x3DF8D2BB, 0x3211AEF9, 0xA16DC729, 0x2F4B1D9E, 0x30F3DCB2, + 0x52EC0D86, 0xE3D077C1, 0x166C2BB3, 0xB999A970, 0x48FA1194, + 0x642247E9, 0x8CC4A8FC, 0x3F1AA0F0, 0x2CD8567D, 0x90EF2233, + 0x4EC78749, 0xD1C1D938, 0xA2FE8CCA, 0x0B3698D4, 0x81CFA6F5, + 0xDE28A57A, 0x8E26DAB7, 0xBFA43FAD, 0x9DE42C3A, 0x920D5078, + 0xCC9B6A5F, 0x4662547E, 0x13C2F68D, 0xB8E890D8, 0xF75E2E39, + 0xAFF582C3, 0x80BE9F5D, 0x937C69D0, 0x2DA96FD5, 0x12B3CF25, + 0x993BC8AC, 0x7DA71018, 0x636EE89C, 0xBB7BDB3B, 0x7809CD26, + 0x18F46E59, 0xB701EC9A, 0x9AA8834F, 0x6E65E695, 0xE67EAAFF, + 0xCF0821BC, 0xE8E6EF15, 0x9BD9BAE7, 0x36CE4A6F, 0x09D4EA9F, + 0x7CD629B0, 0xB2AF31A4, 0x23312A3F, 0x9430C6A5, 0x66C035A2, + 0xBC37744E, 0xCAA6FC82, 0xD0B0E090, 0xD81533A7, 0x984AF104, + 0xDAF741EC, 0x500E7FCD, 0xF62F1791, 0xD68D764D, 0xB04D43EF, + 0x4D54CCAA, 0x04DFE496, 0xB5E39ED1, 0x881B4C6A, 0x1FB8C12C, + 0x517F4665, 0xEA049D5E, 0x355D018C, 0x7473FA87, 0x412EFB0B, + 0x1D5AB367, 0xD25292DB, 0x5633E910, 0x47136DD6, 0x618C9AD7, + 0x0C7A37A1, 0x148E59F8, 0x3C89EB13, 0x27EECEA9, 0xC935B761, + 0xE5EDE11C, 0xB13C7A47, 0xDF599CD2, 0x733F55F2, 0xCE791814, + 0x37BF73C7, 0xCDEA53F7, 0xAA5B5FFD, 0x6F14DF3D, 0xDB867844, + 0xF381CAAF, 0xC43EB968, 0x342C3824, 0x405FC2A3, 0xC372161D, + 0x250CBCE2, 0x498B283C, 0x9541FF0D, 0x017139A8, 0xB3DE080C, + 0xE49CD8B4, 0xC1906456, 0x84617BCB, 0xB670D532, 0x5C74486C, + 0x5742D0B8, + }; + + // Lookup table for row 3 inverse transforms, see section 5.2.1 of original + // spec. + private static final int[] TI3 = { + 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, + 0x9D45F11F, 0xFA58ABAC, 0xE303934B, 0x30FA5520, 0x766DF6AD, + 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, + 0x62A38FB5, 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, + 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B, 0x8F5FE703, + 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, + 0xE0692949, 0xC9C8448E, 0xC2896A75, 0x8E7978F4, 0x583E6B99, + 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D, + 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, + 0x6BAE84BB, 0x81A01CFE, 0x082B94F9, 0x48685870, 0x45FD198F, + 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, + 0x55AB2A66, 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, + 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED, 0xCF1C2B8A, + 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, + 0x34621FD1, 0xA6FE8AC4, 0x2E539D34, 0xF355A0A2, 0x8AE13205, + 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD, + 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, + 0xC45D0571, 0x06D46F04, 0x5015FF60, 0x98FB2419, 0xBDE997D6, + 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, + 0xC8EEDB79, 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, + 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C, 0x0EFFFBFD, + 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, + 0x5B54D19B, 0x362E3A24, 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, + 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C, + 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, + 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814, 0xF1198557, 0x75074CAF, + 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, + 0xFB7E345B, 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, + 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084, 0x4A247D85, + 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, + 0x8652EC0D, 0xC1E3D077, 0xB3166C2B, 0x70B999A9, 0x9448FA11, + 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22, + 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, + 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F, 0x3A9DE42C, 0x78920D50, + 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, + 0xC3AFF582, 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, + 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB, 0x267809CD, + 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, + 0xBCCF0821, 0x15E8E6EF, 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, + 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035, + 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, + 0xECDAF741, 0xCD500E7F, 0x91F62F17, 0x4DD68D76, 0xEFB04D43, + 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, + 0x65517F46, 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, + 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D, 0xD7618C9A, + 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, + 0x1CE5EDE1, 0x47B13C7A, 0xD2DF599C, 0xF2733F55, 0x14CE7918, + 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678, + 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, + 0xE2250CBC, 0x3C498B28, 0x0D9541FF, 0xA8017139, 0x0CB3DE08, + 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, + 0xB85742D0, + }; + + // Lookup table for inverse substitution transform of last round as + // described in the international journal article referenced. + private static final int[] TI4 = { + 0x52525252, 0x09090909, 0x6A6A6A6A, 0xD5D5D5D5, 0x30303030, + 0x36363636, 0xA5A5A5A5, 0x38383838, 0xBFBFBFBF, 0x40404040, + 0xA3A3A3A3, 0x9E9E9E9E, 0x81818181, 0xF3F3F3F3, 0xD7D7D7D7, + 0xFBFBFBFB, 0x7C7C7C7C, 0xE3E3E3E3, 0x39393939, 0x82828282, + 0x9B9B9B9B, 0x2F2F2F2F, 0xFFFFFFFF, 0x87878787, 0x34343434, + 0x8E8E8E8E, 0x43434343, 0x44444444, 0xC4C4C4C4, 0xDEDEDEDE, + 0xE9E9E9E9, 0xCBCBCBCB, 0x54545454, 0x7B7B7B7B, 0x94949494, + 0x32323232, 0xA6A6A6A6, 0xC2C2C2C2, 0x23232323, 0x3D3D3D3D, + 0xEEEEEEEE, 0x4C4C4C4C, 0x95959595, 0x0B0B0B0B, 0x42424242, + 0xFAFAFAFA, 0xC3C3C3C3, 0x4E4E4E4E, 0x08080808, 0x2E2E2E2E, + 0xA1A1A1A1, 0x66666666, 0x28282828, 0xD9D9D9D9, 0x24242424, + 0xB2B2B2B2, 0x76767676, 0x5B5B5B5B, 0xA2A2A2A2, 0x49494949, + 0x6D6D6D6D, 0x8B8B8B8B, 0xD1D1D1D1, 0x25252525, 0x72727272, + 0xF8F8F8F8, 0xF6F6F6F6, 0x64646464, 0x86868686, 0x68686868, + 0x98989898, 0x16161616, 0xD4D4D4D4, 0xA4A4A4A4, 0x5C5C5C5C, + 0xCCCCCCCC, 0x5D5D5D5D, 0x65656565, 0xB6B6B6B6, 0x92929292, + 0x6C6C6C6C, 0x70707070, 0x48484848, 0x50505050, 0xFDFDFDFD, + 0xEDEDEDED, 0xB9B9B9B9, 0xDADADADA, 0x5E5E5E5E, 0x15151515, + 0x46464646, 0x57575757, 0xA7A7A7A7, 0x8D8D8D8D, 0x9D9D9D9D, + 0x84848484, 0x90909090, 0xD8D8D8D8, 0xABABABAB, 0x00000000, + 0x8C8C8C8C, 0xBCBCBCBC, 0xD3D3D3D3, 0x0A0A0A0A, 0xF7F7F7F7, + 0xE4E4E4E4, 0x58585858, 0x05050505, 0xB8B8B8B8, 0xB3B3B3B3, + 0x45454545, 0x06060606, 0xD0D0D0D0, 0x2C2C2C2C, 0x1E1E1E1E, + 0x8F8F8F8F, 0xCACACACA, 0x3F3F3F3F, 0x0F0F0F0F, 0x02020202, + 0xC1C1C1C1, 0xAFAFAFAF, 0xBDBDBDBD, 0x03030303, 0x01010101, + 0x13131313, 0x8A8A8A8A, 0x6B6B6B6B, 0x3A3A3A3A, 0x91919191, + 0x11111111, 0x41414141, 0x4F4F4F4F, 0x67676767, 0xDCDCDCDC, + 0xEAEAEAEA, 0x97979797, 0xF2F2F2F2, 0xCFCFCFCF, 0xCECECECE, + 0xF0F0F0F0, 0xB4B4B4B4, 0xE6E6E6E6, 0x73737373, 0x96969696, + 0xACACACAC, 0x74747474, 0x22222222, 0xE7E7E7E7, 0xADADADAD, + 0x35353535, 0x85858585, 0xE2E2E2E2, 0xF9F9F9F9, 0x37373737, + 0xE8E8E8E8, 0x1C1C1C1C, 0x75757575, 0xDFDFDFDF, 0x6E6E6E6E, + 0x47474747, 0xF1F1F1F1, 0x1A1A1A1A, 0x71717171, 0x1D1D1D1D, + 0x29292929, 0xC5C5C5C5, 0x89898989, 0x6F6F6F6F, 0xB7B7B7B7, + 0x62626262, 0x0E0E0E0E, 0xAAAAAAAA, 0x18181818, 0xBEBEBEBE, + 0x1B1B1B1B, 0xFCFCFCFC, 0x56565656, 0x3E3E3E3E, 0x4B4B4B4B, + 0xC6C6C6C6, 0xD2D2D2D2, 0x79797979, 0x20202020, 0x9A9A9A9A, + 0xDBDBDBDB, 0xC0C0C0C0, 0xFEFEFEFE, 0x78787878, 0xCDCDCDCD, + 0x5A5A5A5A, 0xF4F4F4F4, 0x1F1F1F1F, 0xDDDDDDDD, 0xA8A8A8A8, + 0x33333333, 0x88888888, 0x07070707, 0xC7C7C7C7, 0x31313131, + 0xB1B1B1B1, 0x12121212, 0x10101010, 0x59595959, 0x27272727, + 0x80808080, 0xECECECEC, 0x5F5F5F5F, 0x60606060, 0x51515151, + 0x7F7F7F7F, 0xA9A9A9A9, 0x19191919, 0xB5B5B5B5, 0x4A4A4A4A, + 0x0D0D0D0D, 0x2D2D2D2D, 0xE5E5E5E5, 0x7A7A7A7A, 0x9F9F9F9F, + 0x93939393, 0xC9C9C9C9, 0x9C9C9C9C, 0xEFEFEFEF, 0xA0A0A0A0, + 0xE0E0E0E0, 0x3B3B3B3B, 0x4D4D4D4D, 0xAEAEAEAE, 0x2A2A2A2A, + 0xF5F5F5F5, 0xB0B0B0B0, 0xC8C8C8C8, 0xEBEBEBEB, 0xBBBBBBBB, + 0x3C3C3C3C, 0x83838383, 0x53535353, 0x99999999, 0x61616161, + 0x17171717, 0x2B2B2B2B, 0x04040404, 0x7E7E7E7E, 0xBABABABA, + 0x77777777, 0xD6D6D6D6, 0x26262626, 0xE1E1E1E1, 0x69696969, + 0x14141414, 0x63636363, 0x55555555, 0x21212121, 0x0C0C0C0C, + 0x7D7D7D7D, + }; + + // Lookup table for row 0 of the inverse mix column transform. + private static final int[] TMI0 = { + 0x00000000, 0x0E090D0B, 0x1C121A16, 0x121B171D, 0x3824342C, + 0x362D3927, 0x24362E3A, 0x2A3F2331, 0x70486858, 0x7E416553, + 0x6C5A724E, 0x62537F45, 0x486C5C74, 0x4665517F, 0x547E4662, + 0x5A774B69, 0xE090D0B0, 0xEE99DDBB, 0xFC82CAA6, 0xF28BC7AD, + 0xD8B4E49C, 0xD6BDE997, 0xC4A6FE8A, 0xCAAFF381, 0x90D8B8E8, + 0x9ED1B5E3, 0x8CCAA2FE, 0x82C3AFF5, 0xA8FC8CC4, 0xA6F581CF, + 0xB4EE96D2, 0xBAE79BD9, 0xDB3BBB7B, 0xD532B670, 0xC729A16D, + 0xC920AC66, 0xE31F8F57, 0xED16825C, 0xFF0D9541, 0xF104984A, + 0xAB73D323, 0xA57ADE28, 0xB761C935, 0xB968C43E, 0x9357E70F, + 0x9D5EEA04, 0x8F45FD19, 0x814CF012, 0x3BAB6BCB, 0x35A266C0, + 0x27B971DD, 0x29B07CD6, 0x038F5FE7, 0x0D8652EC, 0x1F9D45F1, + 0x119448FA, 0x4BE30393, 0x45EA0E98, 0x57F11985, 0x59F8148E, + 0x73C737BF, 0x7DCE3AB4, 0x6FD52DA9, 0x61DC20A2, 0xAD766DF6, + 0xA37F60FD, 0xB16477E0, 0xBF6D7AEB, 0x955259DA, 0x9B5B54D1, + 0x894043CC, 0x87494EC7, 0xDD3E05AE, 0xD33708A5, 0xC12C1FB8, + 0xCF2512B3, 0xE51A3182, 0xEB133C89, 0xF9082B94, 0xF701269F, + 0x4DE6BD46, 0x43EFB04D, 0x51F4A750, 0x5FFDAA5B, 0x75C2896A, + 0x7BCB8461, 0x69D0937C, 0x67D99E77, 0x3DAED51E, 0x33A7D815, + 0x21BCCF08, 0x2FB5C203, 0x058AE132, 0x0B83EC39, 0x1998FB24, + 0x1791F62F, 0x764DD68D, 0x7844DB86, 0x6A5FCC9B, 0x6456C190, + 0x4E69E2A1, 0x4060EFAA, 0x527BF8B7, 0x5C72F5BC, 0x0605BED5, + 0x080CB3DE, 0x1A17A4C3, 0x141EA9C8, 0x3E218AF9, 0x302887F2, + 0x223390EF, 0x2C3A9DE4, 0x96DD063D, 0x98D40B36, 0x8ACF1C2B, + 0x84C61120, 0xAEF93211, 0xA0F03F1A, 0xB2EB2807, 0xBCE2250C, + 0xE6956E65, 0xE89C636E, 0xFA877473, 0xF48E7978, 0xDEB15A49, + 0xD0B85742, 0xC2A3405F, 0xCCAA4D54, 0x41ECDAF7, 0x4FE5D7FC, + 0x5DFEC0E1, 0x53F7CDEA, 0x79C8EEDB, 0x77C1E3D0, 0x65DAF4CD, + 0x6BD3F9C6, 0x31A4B2AF, 0x3FADBFA4, 0x2DB6A8B9, 0x23BFA5B2, + 0x09808683, 0x07898B88, 0x15929C95, 0x1B9B919E, 0xA17C0A47, + 0xAF75074C, 0xBD6E1051, 0xB3671D5A, 0x99583E6B, 0x97513360, + 0x854A247D, 0x8B432976, 0xD134621F, 0xDF3D6F14, 0xCD267809, + 0xC32F7502, 0xE9105633, 0xE7195B38, 0xF5024C25, 0xFB0B412E, + 0x9AD7618C, 0x94DE6C87, 0x86C57B9A, 0x88CC7691, 0xA2F355A0, + 0xACFA58AB, 0xBEE14FB6, 0xB0E842BD, 0xEA9F09D4, 0xE49604DF, + 0xF68D13C2, 0xF8841EC9, 0xD2BB3DF8, 0xDCB230F3, 0xCEA927EE, + 0xC0A02AE5, 0x7A47B13C, 0x744EBC37, 0x6655AB2A, 0x685CA621, + 0x42638510, 0x4C6A881B, 0x5E719F06, 0x5078920D, 0x0A0FD964, + 0x0406D46F, 0x161DC372, 0x1814CE79, 0x322BED48, 0x3C22E043, + 0x2E39F75E, 0x2030FA55, 0xEC9AB701, 0xE293BA0A, 0xF088AD17, + 0xFE81A01C, 0xD4BE832D, 0xDAB78E26, 0xC8AC993B, 0xC6A59430, + 0x9CD2DF59, 0x92DBD252, 0x80C0C54F, 0x8EC9C844, 0xA4F6EB75, + 0xAAFFE67E, 0xB8E4F163, 0xB6EDFC68, 0x0C0A67B1, 0x02036ABA, + 0x10187DA7, 0x1E1170AC, 0x342E539D, 0x3A275E96, 0x283C498B, + 0x26354480, 0x7C420FE9, 0x724B02E2, 0x605015FF, 0x6E5918F4, + 0x44663BC5, 0x4A6F36CE, 0x587421D3, 0x567D2CD8, 0x37A10C7A, + 0x39A80171, 0x2BB3166C, 0x25BA1B67, 0x0F853856, 0x018C355D, + 0x13972240, 0x1D9E2F4B, 0x47E96422, 0x49E06929, 0x5BFB7E34, + 0x55F2733F, 0x7FCD500E, 0x71C45D05, 0x63DF4A18, 0x6DD64713, + 0xD731DCCA, 0xD938D1C1, 0xCB23C6DC, 0xC52ACBD7, 0xEF15E8E6, + 0xE11CE5ED, 0xF307F2F0, 0xFD0EFFFB, 0xA779B492, 0xA970B999, + 0xBB6BAE84, 0xB562A38F, 0x9F5D80BE, 0x91548DB5, 0x834F9AA8, + 0x8D4697A3, + }; + + // Lookup table for row 1 of the inverse mix column transform. + private static final int[] TMI1 = { + 0x00000000, 0x0B0E090D, 0x161C121A, 0x1D121B17, 0x2C382434, + 0x27362D39, 0x3A24362E, 0x312A3F23, 0x58704868, 0x537E4165, + 0x4E6C5A72, 0x4562537F, 0x74486C5C, 0x7F466551, 0x62547E46, + 0x695A774B, 0xB0E090D0, 0xBBEE99DD, 0xA6FC82CA, 0xADF28BC7, + 0x9CD8B4E4, 0x97D6BDE9, 0x8AC4A6FE, 0x81CAAFF3, 0xE890D8B8, + 0xE39ED1B5, 0xFE8CCAA2, 0xF582C3AF, 0xC4A8FC8C, 0xCFA6F581, + 0xD2B4EE96, 0xD9BAE79B, 0x7BDB3BBB, 0x70D532B6, 0x6DC729A1, + 0x66C920AC, 0x57E31F8F, 0x5CED1682, 0x41FF0D95, 0x4AF10498, + 0x23AB73D3, 0x28A57ADE, 0x35B761C9, 0x3EB968C4, 0x0F9357E7, + 0x049D5EEA, 0x198F45FD, 0x12814CF0, 0xCB3BAB6B, 0xC035A266, + 0xDD27B971, 0xD629B07C, 0xE7038F5F, 0xEC0D8652, 0xF11F9D45, + 0xFA119448, 0x934BE303, 0x9845EA0E, 0x8557F119, 0x8E59F814, + 0xBF73C737, 0xB47DCE3A, 0xA96FD52D, 0xA261DC20, 0xF6AD766D, + 0xFDA37F60, 0xE0B16477, 0xEBBF6D7A, 0xDA955259, 0xD19B5B54, + 0xCC894043, 0xC787494E, 0xAEDD3E05, 0xA5D33708, 0xB8C12C1F, + 0xB3CF2512, 0x82E51A31, 0x89EB133C, 0x94F9082B, 0x9FF70126, + 0x464DE6BD, 0x4D43EFB0, 0x5051F4A7, 0x5B5FFDAA, 0x6A75C289, + 0x617BCB84, 0x7C69D093, 0x7767D99E, 0x1E3DAED5, 0x1533A7D8, + 0x0821BCCF, 0x032FB5C2, 0x32058AE1, 0x390B83EC, 0x241998FB, + 0x2F1791F6, 0x8D764DD6, 0x867844DB, 0x9B6A5FCC, 0x906456C1, + 0xA14E69E2, 0xAA4060EF, 0xB7527BF8, 0xBC5C72F5, 0xD50605BE, + 0xDE080CB3, 0xC31A17A4, 0xC8141EA9, 0xF93E218A, 0xF2302887, + 0xEF223390, 0xE42C3A9D, 0x3D96DD06, 0x3698D40B, 0x2B8ACF1C, + 0x2084C611, 0x11AEF932, 0x1AA0F03F, 0x07B2EB28, 0x0CBCE225, + 0x65E6956E, 0x6EE89C63, 0x73FA8774, 0x78F48E79, 0x49DEB15A, + 0x42D0B857, 0x5FC2A340, 0x54CCAA4D, 0xF741ECDA, 0xFC4FE5D7, + 0xE15DFEC0, 0xEA53F7CD, 0xDB79C8EE, 0xD077C1E3, 0xCD65DAF4, + 0xC66BD3F9, 0xAF31A4B2, 0xA43FADBF, 0xB92DB6A8, 0xB223BFA5, + 0x83098086, 0x8807898B, 0x9515929C, 0x9E1B9B91, 0x47A17C0A, + 0x4CAF7507, 0x51BD6E10, 0x5AB3671D, 0x6B99583E, 0x60975133, + 0x7D854A24, 0x768B4329, 0x1FD13462, 0x14DF3D6F, 0x09CD2678, + 0x02C32F75, 0x33E91056, 0x38E7195B, 0x25F5024C, 0x2EFB0B41, + 0x8C9AD761, 0x8794DE6C, 0x9A86C57B, 0x9188CC76, 0xA0A2F355, + 0xABACFA58, 0xB6BEE14F, 0xBDB0E842, 0xD4EA9F09, 0xDFE49604, + 0xC2F68D13, 0xC9F8841E, 0xF8D2BB3D, 0xF3DCB230, 0xEECEA927, + 0xE5C0A02A, 0x3C7A47B1, 0x37744EBC, 0x2A6655AB, 0x21685CA6, + 0x10426385, 0x1B4C6A88, 0x065E719F, 0x0D507892, 0x640A0FD9, + 0x6F0406D4, 0x72161DC3, 0x791814CE, 0x48322BED, 0x433C22E0, + 0x5E2E39F7, 0x552030FA, 0x01EC9AB7, 0x0AE293BA, 0x17F088AD, + 0x1CFE81A0, 0x2DD4BE83, 0x26DAB78E, 0x3BC8AC99, 0x30C6A594, + 0x599CD2DF, 0x5292DBD2, 0x4F80C0C5, 0x448EC9C8, 0x75A4F6EB, + 0x7EAAFFE6, 0x63B8E4F1, 0x68B6EDFC, 0xB10C0A67, 0xBA02036A, + 0xA710187D, 0xAC1E1170, 0x9D342E53, 0x963A275E, 0x8B283C49, + 0x80263544, 0xE97C420F, 0xE2724B02, 0xFF605015, 0xF46E5918, + 0xC544663B, 0xCE4A6F36, 0xD3587421, 0xD8567D2C, 0x7A37A10C, + 0x7139A801, 0x6C2BB316, 0x6725BA1B, 0x560F8538, 0x5D018C35, + 0x40139722, 0x4B1D9E2F, 0x2247E964, 0x2949E069, 0x345BFB7E, + 0x3F55F273, 0x0E7FCD50, 0x0571C45D, 0x1863DF4A, 0x136DD647, + 0xCAD731DC, 0xC1D938D1, 0xDCCB23C6, 0xD7C52ACB, 0xE6EF15E8, + 0xEDE11CE5, 0xF0F307F2, 0xFBFD0EFF, 0x92A779B4, 0x99A970B9, + 0x84BB6BAE, 0x8FB562A3, 0xBE9F5D80, 0xB591548D, 0xA8834F9A, + 0xA38D4697, + }; + + // Lookup table for row 2 of the inverse mix column transform. + private static final int[] TMI2 = { + 0x00000000, 0x0D0B0E09, 0x1A161C12, 0x171D121B, 0x342C3824, + 0x3927362D, 0x2E3A2436, 0x23312A3F, 0x68587048, 0x65537E41, + 0x724E6C5A, 0x7F456253, 0x5C74486C, 0x517F4665, 0x4662547E, + 0x4B695A77, 0xD0B0E090, 0xDDBBEE99, 0xCAA6FC82, 0xC7ADF28B, + 0xE49CD8B4, 0xE997D6BD, 0xFE8AC4A6, 0xF381CAAF, 0xB8E890D8, + 0xB5E39ED1, 0xA2FE8CCA, 0xAFF582C3, 0x8CC4A8FC, 0x81CFA6F5, + 0x96D2B4EE, 0x9BD9BAE7, 0xBB7BDB3B, 0xB670D532, 0xA16DC729, + 0xAC66C920, 0x8F57E31F, 0x825CED16, 0x9541FF0D, 0x984AF104, + 0xD323AB73, 0xDE28A57A, 0xC935B761, 0xC43EB968, 0xE70F9357, + 0xEA049D5E, 0xFD198F45, 0xF012814C, 0x6BCB3BAB, 0x66C035A2, + 0x71DD27B9, 0x7CD629B0, 0x5FE7038F, 0x52EC0D86, 0x45F11F9D, + 0x48FA1194, 0x03934BE3, 0x0E9845EA, 0x198557F1, 0x148E59F8, + 0x37BF73C7, 0x3AB47DCE, 0x2DA96FD5, 0x20A261DC, 0x6DF6AD76, + 0x60FDA37F, 0x77E0B164, 0x7AEBBF6D, 0x59DA9552, 0x54D19B5B, + 0x43CC8940, 0x4EC78749, 0x05AEDD3E, 0x08A5D337, 0x1FB8C12C, + 0x12B3CF25, 0x3182E51A, 0x3C89EB13, 0x2B94F908, 0x269FF701, + 0xBD464DE6, 0xB04D43EF, 0xA75051F4, 0xAA5B5FFD, 0x896A75C2, + 0x84617BCB, 0x937C69D0, 0x9E7767D9, 0xD51E3DAE, 0xD81533A7, + 0xCF0821BC, 0xC2032FB5, 0xE132058A, 0xEC390B83, 0xFB241998, + 0xF62F1791, 0xD68D764D, 0xDB867844, 0xCC9B6A5F, 0xC1906456, + 0xE2A14E69, 0xEFAA4060, 0xF8B7527B, 0xF5BC5C72, 0xBED50605, + 0xB3DE080C, 0xA4C31A17, 0xA9C8141E, 0x8AF93E21, 0x87F23028, + 0x90EF2233, 0x9DE42C3A, 0x063D96DD, 0x0B3698D4, 0x1C2B8ACF, + 0x112084C6, 0x3211AEF9, 0x3F1AA0F0, 0x2807B2EB, 0x250CBCE2, + 0x6E65E695, 0x636EE89C, 0x7473FA87, 0x7978F48E, 0x5A49DEB1, + 0x5742D0B8, 0x405FC2A3, 0x4D54CCAA, 0xDAF741EC, 0xD7FC4FE5, + 0xC0E15DFE, 0xCDEA53F7, 0xEEDB79C8, 0xE3D077C1, 0xF4CD65DA, + 0xF9C66BD3, 0xB2AF31A4, 0xBFA43FAD, 0xA8B92DB6, 0xA5B223BF, + 0x86830980, 0x8B880789, 0x9C951592, 0x919E1B9B, 0x0A47A17C, + 0x074CAF75, 0x1051BD6E, 0x1D5AB367, 0x3E6B9958, 0x33609751, + 0x247D854A, 0x29768B43, 0x621FD134, 0x6F14DF3D, 0x7809CD26, + 0x7502C32F, 0x5633E910, 0x5B38E719, 0x4C25F502, 0x412EFB0B, + 0x618C9AD7, 0x6C8794DE, 0x7B9A86C5, 0x769188CC, 0x55A0A2F3, + 0x58ABACFA, 0x4FB6BEE1, 0x42BDB0E8, 0x09D4EA9F, 0x04DFE496, + 0x13C2F68D, 0x1EC9F884, 0x3DF8D2BB, 0x30F3DCB2, 0x27EECEA9, + 0x2AE5C0A0, 0xB13C7A47, 0xBC37744E, 0xAB2A6655, 0xA621685C, + 0x85104263, 0x881B4C6A, 0x9F065E71, 0x920D5078, 0xD9640A0F, + 0xD46F0406, 0xC372161D, 0xCE791814, 0xED48322B, 0xE0433C22, + 0xF75E2E39, 0xFA552030, 0xB701EC9A, 0xBA0AE293, 0xAD17F088, + 0xA01CFE81, 0x832DD4BE, 0x8E26DAB7, 0x993BC8AC, 0x9430C6A5, + 0xDF599CD2, 0xD25292DB, 0xC54F80C0, 0xC8448EC9, 0xEB75A4F6, + 0xE67EAAFF, 0xF163B8E4, 0xFC68B6ED, 0x67B10C0A, 0x6ABA0203, + 0x7DA71018, 0x70AC1E11, 0x539D342E, 0x5E963A27, 0x498B283C, + 0x44802635, 0x0FE97C42, 0x02E2724B, 0x15FF6050, 0x18F46E59, + 0x3BC54466, 0x36CE4A6F, 0x21D35874, 0x2CD8567D, 0x0C7A37A1, + 0x017139A8, 0x166C2BB3, 0x1B6725BA, 0x38560F85, 0x355D018C, + 0x22401397, 0x2F4B1D9E, 0x642247E9, 0x692949E0, 0x7E345BFB, + 0x733F55F2, 0x500E7FCD, 0x5D0571C4, 0x4A1863DF, 0x47136DD6, + 0xDCCAD731, 0xD1C1D938, 0xC6DCCB23, 0xCBD7C52A, 0xE8E6EF15, + 0xE5EDE11C, 0xF2F0F307, 0xFFFBFD0E, 0xB492A779, 0xB999A970, + 0xAE84BB6B, 0xA38FB562, 0x80BE9F5D, 0x8DB59154, 0x9AA8834F, + 0x97A38D46, + }; + + // Lookup table for row 3 of the inverse mix column transform. + private static final int[] TMI3 = { + 0x00000000, 0x090D0B0E, 0x121A161C, 0x1B171D12, 0x24342C38, + 0x2D392736, 0x362E3A24, 0x3F23312A, 0x48685870, 0x4165537E, + 0x5A724E6C, 0x537F4562, 0x6C5C7448, 0x65517F46, 0x7E466254, + 0x774B695A, 0x90D0B0E0, 0x99DDBBEE, 0x82CAA6FC, 0x8BC7ADF2, + 0xB4E49CD8, 0xBDE997D6, 0xA6FE8AC4, 0xAFF381CA, 0xD8B8E890, + 0xD1B5E39E, 0xCAA2FE8C, 0xC3AFF582, 0xFC8CC4A8, 0xF581CFA6, + 0xEE96D2B4, 0xE79BD9BA, 0x3BBB7BDB, 0x32B670D5, 0x29A16DC7, + 0x20AC66C9, 0x1F8F57E3, 0x16825CED, 0x0D9541FF, 0x04984AF1, + 0x73D323AB, 0x7ADE28A5, 0x61C935B7, 0x68C43EB9, 0x57E70F93, + 0x5EEA049D, 0x45FD198F, 0x4CF01281, 0xAB6BCB3B, 0xA266C035, + 0xB971DD27, 0xB07CD629, 0x8F5FE703, 0x8652EC0D, 0x9D45F11F, + 0x9448FA11, 0xE303934B, 0xEA0E9845, 0xF1198557, 0xF8148E59, + 0xC737BF73, 0xCE3AB47D, 0xD52DA96F, 0xDC20A261, 0x766DF6AD, + 0x7F60FDA3, 0x6477E0B1, 0x6D7AEBBF, 0x5259DA95, 0x5B54D19B, + 0x4043CC89, 0x494EC787, 0x3E05AEDD, 0x3708A5D3, 0x2C1FB8C1, + 0x2512B3CF, 0x1A3182E5, 0x133C89EB, 0x082B94F9, 0x01269FF7, + 0xE6BD464D, 0xEFB04D43, 0xF4A75051, 0xFDAA5B5F, 0xC2896A75, + 0xCB84617B, 0xD0937C69, 0xD99E7767, 0xAED51E3D, 0xA7D81533, + 0xBCCF0821, 0xB5C2032F, 0x8AE13205, 0x83EC390B, 0x98FB2419, + 0x91F62F17, 0x4DD68D76, 0x44DB8678, 0x5FCC9B6A, 0x56C19064, + 0x69E2A14E, 0x60EFAA40, 0x7BF8B752, 0x72F5BC5C, 0x05BED506, + 0x0CB3DE08, 0x17A4C31A, 0x1EA9C814, 0x218AF93E, 0x2887F230, + 0x3390EF22, 0x3A9DE42C, 0xDD063D96, 0xD40B3698, 0xCF1C2B8A, + 0xC6112084, 0xF93211AE, 0xF03F1AA0, 0xEB2807B2, 0xE2250CBC, + 0x956E65E6, 0x9C636EE8, 0x877473FA, 0x8E7978F4, 0xB15A49DE, + 0xB85742D0, 0xA3405FC2, 0xAA4D54CC, 0xECDAF741, 0xE5D7FC4F, + 0xFEC0E15D, 0xF7CDEA53, 0xC8EEDB79, 0xC1E3D077, 0xDAF4CD65, + 0xD3F9C66B, 0xA4B2AF31, 0xADBFA43F, 0xB6A8B92D, 0xBFA5B223, + 0x80868309, 0x898B8807, 0x929C9515, 0x9B919E1B, 0x7C0A47A1, + 0x75074CAF, 0x6E1051BD, 0x671D5AB3, 0x583E6B99, 0x51336097, + 0x4A247D85, 0x4329768B, 0x34621FD1, 0x3D6F14DF, 0x267809CD, + 0x2F7502C3, 0x105633E9, 0x195B38E7, 0x024C25F5, 0x0B412EFB, + 0xD7618C9A, 0xDE6C8794, 0xC57B9A86, 0xCC769188, 0xF355A0A2, + 0xFA58ABAC, 0xE14FB6BE, 0xE842BDB0, 0x9F09D4EA, 0x9604DFE4, + 0x8D13C2F6, 0x841EC9F8, 0xBB3DF8D2, 0xB230F3DC, 0xA927EECE, + 0xA02AE5C0, 0x47B13C7A, 0x4EBC3774, 0x55AB2A66, 0x5CA62168, + 0x63851042, 0x6A881B4C, 0x719F065E, 0x78920D50, 0x0FD9640A, + 0x06D46F04, 0x1DC37216, 0x14CE7918, 0x2BED4832, 0x22E0433C, + 0x39F75E2E, 0x30FA5520, 0x9AB701EC, 0x93BA0AE2, 0x88AD17F0, + 0x81A01CFE, 0xBE832DD4, 0xB78E26DA, 0xAC993BC8, 0xA59430C6, + 0xD2DF599C, 0xDBD25292, 0xC0C54F80, 0xC9C8448E, 0xF6EB75A4, + 0xFFE67EAA, 0xE4F163B8, 0xEDFC68B6, 0x0A67B10C, 0x036ABA02, + 0x187DA710, 0x1170AC1E, 0x2E539D34, 0x275E963A, 0x3C498B28, + 0x35448026, 0x420FE97C, 0x4B02E272, 0x5015FF60, 0x5918F46E, + 0x663BC544, 0x6F36CE4A, 0x7421D358, 0x7D2CD856, 0xA10C7A37, + 0xA8017139, 0xB3166C2B, 0xBA1B6725, 0x8538560F, 0x8C355D01, + 0x97224013, 0x9E2F4B1D, 0xE9642247, 0xE0692949, 0xFB7E345B, + 0xF2733F55, 0xCD500E7F, 0xC45D0571, 0xDF4A1863, 0xD647136D, + 0x31DCCAD7, 0x38D1C1D9, 0x23C6DCCB, 0x2ACBD7C5, 0x15E8E6EF, + 0x1CE5EDE1, 0x07F2F0F3, 0x0EFFFBFD, 0x79B492A7, 0x70B999A9, + 0x6BAE84BB, 0x62A38FB5, 0x5D80BE9F, 0x548DB591, 0x4F9AA883, + 0x4697A38D, + }; + + /** + * Return the block cipher size. + * + * @return the AES block cipher size (in bytes). + */ + int getBlockSize() { + return AESConstants.AES_BLOCK_SIZE; + } + + /** + * Verifies if the length argument is a valid key size or not. + * + * @param len the size of the key (in bytes) to be validated. + * + * @return {@code true} if the size of the key is valid else {@code false}. + */ + static boolean isKeySizeValid(int len) { + for (int kLength : AESConstants.AES_KEYSIZES) { + if (len == kLength) { + return true; + } + } + return false; + } + + /** + * Check algorithm and initalize round keys for decryption and encryption. + * + * @param decrypting [in] indicates if encrypting ({@code false}) or + * decrypting ({@code true}). + * @param algorithm [in] the case insentive string name for the AES cipher. + * @param key [in] the symmetric key byte array for encryption/decryption. + * + * @throws InvalidKeyException if an incorrect algorithm is given or if + * an invalid key size is provided. + */ + void init(boolean decrypting, String algorithm, byte[] key) + throws InvalidKeyException { + int decrypt = decrypting ? 1 : 0; + + if (!algorithm.equalsIgnoreCase("AES") + && !algorithm.equalsIgnoreCase("Rijndael")) { + throw new InvalidKeyException ("Invalid algorithm name."); + } + if (key.length == AESConstants.AES_KEYSIZES[0]) { + rounds = AES_128_ROUNDS; + } else if (key.length == AESConstants.AES_KEYSIZES[1]) { + rounds = AES_192_ROUNDS; + } else if (key.length == AESConstants.AES_KEYSIZES[2]) { + rounds = AES_256_ROUNDS; + } else { + throw new InvalidKeyException("Invalid key length (" + key.length + + ")."); + } + if (!MessageDigest.isEqual(prevKey, key)) { + if (sessionK == null) { + sessionK = new int[2][]; + } else { + Arrays.fill(sessionK[0], 0); + Arrays.fill(sessionK[1], 0); + } + sessionK[0] = genRoundKeys(key, rounds); + sessionK[1] = genInvRoundKeys(sessionK[0], rounds); + if (prevKey != null) { + Arrays.fill(prevKey, (byte) 0); + } + prevKey = key.clone(); + } + K = sessionK[decrypt]; + } + + /** + * Generate the cipher's round keys as outlined in section 5.2 of the spec. + * + * @param key [in] the symmetric key byte array. + * + * @return w the cipher round keys. + */ + private static int[] genRoundKeys(byte[] key, int rounds) { + int wLen = WB * (rounds + 1); + int[] w = new int[wLen]; + int nk = key.length / BW; + + for (int i = 0, j = 0; i < nk; i++, j += BW) { + w[i] = ((key[j] & 0xFF) << 24) | ((key[j + 1] & 0xFF) << 16) + | ((key[j + 2] & 0xFF) << 8) | (key[j + 3] & 0xFF); + } + for (int i = nk; i < wLen; i++) { + int tmp = w[i - 1]; + if (i % nk == 0) { + int rW = (tmp << 8) & 0xFFFFFF00 | (tmp >>> 24); + tmp = subWord(rW) ^ RCON[(i / nk) - 1]; + } else if ((nk > 6) && ((i % nk) == WB)) { + tmp = subWord(tmp); + } + w[i] = w[i - nk] ^ tmp; + } + + return w; + } + + /** + * Generate the inverse cipher round keys. + * + * @return w1 the inverse cipher round keys. + */ + private static int[] genInvRoundKeys(int[] w, int rounds) { + int kLen = w.length;; + int[] temp = new int[WB]; + int[] dw = new int[kLen]; + + // Intrinsics requires the inverse key expansion to be reverse order + // except for the first and last round key as the first two round keys + // are without a mix column transform. + for (int i = 1; i < rounds; i++) { + System.arraycopy(w, i * WB, temp, 0, WB); + temp[0] = TMI0[temp[0] >>> 24] ^ TMI1[(temp[0] >> 16) & 0xFF] + ^ TMI2[(temp[0] >> 8) & 0xFF] ^ TMI3[temp[0] & 0xFF]; + temp[1] = TMI0[temp[1] >>> 24] ^ TMI1[(temp[1] >> 16) & 0xFF] + ^ TMI2[(temp[1] >> 8) & 0xFF] ^ TMI3[temp[1] & 0xFF]; + temp[2] = TMI0[temp[2] >>> 24] ^ TMI1[(temp[2] >> 16) & 0xFF] + ^ TMI2[(temp[2] >> 8) & 0xFF] ^ TMI3[temp[2] & 0xFF]; + temp[3] = TMI0[temp[3] >>> 24] ^ TMI1[(temp[3] >> 16) & 0xFF] + ^ TMI2[(temp[3] >> 8) & 0xFF] ^ TMI3[temp[3] & 0xFF]; + System.arraycopy(temp, 0, dw, kLen - (i * WB), WB); + } + System.arraycopy(w, kLen - WB, dw, WB, WB); + System.arraycopy(w, 0, dw, 0, WB); + Arrays.fill(temp, 0); + + return dw; + } + + /** + * Subtitute the word as a step of key expansion. + * + * @param state [in] the targeted word for substituion. + * @param sub [in] the substitute table for cipher and inverse cipher. + * + * @return the substituted word. + */ + private static int subWord(int word) { + byte b0 = (byte) (word >>> 24); + byte b1 = (byte) ((word >> 16) & 0xFF); + byte b2 = (byte) ((word >> 8) & 0xFF); + byte b3 = (byte) (word & 0xFF); + + return ((SBOX[(b0 & 0xF0) >> 4][b0 & 0x0F] & 0xFF) << 24) + | ((SBOX[(b1 & 0xF0) >> 4][b1 & 0x0F] & 0xFF) << 16) + | ((SBOX[(b2 & 0xF0) >> 4][b2 & 0x0F] & 0xFF) << 8) + | (SBOX[(b3 & 0xF0) >> 4][b3 & 0x0F] & 0xFF); + } + + /** + * Method for one block of encryption. + * + * @param p [in] the plaintext to be encrypted. + * @param po [in] the plaintext offset in the array of bytes. + * @param c [out] the ciphertext output. + * @param co [in] the ciphertext offset in the array of bytes. + */ + @IntrinsicCandidate + private void implEncryptBlock(byte[] p, int po, byte[] c, int co) { + int ti0, ti1, ti2, ti3; + int a0, a1, a2, a3; + int w = K.length - WB; + + a0 = ((p[po] & 0xFF) << 24) ^ ((p[po + 1] & 0xFF) << 16) + ^ ((p[po + 2] & 0xFF) << 8) ^ (p[po + 3] & 0xFF) ^ K[0]; + a1 = ((p[po + 4] & 0xFF) << 24) ^ ((p[po + 5] & 0xFF) << 16) + ^ ((p[po + 6] & 0xFF) << 8) ^ (p[po + 7] & 0xFF) ^ K[1]; + a2 = ((p[po + 8] & 0xFF) << 24) ^ ((p[po + 9] & 0xFF) << 16) + ^ ((p[po + 10] & 0xFF) << 8) ^ (p[po + 11] & 0xFF) ^ K[2]; + a3 = ((p[po + 12] & 0xFF) << 24) ^ ((p[po + 13] & 0xFF) << 16) + ^ ((p[po + 14] & 0xFF) << 8) ^ (p[po + 15] & 0xFF) ^ K[3]; + + ti0 = T0[a0 >>> 24] ^ T1[(a1 >> 16) & 0xFF] + ^ T2[(a2 >> 8) & 0xFF] ^ T3[a3 & 0xFF] ^ K[4]; + ti1 = T0[a1 >>> 24] ^ T1[(a2 >> 16) & 0xFF] + ^ T2[(a3 >> 8) & 0xFF] ^ T3[a0 & 0xFF] ^ K[5]; + ti2 = T0[a2 >>> 24] ^ T1[(a3 >> 16) & 0xFF] + ^ T2[(a0 >> 8) & 0xFF] ^ T3[a1 & 0xFF] ^ K[6]; + ti3 = T0[a3 >>> 24] ^ T1[(a0 >> 16) & 0xFF] + ^ T2[(a1 >> 8) & 0xFF] ^ T3[a2 & 0xFF] ^ K[7]; + + a0 = T0[ti0 >>> 24] ^ T1[(ti1 >> 16) & 0xFF] + ^ T2[(ti2 >> 8) & 0xFF] ^ T3[ti3 & 0xFF] ^ K[8]; + a1 = T0[ti1 >>> 24] ^ T1[(ti2 >> 16) & 0xFF] + ^ T2[(ti3 >> 8) & 0xFF] ^ T3[ti0 & 0xFF] ^ K[9]; + a2 = T0[ti2 >>> 24] ^ T1[(ti3 >> 16) & 0xFF] + ^ T2[(ti0 >> 8) & 0xFF] ^ T3[ti1 & 0xFF] ^ K[10]; + a3 = T0[ti3 >>> 24] ^ T1[(ti0 >> 16) & 0xFF] + ^ T2[(ti1 >> 8) & 0xFF] ^ T3[ti2 & 0xFF] ^ K[11]; + + ti0 = T0[a0 >>> 24] ^ T1[(a1 >> 16) & 0xFF] + ^ T2[(a2 >> 8) & 0xFF] ^ T3[a3 & 0xFF] ^ K[12]; + ti1 = T0[a1 >>> 24] ^ T1[(a2 >> 16) & 0xFF] + ^ T2[(a3 >> 8) & 0xFF] ^ T3[a0 & 0xFF] ^ K[13]; + ti2 = T0[a2 >>> 24] ^ T1[(a3 >> 16) & 0xFF] + ^ T2[(a0 >> 8) & 0xFF] ^ T3[a1 & 0xFF] ^ K[14]; + ti3 = T0[a3 >>> 24] ^ T1[(a0 >> 16) & 0xFF] + ^ T2[(a1 >> 8) & 0xFF] ^ T3[a2 & 0xFF] ^ K[15]; + + a0 = T0[ti0 >>> 24] ^ T1[(ti1 >> 16) & 0xFF] + ^ T2[(ti2 >> 8) & 0xFF] ^ T3[ti3 & 0xFF] ^ K[16]; + a1 = T0[ti1 >>> 24] ^ T1[(ti2 >> 16) & 0xFF] + ^ T2[(ti3 >> 8) & 0xFF] ^ T3[ti0 & 0xFF] ^ K[17]; + a2 = T0[ti2 >>> 24] ^ T1[(ti3 >> 16) & 0xFF] + ^ T2[(ti0 >> 8) & 0xFF] ^ T3[ti1 & 0xFF] ^ K[18]; + a3 = T0[ti3 >>> 24] ^ T1[(ti0 >> 16) & 0xFF] + ^ T2[(ti1 >> 8) & 0xFF] ^ T3[ti2 & 0xFF] ^ K[19]; + + ti0 = T0[a0 >>> 24] ^ T1[(a1 >> 16) & 0xFF] + ^ T2[(a2 >> 8) & 0xFF] ^ T3[a3 & 0xFF] ^ K[20]; + ti1 = T0[a1 >>> 24] ^ T1[(a2 >> 16) & 0xFF] + ^ T2[(a3 >> 8) & 0xFF] ^ T3[a0 & 0xFF] ^ K[21]; + ti2 = T0[a2 >>> 24] ^ T1[(a3 >> 16) & 0xFF] + ^ T2[(a0 >> 8) & 0xFF] ^ T3[a1 & 0xFF] ^ K[22]; + ti3 = T0[a3 >>> 24] ^ T1[(a0 >> 16) & 0xFF] + ^ T2[(a1 >> 8) & 0xFF] ^ T3[a2 & 0xFF] ^ K[23]; + + a0 = T0[ti0 >>> 24] ^ T1[(ti1 >> 16) & 0xFF] + ^ T2[(ti2 >> 8) & 0xFF] ^ T3[ti3 & 0xFF] ^ K[24]; + a1 = T0[ti1 >>> 24] ^ T1[(ti2 >> 16) & 0xFF] + ^ T2[(ti3 >> 8) & 0xFF] ^ T3[ti0 & 0xFF] ^ K[25]; + a2 = T0[ti2 >>> 24] ^ T1[(ti3 >> 16) & 0xFF] + ^ T2[(ti0 >> 8) & 0xFF] ^ T3[ti1 & 0xFF] ^ K[26]; + a3 = T0[ti3 >>> 24] ^ T1[(ti0 >> 16) & 0xFF] + ^ T2[(ti1 >> 8) & 0xFF] ^ T3[ti2 & 0xFF] ^ K[27]; + + ti0 = T0[a0 >>> 24] ^ T1[(a1 >> 16) & 0xFF] + ^ T2[(a2 >> 8) & 0xFF] ^ T3[a3 & 0xFF] ^ K[28]; + ti1 = T0[a1 >>> 24] ^ T1[(a2 >> 16) & 0xFF] + ^ T2[(a3 >> 8) & 0xFF] ^ T3[a0 & 0xFF] ^ K[29]; + ti2 = T0[a2 >>> 24] ^ T1[(a3 >> 16) & 0xFF] + ^ T2[(a0 >> 8) & 0xFF] ^ T3[a1 & 0xFF] ^ K[30]; + ti3 = T0[a3 >>> 24] ^ T1[(a0 >> 16) & 0xFF] + ^ T2[(a1 >> 8) & 0xFF] ^ T3[a2 & 0xFF] ^ K[31]; + + a0 = T0[ti0 >>> 24] ^ T1[(ti1 >> 16) & 0xFF] + ^ T2[(ti2 >> 8) & 0xFF] ^ T3[ti3 & 0xFF] ^ K[32]; + a1 = T0[ti1 >>> 24] ^ T1[(ti2 >> 16) & 0xFF] + ^ T2[(ti3 >> 8) & 0xFF] ^ T3[ti0 & 0xFF] ^ K[33]; + a2 = T0[ti2 >>> 24] ^ T1[(ti3 >> 16) & 0xFF] + ^ T2[(ti0 >> 8) & 0xFF] ^ T3[ti1 & 0xFF] ^ K[34]; + a3 = T0[ti3 >>> 24] ^ T1[(ti0 >> 16) & 0xFF] + ^ T2[(ti1 >> 8) & 0xFF] ^ T3[ti2 & 0xFF] ^ K[35]; + + ti0 = T0[a0 >>> 24] ^ T1[(a1 >> 16) & 0xFF] + ^ T2[(a2 >> 8) & 0xFF] ^ T3[a3 & 0xFF] ^ K[36]; + ti1 = T0[a1 >>> 24] ^ T1[(a2 >> 16) & 0xFF] + ^ T2[(a3 >> 8) & 0xFF] ^ T3[a0 & 0xFF] ^ K[37]; + ti2 = T0[a2 >>> 24] ^ T1[(a3 >> 16) & 0xFF] + ^ T2[(a0 >> 8) & 0xFF] ^ T3[a1 & 0xFF] ^ K[38]; + ti3 = T0[a3 >>> 24] ^ T1[(a0 >> 16) & 0xFF] + ^ T2[(a1 >> 8) & 0xFF] ^ T3[a2 & 0xFF] ^ K[39]; + + if (rounds > AES_128_ROUNDS) { + a0 = T0[ti0 >>> 24] ^ T1[(ti1 >> 16) & 0xFF] + ^ T2[(ti2 >> 8) & 0xFF] ^ T3[ti3 & 0xFF] ^ K[40]; + a1 = T0[ti1 >>> 24] ^ T1[(ti2 >> 16) & 0xFF] + ^ T2[(ti3 >> 8) & 0xFF] ^ T3[ti0 & 0xFF] ^ K[41]; + a2 = T0[ti2 >>> 24] ^ T1[(ti3 >> 16) & 0xFF] + ^ T2[(ti0 >> 8) & 0xFF] ^ T3[ti1 & 0xFF] ^ K[42]; + a3 = T0[ti3 >>> 24] ^ T1[(ti0 >> 16) & 0xFF] + ^ T2[(ti1 >> 8) & 0xFF] ^ T3[ti2 & 0xFF] ^ K[43]; + + ti0 = T0[a0 >>> 24] ^ T1[(a1 >> 16) & 0xFF] + ^ T2[(a2 >> 8) & 0xFF] ^ T3[a3 & 0xFF] ^ K[44]; + ti1 = T0[a1 >>> 24] ^ T1[(a2 >> 16) & 0xFF] + ^ T2[(a3 >> 8) & 0xFF] ^ T3[a0 & 0xFF] ^ K[45]; + ti2 = T0[a2 >>> 24] ^ T1[(a3 >> 16) & 0xFF] + ^ T2[(a0 >> 8) & 0xFF] ^ T3[a1 & 0xFF] ^ K[46]; + ti3 = T0[a3 >>> 24] ^ T1[(a0 >> 16) & 0xFF] + ^ T2[(a1 >> 8) & 0xFF] ^ T3[a2 & 0xFF] ^ K[47]; + } + if (rounds > AES_192_ROUNDS) { + a0 = T0[ti0 >>> 24] ^ T1[(ti1 >> 16) & 0xFF] + ^ T2[(ti2 >> 8) & 0xFF] ^ T3[ti3 & 0xFF] ^ K[48]; + a1 = T0[ti1 >>> 24] ^ T1[(ti2 >> 16) & 0xFF] + ^ T2[(ti3 >> 8) & 0xFF] ^ T3[ti0 & 0xFF] ^ K[49]; + a2 = T0[ti2 >>> 24] ^ T1[(ti3 >> 16) & 0xFF] + ^ T2[(ti0 >> 8) & 0xFF] ^ T3[ti1 & 0xFF] ^ K[50]; + a3 = T0[ti3 >>> 24] ^ T1[(ti0 >> 16) & 0xFF] + ^ T2[(ti1 >> 8) & 0xFF] ^ T3[ti2 & 0xFF] ^ K[51]; + + ti0 = T0[a0 >>> 24] ^ T1[(a1 >> 16) & 0xFF] + ^ T2[(a2 >> 8) & 0xFF] ^ T3[a3 & 0xFF] ^ K[52]; + ti1 = T0[a1 >>> 24] ^ T1[(a2 >> 16) & 0xFF] + ^ T2[(a3 >> 8) & 0xFF] ^ T3[a0 & 0xFF] ^ K[53]; + ti2 = T0[a2 >>> 24] ^ T1[(a3 >> 16) & 0xFF] + ^ T2[(a0 >> 8) & 0xFF] ^ T3[a1 & 0xFF] ^ K[54]; + ti3 = T0[a3 >>> 24] ^ T1[(a0 >> 16) & 0xFF] + ^ T2[(a1 >> 8) & 0xFF] ^ T3[a2 & 0xFF] ^ K[55]; + } + + a0 = T2[ti0 >>> 24] & 0xFF000000 + ^ T3[(ti1 >> 16) & 0xFF] & 0xFF0000 + ^ T0[(ti2 >> 8) & 0xFF] & 0xFF00 + ^ T1[ti3 & 0xFF] & 0xFF ^ K[w]; + a1 = T2[ti1 >>> 24] & 0xFF000000 + ^ T3[(ti2 >> 16) & 0xFF] & 0xFF0000 + ^ T0[(ti3 >> 8) & 0xFF] & 0xFF00 + ^ T1[ti0 & 0xFF] & 0xFF ^ K[w + 1]; + a2 = T2[ti2 >>> 24] & 0xFF000000 + ^ T3[(ti3 >> 16) & 0xFF] & 0xFF0000 + ^ T0[(ti0 >> 8) & 0xFF] & 0xFF00 + ^ T1[ti1 & 0xFF] & 0xFF ^ K[w + 2]; + a3 = T2[ti3 >>> 24] & 0xFF000000 + ^ T3[(ti0 >> 16) & 0xFF] & 0xFF0000 + ^ T0[(ti1 >> 8) & 0xFF] & 0xFF00 + ^ T1[ti2 & 0xFF] & 0xFF ^ K[w + 3]; + + c[co] = (byte) (a0 >>> 24); + c[co + 1] = (byte) ((a0 >> 16) & 0xFF); + c[co + 2] = (byte) ((a0 >> 8) & 0xFF); + c[co + 3] = (byte) (a0 & 0xFF); + c[co + 4] = (byte) (a1 >>> 24); + c[co + 5] = (byte) ((a1 >> 16) & 0xFF); + c[co + 6] = (byte) ((a1 >> 8) & 0xFF); + c[co + 7] = (byte) (a1 & 0xFF); + c[co + 8] = (byte) (a2 >>> 24); + c[co + 9] = (byte) ((a2 >> 16) & 0xFF); + c[co + 10] = (byte) ((a2 >> 8) & 0xFF); + c[co + 11] = (byte) (a2 & 0xFF); + c[co + 12] = (byte) (a3 >> 24); + c[co + 13] = (byte) ((a3 >> 16) & 0xFF); + c[co + 14] = (byte) ((a3 >> 8) & 0xFF); + c[co + 15] = (byte) (a3 & 0xFF); + } + + /** + * Method for one block of decryption. + * + * @param c [in] the ciphertext to be decrypted. + * @param co [in] the ciphertext offset in the array of bytes. + * @param p [out] the plaintext output. + * @param po [in] the plaintext offset in the array of bytes. + */ + @IntrinsicCandidate + private void implDecryptBlock(byte[] c, int co, byte[] p, int po) { + int ti0, ti1, ti2, ti3; + int a0, a1, a2, a3; + + ti0 = ((c[co] & 0xFF) << 24) ^ ((c[co + 1] & 0xFF) << 16) + ^ ((c[co + 2] & 0xFF) << 8) ^ (c[co + 3] & 0xFF) ^ K[4]; + ti1 = ((c[co + 4] & 0xFF) << 24) ^ ((c[co + 5] & 0xFF) << 16) + ^ ((c[co + 6] & 0xFF) << 8) ^ (c[co + 7] & 0xFF) ^ K[5]; + ti2 = ((c[co + 8] & 0xFF) << 24) ^ ((c[co + 9] & 0xFF) << 16) + ^ ((c[co + 10] & 0xFF) << 8) ^ (c[co + 11] & 0xFF) ^ K[6]; + ti3 = ((c[co + 12] & 0xFF) << 24) ^ ((c[co + 13] & 0xFF) << 16) + ^ ((c[co + 14] & 0xFF) << 8) ^ (c[co + 15] & 0xFF) ^ K[7]; + + a0 = TI0[ti0 >>> 24] ^ TI1[(ti3 >> 16) & 0xFF] + ^ TI2[(ti2 >> 8) & 0xFF] ^ TI3[ti1 & 0xFF] ^ K[8]; + a1 = TI0[ti1 >>> 24] ^ TI1[(ti0 >> 16) & 0xFF] + ^ TI2[(ti3 >> 8) & 0xFF] ^ TI3[ti2 & 0xFF] ^ K[9]; + a2 = TI0[ti2 >>> 24] ^ TI1[(ti1 >> 16) & 0xFF] + ^ TI2[(ti0 >> 8) & 0xFF] ^ TI3[ti3 & 0xFF] ^ K[10]; + a3 = TI0[ti3 >>> 24] ^ TI1[(ti2 >> 16) & 0xFF] + ^ TI2[(ti1 >> 8) & 0xFF] ^ TI3[ti0 & 0xFF] ^ K[11]; + + ti0 = TI0[a0 >>> 24] ^ TI1[(a3 >> 16) & 0xFF] + ^ TI2[(a2 >> 8) & 0xFF] ^ TI3[a1 & 0xFF] ^ K[12]; + ti1 = TI0[a1 >>> 24] ^ TI1[(a0 >> 16) & 0xFF] + ^ TI2[(a3 >> 8) & 0xFF] ^ TI3[a2 & 0xFF] ^ K[13]; + ti2 = TI0[a2 >>> 24] ^ TI1[(a1 >> 16) & 0xFF] + ^ TI2[(a0 >> 8) & 0xFF] ^ TI3[a3 & 0xFF] ^ K[14]; + ti3 = TI0[a3 >>> 24] ^ TI1[(a2 >> 16) & 0xFF] + ^ TI2[(a1 >> 8) & 0xFF] ^ TI3[a0 & 0xFF] ^ K[15]; + + a0 = TI0[ti0 >>> 24] ^ TI1[(ti3 >> 16) & 0xFF] + ^ TI2[(ti2 >> 8) & 0xFF] ^ TI3[ti1 & 0xFF] ^ K[16]; + a1 = TI0[ti1 >>> 24] ^ TI1[(ti0 >> 16) & 0xFF] + ^ TI2[(ti3 >> 8) & 0xFF] ^ TI3[ti2 & 0xFF] ^ K[17]; + a2 = TI0[ti2 >>> 24] ^ TI1[(ti1 >> 16) & 0xFF] + ^ TI2[(ti0 >> 8) & 0xFF] ^ TI3[ti3 & 0xFF] ^ K[18]; + a3 = TI0[ti3 >>> 24] ^ TI1[(ti2 >> 16) & 0xFF] + ^ TI2[(ti1 >> 8) & 0xFF] ^ TI3[ti0 & 0xFF] ^ K[19]; + + ti0 = TI0[a0 >>> 24] ^ TI1[(a3 >> 16) & 0xFF] + ^ TI2[(a2 >> 8) & 0xFF] ^ TI3[a1 & 0xFF] ^ K[20]; + ti1 = TI0[a1 >>> 24] ^ TI1[(a0 >> 16) & 0xFF] + ^ TI2[(a3 >> 8) & 0xFF] ^ TI3[a2 & 0xFF] ^ K[21]; + ti2 = TI0[a2 >>> 24] ^ TI1[(a1 >> 16) & 0xFF] + ^ TI2[(a0 >> 8) & 0xFF] ^ TI3[a3 & 0xFF] ^ K[22]; + ti3 = TI0[a3 >>> 24] ^ TI1[(a2 >> 16) & 0xFF] + ^ TI2[(a1 >> 8) & 0xFF] ^ TI3[a0 & 0xFF] ^ K[23]; + + a0 = TI0[ti0 >>> 24] ^ TI1[(ti3 >> 16) & 0xFF] + ^ TI2[(ti2 >> 8) & 0xFF] ^ TI3[ti1 & 0xFF] ^ K[24]; + a1 = TI0[ti1 >>> 24] ^ TI1[(ti0 >> 16) & 0xFF] + ^ TI2[(ti3 >> 8) & 0xFF] ^ TI3[ti2 & 0xFF] ^ K[25]; + a2 = TI0[ti2 >>> 24] ^ TI1[(ti1 >> 16) & 0xFF] + ^ TI2[(ti0 >> 8) & 0xFF] ^ TI3[ti3 & 0xFF] ^ K[26]; + a3 = TI0[ti3 >>> 24] ^ TI1[(ti2 >> 16) & 0xFF] + ^ TI2[(ti1 >> 8) & 0xFF] ^ TI3[ti0 & 0xFF] ^ K[27]; + + ti0 = TI0[a0 >>> 24] ^ TI1[(a3 >> 16) & 0xFF] + ^ TI2[(a2 >> 8) & 0xFF] ^ TI3[a1 & 0xFF] ^ K[28]; + ti1 = TI0[a1 >>> 24] ^ TI1[(a0 >> 16) & 0xFF] + ^ TI2[(a3 >> 8) & 0xFF] ^ TI3[a2 & 0xFF] ^ K[29]; + ti2 = TI0[a2 >>> 24] ^ TI1[(a1 >> 16) & 0xFF] + ^ TI2[(a0 >> 8) & 0xFF] ^ TI3[a3 & 0xFF] ^ K[30]; + ti3 = TI0[a3 >>> 24] ^ TI1[(a2 >> 16) & 0xFF] + ^ TI2[(a1 >> 8) & 0xFF] ^ TI3[a0 & 0xFF] ^ K[31]; + + a0 = TI0[ti0 >>> 24] ^ TI1[(ti3 >> 16) & 0xFF] + ^ TI2[(ti2 >> 8) & 0xFF] ^ TI3[ti1 & 0xFF] ^ K[32]; + a1 = TI0[ti1 >>> 24] ^ TI1[(ti0 >> 16) & 0xFF] + ^ TI2[(ti3 >> 8) & 0xFF] ^ TI3[ti2 & 0xFF] ^ K[33]; + a2 = TI0[ti2 >>> 24] ^ TI1[(ti1 >> 16) & 0xFF] + ^ TI2[(ti0 >> 8) & 0xFF] ^ TI3[ti3 & 0xFF] ^ K[34]; + a3 = TI0[ti3 >>> 24] ^ TI1[(ti2 >> 16) & 0xFF] + ^ TI2[(ti1 >> 8) & 0xFF] ^ TI3[ti0 & 0xFF] ^ K[35]; + + ti0 = TI0[a0 >>> 24] ^ TI1[(a3 >> 16) & 0xFF] + ^ TI2[(a2 >> 8) & 0xFF] ^ TI3[a1 & 0xFF] ^ K[36]; + ti1 = TI0[a1 >>> 24] ^ TI1[(a0 >> 16) & 0xFF] + ^ TI2[(a3 >> 8) & 0xFF] ^ TI3[a2 & 0xFF] ^ K[37]; + ti2 = TI0[a2 >>> 24] ^ TI1[(a1 >> 16) & 0xFF] + ^ TI2[(a0 >> 8) & 0xFF] ^ TI3[a3 & 0xFF] ^ K[38]; + ti3 = TI0[a3 >>> 24] ^ TI1[(a2 >> 16) & 0xFF] + ^ TI2[(a1 >> 8) & 0xFF] ^ TI3[a0 & 0xFF] ^ K[39]; + + a0 = TI0[ti0 >>> 24] ^ TI1[(ti3 >> 16) & 0xFF] + ^ TI2[(ti2 >> 8) & 0xFF] ^ TI3[ti1 & 0xFF] ^ K[40]; + a1 = TI0[ti1 >>> 24] ^ TI1[(ti0 >> 16) & 0xFF] + ^ TI2[(ti3 >> 8) & 0xFF] ^ TI3[ti2 & 0xFF] ^ K[41]; + a2 = TI0[ti2 >>> 24] ^ TI1[(ti1 >> 16) & 0xFF] + ^ TI2[(ti0 >> 8) & 0xFF] ^ TI3[ti3 & 0xFF] ^ K[42]; + a3 = TI0[ti3 >>> 24] ^ TI1[(ti2 >> 16) & 0xFF] + ^ TI2[(ti1 >> 8) & 0xFF] ^ TI3[ti0 & 0xFF] ^ K[43]; + + if (rounds > AES_128_ROUNDS) { + ti0 = TI0[a0 >>> 24] ^ TI1[(a3 >> 16) & 0xFF] + ^ TI2[(a2 >> 8) & 0xFF] ^ TI3[a1 & 0xFF] ^ K[44]; + ti1 = TI0[a1 >>> 24] ^ TI1[(a0 >> 16) & 0xFF] + ^ TI2[(a3 >> 8) & 0xFF] ^ TI3[a2 & 0xFF] ^ K[45]; + ti2 = TI0[a2 >>> 24] ^ TI1[(a1 >> 16) & 0xFF] + ^ TI2[(a0 >> 8) & 0xFF] ^ TI3[a3 & 0xFF] ^ K[46]; + ti3 = TI0[a3 >>> 24] ^ TI1[(a2 >> 16) & 0xFF] + ^ TI2[(a1 >> 8) & 0xFF] ^ TI3[a0 & 0xFF] ^ K[47]; + + a0 = TI0[ti0 >>> 24] ^ TI1[(ti3 >> 16) & 0xFF] + ^ TI2[(ti2 >> 8) & 0xFF] ^ TI3[ti1 & 0xFF] ^ K[48]; + a1 = TI0[ti1 >>> 24] ^ TI1[(ti0 >> 16) & 0xFF] + ^ TI2[(ti3 >> 8) & 0xFF] ^ TI3[ti2 & 0xFF] ^ K[49]; + a2 = TI0[ti2 >>> 24] ^ TI1[(ti1 >> 16) & 0xFF] + ^ TI2[(ti0 >> 8) & 0xFF] ^ TI3[ti3 & 0xFF] ^ K[50]; + a3 = TI0[ti3 >>> 24] ^ TI1[(ti2 >> 16) & 0xFF] + ^ TI2[(ti1 >> 8) & 0xFF] ^ TI3[ti0 & 0xFF] ^ K[51]; + } + if (rounds > AES_192_ROUNDS) { + ti0 = TI0[a0 >>> 24] ^ TI1[(a3 >> 16) & 0xFF] + ^ TI2[(a2 >> 8) & 0xFF] ^ TI3[a1 & 0xFF] ^ K[52]; + ti1 = TI0[a1 >>> 24] ^ TI1[(a0 >> 16) & 0xFF] + ^ TI2[(a3 >> 8) & 0xFF] ^ TI3[a2 & 0xFF] ^ K[53]; + ti2 = TI0[a2 >>> 24] ^ TI1[(a1 >> 16) & 0xFF] + ^ TI2[(a0 >> 8) & 0xFF] ^ TI3[a3 & 0xFF] ^ K[54]; + ti3 = TI0[a3 >>> 24] ^ TI1[(a2 >> 16) & 0xFF] + ^ TI2[(a1 >> 8) & 0xFF] ^ TI3[a0 & 0xFF] ^ K[55]; + + a0 = TI0[ti0 >>> 24] ^ TI1[(ti3 >> 16) & 0xFF] + ^ TI2[(ti2 >> 8) & 0xFF] ^ TI3[ti1 & 0xFF] ^ K[56]; + a1 = TI0[ti1 >>> 24] ^ TI1[(ti0 >> 16) & 0xFF] + ^ TI2[(ti3 >> 8) & 0xFF] ^ TI3[ti2 & 0xFF] ^ K[57]; + a2 = TI0[ti2 >>> 24] ^ TI1[(ti1 >> 16) & 0xFF] + ^ TI2[(ti0 >> 8) & 0xFF] ^ TI3[ti3 & 0xFF] ^ K[58]; + a3 = TI0[ti3 >>> 24] ^ TI1[(ti2 >> 16) & 0xFF] + ^ TI2[(ti1 >> 8) & 0xFF] ^ TI3[ti0 & 0xFF] ^ K[59]; + } + + ti0 = TI4[a0 >>> 24] & 0xFF000000 ^ TI4[(a3 >> 16) & 0xFF] & 0xFF0000 + ^ TI4[(a2 >> 8) & 0xFF] & 0xFF00 ^ TI4[a1 & 0xFF] & 0xFF ^ K[0]; + ti1 = TI4[a1 >>> 24] & 0xFF000000 ^ TI4[(a0 >> 16) & 0xFF] & 0xFF0000 + ^ TI4[(a3 >> 8) & 0xFF] & 0xFF00 ^ TI4[a2 & 0xFF] & 0xFF ^ K[1]; + ti2 = TI4[a2 >>> 24] & 0xFF000000 ^ TI4[(a1 >> 16) & 0xFF] & 0xFF0000 + ^ TI4[(a0 >> 8) & 0xFF] & 0xFF00 ^ TI4[a3 & 0xFF] & 0xFF ^ K[2]; + ti3 = TI4[a3 >>> 24] & 0xFF000000 ^ TI4[(a2 >> 16) & 0xFF] & 0xFF0000 + ^ TI4[(a1 >> 8) & 0xFF] & 0xFF00 ^ TI4[a0 & 0xFF] & 0xFF ^ K[3]; + + p[po] = (byte) (ti0 >>> 24); + p[po + 1] = (byte) ((ti0 >> 16) & 0xFF); + p[po + 2] = (byte) ((ti0 >> 8) & 0xFF); + p[po + 3] = (byte) (ti0 & 0xFF); + p[po + 4] = (byte) (ti1 >>> 24); + p[po + 5] = (byte) ((ti1 >> 16) & 0xFF); + p[po + 6] = (byte) ((ti1 >> 8) & 0xFF); + p[po + 7] = (byte) (ti1 & 0xFF); + p[po + 8] = (byte) (ti2 >>> 24); + p[po + 9] = (byte) ((ti2 >> 16) & 0xFF); + p[po + 10] = (byte) ((ti2 >> 8) & 0xFF); + p[po + 11] = (byte) (ti2 & 0xFF); + p[po + 12] = (byte) (ti3 >>> 24); + p[po + 13] = (byte) ((ti3 >> 16) & 0xFF); + p[po + 14] = (byte) ((ti3 >> 8) & 0xFF); + p[po + 15] = (byte) (ti3 & 0xFF); + } + + /** + * Method for one block of encryption. + * + * @param plain [in] the plaintext to be encrypted. + * @param pOff [in] the plaintext offset in the array of bytes. + * @param cipher [out] the encrypted ciphertext output. + * @param cOff [in] the ciphertext offset in the array of bytes. + */ + void encryptBlock(byte[] plain, int pOff, byte[] cipher, int cOff) { + implEncryptBlock(plain, pOff, cipher, cOff); + } + + /** + * Method for one block of decryption. + * + * @param cipher [in] the ciphertext to be decrypted. + * @param cOff [in] the ciphertext offset in the array of bytes. + * @param plain [out] the decrypted plaintext output. + * @param pOff [in] the plaintext offset in the array of bytes. + */ + void decryptBlock(byte[] cipher, int cOff, byte[] plain, int pOff) { + implDecryptBlock(cipher, cOff, plain, pOff); + } +} diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java b/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java index d86d161370f..b27320ed24b 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java @@ -78,14 +78,21 @@ public class DHKEM implements KEMSpi { byte[] pkEm = params.SerializePublicKey(pkE); byte[] pkRm = params.SerializePublicKey(pkR); byte[] kem_context = concat(pkEm, pkRm); + byte[] key = null; try { byte[] dh = params.DH(skE, pkR); - byte[] key = params.ExtractAndExpand(dh, kem_context); + key = params.ExtractAndExpand(dh, kem_context); return new KEM.Encapsulated( new SecretKeySpec(key, from, to - from, algorithm), pkEm, null); } catch (Exception e) { throw new ProviderException("internal error", e); + } finally { + // `key` has been cloned into the `SecretKeySpec` within the + // returned `KEM.Encapsulated`, so it can now be cleared. + if (key != null) { + Arrays.fill(key, (byte)0); + } } } @@ -98,17 +105,22 @@ public class DHKEM implements KEMSpi { if (encapsulation.length != params.Npk) { throw new DecapsulateException("incorrect encapsulation size"); } + byte[] key = null; try { PublicKey pkE = params.DeserializePublicKey(encapsulation); byte[] dh = params.DH(skR, pkE); byte[] pkRm = params.SerializePublicKey(pkR); byte[] kem_context = concat(encapsulation, pkRm); - byte[] key = params.ExtractAndExpand(dh, kem_context); + key = params.ExtractAndExpand(dh, kem_context); return new SecretKeySpec(key, from, to - from, algorithm); } catch (IOException | InvalidKeyException e) { throw new DecapsulateException("Cannot decapsulate", e); } catch (Exception e) { throw new ProviderException("internal error", e); + } finally { + if (key != null) { + Arrays.fill(key, (byte)0); + } } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/GCTR.java b/src/java.base/share/classes/com/sun/crypto/provider/GCTR.java index 926a56c140b..3f3373860f3 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/GCTR.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/GCTR.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 @@ -40,7 +40,7 @@ import java.util.Arrays; * to 16 bytes. * * If any invariant is broken, failures can occur because the - * AESCrypt.encryptBlock method can be intrinsified on the HotSpot VM + * AES_Crypt.encryptBlock method can be intrinsified on the HotSpot VM * (see JDK-8067648 for details). * * The counter mode operations can be intrinsified and parallelized diff --git a/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java b/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java index 3d1396121ad..2f4df1eba03 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java @@ -102,7 +102,7 @@ abstract class GaloisCounterMode extends CipherSpi { /** * * @param keySize length of key. - * @param embeddedCipher Cipher object, such as AESCrypt. + * @param embeddedCipher Cipher object, such as AES_Crypt. */ GaloisCounterMode(int keySize, SymmetricCipher embeddedCipher) { blockCipher = embeddedCipher; @@ -198,7 +198,7 @@ abstract class GaloisCounterMode extends CipherSpi { protected int engineGetKeySize(Key key) throws InvalidKeyException { byte[] encoded = key.getEncoded(); Arrays.fill(encoded, (byte)0); - if (!AESCrypt.isKeySizeValid(encoded.length)) { + if (!AES_Crypt.isKeySizeValid(encoded.length)) { throw new InvalidKeyException("Invalid key length: " + encoded.length + " bytes"); } @@ -1693,25 +1693,25 @@ abstract class GaloisCounterMode extends CipherSpi { public static final class AESGCM extends GaloisCounterMode { public AESGCM() { - super(-1, new AESCrypt()); + super(-1, new AES_Crypt()); } } public static final class AES128 extends GaloisCounterMode { public AES128() { - super(16, new AESCrypt()); + super(16, new AES_Crypt()); } } public static final class AES192 extends GaloisCounterMode { public AES192() { - super(24, new AESCrypt()); + super(24, new AES_Crypt()); } } public static final class AES256 extends GaloisCounterMode { public AES256() { - super(32, new AESCrypt()); + super(32, new AES_Crypt()); } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/HKDFKeyDerivation.java b/src/java.base/share/classes/com/sun/crypto/provider/HKDFKeyDerivation.java index 9b4cf557598..5b39339778a 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/HKDFKeyDerivation.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/HKDFKeyDerivation.java @@ -25,6 +25,8 @@ package com.sun.crypto.provider; +import jdk.internal.access.SharedSecrets; + import javax.crypto.KDFSpi; import javax.crypto.Mac; import javax.crypto.SecretKey; @@ -123,8 +125,12 @@ abstract class HKDFKeyDerivation extends KDFSpi { + "empty"); } - return new SecretKeySpec(engineDeriveData(derivationSpec), alg); - + var data = engineDeriveData(derivationSpec); + try { + return new SecretKeySpec(data, alg); + } finally { + Arrays.fill(data, (byte)0); + } } /** @@ -346,7 +352,7 @@ abstract class HKDFKeyDerivation extends KDFSpi { "prk must be at least " + hmacLen + " bytes"); } - SecretKey pseudoRandomKey = new SecretKeySpec(prk, hmacAlgName); + SecretKeySpec pseudoRandomKey = new SecretKeySpec(prk, hmacAlgName); Mac hmacObj = Mac.getInstance(hmacAlgName); @@ -382,6 +388,9 @@ abstract class HKDFKeyDerivation extends KDFSpi { // sized the buffers to their largest possible size up-front, // but just in case... throw new ProviderException(sbe); + } finally { + SharedSecrets.getJavaxCryptoSpecAccess() + .clearSecretKeySpec(pseudoRandomKey); } return kdfOutput; } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/HmacCore.java b/src/java.base/share/classes/com/sun/crypto/provider/HmacCore.java index c6707fb9941..e841243c11b 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/HmacCore.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/HmacCore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, 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 @@ -233,6 +233,7 @@ abstract class HmacCore extends MacSpi implements Cloneable { md.update(tmp); md.digest(tmp, 0, tmp.length); + md.reset(); return tmp; } catch (DigestException e) { // should never occur diff --git a/src/java.base/share/classes/com/sun/crypto/provider/KeyWrapCipher.java b/src/java.base/share/classes/com/sun/crypto/provider/KeyWrapCipher.java index ba2825fa36c..e0c1873213b 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/KeyWrapCipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/KeyWrapCipher.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 @@ -137,7 +137,7 @@ abstract class KeyWrapCipher extends CipherSpi { } int keyLen = keyBytes.length; if (!key.getAlgorithm().equalsIgnoreCase("AES") || - !AESCrypt.isKeySizeValid(keyLen) || + !AES_Crypt.isKeySizeValid(keyLen) || (fixedKeySize != -1 && fixedKeySize != keyLen)) { throw new InvalidKeyException("Invalid key length: " + keyLen + " bytes"); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java b/src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java index 6d2c4da3b8b..df37da2347f 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, 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 @@ -68,7 +68,7 @@ abstract class PBES2Core extends CipherSpi { if (cipherAlgo.equals("AES")) { blkSize = AESConstants.AES_BLOCK_SIZE; - cipher = new CipherCore(new AESCrypt(), blkSize); + cipher = new CipherCore(new AES_Crypt(), blkSize); switch(kdfAlgo) { case "HmacSHA1": diff --git a/src/java.base/share/classes/com/sun/crypto/provider/SymmetricCipher.java b/src/java.base/share/classes/com/sun/crypto/provider/SymmetricCipher.java index 7b428f86aa7..ad93679140c 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/SymmetricCipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/SymmetricCipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -36,7 +36,7 @@ import java.security.InvalidKeyException; * @author Jan Luehe * * - * @see AESCrypt + * @see AES_Crypt * @see DESCrypt * @see DESedeCrypt * @see BlowfishCrypt diff --git a/src/java.base/share/classes/java/io/PrintStream.java b/src/java.base/share/classes/java/io/PrintStream.java index 0c1dd20e668..5d8e2b167c9 100644 --- a/src/java.base/share/classes/java/io/PrintStream.java +++ b/src/java.base/share/classes/java/io/PrintStream.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 @@ -508,9 +508,6 @@ public class PrintStream extends FilterOutputStream out.flush(); } } - catch (InterruptedIOException x) { - Thread.currentThread().interrupt(); - } catch (IOException x) { trouble = true; } @@ -541,9 +538,6 @@ public class PrintStream extends FilterOutputStream out.flush(); } } - catch (InterruptedIOException x) { - Thread.currentThread().interrupt(); - } catch (IOException x) { trouble = true; } @@ -626,8 +620,6 @@ public class PrintStream extends FilterOutputStream } } } - } catch (InterruptedIOException x) { - Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } @@ -649,9 +641,6 @@ public class PrintStream extends FilterOutputStream out.flush(); } } - catch (InterruptedIOException x) { - Thread.currentThread().interrupt(); - } catch (IOException x) { trouble = true; } @@ -668,9 +657,6 @@ public class PrintStream extends FilterOutputStream out.flush(); } } - catch (InterruptedIOException x) { - Thread.currentThread().interrupt(); - } catch (IOException x) { trouble = true; } @@ -692,9 +678,6 @@ public class PrintStream extends FilterOutputStream out.flush(); } } - catch (InterruptedIOException x) { - Thread.currentThread().interrupt(); - } catch (IOException x) { trouble = true; } @@ -711,9 +694,6 @@ public class PrintStream extends FilterOutputStream out.flush(); } } - catch (InterruptedIOException x) { - Thread.currentThread().interrupt(); - } catch (IOException x) { trouble = true; } @@ -1182,8 +1162,6 @@ public class PrintStream extends FilterOutputStream formatter = new Formatter((Appendable) this); formatter.format(Locale.getDefault(Locale.Category.FORMAT), format, args); } - } catch (InterruptedIOException x) { - Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } @@ -1238,8 +1216,6 @@ public class PrintStream extends FilterOutputStream formatter = new Formatter(this, l); formatter.format(l, format, args); } - } catch (InterruptedIOException x) { - Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } diff --git a/src/java.base/share/classes/java/io/PrintWriter.java b/src/java.base/share/classes/java/io/PrintWriter.java index dd6deb75ab7..d09a1b63798 100644 --- a/src/java.base/share/classes/java/io/PrintWriter.java +++ b/src/java.base/share/classes/java/io/PrintWriter.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 @@ -460,8 +460,6 @@ public class PrintWriter extends Writer { try { ensureOpen(); out.write(c); - } catch (InterruptedIOException x) { - Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } @@ -484,8 +482,6 @@ public class PrintWriter extends Writer { try { ensureOpen(); out.write(buf, off, len); - } catch (InterruptedIOException x) { - Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } @@ -517,8 +513,6 @@ public class PrintWriter extends Writer { try { ensureOpen(); out.write(s, off, len); - } catch (InterruptedIOException x) { - Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } @@ -541,8 +535,6 @@ public class PrintWriter extends Writer { out.write(System.lineSeparator()); if (autoFlush) out.flush(); - } catch (InterruptedIOException x) { - Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } @@ -973,8 +965,6 @@ public class PrintWriter extends Writer { formatter.format(Locale.getDefault(), format, args); if (autoFlush) out.flush(); - } catch (InterruptedIOException x) { - Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } @@ -1032,8 +1022,6 @@ public class PrintWriter extends Writer { formatter.format(l, format, args); if (autoFlush) out.flush(); - } catch (InterruptedIOException x) { - Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } 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 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence 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 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence * @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 accd448a0cd..d9913e354a4 100644 --- a/src/java.base/share/classes/java/lang/Byte.java +++ b/src/java.base/share/classes/java/lang/Byte.java @@ -55,8 +55,6 @@ import static java.lang.constant.ConstantDescs.DEFAULT_NAME; * use instances for synchronization, or unpredictable behavior may * occur. For example, in a future release, synchronization may fail. * - * @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 661a0ceb42b..53b027dd773 100644 --- a/src/java.base/share/classes/java/lang/Double.java +++ b/src/java.base/share/classes/java/lang/Double.java @@ -1,5 +1,6 @@ /* * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. * 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 +34,7 @@ import java.util.Optional; import jdk.internal.math.FloatingDecimal; import jdk.internal.math.DoubleConsts; import jdk.internal.math.DoubleToDecimal; +import jdk.internal.util.DecimalDigits; import jdk.internal.vm.annotation.IntrinsicCandidate; /** @@ -352,9 +354,6 @@ import jdk.internal.vm.annotation.IntrinsicCandidate; * @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.ValueBased @@ -695,7 +694,6 @@ public final class Double extends Number * @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) { /* @@ -703,56 +701,80 @@ public final class Double extends Number * 7.19.6.1; however, the output of this method is more * tightly specified. */ - if (!isFinite(d) ) + if (!isFinite(d)) { // For infinity and NaN, use the decimal output. return Double.toString(d); - else { - // Initialized to maximum size of output. - StringBuilder answer = new StringBuilder(24); - - if (Math.copySign(1.0, d) == -1.0) // value is negative, - answer.append("-"); // so append sign info - - answer.append("0x"); - - d = Math.abs(d); - - if(d == 0.0) { - answer.append("0.0p0"); - } else { - boolean subnormal = (d < Double.MIN_NORMAL); - - // Isolate significand bits and OR in a high-order bit - // so that the string representation has a known - // length. - long signifBits = (Double.doubleToLongBits(d) - & DoubleConsts.SIGNIF_BIT_MASK) | - 0x1000000000000000L; - - // Subnormal values have a 0 implicit bit; normal - // values have a 1 implicit bit. - answer.append(subnormal ? "0." : "1."); - - // Isolate the low-order 13 digits of the hex - // representation. If all the digits are zero, - // replace with a single 0; otherwise, remove all - // trailing zeros. - String signif = Long.toHexString(signifBits).substring(3,16); - answer.append(signif.equals("0000000000000") ? // 13 zeros - "0": - signif.replaceFirst("0{1,12}$", "")); - - answer.append('p'); - // If the value is subnormal, use the E_min exponent - // value for double; otherwise, extract and report d's - // exponent (the representation of a subnormal uses - // E_min -1). - answer.append(subnormal ? - Double.MIN_EXPONENT: - Math.getExponent(d)); - } - return answer.toString(); } + + long doubleToLongBits = Double.doubleToLongBits(d); + boolean negative = doubleToLongBits < 0; + + if (d == 0.0) { + return negative ? "-0x0.0p0" : "0x0.0p0"; + } + d = Math.abs(d); + // Check if the value is subnormal (less than the smallest normal value) + boolean subnormal = d < Double.MIN_NORMAL; + + // Isolate significand bits and OR in a high-order bit + // so that the string representation has a known length. + // This ensures we always have 13 hex digits to work with (52 bits / 4 bits per hex digit) + long signifBits = doubleToLongBits & DoubleConsts.SIGNIF_BIT_MASK; + + // Calculate the number of trailing zeros in the significand (in groups of 4 bits) + // This is used to remove trailing zeros from the hex representation + // We limit to 12 because we want to keep at least 1 hex digit (13 total - 12 = 1) + // assert 0 <= trailingZeros && trailingZeros <= 12 + int trailingZeros = Long.numberOfTrailingZeros(signifBits | 1L << 4 * 12) >> 2; + + // Determine the exponent value based on whether the number is subnormal or normal + // Subnormal numbers use the minimum exponent, normal numbers use the actual exponent + int exp = subnormal ? Double.MIN_EXPONENT : Math.getExponent(d); + + // Calculate the total length of the resulting string: + // Sign (optional) + prefix "0x" + implicit bit + "." + hex digits + "p" + exponent + int charlen = (negative ? 1 : 0) // sign character + + 4 // "0x1." or "0x0." + + 13 - trailingZeros // hex digits (13 max, minus trailing zeros) + + 1 // "p" + + DecimalDigits.stringSize(exp) // exponent + ; + + // Create a byte array to hold the result characters + byte[] chars = new byte[charlen]; + int index = 0; + + // Add the sign character if the number is negative + if (negative) { // value is negative + chars[index++] = '-'; + } + + // Add the prefix and the implicit bit ('1' for normal, '0' for subnormal) + // Subnormal values have a 0 implicit bit; normal values have a 1 implicit bit. + chars[index ] = '0'; // Hex prefix + chars[index + 1] = 'x'; // Hex prefix + chars[index + 2] = (byte) (subnormal ? '0' : '1'); // Implicit bit + chars[index + 3] = '.'; // Decimal point + index += 4; + + // Convert significand to hex digits manually to avoid creating temporary strings + // Extract the 13 hex digits (52 bits) from signifBits + // We need to extract bits 48-51, 44-47, ..., 0-3 (13 groups of 4 bits) + for (int sh = 4 * 12, end = 4 * trailingZeros; sh >= end; sh -= 4) { + // Extract 4 bits at a time from left to right + // Shift right by sh positions and mask with 0xF + // Integer.digits maps values 0-15 to '0'-'f' characters + chars[index++] = Integer.digits[((int)(signifBits >> sh)) & 0xF]; + } + + // Add the exponent indicator + chars[index] = 'p'; + + // Append the exponent value to the character array + // This method writes the decimal representation of exp directly into the byte array + DecimalDigits.uncheckedGetCharsLatin1(exp, charlen, chars); + + return String.newStringWithLatin1Bytes(chars); } /** diff --git a/src/java.base/share/classes/java/lang/Float.java b/src/java.base/share/classes/java/lang/Float.java index db694571567..c553dc41c2c 100644 --- a/src/java.base/share/classes/java/lang/Float.java +++ b/src/java.base/share/classes/java/lang/Float.java @@ -70,9 +70,6 @@ import jdk.internal.vm.annotation.IntrinsicCandidate; * @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.ValueBased @@ -411,7 +408,6 @@ public final class Float extends Number * @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 41487a469b6..20d1edb6d5f 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -68,10 +68,6 @@ import static java.lang.String.UTF16; * 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.ValueBased @@ -367,15 +363,9 @@ public final class Integer extends Number // assert shift > 0 && shift <=5 : "Illegal shift value"; int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val); int chars = Math.max(((mag + (shift - 1)) / shift), 1); - if (COMPACT_STRINGS) { - byte[] buf = new byte[chars]; - formatUnsignedInt(val, shift, buf, chars); - return new String(buf, LATIN1); - } else { - byte[] buf = new byte[chars * 2]; - formatUnsignedIntUTF16(val, shift, buf, chars); - return new String(buf, UTF16); - } + byte[] buf = new byte[chars]; + formatUnsignedInt(val, shift, buf, chars); + return String.newStringWithLatin1Bytes(buf); } /** @@ -398,26 +388,6 @@ public final class Integer extends Number } while (charPos > 0); } - /** - * Format an {@code int} (treated as unsigned) into a byte buffer (UTF16 version). If - * {@code len} exceeds the formatted ASCII representation of {@code val}, - * {@code buf} will be padded with leading zeroes. - * - * @param val the unsigned int to format - * @param shift the log2 of the base to format in (4 for hex, 3 for octal, 1 for binary) - * @param buf the byte buffer to write to - * @param len the number of characters to write - */ - private static void formatUnsignedIntUTF16(int val, int shift, byte[] buf, int len) { - int charPos = len; - int radix = 1 << shift; - int mask = radix - 1; - do { - StringUTF16.putChar(buf, --charPos, Integer.digits[val & mask]); - val >>>= shift; - } while (charPos > 0); - } - /** * Returns a {@code String} object representing the * specified integer. The argument is converted to signed decimal @@ -431,15 +401,9 @@ public final class Integer extends Number @IntrinsicCandidate public static String toString(int i) { int size = DecimalDigits.stringSize(i); - if (COMPACT_STRINGS) { - byte[] buf = new byte[size]; - DecimalDigits.uncheckedGetCharsLatin1(i, size, buf); - return new String(buf, LATIN1); - } else { - byte[] buf = new byte[size * 2]; - DecimalDigits.uncheckedGetCharsUTF16(i, size, buf); - return new String(buf, UTF16); - } + byte[] buf = new byte[size]; + DecimalDigits.uncheckedGetCharsLatin1(i, size, buf); + return String.newStringWithLatin1Bytes(buf); } /** diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 2fb2d18a78c..b0477fdab6d 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -68,10 +68,6 @@ import static java.lang.String.UTF16; * 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.ValueBased @@ -395,15 +391,9 @@ public final class Long extends Number // assert shift > 0 && shift <=5 : "Illegal shift value"; int mag = Long.SIZE - Long.numberOfLeadingZeros(val); int chars = Math.max(((mag + (shift - 1)) / shift), 1); - if (COMPACT_STRINGS) { - byte[] buf = new byte[chars]; - formatUnsignedLong0(val, shift, buf, 0, chars); - return new String(buf, LATIN1); - } else { - byte[] buf = new byte[chars * 2]; - formatUnsignedLong0UTF16(val, shift, buf, 0, chars); - return new String(buf, UTF16); - } + byte[] buf = new byte[chars]; + formatUnsignedLong0(val, shift, buf, 0, chars); + return String.newStringWithLatin1Bytes(buf); } /** @@ -427,27 +417,6 @@ public final class Long extends Number } while (charPos > offset); } - /** - * Format a long (treated as unsigned) into a byte buffer (UTF16 version). If - * {@code len} exceeds the formatted ASCII representation of {@code val}, - * {@code buf} will be padded with leading zeroes. - * - * @param val the unsigned long to format - * @param shift the log2 of the base to format in (4 for hex, 3 for octal, 1 for binary) - * @param buf the byte buffer to write to - * @param offset the offset in the destination buffer to start at - * @param len the number of characters to write - */ - private static void formatUnsignedLong0UTF16(long val, int shift, byte[] buf, int offset, int len) { - int charPos = offset + len; - int radix = 1 << shift; - int mask = radix - 1; - do { - StringUTF16.putChar(buf, --charPos, Integer.digits[((int) val) & mask]); - val >>>= shift; - } while (charPos > offset); - } - /** * Returns a {@code String} object representing the specified * {@code long}. The argument is converted to signed decimal @@ -460,15 +429,9 @@ public final class Long extends Number */ public static String toString(long i) { int size = DecimalDigits.stringSize(i); - if (COMPACT_STRINGS) { - byte[] buf = new byte[size]; - DecimalDigits.uncheckedGetCharsLatin1(i, size, buf); - return new String(buf, LATIN1); - } else { - byte[] buf = new byte[size * 2]; - DecimalDigits.uncheckedGetCharsUTF16(i, size, buf); - return new String(buf, UTF16); - } + byte[] buf = new byte[size]; + DecimalDigits.uncheckedGetCharsLatin1(i, size, buf); + return String.newStringWithLatin1Bytes(buf); } /** 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 final class Math { * * @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 final class Math { * * @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 final class Math { * * @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 final class Math { * * @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/Object.java b/src/java.base/share/classes/java/lang/Object.java index 11dcab1b005..185882cc7cc 100644 --- a/src/java.base/share/classes/java/lang/Object.java +++ b/src/java.base/share/classes/java/lang/Object.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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 @@ -383,7 +383,7 @@ public class Object { try { wait0(timeoutMillis); } catch (InterruptedException e) { - // virtual thread's interrupt status needs to be cleared + // virtual thread's interrupted status needs to be cleared vthread.getAndClearInterrupt(); throw e; } diff --git a/src/java.base/share/classes/java/lang/Process.java b/src/java.base/share/classes/java/lang/Process.java index 0a55343926f..577ee538326 100644 --- a/src/java.base/share/classes/java/lang/Process.java +++ b/src/java.base/share/classes/java/lang/Process.java @@ -774,7 +774,7 @@ public abstract class Process { * @implSpec * This implementation executes {@link #waitFor()} in a separate thread * repeatedly until it returns successfully. If the execution of - * {@code waitFor} is interrupted, the thread's interrupt status is preserved. + * {@code waitFor} is interrupted, the thread's interrupted status is preserved. *

* When {@link #waitFor()} returns successfully the CompletableFuture is * {@linkplain java.util.concurrent.CompletableFuture#complete completed} regardless diff --git a/src/java.base/share/classes/java/lang/Short.java b/src/java.base/share/classes/java/lang/Short.java index f0ae8b28e45..4c64427b6df 100644 --- a/src/java.base/share/classes/java/lang/Short.java +++ b/src/java.base/share/classes/java/lang/Short.java @@ -55,8 +55,6 @@ import static java.lang.constant.ConstantDescs.DEFAULT_NAME; * use instances for synchronization, or unpredictable behavior may * occur. For example, in a future release, synchronization may fail. * - * @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 @@ import jdk.internal.vm.annotation.IntrinsicCandidate; * @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 @@ public final class StrictMath { * @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 final class StrictMath { * * @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 final class StrictMath { * * @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 final class StrictMath { * * @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 final class StrictMath { * * @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/String.java b/src/java.base/share/classes/java/lang/String.java index a18ac3250dc..52f908c9e98 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -914,11 +914,10 @@ public final class String return ba; } - int blen = (coder == LATIN1) ? ae.encodeFromLatin1(val, 0, len, ba) - : ae.encodeFromUTF16(val, 0, len, ba); - if (blen != -1) { - return trimArray(ba, blen); - } + int blen = coder == LATIN1 + ? ae.encodeFromLatin1(val, 0, len, ba, 0) + : ae.encodeFromUTF16(val, 0, len, ba, 0); + return trimArray(ba, blen); } byte[] ba = new byte[en]; diff --git a/src/java.base/share/classes/java/lang/Thread.java b/src/java.base/share/classes/java/lang/Thread.java index 40f2dacadd2..ace29f30a56 100644 --- a/src/java.base/share/classes/java/lang/Thread.java +++ b/src/java.base/share/classes/java/lang/Thread.java @@ -228,7 +228,7 @@ public class Thread implements Runnable { // thread name private volatile String name; - // interrupt status (read/written by VM) + // interrupted status (read/written by VM) volatile boolean interrupted; // context ClassLoader @@ -355,7 +355,7 @@ public class Thread implements Runnable { /* The object in which this thread is blocked in an interruptible I/O * operation, if any. The blocker's interrupt method should be invoked - * after setting this thread's interrupt status. + * after setting this thread's interrupted status. */ private Interruptible nioBlocker; @@ -1535,22 +1535,22 @@ public class Thread implements Runnable { * Object#wait(long, int) wait(long, int)} methods of the {@link Object} * class, or of the {@link #join()}, {@link #join(long)}, {@link * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)} - * methods of this class, then its interrupt status will be cleared and it + * methods of this class, then its interrupted status will be cleared and it * will receive an {@link InterruptedException}. * *

If this thread is blocked in an I/O operation upon an {@link * java.nio.channels.InterruptibleChannel InterruptibleChannel} - * then the channel will be closed, the thread's interrupt + * then the channel will be closed, the thread's interrupted * status will be set, and the thread will receive a {@link * java.nio.channels.ClosedByInterruptException}. * *

If this thread is blocked in a {@link java.nio.channels.Selector} - * then the thread's interrupt status will be set and it will return + * then the thread's interrupted status will be set and it will return * immediately from the selection operation, possibly with a non-zero * value, just as if the selector's {@link * java.nio.channels.Selector#wakeup wakeup} method were invoked. * - *

If none of the previous conditions hold then this thread's interrupt + *

If none of the previous conditions hold then this thread's interrupted * status will be set.

* *

Interrupting a thread that is not alive need not have any effect. @@ -1560,7 +1560,7 @@ public class Thread implements Runnable { * will report it via {@link #interrupted()} and {@link #isInterrupted()}. */ public void interrupt() { - // Setting the interrupt status must be done before reading nioBlocker. + // Setting the interrupted status must be done before reading nioBlocker. interrupted = true; interrupt0(); // inform VM of interrupt diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index 19465eb32db..a23cbb72a6c 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.java @@ -483,12 +483,12 @@ final class VirtualThread extends BaseVirtualThread { Thread carrier = Thread.currentCarrierThread(); setCarrierThread(carrier); - // sync up carrier thread interrupt status if needed + // sync up carrier thread interrupted status if needed if (interrupted) { carrier.setInterrupt(); } else if (carrier.isInterrupted()) { synchronized (interruptLock) { - // need to recheck interrupt status + // need to recheck interrupted status if (!interrupted) { carrier.clearInterrupt(); } @@ -721,7 +721,7 @@ final class VirtualThread extends BaseVirtualThread { /** * Parks until unparked or interrupted. If already unparked then the parking * permit is consumed and this method completes immediately (meaning it doesn't - * yield). It also completes immediately if the interrupt status is set. + * yield). It also completes immediately if the interrupted status is set. */ @Override void park() { @@ -756,7 +756,7 @@ final class VirtualThread extends BaseVirtualThread { * Parks up to the given waiting time or until unparked or interrupted. * If already unparked then the parking permit is consumed and this method * completes immediately (meaning it doesn't yield). It also completes immediately - * if the interrupt status is set or the waiting time is {@code <= 0}. + * if the interrupted status is set or the waiting time is {@code <= 0}. * * @param nanos the maximum number of nanoseconds to wait. */ @@ -799,7 +799,7 @@ final class VirtualThread extends BaseVirtualThread { /** * Parks the current carrier thread up to the given waiting time or until * unparked or interrupted. If the virtual thread is interrupted then the - * interrupt status will be propagated to the carrier thread. + * interrupted status will be propagated to the carrier thread. * @param timed true for a timed park, false for untimed * @param nanos the waiting time in nanoseconds */ diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java index cb1bf8294d2..5b8a4478be5 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -33,7 +33,6 @@ import jdk.internal.constant.MethodTypeDescImpl; import jdk.internal.foreign.abi.NativeEntryPoint; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; -import jdk.internal.vm.annotation.AOTRuntimeSetup; import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Hidden; @@ -1536,11 +1535,6 @@ abstract class MethodHandleImpl { } static { - runtimeSetup(); - } - - @AOTRuntimeSetup - private static void runtimeSetup() { SharedSecrets.setJavaLangInvokeAccess(new JavaLangInvokeAccess() { @Override public Class getDeclaringClass(Object rmname) { diff --git a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java index d5f4470d9c9..4f4e35c6727 100644 --- a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java +++ b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java @@ -54,7 +54,6 @@ import static java.util.Objects.*; import jdk.internal.module.Checks; import jdk.internal.module.ModuleInfo; -import jdk.internal.vm.annotation.AOTRuntimeSetup; import jdk.internal.vm.annotation.AOTSafeClassInitializer; @@ -2668,11 +2667,6 @@ public final class ModuleDescriptor } static { - runtimeSetup(); - } - - @AOTRuntimeSetup - private static void runtimeSetup() { /** * Setup the shared secret to allow code in other packages access * private package methods in java.lang.module. diff --git a/src/java.base/share/classes/java/lang/ref/Reference.java b/src/java.base/share/classes/java/lang/ref/Reference.java index 43d9f414b3c..88bdb99dfd6 100644 --- a/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/src/java.base/share/classes/java/lang/ref/Reference.java @@ -25,7 +25,6 @@ package java.lang.ref; -import jdk.internal.vm.annotation.AOTRuntimeSetup; import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; @@ -291,11 +290,6 @@ public abstract sealed class Reference<@jdk.internal.RequiresIdentity T> } static { - runtimeSetup(); - } - - @AOTRuntimeSetup - private static void runtimeSetup() { // provide access in SharedSecrets SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() { @Override 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 24b55600954..18aa6f29f1f 100644 --- a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java +++ b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java @@ -25,18 +25,26 @@ package java.lang.runtime; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassHierarchyResolver; +import java.lang.classfile.Opcode; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; import java.lang.invoke.ConstantCallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.invoke.StringConcatFactory; import java.lang.invoke.TypeDescriptor; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Objects; +import static java.lang.classfile.ClassFile.ACC_STATIC; +import static java.lang.constant.ConstantDescs.*; import static java.util.Objects.requireNonNull; /** @@ -58,15 +66,18 @@ public final class ObjectMethods { private static final MethodHandle TRUE = MethodHandles.constant(boolean.class, true); private static final MethodHandle ZERO = MethodHandles.zero(int.class); private static final MethodHandle CLASS_IS_INSTANCE; - private static final MethodHandle OBJECTS_EQUALS; - private static final MethodHandle OBJECTS_HASHCODE; - private static final MethodHandle OBJECTS_TOSTRING; + private static final MethodHandle IS_NULL; + private static final MethodHandle IS_ARG0_NULL; + private static final MethodHandle IS_ARG1_NULL; private static final MethodHandle OBJECT_EQ; private static final MethodHandle HASH_COMBINER; + private static final MethodType MT_OBJECT_BOOLEAN = MethodType.methodType(boolean.class, Object.class); + private static final MethodType MT_INT = MethodType.methodType(int.class); + private static final MethodTypeDesc MTD_OBJECT_BOOLEAN = MethodTypeDesc.of(CD_boolean, CD_Object); + private static final MethodTypeDesc MTD_INT = MethodTypeDesc.of(CD_int); private static final HashMap, MethodHandle> primitiveEquals = new HashMap<>(); private static final HashMap, MethodHandle> primitiveHashers = new HashMap<>(); - private static final HashMap, MethodHandle> primitiveToString = new HashMap<>(); static { try { @@ -76,12 +87,12 @@ public final class ObjectMethods { CLASS_IS_INSTANCE = publicLookup.findVirtual(Class.class, "isInstance", MethodType.methodType(boolean.class, Object.class)); - OBJECTS_EQUALS = publicLookup.findStatic(Objects.class, "equals", - MethodType.methodType(boolean.class, Object.class, Object.class)); - OBJECTS_HASHCODE = publicLookup.findStatic(Objects.class, "hashCode", - MethodType.methodType(int.class, Object.class)); - OBJECTS_TOSTRING = publicLookup.findStatic(Objects.class, "toString", - MethodType.methodType(String.class, Object.class)); + + var objectsIsNull = publicLookup.findStatic(Objects.class, "isNull", + MethodType.methodType(boolean.class, Object.class)); + IS_NULL = objectsIsNull; + IS_ARG0_NULL = MethodHandles.dropArguments(objectsIsNull, 1, Object.class); + IS_ARG1_NULL = MethodHandles.dropArguments(objectsIsNull, 0, Object.class); OBJECT_EQ = lookup.findStatic(OBJECT_METHODS_CLASS, "eq", MethodType.methodType(boolean.class, Object.class, Object.class)); @@ -121,23 +132,6 @@ public final class ObjectMethods { MethodType.methodType(int.class, double.class))); primitiveHashers.put(boolean.class, lookup.findStatic(Boolean.class, "hashCode", MethodType.methodType(int.class, boolean.class))); - - primitiveToString.put(byte.class, lookup.findStatic(Byte.class, "toString", - MethodType.methodType(String.class, byte.class))); - primitiveToString.put(short.class, lookup.findStatic(Short.class, "toString", - MethodType.methodType(String.class, short.class))); - primitiveToString.put(char.class, lookup.findStatic(Character.class, "toString", - MethodType.methodType(String.class, char.class))); - primitiveToString.put(int.class, lookup.findStatic(Integer.class, "toString", - MethodType.methodType(String.class, int.class))); - primitiveToString.put(long.class, lookup.findStatic(Long.class, "toString", - MethodType.methodType(String.class, long.class))); - primitiveToString.put(float.class, lookup.findStatic(Float.class, "toString", - MethodType.methodType(String.class, float.class))); - primitiveToString.put(double.class, lookup.findStatic(Double.class, "toString", - MethodType.methodType(String.class, double.class))); - primitiveToString.put(boolean.class, lookup.findStatic(Boolean.class, "toString", - MethodType.methodType(String.class, boolean.class))); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); @@ -159,24 +153,41 @@ public final class ObjectMethods { private static boolean eq(boolean a, boolean b) { return a == b; } /** Get the method handle for combining two values of a given type */ - private static MethodHandle equalator(Class clazz) { - return (clazz.isPrimitive() - ? primitiveEquals.get(clazz) - : OBJECTS_EQUALS.asType(MethodType.methodType(boolean.class, clazz, clazz))); + private static MethodHandle equalator(MethodHandles.Lookup lookup, Class clazz) throws Throwable { + if (clazz.isPrimitive()) + return primitiveEquals.get(clazz); + MethodType mt = MethodType.methodType(boolean.class, clazz, clazz); + return MethodHandles.guardWithTest(IS_ARG0_NULL.asType(mt), + IS_ARG1_NULL.asType(mt), + lookup.findVirtual(clazz, "equals", MT_OBJECT_BOOLEAN).asType(mt)); } /** Get the hasher for a value of a given type */ - private static MethodHandle hasher(Class clazz) { - return (clazz.isPrimitive() - ? primitiveHashers.get(clazz) - : OBJECTS_HASHCODE.asType(MethodType.methodType(int.class, clazz))); + private static MethodHandle hasher(MethodHandles.Lookup lookup, Class clazz) throws Throwable { + if (clazz.isPrimitive()) + return primitiveHashers.get(clazz); + MethodType mt = MethodType.methodType(int.class, clazz); + return MethodHandles.guardWithTest(IS_NULL.asType(MethodType.methodType(boolean.class, clazz)), + MethodHandles.dropArguments(MethodHandles.zero(int.class), 0, clazz), + lookup.findVirtual(clazz, "hashCode", MT_INT).asType(mt)); } - /** Get the stringifier for a value of a given type */ - private static MethodHandle stringifier(Class clazz) { - return (clazz.isPrimitive() - ? primitiveToString.get(clazz) - : OBJECTS_TOSTRING.asType(MethodType.methodType(String.class, clazz))); + // If this type must be a monomorphic receiver, that is, one that has no + // subtypes in the JVM. For example, Object-typed fields may have a more + // specific one type at runtime and thus need optimizations. + private static boolean isMonomorphic(Class type) { + // Includes primitives and final classes, but not arrays. + // All array classes are reported to be final, but Object[] can have subtypes like String[] + return Modifier.isFinal(type.getModifiers()) && !type.isArray(); + } + + private static String specializerClassName(Class targetClass, String kind) { + String name = targetClass.getName(); + if (targetClass.isHidden()) { + // use the original class name + name = name.replace('/', '_'); + } + return name + "$$" + kind + "Specializer"; } /** @@ -185,8 +196,8 @@ public final class ObjectMethods { * @param getters the list of getters * @return the method handle */ - private static MethodHandle makeEquals(Class receiverClass, - List getters) { + private static MethodHandle makeEquals(MethodHandles.Lookup lookup, Class receiverClass, + List getters) throws Throwable { MethodType rr = MethodType.methodType(boolean.class, receiverClass, receiverClass); MethodType ro = MethodType.methodType(boolean.class, receiverClass, Object.class); MethodHandle instanceFalse = MethodHandles.dropArguments(FALSE, 0, receiverClass, Object.class); // (RO)Z @@ -195,8 +206,70 @@ public final class ObjectMethods { MethodHandle isInstance = MethodHandles.dropArguments(CLASS_IS_INSTANCE.bindTo(receiverClass), 0, receiverClass); // (RO)Z MethodHandle accumulator = MethodHandles.dropArguments(TRUE, 0, receiverClass, receiverClass); // (RR)Z - for (MethodHandle getter : getters) { - MethodHandle equalator = equalator(getter.type().returnType()); // (TT)Z + int size = getters.size(); + MethodHandle[] equalators = new MethodHandle[size]; + boolean hasPolymorphism = false; + for (int i = 0; i < size; i++) { + var getter = getters.get(i); + var type = getter.type().returnType(); + if (isMonomorphic(type)) { + equalators[i] = equalator(lookup, type); + } else { + hasPolymorphism = true; + } + } + + // Currently, hotspot does not support polymorphic inlining. + // As a result, if we have a MethodHandle to Object.equals, + // it does not enjoy separate profiles like individual invokevirtuals, + // and we must spin bytecode to accomplish separate profiling. + if (hasPolymorphism) { + String[] names = new String[size]; + + var classFileContext = ClassFile.of(ClassFile.ClassHierarchyResolverOption.of(ClassHierarchyResolver.ofClassLoading(lookup))); + var bytes = classFileContext.build(ClassDesc.of(specializerClassName(lookup.lookupClass(), "Equalator")), clb -> { + for (int i = 0; i < size; i++) { + if (equalators[i] == null) { + var name = "equalator".concat(Integer.toString(i)); + names[i] = name; + var type = getters.get(i).type().returnType(); + boolean isInterface = type.isInterface(); + var typeDesc = type.describeConstable().orElseThrow(); + clb.withMethodBody(name, MethodTypeDesc.of(CD_boolean, typeDesc, typeDesc), ACC_STATIC, cob -> { + var nonNullPath = cob.newLabel(); + var fail = cob.newLabel(); + cob.aload(0) + .ifnonnull(nonNullPath) + .aload(1) + .ifnonnull(fail) + .iconst_1() // arg0 null, arg1 null + .ireturn() + .labelBinding(fail) + .iconst_0() // arg0 null, arg1 non-null + .ireturn() + .labelBinding(nonNullPath) + .aload(0) // arg0.equals(arg1) - bytecode subject to customized profiling + .aload(1) + .invoke(isInterface ? Opcode.INVOKEINTERFACE : Opcode.INVOKEVIRTUAL, typeDesc, "equals", MTD_OBJECT_BOOLEAN, isInterface) + .ireturn(); + }); + } + } + }); + + var specializerLookup = lookup.defineHiddenClass(bytes, true, MethodHandles.Lookup.ClassOption.STRONG); + + for (int i = 0; i < size; i++) { + if (equalators[i] == null) { + var type = getters.get(i).type().returnType(); + equalators[i] = specializerLookup.findStatic(specializerLookup.lookupClass(), names[i], MethodType.methodType(boolean.class, type, type)); + } + } + } + + for (int i = 0; i < size; i++) { + var getter = getters.get(i); + MethodHandle equalator = equalators[i]; // (TT)Z MethodHandle thisFieldEqual = MethodHandles.filterArguments(equalator, 0, getter, getter); // (RR)Z accumulator = MethodHandles.guardWithTest(thisFieldEqual, accumulator, instanceFalse.asType(rr)); } @@ -212,13 +285,68 @@ public final class ObjectMethods { * @param getters the list of getters * @return the method handle */ - private static MethodHandle makeHashCode(Class receiverClass, - List getters) { + private static MethodHandle makeHashCode(MethodHandles.Lookup lookup, Class receiverClass, + List getters) throws Throwable { MethodHandle accumulator = MethodHandles.dropArguments(ZERO, 0, receiverClass); // (R)I + int size = getters.size(); + MethodHandle[] hashers = new MethodHandle[size]; + boolean hasPolymorphism = false; + for (int i = 0; i < size; i++) { + var getter = getters.get(i); + var type = getter.type().returnType(); + if (isMonomorphic(type)) { + hashers[i] = hasher(lookup, type); + } else { + hasPolymorphism = true; + } + } + + // Currently, hotspot does not support polymorphic inlining. + // As a result, if we have a MethodHandle to Object.hashCode, + // it does not enjoy separate profiles like individual invokevirtuals, + // and we must spin bytecode to accomplish separate profiling. + if (hasPolymorphism) { + String[] names = new String[size]; + + var classFileContext = ClassFile.of(ClassFile.ClassHierarchyResolverOption.of(ClassHierarchyResolver.ofClassLoading(lookup))); + var bytes = classFileContext.build(ClassDesc.of(specializerClassName(lookup.lookupClass(), "Hasher")), clb -> { + for (int i = 0; i < size; i++) { + if (hashers[i] == null) { + var name = "hasher".concat(Integer.toString(i)); + names[i] = name; + var type = getters.get(i).type().returnType(); + boolean isInterface = type.isInterface(); + var typeDesc = type.describeConstable().orElseThrow(); + clb.withMethodBody(name, MethodTypeDesc.of(CD_int, typeDesc), ACC_STATIC, cob -> { + var nonNullPath = cob.newLabel(); + cob.aload(0) + .ifnonnull(nonNullPath) + .iconst_0() // null hash is 0 + .ireturn() + .labelBinding(nonNullPath) + .aload(0) // arg0.hashCode() - bytecode subject to customized profiling + .invoke(isInterface ? Opcode.INVOKEINTERFACE : Opcode.INVOKEVIRTUAL, typeDesc, "hashCode", MTD_INT, isInterface) + .ireturn(); + }); + } + } + }); + + var specializerLookup = lookup.defineHiddenClass(bytes, true, MethodHandles.Lookup.ClassOption.STRONG); + + for (int i = 0; i < size; i++) { + if (hashers[i] == null) { + var type = getters.get(i).type().returnType(); + hashers[i] = specializerLookup.findStatic(specializerLookup.lookupClass(), names[i], MethodType.methodType(int.class, type)); + } + } + } + // @@@ Use loop combinator instead? - for (MethodHandle getter : getters) { - MethodHandle hasher = hasher(getter.type().returnType()); // (T)I + for (int i = 0; i < size; i++) { + var getter = getters.get(i); + MethodHandle hasher = hashers[i]; // (T)I MethodHandle hashThisField = MethodHandles.filterArguments(hasher, 0, getter); // (R)I MethodHandle combineHashes = MethodHandles.filterArguments(HASH_COMBINER, 0, accumulator, hashThisField); // (RR)I accumulator = MethodHandles.permuteArguments(combineHashes, accumulator.type(), 0, 0); // adapt (R)I to (RR)I @@ -403,12 +531,12 @@ public final class ObjectMethods { 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..14d81d30c3d 100644 --- a/src/java.base/share/classes/java/math/BigDecimal.java +++ b/src/java.base/share/classes/java/math/BigDecimal.java @@ -154,7 +154,7 @@ import jdk.internal.vm.annotation.Stable; * Subtractmax(minuend.scale(), subtrahend.scale()) * Multiplymultiplier.scale() + multiplicand.scale() * Dividedividend.scale() - divisor.scale() - * Square rootradicand.scale()/2 + * Square rootceil(radicand.scale()/2.0) * * * @@ -327,10 +327,6 @@ import jdk.internal.vm.annotation.Stable; * @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 class BigDecimal extends Number implements Comparable { * 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 class BigDecimal extends Number implements Comparable { * @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 @@ -2119,7 +2113,7 @@ public class BigDecimal extends Number implements Comparable { * with rounding according to the context settings. * *

The preferred scale of the returned result is equal to - * {@code this.scale()/2}. The value of the returned result is + * {@code Math.ceilDiv(this.scale(), 2)}. The value of the returned result is * always within one ulp of the exact decimal value for the * precision in question. If the rounding mode is {@link * RoundingMode#HALF_UP HALF_UP}, {@link RoundingMode#HALF_DOWN @@ -2180,7 +2174,7 @@ public class BigDecimal extends Number implements Comparable { // The code below favors relative simplicity over checking // for special cases that could run faster. - final int preferredScale = this.scale/2; + final int preferredScale = Math.ceilDiv(this.scale, 2); BigDecimal result; if (mc.roundingMode == RoundingMode.UNNECESSARY || mc.precision == 0) { // Exact result requested diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 6253adffb2b..ed26f5c1211 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2768,9 +2768,9 @@ public class BigInteger extends Number implements Comparable { * @throws ArithmeticException if {@code n} is even and {@code this} is negative. * @see #sqrt() * @since 26 - * @apiNote Note that calling {@code nthRoot(2)} is equivalent to calling {@code sqrt()}. + * @apiNote Note that calling {@code rootn(2)} is equivalent to calling {@code sqrt()}. */ - public BigInteger nthRoot(int n) { + public BigInteger rootn(int n) { if (n == 1) return this; @@ -2778,7 +2778,7 @@ public class BigInteger extends Number implements Comparable { return sqrt(); checkRootDegree(n); - return new MutableBigInteger(this.mag).nthRootRem(n)[0].toBigInteger(signum); + return new MutableBigInteger(this.mag).rootnRem(n)[0].toBigInteger(signum); } /** @@ -2793,12 +2793,12 @@ public class BigInteger extends Number implements Comparable { * @throws ArithmeticException if {@code n} is even and {@code this} is negative. * @see #sqrt() * @see #sqrtAndRemainder() - * @see #nthRoot(int) + * @see #rootn(int) * @since 26 - * @apiNote Note that calling {@code nthRootAndRemainder(2)} is equivalent to calling + * @apiNote Note that calling {@code rootnAndRemainder(2)} is equivalent to calling * {@code sqrtAndRemainder()}. */ - public BigInteger[] nthRootAndRemainder(int n) { + public BigInteger[] rootnAndRemainder(int n) { if (n == 1) return new BigInteger[] { this, ZERO }; @@ -2806,7 +2806,7 @@ public class BigInteger extends Number implements Comparable { return sqrtAndRemainder(); checkRootDegree(n); - MutableBigInteger[] rootRem = new MutableBigInteger(this.mag).nthRootRem(n); + MutableBigInteger[] rootRem = new MutableBigInteger(this.mag).rootnRem(n); return new BigInteger[] { rootRem[0].toBigInteger(signum), rootRem[1].toBigInteger(signum) 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 @@ import java.io.*; * @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/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index dd1da29ddd2..1ede4cf32f8 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1906,7 +1906,7 @@ class MutableBigInteger { * @param n the root degree * @return the integer {@code n}th root of {@code this} and the remainder */ - MutableBigInteger[] nthRootRem(int n) { + MutableBigInteger[] rootnRem(int n) { // Special cases. if (this.isZero() || this.isOne()) return new MutableBigInteger[] { this, new MutableBigInteger() }; @@ -1923,7 +1923,7 @@ class MutableBigInteger { if (bitLength <= Long.SIZE) { // Initial estimate is the root of the unsigned long value. final long x = this.toLong(); - long sLong = (long) nthRootApprox(Math.nextUp(x >= 0 ? x : x + 0x1p64), n) + 1L; + long sLong = (long) rootnApprox(Math.nextUp(x >= 0 ? x : x + 0x1p64), n) + 1L; /* The integer-valued recurrence formula in the algorithm of Brent&Zimmermann * simply discards the fraction part of the real-valued Newton recurrence * on the function f discussed in the referenced work. @@ -1996,7 +1996,7 @@ class MutableBigInteger { // Use the root of the shifted value as an estimate. // rad ≤ 2^ME, so Math.nextUp(rad) < Double.MAX_VALUE rad = Math.nextUp(rad); - approx = nthRootApprox(rad, n); + approx = rootnApprox(rad, n); } else { // fp arithmetic gives too few correct bits // Set the root shift to the root's bit length minus 1 // The initial estimate will be 2^rootLen == 2 << (rootLen - 1) @@ -2050,7 +2050,7 @@ class MutableBigInteger { MutableBigInteger x = new MutableBigInteger(this); x.rightShift(rootSh * n); - newtonRecurrenceNthRoot(x, s, n, s.toBigInteger().pow(n - 1)); + newtonRecurrenceRootn(x, s, n, s.toBigInteger().pow(n - 1)); s.add(ONE); // round up to ensure s is an upper bound of the root } @@ -2060,7 +2060,7 @@ class MutableBigInteger { } // Do the 1st iteration outside the loop to ensure an overestimate - newtonRecurrenceNthRoot(this, s, n, s.toBigInteger().pow(n - 1)); + newtonRecurrenceRootn(this, s, n, s.toBigInteger().pow(n - 1)); // Refine the estimate. do { BigInteger sBig = s.toBigInteger(); @@ -2069,18 +2069,18 @@ class MutableBigInteger { if (rem.subtract(this) <= 0) return new MutableBigInteger[] { s, rem }; - newtonRecurrenceNthRoot(this, s, n, sToN1); + newtonRecurrenceRootn(this, s, n, sToN1); } while (true); } - private static double nthRootApprox(double x, int n) { + private static double rootnApprox(double x, int n) { return Math.nextUp(n == 3 ? Math.cbrt(x) : Math.pow(x, Math.nextUp(1.0 / n))); } /** * Computes {@code ((n-1)*s + x/sToN1)/n} and places the result in {@code s}. */ - private static void newtonRecurrenceNthRoot( + private static void newtonRecurrenceRootn( MutableBigInteger x, MutableBigInteger s, int n, BigInteger sToN1) { MutableBigInteger dividend = new MutableBigInteger(); s.mul(n - 1, dividend); 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 @@ package java.math; * 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/net/DatagramSocket.java b/src/java.base/share/classes/java/net/DatagramSocket.java index 87b52699993..20b4d8a96f1 100644 --- a/src/java.base/share/classes/java/net/DatagramSocket.java +++ b/src/java.base/share/classes/java/net/DatagramSocket.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 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 @@ -611,13 +611,13 @@ public class DatagramSocket implements java.io.Closeable { * with a {@link DatagramChannel DatagramChannel}. In that case, * interrupting a thread receiving a datagram packet will close the * underlying channel and cause this method to throw {@link - * java.nio.channels.ClosedByInterruptException} with the interrupt - * status set. + * java.nio.channels.ClosedByInterruptException} with the thread's + * interrupted status set. *

  • The datagram socket uses the system-default socket implementation and * a {@linkplain Thread#isVirtual() virtual thread} is receiving a * datagram packet. In that case, interrupting the virtual thread will * cause it to wakeup and close the socket. This method will then throw - * {@code SocketException} with the interrupt status set. + * {@code SocketException} with the thread's interrupted status set. * * * @param p the {@code DatagramPacket} into which to place diff --git a/src/java.base/share/classes/java/net/ServerSocket.java b/src/java.base/share/classes/java/net/ServerSocket.java index 945693ef65e..af7cedfd966 100644 --- a/src/java.base/share/classes/java/net/ServerSocket.java +++ b/src/java.base/share/classes/java/net/ServerSocket.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 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 @@ -406,13 +406,13 @@ public class ServerSocket implements java.io.Closeable { * with a {@link ServerSocketChannel ServerSocketChannel}. In that * case, interrupting a thread accepting a connection will close the * underlying channel and cause this method to throw {@link - * java.nio.channels.ClosedByInterruptException} with the interrupt - * status set. + * java.nio.channels.ClosedByInterruptException} with the thread's + * interrupted status set. *
  • The socket uses the system-default socket implementation and a * {@linkplain Thread#isVirtual() virtual thread} is accepting a * connection. In that case, interrupting the virtual thread will * cause it to wakeup and close the socket. This method will then throw - * {@code SocketException} with the interrupt status set. + * {@code SocketException} with the thread's interrupted status set. * * * @implNote diff --git a/src/java.base/share/classes/java/net/Socket.java b/src/java.base/share/classes/java/net/Socket.java index 692c3395f78..a2aee2e45a1 100644 --- a/src/java.base/share/classes/java/net/Socket.java +++ b/src/java.base/share/classes/java/net/Socket.java @@ -573,12 +573,13 @@ public class Socket implements java.io.Closeable { * a {@link SocketChannel SocketChannel}. * In that case, interrupting a thread establishing a connection will * close the underlying channel and cause this method to throw - * {@link ClosedByInterruptException} with the interrupt status set. + * {@link ClosedByInterruptException} with the thread's interrupted + * status set. *
  • The socket uses the system-default socket implementation and a * {@linkplain Thread#isVirtual() virtual thread} is establishing a * connection. In that case, interrupting the virtual thread will * cause it to wakeup and close the socket. This method will then throw - * {@code SocketException} with the interrupt status set. + * {@code SocketException} with the thread's interrupted status set. * * * @param endpoint the {@code SocketAddress} @@ -613,12 +614,13 @@ public class Socket implements java.io.Closeable { * a {@link SocketChannel SocketChannel}. * In that case, interrupting a thread establishing a connection will * close the underlying channel and cause this method to throw - * {@link ClosedByInterruptException} with the interrupt status set. + * {@link ClosedByInterruptException} with the thread's interrupted + * status set. *
  • The socket uses the system-default socket implementation and a * {@linkplain Thread#isVirtual() virtual thread} is establishing a * connection. In that case, interrupting the virtual thread will * cause it to wakeup and close the socket. This method will then throw - * {@code SocketException} with the interrupt status set. + * {@code SocketException} with the thread's interrupted status set. * * * @apiNote Establishing a TCP/IP connection is subject to connect timeout settings @@ -886,13 +888,14 @@ public class Socket implements java.io.Closeable { * a {@link SocketChannel SocketChannel}. * In that case, interrupting a thread reading from the input stream * will close the underlying channel and cause the read method to - * throw {@link ClosedByInterruptException} with the interrupt - * status set. + * throw {@link ClosedByInterruptException} with the thread's + * interrupted status set. *
  • The socket uses the system-default socket implementation and a * {@linkplain Thread#isVirtual() virtual thread} is reading from the * input stream. In that case, interrupting the virtual thread will * cause it to wakeup and close the socket. The read method will then - * throw {@code SocketException} with the interrupt status set. + * throw {@code SocketException} with the thread's interrupted + * status set. * * *

    Under abnormal conditions the underlying connection may be @@ -1026,13 +1029,14 @@ public class Socket implements java.io.Closeable { * a {@link SocketChannel SocketChannel}. * In that case, interrupting a thread writing to the output stream * will close the underlying channel and cause the write method to - * throw {@link ClosedByInterruptException} with the interrupt status - * set. + * throw {@link ClosedByInterruptException} with the thread's + * interrupted status set. *

  • The socket uses the system-default socket implementation and a * {@linkplain Thread#isVirtual() virtual thread} is writing to the * output stream. In that case, interrupting the virtual thread will * cause it to wakeup and close the socket. The write method will then - * throw {@code SocketException} with the interrupt status set. + * throw {@code SocketException} with the thread's interrupted + * status set. * * *

    Closing the returned {@link java.io.OutputStream OutputStream} diff --git a/src/java.base/share/classes/java/net/URI.java b/src/java.base/share/classes/java/net/URI.java index d130dc3b460..d568ab1c114 100644 --- a/src/java.base/share/classes/java/net/URI.java +++ b/src/java.base/share/classes/java/net/URI.java @@ -43,7 +43,6 @@ import java.text.Normalizer; import jdk.internal.access.JavaNetUriAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.util.Exceptions; -import jdk.internal.vm.annotation.AOTRuntimeSetup; import jdk.internal.vm.annotation.AOTSafeClassInitializer; import sun.nio.cs.UTF_8; @@ -3731,11 +3730,6 @@ public final class URI } static { - runtimeSetup(); - } - - @AOTRuntimeSetup - private static void runtimeSetup() { SharedSecrets.setJavaNetUriAccess( new JavaNetUriAccess() { public URI create(String scheme, String path) { diff --git a/src/java.base/share/classes/java/net/URL.java b/src/java.base/share/classes/java/net/URL.java index c82236b5b85..1e86f41fd3f 100644 --- a/src/java.base/share/classes/java/net/URL.java +++ b/src/java.base/share/classes/java/net/URL.java @@ -42,7 +42,6 @@ import java.util.ServiceLoader; import jdk.internal.access.JavaNetURLAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.misc.VM; -import jdk.internal.vm.annotation.AOTRuntimeSetup; import jdk.internal.vm.annotation.AOTSafeClassInitializer; import sun.net.util.IPAddressUtil; import static jdk.internal.util.Exceptions.filterNonSocketInfo; @@ -1747,11 +1746,6 @@ public final class URL implements java.io.Serializable { } static { - runtimeSetup(); - } - - @AOTRuntimeSetup - private static void runtimeSetup() { SharedSecrets.setJavaNetURLAccess( new JavaNetURLAccess() { @Override diff --git a/src/java.base/share/classes/java/nio/channels/ClosedByInterruptException.java b/src/java.base/share/classes/java/nio/channels/ClosedByInterruptException.java index c612458c9e4..a2f62aaed32 100644 --- a/src/java.base/share/classes/java/nio/channels/ClosedByInterruptException.java +++ b/src/java.base/share/classes/java/nio/channels/ClosedByInterruptException.java @@ -28,7 +28,7 @@ package java.nio.channels; /** * Checked exception received by a thread when another thread interrupts it * while it is blocked in an I/O operation upon a channel. Before this - * exception is thrown the channel will have been closed and the interrupt + * exception is thrown the channel will have been closed and the interrupted * status of the previously-blocked thread will have been set. * * @since 1.4 diff --git a/src/java.base/share/classes/java/nio/channels/DatagramChannel.java b/src/java.base/share/classes/java/nio/channels/DatagramChannel.java index 392d9add37f..5e72efcdea6 100644 --- a/src/java.base/share/classes/java/nio/channels/DatagramChannel.java +++ b/src/java.base/share/classes/java/nio/channels/DatagramChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, 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 @@ -290,7 +290,7 @@ public abstract class DatagramChannel * If another thread interrupts the current thread * while the connect operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws UnresolvedAddressException * If the given remote address is not fully resolved @@ -389,7 +389,7 @@ public abstract class DatagramChannel * If another thread interrupts the current thread * while the read operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws IOException * If some other I/O error occurs @@ -443,7 +443,7 @@ public abstract class DatagramChannel * If another thread interrupts the current thread * while the read operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws UnresolvedAddressException * If the given remote address is not fully resolved diff --git a/src/java.base/share/classes/java/nio/channels/FileChannel.java b/src/java.base/share/classes/java/nio/channels/FileChannel.java index 6e78eefcca6..e31d01b34c0 100644 --- a/src/java.base/share/classes/java/nio/channels/FileChannel.java +++ b/src/java.base/share/classes/java/nio/channels/FileChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, 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 @@ -662,7 +662,7 @@ public abstract class FileChannel * @throws ClosedByInterruptException * If another thread interrupts the current thread while the * transfer is in progress, thereby closing both channels and - * setting the current thread's interrupt status + * setting the current thread's interrupted status * * @throws IOException * If some other I/O error occurs @@ -732,7 +732,7 @@ public abstract class FileChannel * @throws ClosedByInterruptException * If another thread interrupts the current thread while the * transfer is in progress, thereby closing both channels and - * setting the current thread's interrupt status + * setting the current thread's interrupted status * * @throws IOException * If some other I/O error occurs @@ -780,7 +780,7 @@ public abstract class FileChannel * If another thread interrupts the current thread * while the read operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws IOException * If some other I/O error occurs @@ -829,7 +829,7 @@ public abstract class FileChannel * If another thread interrupts the current thread * while the write operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws IOException * If some other I/O error occurs @@ -1093,10 +1093,10 @@ public abstract class FileChannel * this method then an {@link AsynchronousCloseException} will be thrown. * *

    If the invoking thread is interrupted while waiting to acquire the - * lock then its interrupt status will be set and a {@link + * lock then its interrupted status will be set and a {@link * FileLockInterruptionException} will be thrown. If the invoker's - * interrupt status is set when this method is invoked then that exception - * will be thrown immediately; the thread's interrupt status will not be + * interrupted status is set when this method is invoked then that exception + * will be thrown immediately; the thread's interrupted status will not be * changed. * *

    The region specified by the {@code position} and {@code size} diff --git a/src/java.base/share/classes/java/nio/channels/FileLockInterruptionException.java b/src/java.base/share/classes/java/nio/channels/FileLockInterruptionException.java index 7ecae1b4a46..ae1f12f15fc 100644 --- a/src/java.base/share/classes/java/nio/channels/FileLockInterruptionException.java +++ b/src/java.base/share/classes/java/nio/channels/FileLockInterruptionException.java @@ -28,7 +28,7 @@ package java.nio.channels; /** * Checked exception received by a thread when another thread interrupts it * while it is waiting to acquire a file lock. Before this exception is thrown - * the interrupt status of the previously-blocked thread will have been set. + * the interrupted status of the previously-blocked thread will have been set. * * @since 1.4 */ 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..0b03b9c8196 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 @@ -120,7 +123,7 @@ public interface GatheringByteChannel * If another thread interrupts the current thread * while the write operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws IOException * If some other I/O error occurs @@ -158,7 +161,7 @@ public interface GatheringByteChannel * If another thread interrupts the current thread * while the write operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws IOException * If some other I/O error occurs diff --git a/src/java.base/share/classes/java/nio/channels/InterruptibleChannel.java b/src/java.base/share/classes/java/nio/channels/InterruptibleChannel.java index d13a37aeae4..c1c3628f7a4 100644 --- a/src/java.base/share/classes/java/nio/channels/InterruptibleChannel.java +++ b/src/java.base/share/classes/java/nio/channels/InterruptibleChannel.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 @@ -45,11 +45,11 @@ import java.io.IOException; * another thread may invoke the blocked thread's {@link Thread#interrupt() * interrupt} method. This will cause the channel to be closed, the blocked * thread to receive a {@link ClosedByInterruptException}, and the blocked - * thread's interrupt status to be set. + * thread's interrupted status to be set. * - *

    If a thread's interrupt status is already set and it invokes a blocking + *

    If a thread's interrupted status is already set and it invokes a blocking * I/O operation upon a channel then the channel will be closed and the thread - * will immediately receive a {@link ClosedByInterruptException}; its interrupt + * will immediately receive a {@link ClosedByInterruptException}; its interrupted * status will remain set. * *

    A channel supports asynchronous closing and interruption if, and only diff --git a/src/java.base/share/classes/java/nio/channels/ReadableByteChannel.java b/src/java.base/share/classes/java/nio/channels/ReadableByteChannel.java index 7d544458390..37b3a5442bc 100644 --- a/src/java.base/share/classes/java/nio/channels/ReadableByteChannel.java +++ b/src/java.base/share/classes/java/nio/channels/ReadableByteChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2019, 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 @@ -101,7 +101,7 @@ public interface ReadableByteChannel extends Channel { * If another thread interrupts the current thread * while the read operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws IOException * If some other I/O error occurs diff --git a/src/java.base/share/classes/java/nio/channels/ScatteringByteChannel.java b/src/java.base/share/classes/java/nio/channels/ScatteringByteChannel.java index 27fce3c1e09..66ff5047d70 100644 --- a/src/java.base/share/classes/java/nio/channels/ScatteringByteChannel.java +++ b/src/java.base/share/classes/java/nio/channels/ScatteringByteChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, 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 @@ -119,7 +119,7 @@ public interface ScatteringByteChannel * If another thread interrupts the current thread * while the read operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws IOException * If some other I/O error occurs @@ -160,7 +160,7 @@ public interface ScatteringByteChannel * If another thread interrupts the current thread * while the read operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws IOException * If some other I/O error occurs diff --git a/src/java.base/share/classes/java/nio/channels/Selector.java b/src/java.base/share/classes/java/nio/channels/Selector.java index d8c0dc261bd..b90b8929a51 100644 --- a/src/java.base/share/classes/java/nio/channels/Selector.java +++ b/src/java.base/share/classes/java/nio/channels/Selector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, 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 @@ -236,7 +236,7 @@ import java.util.function.Consumer; * *

  • By invoking the blocked thread's {@link * java.lang.Thread#interrupt() interrupt} method, in which case its - * interrupt status will be set and the selector's {@link #wakeup wakeup} + * interrupted status will be set and the selector's {@link #wakeup wakeup} * method will be invoked.

  • * * diff --git a/src/java.base/share/classes/java/nio/channels/ServerSocketChannel.java b/src/java.base/share/classes/java/nio/channels/ServerSocketChannel.java index b2ee728cf7f..6cf2520fd78 100644 --- a/src/java.base/share/classes/java/nio/channels/ServerSocketChannel.java +++ b/src/java.base/share/classes/java/nio/channels/ServerSocketChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, 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 @@ -328,7 +328,7 @@ public abstract class ServerSocketChannel * If another thread interrupts the current thread * while the accept operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws NotYetBoundException * If this channel's socket has not yet been bound diff --git a/src/java.base/share/classes/java/nio/channels/SocketChannel.java b/src/java.base/share/classes/java/nio/channels/SocketChannel.java index 26878ab4006..493f9e88ebf 100644 --- a/src/java.base/share/classes/java/nio/channels/SocketChannel.java +++ b/src/java.base/share/classes/java/nio/channels/SocketChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, 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 @@ -250,7 +250,7 @@ public abstract class SocketChannel * If another thread interrupts the current thread * while the connect operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws UnresolvedAddressException * If the given remote address is an InetSocketAddress that is not fully @@ -485,7 +485,7 @@ public abstract class SocketChannel * If another thread interrupts the current thread * while the connect operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws UnresolvedAddressException * If the given remote address is an InetSocketAddress that is not fully resolved @@ -542,7 +542,7 @@ public abstract class SocketChannel * If another thread interrupts the current thread * while the connect operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws IOException * If some other I/O error occurs 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..063cd5eb938 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 @@ -95,7 +98,7 @@ public interface WritableByteChannel * If another thread interrupts the current thread * while the write operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws IOException * If some other I/O error occurs diff --git a/src/java.base/share/classes/java/time/Duration.java b/src/java.base/share/classes/java/time/Duration.java index 23577a8a634..7b3289a1f59 100644 --- a/src/java.base/share/classes/java/time/Duration.java +++ b/src/java.base/share/classes/java/time/Duration.java @@ -138,6 +138,37 @@ public final class Duration * Constant for a duration of zero. */ public static final Duration ZERO = new Duration(0, 0); + /** + * The minimum supported {@code Duration}, which is {@link Long#MIN_VALUE} + * seconds. + * + * @apiNote This constant represents the smallest possible instance of + * {@code Duration}. Since {@code Duration} is directed, the smallest + * possible duration is negative. + * + * The constant is intended to be used as a sentinel value or in tests. + * Care should be taken when performing arithmetic on {@code MIN} as there + * is a high risk that {@link ArithmeticException} or {@link DateTimeException} + * will be thrown. + * + * @since 26 + */ + public static final Duration MIN = new Duration(Long.MIN_VALUE, 0); + /** + * The maximum supported {@code Duration}, which is {@link Long#MAX_VALUE} + * seconds and {@code 999,999,999} nanoseconds. + * + * @apiNote This constant represents the largest possible instance of + * {@code Duration}. + * + * The constant is intended to be used as a sentinel value or in tests. + * Care should be taken when performing arithmetic on {@code MAX} as there + * is a high risk that {@link ArithmeticException} or {@link DateTimeException} + * will be thrown. + * + * @since 26 + */ + public static final Duration MAX = new Duration(Long.MAX_VALUE, 999_999_999); /** * Serialization version. */ diff --git a/src/java.base/share/classes/java/time/Instant.java b/src/java.base/share/classes/java/time/Instant.java index db8db4aaa38..640743f0fb7 100644 --- a/src/java.base/share/classes/java/time/Instant.java +++ b/src/java.base/share/classes/java/time/Instant.java @@ -788,6 +788,32 @@ public final class Instant return (Instant) amountToAdd.addTo(this); } + /** + * Returns a copy of this instant with the specified duration added, with + * saturated semantics. + *

    + * If the result is "earlier" than {@link Instant#MIN}, this method returns + * {@code MIN}. If the result is "later" than {@link Instant#MAX}, it + * returns {@code MAX}. Otherwise it returns {@link #plus(TemporalAmount) plus(duration)}. + * + * @apiNote This method can be used to calculate a deadline from + * this instant and a timeout. Unlike {@code plus(duration)}, + * this method never throws {@link ArithmeticException} or {@link DateTimeException} + * due to numeric overflow or {@code Instant} range violation. + * + * @param duration the duration to add, not null + * @return an {@code Instant} based on this instant with the addition made, not null + * + * @since 26 + */ + public Instant plusSaturating(Duration duration) { + if (duration.isNegative()) { + return until(Instant.MIN).compareTo(duration) >= 0 ? Instant.MIN : plus(duration); + } else { + return until(Instant.MAX).compareTo(duration) <= 0 ? Instant.MAX : plus(duration); + } + } + /** * Returns a copy of this instant with the specified amount added. *

    diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java index 26192b8e178..16d7193c556 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java @@ -1904,11 +1904,11 @@ public final class DateTimeFormatter { try { DateTimePrintContext context = new DateTimePrintContext(temporal, this); if (appendable instanceof StringBuilder) { - printerParser.format(context, (StringBuilder) appendable); + printerParser.format(context, (StringBuilder) appendable, false); } else { // buffer output to avoid writing to appendable in case of error StringBuilder buf = new StringBuilder(32); - printerParser.format(context, buf); + printerParser.format(context, buf, false); appendable.append(buf); } } catch (IOException ex) { diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index 9f5b82775b9..4708094effb 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2484,10 +2485,15 @@ public final class DateTimeFormatterBuilder { * * @param context the context to format using, not null * @param buf the buffer to append to, not null + * @param optional whether the enclosing formatter is optional. + * If true and this formatter is nested in an optional formatter + * and the data is not available, then no error is returned and + * nothing is appended to the buffer. If false and the data is not available + * then an exception is thrown or false is returned as appropriate. * @return false if unable to query the value from the date-time, true otherwise * @throws DateTimeException if the date-time cannot be printed successfully */ - boolean format(DateTimePrintContext context, StringBuilder buf); + boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional); /** * Parses text into date-time information. @@ -2537,21 +2543,13 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { int length = buf.length(); - if (optional) { - context.startOptional(); - } - try { - for (DateTimePrinterParser pp : printerParsers) { - if (pp.format(context, buf) == false) { - buf.setLength(length); // reset buffer - return true; - } - } - } finally { - if (optional) { - context.endOptional(); + boolean effectiveOptional = optional | this.optional; + for (DateTimePrinterParser pp : printerParsers) { + if (!pp.format(context, buf, effectiveOptional)) { + buf.setLength(length); // reset buffer + return true; } } return true; @@ -2620,9 +2618,9 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { int preLen = buf.length(); - if (printerParser.format(context, buf) == false) { + if (printerParser.format(context, buf, optional) == false) { return false; } int len = buf.length() - preLen; @@ -2689,7 +2687,7 @@ public final class DateTimeFormatterBuilder { LENIENT; @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { return true; // nothing to do here } @@ -2731,7 +2729,7 @@ public final class DateTimeFormatterBuilder { this.value = value; } - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { return true; } @@ -2757,7 +2755,7 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { buf.append(literal); return true; } @@ -2807,7 +2805,7 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { buf.append(literal); return true; } @@ -2919,8 +2917,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Long valueLong = context.getValue(field); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Long valueLong = context.getValue(field, optional); if (valueLong == null) { return false; } @@ -3374,8 +3372,8 @@ public final class DateTimeFormatterBuilder { }; @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Long value = context.getValue(field); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Long value = context.getValue(field, optional); if (value == null) { return false; } @@ -3563,8 +3561,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Long value = context.getValue(field); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Long value = context.getValue(field, optional); if (value == null) { return false; } @@ -3711,8 +3709,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Long value = context.getValue(field); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Long value = context.getValue(field, optional); if (value == null) { return false; } @@ -3724,7 +3722,7 @@ public final class DateTimeFormatterBuilder { text = provider.getText(chrono, field, value, textStyle, context.getLocale()); } if (text == null) { - return numberPrinterParser().format(context, buf); + return numberPrinterParser().format(context, buf, optional); } buf.append(text); return true; @@ -3806,9 +3804,9 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { // use INSTANT_SECONDS, thus this code is not bound by Instant.MAX - Long inSecs = context.getValue(INSTANT_SECONDS); + Long inSecs = context.getValue(INSTANT_SECONDS, optional); Long inNanos = null; if (context.getTemporal().isSupported(NANO_OF_SECOND)) { inNanos = context.getTemporal().getLong(NANO_OF_SECOND); @@ -3992,8 +3990,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Long offsetSecs = context.getValue(OFFSET_SECONDS); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Long offsetSecs = context.getValue(OFFSET_SECONDS, optional); if (offsetSecs == null) { return false; } @@ -4291,8 +4289,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Long offsetSecs = context.getValue(OFFSET_SECONDS); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Long offsetSecs = context.getValue(OFFSET_SECONDS, optional); if (offsetSecs == null) { return false; } @@ -4505,8 +4503,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - ZoneId zone = context.getValue(TemporalQueries.zoneId()); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + ZoneId zone = context.getValue(TemporalQueries.zoneId(), optional); if (zone == null) { return false; } @@ -4627,8 +4625,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - ZoneId zone = context.getValue(query); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + ZoneId zone = context.getValue(query, optional); if (zone == null) { return false; } @@ -5052,8 +5050,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Chronology chrono = context.getValue(TemporalQueries.chronology()); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Chronology chrono = context.getValue(TemporalQueries.chronology(), optional); if (chrono == null) { return false; } @@ -5149,9 +5147,9 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { Chronology chrono = Chronology.from(context.getTemporal()); - return formatter(context.getLocale(), chrono).toPrinterParser(false).format(context, buf); + return formatter(context.getLocale(), chrono).toPrinterParser(false).format(context, buf, optional); } @Override @@ -5260,8 +5258,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - return printerParser(context.getLocale()).format(context, buf); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + return printerParser(context.getLocale()).format(context, buf, optional); } @Override @@ -5364,12 +5362,12 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Long hod = context.getValue(HOUR_OF_DAY); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Long hod = context.getValue(HOUR_OF_DAY, optional); if (hod == null) { return false; } - Long moh = context.getValue(MINUTE_OF_HOUR); + Long moh = context.getValue(MINUTE_OF_HOUR, optional); long value = Math.floorMod(hod, 24) * 60 + (moh != null ? Math.floorMod(moh, 60) : 0); Locale locale = context.getLocale(); LocaleStore store = findDayPeriodStore(locale); diff --git a/src/java.base/share/classes/java/time/format/DateTimePrintContext.java b/src/java.base/share/classes/java/time/format/DateTimePrintContext.java index d755ba3ee78..051796b6a9c 100644 --- a/src/java.base/share/classes/java/time/format/DateTimePrintContext.java +++ b/src/java.base/share/classes/java/time/format/DateTimePrintContext.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -86,11 +87,6 @@ import java.util.Objects; *

    * This class provides a single wrapper to items used in the format. * - * @implSpec - * This class is a mutable context intended for use from a single thread. - * Usage of the class is thread-safe within standard printing as the framework creates - * a new instance of the class for each format and printing is single-threaded. - * * @since 1.8 */ final class DateTimePrintContext { @@ -103,10 +99,6 @@ final class DateTimePrintContext { * The formatter, not null. */ private final DateTimeFormatter formatter; - /** - * Whether the current formatter is optional. - */ - private int optional; /** * Creates a new instance of the context. @@ -115,7 +107,6 @@ final class DateTimePrintContext { * @param formatter the formatter controlling the format, not null */ DateTimePrintContext(TemporalAccessor temporal, DateTimeFormatter formatter) { - super(); this.temporal = adjust(temporal, formatter); this.formatter = formatter; } @@ -348,30 +339,17 @@ final class DateTimePrintContext { } //----------------------------------------------------------------------- - /** - * Starts the printing of an optional segment of the input. - */ - void startOptional() { - this.optional++; - } - - /** - * Ends the printing of an optional segment of the input. - */ - void endOptional() { - this.optional--; - } - /** * Gets a value using a query. * * @param query the query to use, not null + * @param optional whether the query is optional, true if the query may be missing * @return the result, null if not found and optional is true * @throws DateTimeException if the type is not available and the section is not optional */ - R getValue(TemporalQuery query) { + R getValue(TemporalQuery query, boolean optional) { R result = temporal.query(query); - if (result == null && optional == 0) { + if (result == null && !optional) { throw new DateTimeException("Unable to extract " + query + " from temporal " + temporal); } @@ -384,11 +362,12 @@ final class DateTimePrintContext { * This will return the value for the specified field. * * @param field the field to find, not null + * @param optional whether the field is optional, true if the field may be missing * @return the value, null if not found and optional is true * @throws DateTimeException if the field is not available and the section is not optional */ - Long getValue(TemporalField field) { - if (optional > 0 && !temporal.isSupported(field)) { + Long getValue(TemporalField field, boolean optional) { + if (optional && !temporal.isSupported(field)) { return null; } return temporal.getLong(field); 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/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index a55ddee648e..452b621ea05 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -204,15 +204,18 @@ import sun.util.locale.provider.TimeZoneNameUtility; * key="x"/value="java-1-7" * * - * BCP 47 deviation: Although BCP 47 requires field values to be registered - * in the IANA Language Subtag Registry, the {@code Locale} class - * does not validate this requirement. For example, the variant code "foobar" - * is well-formed since it is composed of 5 to 8 alphanumerics, but is not defined - * the IANA Language Subtag Registry. The {@link Builder} - * only checks if an individual field satisfies the syntactic - * requirement (is well-formed), but does not validate the value - * itself. Conversely, {@link #of(String, String, String) Locale::of} and its - * overloads do not make any syntactic checks on the input. + * BCP 47 deviation: BCP47 defines the following two levels of + * conformance, + * "valid" and "well-formed". A valid tag requires that it is well-formed, its + * subtag values are registered in the IANA Language Subtag Registry, and it does not + * contain duplicate variant or extension singleton subtags. The {@code Locale} + * class does not enforce that subtags are registered in the Subtag Registry. + * {@link Builder} only checks if an individual field satisfies the syntactic + * requirement (is well-formed). When passed duplicate variants, {@code Builder} + * accepts and includes them. When passed duplicate extension singletons, {@code + * Builder} accepts but ignores the duplicate key and its associated value. + * Conversely, {@link #of(String, String, String) Locale::of} and its + * overloads do not check if the input is well-formed at all. * *

    Unicode BCP 47 U Extension

    * @@ -246,7 +249,11 @@ import sun.util.locale.provider.TimeZoneNameUtility; * can be empty, or a series of subtags 3-8 alphanums in length). A * well-formed locale attribute has the form * {@code [0-9a-zA-Z]{3,8}} (it is a single subtag with the same - * form as a locale type subtag). + * form as a locale type subtag). Duplicate locale attributes as well + * as locale keys do not convey meaning. For methods in {@code Locale} and + * {@code Locale.Builder} that accept extensions, occurrences of duplicate + * locale attributes as well as locale keys and their associated type are accepted + * but ignored. * *

    The Unicode locale extension specifies optional behavior in * locale-sensitive services. Although the LDML specification defines @@ -561,6 +568,8 @@ import sun.util.locale.provider.TimeZoneNameUtility; * RFC 4647: Matching of Language Tags * @spec https://www.rfc-editor.org/info/rfc5646 * RFC 5646: Tags for Identifying Languages + * @spec https://www.rfc-editor.org/info/rfc6067 + * RFC 6067: BCP 47 Extension U * @spec https://www.unicode.org/reports/tr35 * Unicode Locale Data Markup Language (LDML) * @see Builder @@ -1743,6 +1752,12 @@ public final class Locale implements Cloneable, Serializable { * to {@link Locale.Builder#setLanguageTag(String)} which throws an exception * in this case. * + *

    Duplicate variants are accepted and included by the builder. + * However, duplicate extension singleton keys and their associated type + * are accepted but ignored. The same behavior applies to duplicate locale + * keys and attributes within a U extension. Note that subsequent subtags after + * the occurrence of a duplicate are not ignored. + * *

    The following conversions are performed:

      * *
    • The language code "und" is mapped to language "". @@ -2717,6 +2732,12 @@ public final class Locale implements Cloneable, Serializable { * just discards ill-formed and following portions of the * tag). * + *

      Duplicate variants are accepted and included by the builder. + * However, duplicate extension singleton keys and their associated type + * are accepted but ignored. The same behavior applies to duplicate locale + * keys and attributes within a U extension. Note that subsequent subtags after + * the occurrence of a duplicate are not ignored. + * *

      See {@link Locale##langtag_conversions converions} for a full list * of conversions that are performed on {@code languageTag}. * @@ -2808,7 +2829,8 @@ public final class Locale implements Cloneable, Serializable { * Sets the variant. If variant is null or the empty string, the * variant in this {@code Builder} is removed. Otherwise, it * must consist of one or more {@linkplain Locale##def_variant well-formed} - * subtags, or an exception is thrown. + * subtags, or an exception is thrown. Duplicate variants are + * accepted and included by the builder. * *

      Note: This method checks if {@code variant} * satisfies the IETF BCP 47 variant subtag's syntax requirements, @@ -2841,7 +2863,8 @@ public final class Locale implements Cloneable, Serializable { *

      Note: The key {@link #UNICODE_LOCALE_EXTENSION * UNICODE_LOCALE_EXTENSION} ('u') is used for the Unicode locale extension. * Setting a value for this key replaces any existing Unicode locale key/type - * pairs with those defined in the extension. + * pairs with those defined in the extension. Duplicate locale attributes + * as well as locale keys and their associated type are accepted but ignored. * *

      Note: The key {@link #PRIVATE_USE_EXTENSION * PRIVATE_USE_EXTENSION} ('x') is used for the private use code. To be diff --git a/src/java.base/share/classes/java/util/concurrent/ExecutorService.java b/src/java.base/share/classes/java/util/concurrent/ExecutorService.java index f899b56b288..7b5ca34ac0d 100644 --- a/src/java.base/share/classes/java/util/concurrent/ExecutorService.java +++ b/src/java.base/share/classes/java/util/concurrent/ExecutorService.java @@ -133,7 +133,7 @@ import java.util.List; * } catch (InterruptedException ex) { * // (Re-)Cancel if current thread also interrupted * pool.shutdownNow(); - * // Preserve interrupt status + * // Preserve interrupted status * Thread.currentThread().interrupt(); * } * }} @@ -375,7 +375,7 @@ public interface ExecutorService extends Executor, AutoCloseable { *

      If interrupted while waiting, this method stops all executing tasks as * if by invoking {@link #shutdownNow()}. It then continues to wait until all * actively executing tasks have completed. Tasks that were awaiting - * execution are not executed. The interrupt status will be re-asserted + * execution are not executed. The interrupted status will be re-asserted * before this method returns. * *

      If already terminated, invoking this method has no effect. diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java index 482fe3cf801..1f2c8d2ffa6 100644 --- a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java +++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java @@ -875,7 +875,7 @@ public class ForkJoinPool extends AbstractExecutorService * ==================== * * Regular ForkJoinTasks manage task cancellation (method cancel) - * independently from the interrupt status of threads running + * independently from the interrupted status of threads running * tasks. Interrupts are issued internally only while * terminating, to wake up workers and cancel queued tasks. By * default, interrupts are cleared only when necessary to ensure @@ -900,7 +900,7 @@ public class ForkJoinPool extends AbstractExecutorService * with results accessed via join() differ from those via get(), * which differ from those invoked using pool submit methods by * non-workers (which comply with Future.get() specs). Internal - * usages of ForkJoinTasks ignore interrupt status when executing + * usages of ForkJoinTasks ignore interrupted status when executing * or awaiting completion. Otherwise, reporting task results or * exceptions is preferred to throwing InterruptedExceptions, * which are in turn preferred to timeouts. Similarly, completion @@ -4171,7 +4171,7 @@ public class ForkJoinPool extends AbstractExecutorService * method stops all executing tasks as if by invoking {@link * #shutdownNow()}. It then continues to wait until all actively * executing tasks have completed. Tasks that were awaiting - * execution are not executed. The interrupt status will be + * execution are not executed. The interrupted status will be * re-asserted before this method returns. * * @since 19 diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java index b942d3ecd09..566fc417952 100644 --- a/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java +++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java @@ -267,10 +267,8 @@ public class ForkJoinWorkerThread extends Thread { @Override // to record changes public void setContextClassLoader(ClassLoader cl) { - if (ClassLoader.getSystemClassLoader() != cl) { - resetCCL = true; - super.setContextClassLoader(cl); - } + resetCCL = ClassLoader.getSystemClassLoader() != cl; + super.setContextClassLoader(cl); } @Override // to re-establish CCL if necessary diff --git a/src/java.base/share/classes/java/util/concurrent/FutureTask.java b/src/java.base/share/classes/java/util/concurrent/FutureTask.java index 2ec97629105..a571cb77cce 100644 --- a/src/java.base/share/classes/java/util/concurrent/FutureTask.java +++ b/src/java.base/share/classes/java/util/concurrent/FutureTask.java @@ -69,7 +69,7 @@ public class FutureTask implements RunnableFuture { /* * Revision notes: This differs from previous versions of this * class that relied on AbstractQueuedSynchronizer, mainly to - * avoid surprising users about retaining interrupt status during + * avoid surprising users about retaining interrupted status during * cancellation races. Sync control in the current design relies * on a "state" field updated via CAS to track completion, along * with a simple Treiber stack to hold waiting threads. diff --git a/src/java.base/share/classes/java/util/concurrent/Semaphore.java b/src/java.base/share/classes/java/util/concurrent/Semaphore.java index 0e7a9ccc0b3..fce0c39cb78 100644 --- a/src/java.base/share/classes/java/util/concurrent/Semaphore.java +++ b/src/java.base/share/classes/java/util/concurrent/Semaphore.java @@ -334,7 +334,7 @@ public class Semaphore implements java.io.Serializable { * while waiting for a permit then it will continue to wait, but the * time at which the thread is assigned a permit may change compared to * the time it would have received the permit had no interruption - * occurred. When the thread does return from this method its interrupt + * occurred. When the thread does return from this method its interrupted * status will be set. */ public void acquireUninterruptibly() { @@ -494,7 +494,7 @@ public class Semaphore implements java.io.Serializable { *

      If the current thread is {@linkplain Thread#interrupt interrupted} * while waiting for permits then it will continue to wait and its * position in the queue is not affected. When the thread does return - * from this method its interrupt status will be set. + * from this method its interrupted status will be set. * * @param permits the number of permits to acquire * @throws IllegalArgumentException if {@code permits} is negative diff --git a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java index c660af6a0ba..ba81123fc35 100644 --- a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java +++ b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java @@ -652,7 +652,7 @@ public abstract class AbstractQueuedLongSynchronizer /** * Acquires in exclusive mode, aborting if interrupted. - * Implemented by first checking interrupt status, then invoking + * Implemented by first checking interrupted status, then invoking * at least once {@link #tryAcquire}, returning on * success. Otherwise the thread is queued, possibly repeatedly * blocking and unblocking, invoking {@link #tryAcquire} @@ -674,7 +674,7 @@ public abstract class AbstractQueuedLongSynchronizer /** * Attempts to acquire in exclusive mode, aborting if interrupted, * and failing if the given timeout elapses. Implemented by first - * checking interrupt status, then invoking at least once {@link + * checking interrupted status, then invoking at least once {@link * #tryAcquire}, returning on success. Otherwise, the thread is * queued, possibly repeatedly blocking and unblocking, invoking * {@link #tryAcquire} until success or the thread is interrupted @@ -741,7 +741,7 @@ public abstract class AbstractQueuedLongSynchronizer /** * Acquires in shared mode, aborting if interrupted. Implemented - * by first checking interrupt status, then invoking at least once + * by first checking interrupted status, then invoking at least once * {@link #tryAcquireShared}, returning on success. Otherwise the * thread is queued, possibly repeatedly blocking and unblocking, * invoking {@link #tryAcquireShared} until success or the thread @@ -763,7 +763,7 @@ public abstract class AbstractQueuedLongSynchronizer /** * Attempts to acquire in shared mode, aborting if interrupted, and * failing if the given timeout elapses. Implemented by first - * checking interrupt status, then invoking at least once {@link + * checking interrupted status, then invoking at least once {@link * #tryAcquireShared}, returning on success. Otherwise, the * thread is queued, possibly repeatedly blocking and unblocking, * invoking {@link #tryAcquireShared} until success or the thread diff --git a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java index 0ff216c80a0..c0779545083 100644 --- a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java +++ b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java @@ -1032,7 +1032,7 @@ public abstract class AbstractQueuedSynchronizer /** * Acquires in exclusive mode, aborting if interrupted. - * Implemented by first checking interrupt status, then invoking + * Implemented by first checking interrupted status, then invoking * at least once {@link #tryAcquire}, returning on * success. Otherwise the thread is queued, possibly repeatedly * blocking and unblocking, invoking {@link #tryAcquire} @@ -1054,7 +1054,7 @@ public abstract class AbstractQueuedSynchronizer /** * Attempts to acquire in exclusive mode, aborting if interrupted, * and failing if the given timeout elapses. Implemented by first - * checking interrupt status, then invoking at least once {@link + * checking interrupted status, then invoking at least once {@link * #tryAcquire}, returning on success. Otherwise, the thread is * queued, possibly repeatedly blocking and unblocking, invoking * {@link #tryAcquire} until success or the thread is interrupted @@ -1121,7 +1121,7 @@ public abstract class AbstractQueuedSynchronizer /** * Acquires in shared mode, aborting if interrupted. Implemented - * by first checking interrupt status, then invoking at least once + * by first checking interrupted status, then invoking at least once * {@link #tryAcquireShared}, returning on success. Otherwise the * thread is queued, possibly repeatedly blocking and unblocking, * invoking {@link #tryAcquireShared} until success or the thread @@ -1143,7 +1143,7 @@ public abstract class AbstractQueuedSynchronizer /** * Attempts to acquire in shared mode, aborting if interrupted, and * failing if the given timeout elapses. Implemented by first - * checking interrupt status, then invoking at least once {@link + * checking interrupted status, then invoking at least once {@link * #tryAcquireShared}, returning on success. Otherwise, the * thread is queued, possibly repeatedly blocking and unblocking, * invoking {@link #tryAcquireShared} until success or the thread diff --git a/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java b/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java index 917678b5f1e..38531c80a30 100644 --- a/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java +++ b/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java @@ -121,7 +121,7 @@ import jdk.internal.misc.Unsafe; * } * * waiters.remove(); - * // ensure correct interrupt status on return + * // ensure correct interrupted status on return * if (wasInterrupted) * Thread.currentThread().interrupt(); * } @@ -207,7 +207,7 @@ public final class LockSupport { *

      This method does not report which of these caused the * method to return. Callers should re-check the conditions which caused * the thread to park in the first place. Callers may also determine, - * for example, the interrupt status of the thread upon return. + * for example, the interrupted status of the thread upon return. * * @param blocker the synchronization object responsible for this * thread parking @@ -252,7 +252,7 @@ public final class LockSupport { *

      This method does not report which of these caused the * method to return. Callers should re-check the conditions which caused * the thread to park in the first place. Callers may also determine, - * for example, the interrupt status of the thread, or the elapsed time + * for example, the interrupted status of the thread, or the elapsed time * upon return. * * @param blocker the synchronization object responsible for this @@ -300,7 +300,7 @@ public final class LockSupport { *

      This method does not report which of these caused the * method to return. Callers should re-check the conditions which caused * the thread to park in the first place. Callers may also determine, - * for example, the interrupt status of the thread, or the current time + * for example, the interrupted status of the thread, or the current time * upon return. * * @param blocker the synchronization object responsible for this @@ -360,7 +360,7 @@ public final class LockSupport { *

      This method does not report which of these caused the * method to return. Callers should re-check the conditions which caused * the thread to park in the first place. Callers may also determine, - * for example, the interrupt status of the thread upon return. + * for example, the interrupted status of the thread upon return. */ public static void park() { if (Thread.currentThread().isVirtual()) { @@ -395,7 +395,7 @@ public final class LockSupport { *

      This method does not report which of these caused the * method to return. Callers should re-check the conditions which caused * the thread to park in the first place. Callers may also determine, - * for example, the interrupt status of the thread, or the elapsed time + * for example, the interrupted status of the thread, or the elapsed time * upon return. * * @param nanos the maximum number of nanoseconds to wait @@ -434,7 +434,7 @@ public final class LockSupport { *

      This method does not report which of these caused the * method to return. Callers should re-check the conditions which caused * the thread to park in the first place. Callers may also determine, - * for example, the interrupt status of the thread, or the current time + * for example, the interrupted status of the thread, or the current time * upon return. * * @param deadline the absolute time, in milliseconds from the Epoch, diff --git a/src/java.base/share/classes/javax/crypto/Mac.java b/src/java.base/share/classes/javax/crypto/Mac.java index 82874693cf2..4405e39d7a0 100644 --- a/src/java.base/share/classes/javax/crypto/Mac.java +++ b/src/java.base/share/classes/javax/crypto/Mac.java @@ -627,6 +627,7 @@ public class Mac implements Cloneable { } byte[] mac = doFinal(); System.arraycopy(mac, 0, output, outOffset, macLen); + Arrays.fill(mac, (byte)0); } /** diff --git a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java index e20a1e77423..b0a71529fa7 100644 --- a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java +++ b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java @@ -25,6 +25,7 @@ package jdk.internal.access; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.Stable; import javax.crypto.SealedObject; @@ -58,8 +59,20 @@ import javax.security.auth.x500.X500Principal; * increased complexity and lack of sustainability. * Use this only as a last resort! * + * + *

      Notes on the @AOTSafeClassInitializer annotation: + * + *

      All static fields in SharedSecrets that are initialized in the AOT + * assembly phase must be stateless (as checked by the HotSpot C++ class + * CDSHeapVerifier::SharedSecretsAccessorFinder) so they can be safely + * stored in the AOT cache. + * + *

      Static fields such as javaObjectInputFilterAccess point to a Lambda + * which is not stateless. The AOT assembly phase must not execute any Java + * code that would lead to the initialization of such fields, or else the AOT + * cache creation will fail. */ - +@AOTSafeClassInitializer public class SharedSecrets { // This field is not necessarily stable private static JavaAWTFontAccess javaAWTFontAccess; diff --git a/src/java.base/share/classes/jdk/internal/loader/Resource.java b/src/java.base/share/classes/jdk/internal/loader/Resource.java index b72f4df7d52..312dfc859aa 100644 --- a/src/java.base/share/classes/jdk/internal/loader/Resource.java +++ b/src/java.base/share/classes/jdk/internal/loader/Resource.java @@ -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 @@ -33,7 +33,6 @@ import java.security.CodeSigner; import java.util.jar.Manifest; import java.nio.ByteBuffer; import java.util.Arrays; -import sun.nio.ByteBuffered; /** * This class is used to represent a Resource that has been loaded @@ -130,10 +129,6 @@ public abstract class Resource { * @return Resource data or null. */ public ByteBuffer getByteBuffer() throws IOException { - InputStream in = cachedInputStream(); - if (in instanceof ByteBuffered) { - return ((ByteBuffered)in).getByteBuffer(); - } return null; } 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 @@ import static java.lang.Double.SIZE; /** * 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 @@ import static java.lang.Float.SIZE; /** * 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/misc/ThreadFlock.java b/src/java.base/share/classes/jdk/internal/misc/ThreadFlock.java index 423ffa03d31..32f6d5d4905 100644 --- a/src/java.base/share/classes/jdk/internal/misc/ThreadFlock.java +++ b/src/java.base/share/classes/jdk/internal/misc/ThreadFlock.java @@ -379,7 +379,7 @@ public class ThreadFlock implements AutoCloseable { *

      This method may only be invoked by the flock owner. * *

      If interrupted then this method continues to wait until all threads - * finish, before completing with the interrupt status set. + * finish, before completing with the interrupted status set. * *

      A ThreadFlock is intended to be used in a structured manner. If * this method is called to close a flock before nested flocks are closed then it diff --git a/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java b/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java index ce837027faa..d24cc77600c 100644 --- a/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java +++ b/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, 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 @@ -467,8 +467,10 @@ public final class ModulePatcher { } @Override public ByteBuffer getByteBuffer() throws IOException { - byte[] bytes = getInputStream().readAllBytes(); - return ByteBuffer.wrap(bytes); + try (InputStream in = getInputStream()) { + byte[] bytes = in.readAllBytes(); + return ByteBuffer.wrap(bytes); + } } @Override public InputStream getInputStream() throws IOException { diff --git a/src/java.base/share/classes/sun/net/www/URLConnection.java b/src/java.base/share/classes/sun/net/www/URLConnection.java index 66005ab9b2a..becbf88da73 100644 --- a/src/java.base/share/classes/sun/net/www/URLConnection.java +++ b/src/java.base/share/classes/sun/net/www/URLConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 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 @@ -101,9 +101,21 @@ public abstract class URLConnection extends java.net.URLConnection { return Collections.emptyMap(); } + /** + * This method is called whenever the headers related methods are called on the + * {@code URLConnection}. This method does any necessary checks and initializations + * to make sure that the headers can be served. If this {@code URLConnection} cannot + * serve the headers, then this method throws an {@code IOException}. + * + * @throws IOException if the headers cannot be served + */ + protected void ensureCanServeHeaders() throws IOException { + getInputStream(); + } + public String getHeaderField(String name) { try { - getInputStream(); + ensureCanServeHeaders(); } catch (Exception e) { return null; } @@ -111,13 +123,13 @@ public abstract class URLConnection extends java.net.URLConnection { } - Map> headerFields; + private Map> headerFields; @Override public Map> getHeaderFields() { if (headerFields == null) { try { - getInputStream(); + ensureCanServeHeaders(); if (properties == null) { headerFields = super.getHeaderFields(); } else { @@ -137,7 +149,7 @@ public abstract class URLConnection extends java.net.URLConnection { */ public String getHeaderFieldKey(int n) { try { - getInputStream(); + ensureCanServeHeaders(); } catch (Exception e) { return null; } @@ -152,7 +164,7 @@ public abstract class URLConnection extends java.net.URLConnection { */ public String getHeaderField(int n) { try { - getInputStream(); + ensureCanServeHeaders(); } catch (Exception e) { return null; } @@ -221,7 +233,7 @@ public abstract class URLConnection extends java.net.URLConnection { */ public int getContentLength() { try { - getInputStream(); + ensureCanServeHeaders(); } catch (Exception e) { return -1; } diff --git a/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java index fc947f8977f..8330c2b9b80 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java @@ -25,15 +25,30 @@ package sun.net.www.protocol.file; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FilePermission; +import java.io.IOException; +import java.io.InputStream; +import java.net.FileNameMap; import java.net.MalformedURLException; import java.net.URL; -import java.net.FileNameMap; -import java.io.*; -import java.text.Collator; import java.security.Permission; -import sun.net.www.*; -import java.util.*; +import java.text.Collator; import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; + +import sun.net.www.MessageHeader; +import sun.net.www.ParseUtil; +import sun.net.www.URLConnection; /** * Open a file input stream given a URL. @@ -67,25 +82,49 @@ public class FileURLConnection extends URLConnection { this.file = file; } - /* + /** + * If already connected, then this method is a no-op. + * If not already connected, then this method does + * readability checks for the File. + *

      + * If the File is a directory then the readability check + * is done by verifying that File.list() does not return + * null. On the other hand, if the File is not a directory, + * then this method constructs a temporary FileInputStream + * for the File and lets the FileInputStream's constructor + * implementation do the necessary readability checks. + * That temporary FileInputStream is closed before returning + * from this method. + *

      + * In either case, if the readability checks fail, then + * an IOException is thrown from this method and the + * FileURLConnection stays unconnected. + *

      + * A normal return from this method implies that the + * FileURLConnection is connected and the readability + * checks have passed for the File. + *

      * Note: the semantics of FileURLConnection object is that the * results of the various URLConnection calls, such as * getContentType, getInputStream or getContentLength reflect * whatever was true when connect was called. */ + @Override public void connect() throws IOException { if (!connected) { - isDirectory = file.isDirectory(); + // verify readability of the directory or the regular file if (isDirectory) { String[] fileList = file.list(); - if (fileList == null) + if (fileList == null) { throw new FileNotFoundException(file.getPath() + " exists, but is not accessible"); + } directoryListing = Arrays.asList(fileList); } else { - is = new BufferedInputStream(new FileInputStream(file.getPath())); + // let FileInputStream constructor do the necessary readability checks + // and propagate any failures + new FileInputStream(file.getPath()).close(); } - connected = true; } } @@ -112,9 +151,9 @@ public class FileURLConnection extends URLConnection { FileNameMap map = java.net.URLConnection.getFileNameMap(); String contentType = map.getContentTypeFor(file.getPath()); if (contentType != null) { - properties.add(CONTENT_TYPE, contentType); + properties.set(CONTENT_TYPE, contentType); } - properties.add(CONTENT_LENGTH, Long.toString(length)); + properties.set(CONTENT_LENGTH, Long.toString(length)); /* * Format the last-modified field into the preferred @@ -126,30 +165,34 @@ public class FileURLConnection extends URLConnection { SimpleDateFormat fo = new SimpleDateFormat ("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US); fo.setTimeZone(TimeZone.getTimeZone("GMT")); - properties.add(LAST_MODIFIED, fo.format(date)); + properties.set(LAST_MODIFIED, fo.format(date)); } } else { - properties.add(CONTENT_TYPE, TEXT_PLAIN); + properties.set(CONTENT_TYPE, TEXT_PLAIN); } initializedHeaders = true; } } - public Map> getHeaderFields() { + @Override + public Map> getHeaderFields() { initializeHeaders(); return super.getHeaderFields(); } + @Override public String getHeaderField(String name) { initializeHeaders(); return super.getHeaderField(name); } + @Override public String getHeaderField(int n) { initializeHeaders(); return super.getHeaderField(n); } + @Override public int getContentLength() { initializeHeaders(); if (length > Integer.MAX_VALUE) @@ -157,54 +200,74 @@ public class FileURLConnection extends URLConnection { return (int) length; } + @Override public long getContentLengthLong() { initializeHeaders(); return length; } + @Override public String getHeaderFieldKey(int n) { initializeHeaders(); return super.getHeaderFieldKey(n); } + @Override public MessageHeader getProperties() { initializeHeaders(); return super.getProperties(); } + @Override public long getLastModified() { initializeHeaders(); return lastModified; } + @Override public synchronized InputStream getInputStream() throws IOException { connect(); + // connect() does the necessary readability checks and is expected to + // throw IOException if any of those checks fail. A normal completion of connect() + // must mean that connect succeeded. + assert connected : "not connected"; - if (is == null) { - if (isDirectory) { + // a FileURLConnection only ever creates and provides a single InputStream + if (is != null) { + return is; + } - if (directoryListing == null) { - throw new FileNotFoundException(file.getPath()); - } + if (isDirectory) { + // a successful connect() implies the directoryListing is non-null + // if the file is a directory + assert directoryListing != null : "missing directory listing"; - directoryListing.sort(Collator.getInstance()); + directoryListing.sort(Collator.getInstance()); - StringBuilder sb = new StringBuilder(); - for (String fileName : directoryListing) { - sb.append(fileName); - sb.append("\n"); - } - // Put it into a (default) locale-specific byte-stream. - is = new ByteArrayInputStream(sb.toString().getBytes()); - } else { - throw new FileNotFoundException(file.getPath()); + StringBuilder sb = new StringBuilder(); + for (String fileName : directoryListing) { + sb.append(fileName); + sb.append("\n"); } + // Put it into a (default) locale-specific byte-stream. + is = new ByteArrayInputStream(sb.toString().getBytes()); + } else { + is = new BufferedInputStream(new FileInputStream(file.getPath())); } return is; } + @Override + protected synchronized void ensureCanServeHeaders() throws IOException { + // connect() (if not already connected) does the readability checks + // and throws an IOException if those checks fail. A successful + // completion from connect() implies the File is readable. + connect(); + } + + Permission permission; /* since getOutputStream isn't supported, only read permission is diff --git a/src/java.base/share/classes/sun/nio/ByteBuffered.java b/src/java.base/share/classes/sun/nio/ByteBuffered.java deleted file mode 100644 index a547cefeb89..00000000000 --- a/src/java.base/share/classes/sun/nio/ByteBuffered.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.nio; - -import java.nio.ByteBuffer; -import java.io.IOException; - -/** - * This is an interface to adapt existing APIs to use {@link java.nio.ByteBuffer - * ByteBuffers} as the underlying data format. Only the initial producer and - * final consumer have to be changed. - * - *

      - * For example, the Zip/Jar code supports {@link java.io.InputStream InputStreams}. - * To make the Zip code use {@link java.nio.MappedByteBuffer MappedByteBuffers} as - * the underlying data structure, it can create a class of InputStream that wraps - * the ByteBuffer, and implements the ByteBuffered interface. A co-operating class - * several layers away can ask the InputStream if it is an instance of ByteBuffered, - * then call the {@link #getByteBuffer()} method. - */ -public interface ByteBuffered { - - /** - * Returns the {@code ByteBuffer} behind this object, if this particular - * instance has one. An implementation of {@code getByteBuffer()} is allowed - * to return {@code null} for any reason. - * - * @return The {@code ByteBuffer}, if this particular instance has one, - * or {@code null} otherwise. - * - * @throws IOException - * If the ByteBuffer is no longer valid. - * - * @since 1.5 - */ - public ByteBuffer getByteBuffer() throws IOException; -} diff --git a/src/java.base/share/classes/sun/nio/ch/Interruptible.java b/src/java.base/share/classes/sun/nio/ch/Interruptible.java index b5d9a7d2b3f..25f762a1d6a 100644 --- a/src/java.base/share/classes/sun/nio/ch/Interruptible.java +++ b/src/java.base/share/classes/sun/nio/ch/Interruptible.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, 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,14 +35,14 @@ public interface Interruptible { * Invoked by Thread.interrupt when the given Thread is interrupted. Thread.interrupt * invokes this method while holding the given Thread's interrupt lock. This method * is also invoked by AbstractInterruptibleChannel when beginning an I/O operation - * with the current thread's interrupt status set. This method must not block. + * with the current thread's interrupted status set. This method must not block. */ void interrupt(Thread target); /** * Invoked by Thread.interrupt after releasing the Thread's interrupt lock. * It may also be invoked by AbstractInterruptibleChannel or AbstractSelector when - * beginning an I/O operation with the current thread's interrupt status set, or at + * beginning an I/O operation with the current thread's interrupted status set, or at * the end of an I/O operation when any thread doing I/O on the channel (or selector) * has been interrupted. This method closes the channel (or wakes up the Selector) to * ensure that AsynchronousCloseException or ClosedByInterruptException is thrown. diff --git a/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java b/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java index b4ced428b33..16a6d5df003 100644 --- a/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java +++ b/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.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,25 +25,17 @@ package sun.nio.cs; -/* - * FastPath char[]/byte[] -> byte[] encoder, REPLACE on malformed input or - * unmappable input. +/** + * Fast-path for {@code byte[]}-to-{@code byte[]} encoding, + * {@link java.nio.charset.CodingErrorAction#REPLACE REPLACE} on malformed + * input, or unmappable input. */ - public interface ArrayEncoder { - // is only used by j.u.zip.ZipCoder for utf8 - int encode(char[] src, int off, int len, byte[] dst); + int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst, int dp); - default int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) { - return -1; - } + int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst, int dp); - default int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) { - return -1; - } + boolean isASCIICompatible(); - default boolean isASCIICompatible() { - return false; - } } diff --git a/src/java.base/share/classes/sun/nio/cs/CESU_8.java b/src/java.base/share/classes/sun/nio/cs/CESU_8.java index 9b907bcbc65..409b375ec88 100644 --- a/src/java.base/share/classes/sun/nio/cs/CESU_8.java +++ b/src/java.base/share/classes/sun/nio/cs/CESU_8.java @@ -394,8 +394,7 @@ class CESU_8 extends Unicode } } - private static class Encoder extends CharsetEncoder - implements ArrayEncoder { + private static class Encoder extends CharsetEncoder { private Encoder(Charset cs) { super(cs, 1.1f, 3.0f); @@ -544,48 +543,6 @@ class CESU_8 extends Unicode return encodeBufferLoop(src, dst); } - // returns -1 if there is malformed char(s) and the - // "action" for malformed input is not REPLACE. - public int encode(char[] sa, int sp, int len, byte[] da) { - int sl = sp + len; - int dp = 0; - - // Handle ASCII-only prefix - int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(len, da.length)); - sp += n; - dp += n; - - while (sp < sl) { - char c = sa[sp++]; - if (c < 0x80) { - // Have at most seven bits - da[dp++] = (byte)c; - } else if (c < 0x800) { - // 2 bytes, 11 bits - da[dp++] = (byte)(0xc0 | (c >> 6)); - da[dp++] = (byte)(0x80 | (c & 0x3f)); - } else if (Character.isSurrogate(c)) { - if (sgp == null) - sgp = new Surrogate.Parser(); - int uc = sgp.parse(c, sa, sp - 1, sl); - if (uc < 0) { - if (malformedInputAction() != CodingErrorAction.REPLACE) - return -1; - da[dp++] = replacement()[0]; - } else { - to3Bytes(da, dp, Character.highSurrogate(uc)); - dp += 3; - to3Bytes(da, dp, Character.lowSurrogate(uc)); - dp += 3; - sp++; // 2 chars - } - } else { - // 3 bytes, 16 bits - to3Bytes(da, dp, c); - dp += 3; - } - } - return dp; - } } + } diff --git a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java index 165e1e21c0f..0969669a35b 100644 --- a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java @@ -682,40 +682,7 @@ public class DoubleByte { } @Override - public int encode(char[] src, int sp, int len, byte[] dst) { - int dp = 0; - int sl = sp + len; - if (isASCIICompatible) { - int n = JLA.encodeASCII(src, sp, dst, dp, len); - sp += n; - dp += n; - } - while (sp < sl) { - char c = src[sp++]; - int bb = encodeChar(c); - if (bb == UNMAPPABLE_ENCODING) { - if (Character.isHighSurrogate(c) && sp < sl && - Character.isLowSurrogate(src[sp])) { - sp++; - } - dst[dp++] = repl[0]; - if (repl.length > 1) - dst[dp++] = repl[1]; - continue; - } //else - if (bb > MAX_SINGLEBYTE) { // DoubleByte - dst[dp++] = (byte)(bb >> 8); - dst[dp++] = (byte)bb; - } else { // SingleByte - dst[dp++] = (byte)bb; - } - } - return dp; - } - - @Override - public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) { - int dp = 0; + public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst, int dp) { int sl = sp + len; while (sp < sl) { char c = (char)(src[sp++] & 0xff); @@ -740,8 +707,7 @@ public class DoubleByte { } @Override - public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) { - int dp = 0; + public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst, int dp) { int sl = sp + len; while (sp < sl) { char c = StringUTF16.getChar(src, sp++); @@ -1000,49 +966,7 @@ public class DoubleByte { } @Override - public int encode(char[] src, int sp, int len, byte[] dst) { - int dp = 0; - int sl = sp + len; - while (sp < sl) { - char c = src[sp++]; - int bb = encodeChar(c); - - if (bb == UNMAPPABLE_ENCODING) { - if (Character.isHighSurrogate(c) && sp < sl && - Character.isLowSurrogate(src[sp])) { - sp++; - } - dst[dp++] = repl[0]; - if (repl.length > 1) - dst[dp++] = repl[1]; - continue; - } //else - if (bb > MAX_SINGLEBYTE) { // DoubleByte - if (currentState == SBCS) { - currentState = DBCS; - dst[dp++] = SO; - } - dst[dp++] = (byte)(bb >> 8); - dst[dp++] = (byte)bb; - } else { // SingleByte - if (currentState == DBCS) { - currentState = SBCS; - dst[dp++] = SI; - } - dst[dp++] = (byte)bb; - } - } - - if (currentState == DBCS) { - currentState = SBCS; - dst[dp++] = SI; - } - return dp; - } - - @Override - public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) { - int dp = 0; + public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst, int dp) { int sl = sp + len; while (sp < sl) { char c = (char)(src[sp++] & 0xff); @@ -1077,8 +1001,7 @@ public class DoubleByte { } @Override - public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) { - int dp = 0; + public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst, int dp) { int sl = sp + len; while (sp < sl) { char c = StringUTF16.getChar(src, sp++); diff --git a/src/java.base/share/classes/sun/nio/cs/HKSCS.java b/src/java.base/share/classes/sun/nio/cs/HKSCS.java index cfe9f879c04..f96fbf6be29 100644 --- a/src/java.base/share/classes/sun/nio/cs/HKSCS.java +++ b/src/java.base/share/classes/sun/nio/cs/HKSCS.java @@ -352,37 +352,9 @@ public class HKSCS { return encodeBufferLoop(src, dst); } - public int encode(char[] src, int sp, int len, byte[] dst) { - int dp = 0; + @Override + public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst, int dp) { int sl = sp + len; - while (sp < sl) { - char c = src[sp++]; - int bb = encodeChar(c); - if (bb == UNMAPPABLE_ENCODING) { - if (!Character.isHighSurrogate(c) || sp == sl || - !Character.isLowSurrogate(src[sp]) || - (bb = encodeSupp(Character.toCodePoint(c, src[sp++]))) - == UNMAPPABLE_ENCODING) { - dst[dp++] = repl[0]; - if (repl.length > 1) - dst[dp++] = repl[1]; - continue; - } - } - if (bb > MAX_SINGLEBYTE) { // DoubleByte - dst[dp++] = (byte)(bb >> 8); - dst[dp++] = (byte)bb; - } else { // SingleByte - dst[dp++] = (byte)bb; - } - } - return dp; - } - - public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) { - int dp = 0; - int sl = sp + len; - int dl = dst.length; while (sp < sl) { char c = StringUTF16.getChar(src, sp++); int bb = encodeChar(c); diff --git a/src/java.base/share/classes/sun/nio/cs/SingleByte.java b/src/java.base/share/classes/sun/nio/cs/SingleByte.java index 8efa6b295ff..a5bf06cb251 100644 --- a/src/java.base/share/classes/sun/nio/cs/SingleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/SingleByte.java @@ -290,32 +290,8 @@ public class SingleByte repl = newReplacement[0]; } - public int encode(char[] src, int sp, int len, byte[] dst) { - int dp = 0; - int sl = sp + Math.min(len, dst.length); - while (sp < sl) { - char c = src[sp++]; - int b = encode(c); - if (b != UNMAPPABLE_ENCODING) { - dst[dp++] = (byte)b; - continue; - } - if (Character.isHighSurrogate(c) && sp < sl && - Character.isLowSurrogate(src[sp])) { - if (len > dst.length) { - sl++; - len--; - } - sp++; - } - dst[dp++] = repl; - } - return dp; - } - @Override - public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) { - int dp = 0; + public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst, int dp) { int sl = sp + Math.min(len, dst.length); while (sp < sl) { char c = (char)(src[sp++] & 0xff); @@ -330,8 +306,7 @@ public class SingleByte } @Override - public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) { - int dp = 0; + public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst, int dp) { int sl = sp + Math.min(len, dst.length); while (sp < sl) { char c = StringUTF16.getChar(src, sp++); diff --git a/src/java.base/share/classes/sun/security/ec/ECDHKeyAgreement.java b/src/java.base/share/classes/sun/security/ec/ECDHKeyAgreement.java index 8f6bdb8d80a..714b06c93ba 100644 --- a/src/java.base/share/classes/sun/security/ec/ECDHKeyAgreement.java +++ b/src/java.base/share/classes/sun/security/ec/ECDHKeyAgreement.java @@ -52,6 +52,7 @@ import java.security.interfaces.ECPublicKey; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECParameterSpec; import java.security.spec.EllipticCurve; +import java.util.Arrays; import java.util.Optional; /** @@ -259,7 +260,12 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi { throw new NoSuchAlgorithmException( "Unsupported secret key algorithm: " + algorithm); } - return new SecretKeySpec(engineGenerateSecret(), algorithm); + byte[] bytes = engineGenerateSecret(); + try { + return new SecretKeySpec(bytes, algorithm); + } finally { + Arrays.fill(bytes, (byte)0); + } } private static diff --git a/src/java.base/share/classes/sun/security/ec/XDHKeyAgreement.java b/src/java.base/share/classes/sun/security/ec/XDHKeyAgreement.java index 01dce4c53e9..d7ea1c674e7 100644 --- a/src/java.base/share/classes/sun/security/ec/XDHKeyAgreement.java +++ b/src/java.base/share/classes/sun/security/ec/XDHKeyAgreement.java @@ -41,11 +41,12 @@ import javax.crypto.KeyAgreementSpi; import javax.crypto.SecretKey; import javax.crypto.ShortBufferException; import javax.crypto.spec.SecretKeySpec; +import java.util.Arrays; import java.util.function.Function; public class XDHKeyAgreement extends KeyAgreementSpi { - private byte[] privateKey; + private XECPrivateKey privateKey; private byte[] secret; private XECOperations ops; private XECParameters lockedParams = null; @@ -101,15 +102,16 @@ public class XDHKeyAgreement extends KeyAgreementSpi { throw new InvalidKeyException ("Unsupported key type"); } - XECPrivateKey privateKey = (XECPrivateKey) key; + privateKey = (XECPrivateKey) key; XECParameters xecParams = XECParameters.get( InvalidKeyException::new, privateKey.getParams()); checkLockedParams(InvalidKeyException::new, xecParams); this.ops = new XECOperations(xecParams); - this.privateKey = privateKey.getScalar().orElseThrow( + byte[] tmp = privateKey.getScalar().orElseThrow( () -> new InvalidKeyException("No private key value") ); + Arrays.fill(tmp, (byte)0); secret = null; } @@ -144,9 +146,11 @@ public class XDHKeyAgreement extends KeyAgreementSpi { // The privateKey may be modified to a value that is equivalent for // the purposes of this algorithm. + byte[] scalar = this.privateKey.getScalar().get(); byte[] computedSecret = ops.encodedPointMultiply( - this.privateKey, + scalar, publicKey.getU()); + Arrays.fill(scalar, (byte)0); // test for contributory behavior if (allZero(computedSecret)) { @@ -213,7 +217,12 @@ public class XDHKeyAgreement extends KeyAgreementSpi { throw new NoSuchAlgorithmException( "Unsupported secret key algorithm: " + algorithm); } - return new SecretKeySpec(engineGenerateSecret(), algorithm); + byte[] bytes = engineGenerateSecret(); + try { + return new SecretKeySpec(bytes, algorithm); + } finally { + Arrays.fill(bytes, (byte)0); + } } static class X25519 extends XDHKeyAgreement { diff --git a/src/java.base/share/classes/sun/security/ec/XDHPrivateKeyImpl.java b/src/java.base/share/classes/sun/security/ec/XDHPrivateKeyImpl.java index 416a3e10af4..9b6a5676f4e 100644 --- a/src/java.base/share/classes/sun/security/ec/XDHPrivateKeyImpl.java +++ b/src/java.base/share/classes/sun/security/ec/XDHPrivateKeyImpl.java @@ -27,6 +27,7 @@ package sun.security.ec; import java.io.*; import java.security.interfaces.XECPrivateKey; +import java.util.Arrays; import java.util.Optional; import java.security.*; import java.security.spec.*; @@ -106,12 +107,15 @@ public final class XDHPrivateKeyImpl extends PKCS8Key implements XECPrivateKey { XECParameters params = paramSpec.getName().equalsIgnoreCase("X25519") ? XECParameters.X25519 : XECParameters.X448; + var kClone = k.clone(); try { return new XDHPublicKeyImpl(params, - new XECOperations(params).computePublic(k.clone())); + new XECOperations(params).computePublic(kClone)); } catch (InvalidKeyException e) { throw new ProviderException( "Unexpected error calculating public key", e); + } finally { + Arrays.fill(kClone, (byte)0); } } diff --git a/src/java.base/share/classes/sun/security/ec/ed/EdDSASignature.java b/src/java.base/share/classes/sun/security/ec/ed/EdDSASignature.java index 1757f9eb67d..6af39de34dc 100644 --- a/src/java.base/share/classes/sun/security/ec/ed/EdDSASignature.java +++ b/src/java.base/share/classes/sun/security/ec/ed/EdDSASignature.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, 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 @@ -44,6 +44,7 @@ import java.security.interfaces.EdECPublicKey; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.EdDSAParameterSpec; import java.security.spec.NamedParameterSpec; +import java.util.Arrays; import java.util.function.Function; public class EdDSASignature extends SignatureSpi { @@ -92,7 +93,7 @@ public class EdDSASignature extends SignatureSpi { } } - private byte[] privateKey; + private EdECPrivateKey privateKey; private AffinePoint publicKeyPoint; private byte[] publicKeyBytes; private EdDSAOperations ops; @@ -141,11 +142,13 @@ public class EdDSASignature extends SignatureSpi { if (!(privateKey instanceof EdECPrivateKey)) { throw new InvalidKeyException("Unsupported key type"); } - EdECPrivateKey edKey = (EdECPrivateKey) privateKey; + this.privateKey = (EdECPrivateKey) privateKey; + + initImpl(this.privateKey.getParams()); + byte[] tmp = this.privateKey.getBytes().orElseThrow( + () -> new InvalidKeyException("No private key value")); + Arrays.fill(tmp, (byte)0); - initImpl(edKey.getParams()); - this.privateKey = edKey.getBytes().orElseThrow( - () -> new InvalidKeyException("No private key value")); this.publicKeyPoint = null; this.publicKeyBytes = null; } @@ -199,10 +202,14 @@ public class EdDSASignature extends SignatureSpi { throw new SignatureException("Missing private key"); } ensureMessageInit(); - byte[] result = ops.sign(this.sigParams, this.privateKey, - message.getMessage()); - message = null; - return result; + byte[] bytes = this.privateKey.getBytes().get(); + try { + return ops.sign(this.sigParams, bytes, + message.getMessage()); + } finally { + Arrays.fill(bytes, (byte)0); + message = null; + } } @Override diff --git a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java index 101a42a5407..4e82fd25a7b 100644 --- a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java @@ -170,7 +170,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { // Buffer next epoch message if necessary. if (this.readEpoch < recordEpoch) { - // Discard the record younger than the current epcoh if: + // Discard the record younger than the current epoch if: // 1. it is not a handshake message, or // 3. it is not of next epoch. if ((contentType != ContentType.HANDSHAKE.id && @@ -1445,7 +1445,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { // if (expectCCSFlight) { // Have the ChangeCipherSpec/Finished flight been received? - boolean isReady = hasFinishedMessage(bufferedFragments); + boolean isReady = hasFinishedMessage(); if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Has the final flight been received? " + isReady); @@ -1492,7 +1492,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { // // an abbreviated handshake // - if (hasFinishedMessage(bufferedFragments)) { + if (hasFinishedMessage()) { if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { SSLLogger.fine("It's an abbreviated handshake."); } @@ -1565,7 +1565,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { } } - if (!hasFinishedMessage(bufferedFragments)) { + if (!hasFinishedMessage()) { // not yet have the ChangeCipherSpec/Finished messages if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { SSLLogger.fine( @@ -1601,35 +1601,33 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { return false; } - // Looking for the ChangeCipherSpec and Finished messages. + // Looking for the ChangeCipherSpec, Finished and + // NewSessionTicket messages. // // As the cached Finished message should be a ciphertext, we don't // exactly know a ciphertext is a Finished message or not. According // to the spec of TLS/DTLS handshaking, a Finished message is always // sent immediately after a ChangeCipherSpec message. The first // ciphertext handshake message should be the expected Finished message. - private boolean hasFinishedMessage(Set fragments) { - + private boolean hasFinishedMessage() { boolean hasCCS = false; boolean hasFin = false; - for (RecordFragment fragment : fragments) { + + for (RecordFragment fragment : bufferedFragments) { if (fragment.contentType == ContentType.CHANGE_CIPHER_SPEC.id) { - if (hasFin) { - return true; - } hasCCS = true; - } else if (fragment.contentType == ContentType.HANDSHAKE.id) { - // Finished is the first expected message of a new epoch. - if (fragment.isCiphertext) { - if (hasCCS) { - return true; - } - hasFin = true; - } + } else if (fragment.contentType == ContentType.HANDSHAKE.id + && fragment.isCiphertext) { + hasFin = true; } } - return false; + // NewSessionTicket message presence in the Finished flight + // should only be expected on the client side, and only + // if stateless resumption is enabled. + return hasCCS && hasFin && (!tc.sslConfig.isClientMode + || !tc.handshakeContext.statelessResumption + || hasCompleted(SSLHandshake.NEW_SESSION_TICKET.id)); } // Is client CertificateVerify a mandatory message? @@ -1674,7 +1672,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { int presentMsgSeq, int endMsgSeq) { // The caller should have checked the completion of the first - // present handshake message. Need not to check it again. + // present handshake message. Need not check it again. for (RecordFragment rFrag : fragments) { if ((rFrag.contentType != ContentType.HANDSHAKE.id) || rFrag.isCiphertext) { diff --git a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java index 082914b4b4b..fb0490d70f1 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java @@ -286,7 +286,7 @@ enum SSLExtension implements SSLStringizer { ProtocolVersion.PROTOCOLS_10_12, SessionTicketExtension.shNetworkProducer, SessionTicketExtension.shOnLoadConsumer, - null, + SessionTicketExtension.shOnLoadAbsence, null, null, SessionTicketExtension.steStringizer), diff --git a/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java b/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java index 444af5d6dae..9a84bbad8fd 100644 --- a/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java @@ -72,6 +72,8 @@ final class SessionTicketExtension { new T12SHSessionTicketProducer(); static final ExtensionConsumer shOnLoadConsumer = new T12SHSessionTicketConsumer(); + static final HandshakeAbsence shOnLoadAbsence = + new T12SHSessionTicketOnLoadAbsence(); static final SSLStringizer steStringizer = new SessionTicketStringizer(); // No need to compress a ticket if it can fit in a single packet. @@ -529,4 +531,27 @@ final class SessionTicketExtension { chc.statelessResumption = true; } } + + /** + * The absence processing if a "session_ticket" extension is + * not present in the ServerHello handshake message. + */ + private static final class T12SHSessionTicketOnLoadAbsence + implements HandshakeAbsence { + + @Override + public void absent(ConnectionContext context, + HandshakeMessage message) { + ClientHandshakeContext chc = (ClientHandshakeContext) context; + + // Disable stateless resumption if server doesn't send the extension. + if (chc.statelessResumption) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.info( + "Server doesn't support stateless resumption"); + } + chc.statelessResumption = false; + } + } + } } diff --git a/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java b/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java index 1383db1ce82..ec200c6e495 100644 --- a/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java +++ b/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.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 @@ -278,7 +278,7 @@ final class StatusResponseManager { } } } catch (InterruptedException intex) { - // Log and reset the interrupt state + // Log and reset the interrupted state Thread.currentThread().interrupt(); if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { SSLLogger.fine("Interrupt occurred while fetching: " + diff --git a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java index e48096cc363..c607fe0f25d 100644 --- a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java @@ -33,8 +33,10 @@ import java.security.KeyStore.Builder; import java.security.KeyStore.Entry; import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.PrivateKey; +import java.security.UnrecoverableEntryException; import java.security.cert.X509Certificate; import java.util.*; import java.util.concurrent.atomic.AtomicLong; @@ -221,10 +223,17 @@ final class X509KeyManagerImpl extends X509KeyManagerCertChecking { // parse the alias int firstDot = alias.indexOf('.'); int secondDot = alias.indexOf('.', firstDot + 1); - if ((firstDot == -1) || (secondDot == firstDot)) { - // invalid alias + + if ((firstDot < 1) + || (secondDot - firstDot < 2) + || (alias.length() - secondDot < 2)) { + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + SSLLogger.warning("Invalid alias format: " + alias); + } return null; } + try { int builderIndex = Integer.parseInt (alias.substring(firstDot + 1, secondDot)); @@ -240,8 +249,16 @@ final class X509KeyManagerImpl extends X509KeyManagerCertChecking { entry = (PrivateKeyEntry)newEntry; entryCacheMap.put(alias, new SoftReference<>(entry)); return entry; - } catch (Exception e) { - // ignore + } catch (UnrecoverableEntryException | + KeyStoreException | + NumberFormatException | + NoSuchAlgorithmException | + IndexOutOfBoundsException e) { + // ignore and only log exception + if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + SSLLogger.warning("Exception thrown while getting an alias " + + alias + ": " + e); + } return null; } } @@ -278,8 +295,9 @@ final class X509KeyManagerImpl extends X509KeyManagerCertChecking { if (results != null) { for (EntryStatus status : results) { if (status.checkResult == CheckResult.OK) { - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { - SSLLogger.fine("KeyMgr: choosing key: " + status); + if (SSLLogger.isOn + && SSLLogger.isOn("ssl,keymanager")) { + SSLLogger.fine("Choosing key: " + status); } return makeAlias(status); } @@ -294,15 +312,15 @@ final class X509KeyManagerImpl extends X509KeyManagerCertChecking { } } if (allResults == null) { - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { - SSLLogger.fine("KeyMgr: no matching key found"); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + SSLLogger.fine("No matching key found"); } return null; } Collections.sort(allResults); - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { SSLLogger.fine( - "KeyMgr: no good matching key found, " + "No good matching key found, " + "returning best match out of", allResults); } return makeAlias(allResults.get(0)); @@ -340,14 +358,14 @@ final class X509KeyManagerImpl extends X509KeyManagerCertChecking { } } if (allResults == null || allResults.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { - SSLLogger.fine("KeyMgr: no matching alias found"); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + SSLLogger.fine("No matching alias found"); } return null; } Collections.sort(allResults); - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { - SSLLogger.fine("KeyMgr: getting aliases", allResults); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + SSLLogger.fine("Getting aliases", allResults); } return toAliases(allResults); } 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 class DerValue { 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 @@ package sun.security.x509; 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 @@ public class AVA implements DerEncoder { 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 class AVA implements DerEncoder { * 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 class AVA implements DerEncoder { * 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 @@ public class AVA implements DerEncoder { } } + /* + * 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 class AlgorithmId implements Serializable, DerEncoder { 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 class AlgorithmId implements Serializable, DerEncoder { 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 @@ public class AlgorithmId implements Serializable, DerEncoder { 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/legal/aes.md b/src/java.base/share/legal/aes.md deleted file mode 100644 index 6d0ee2e2bb4..00000000000 --- a/src/java.base/share/legal/aes.md +++ /dev/null @@ -1,36 +0,0 @@ -## Cryptix AES v3.2.0 - -### Cryptix General License -

      -
      -Cryptix General License
      -
      -Copyright (c) 1995-2005 The Cryptix Foundation Limited.
      -All rights reserved.
      -
      -Redistribution and use in source and binary forms, with or without
      -modification, are permitted provided that the following conditions are
      -met:
      -
      -  1. Redistributions of source code must retain the copyright notice,
      -     this list of conditions and the following disclaimer.
      -
      -  2. Redistributions in binary form must reproduce the above copyright
      -     notice, this list of conditions and the following disclaimer in
      -     the documentation and/or other materials provided with the
      -     distribution.
      -
      -THIS SOFTWARE IS PROVIDED BY THE CRYPTIX FOUNDATION LIMITED AND
      -CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
      -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
      -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
      -IN NO EVENT SHALL THE CRYPTIX FOUNDATION LIMITED OR CONTRIBUTORS BE
      -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
      -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
      -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
      -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
      -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
      -IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      -
      -
      diff --git a/src/java.base/share/man/java.md b/src/java.base/share/man/java.md index d628cfee817..1e9eaa67d6d 100644 --- a/src/java.base/share/man/java.md +++ b/src/java.base/share/man/java.md @@ -2566,25 +2566,6 @@ Java HotSpot VM. : Sets the maximum size (in bytes) of the heap for the young generation (nursery). The default value is set ergonomically. -`-XX:MaxRAM=`*size* -: Sets the maximum amount of memory that the JVM may use for the Java heap - before applying ergonomics heuristics. The default value is the maximum - amount of available memory to the JVM process or 128 GB, whichever is lower. - - The maximum amount of available memory to the JVM process is the minimum - of the machine's physical memory and any constraints set by the environment - (e.g. container). - - Specifying this option disables automatic use of compressed oops if - the combined result of this and other options influencing the maximum amount - of memory is larger than the range of memory addressable by compressed oops. - See `-XX:UseCompressedOops` for further information about compressed oops. - - The following example shows how to set the maximum amount of available - memory for sizing the Java heap to 2 GB: - - > `-XX:MaxRAM=2G` - `-XX:MaxRAMPercentage=`*percent* : Sets the maximum amount of memory that the JVM may use for the Java heap before applying ergonomics heuristics as a percentage of the maximum amount @@ -2951,6 +2932,25 @@ they're used. (`-XX:+UseParallelGC` or `-XX:+UseG1GC`). Other collectors employing multiple threads always perform reference processing in parallel. +`-XX:MaxRAM=`*size* +: Sets the maximum amount of memory that the JVM may use for the Java heap + before applying ergonomics heuristics. The default value is the amount of + available memory to the JVM process. + + The maximum amount of available memory to the JVM process is the minimum + of the machine's physical memory and any constraints set by the environment + (e.g. container). + + Specifying this option disables automatic use of compressed oops if + the combined result of this and other options influencing the maximum amount + of memory is larger than the range of memory addressable by compressed oops. + See `-XX:UseCompressedOops` for further information about compressed oops. + + The following example shows how to set the maximum amount of available + memory for sizing the Java heap to 2 GB: + + > `-XX:MaxRAM=2G` + ## Obsolete Java Options These `java` options are still accepted but ignored, and a warning is issued diff --git a/src/java.base/share/native/libverify/check_code.c b/src/java.base/share/native/libverify/check_code.c index 7266ac8f93c..32df102dcb3 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 (offset >= 0 && offset < code_length && code_data[offset] >= 0); + 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; + 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.base/windows/classes/java/lang/ProcessImpl.java b/src/java.base/windows/classes/java/lang/ProcessImpl.java index 7f7c1e75013..78180cce678 100644 --- a/src/java.base/windows/classes/java/lang/ProcessImpl.java +++ b/src/java.base/windows/classes/java/lang/ProcessImpl.java @@ -199,7 +199,6 @@ final class ProcessImpl extends Process { } private static final int VERIFICATION_CMD_BAT = 0; - private static final int VERIFICATION_WIN32 = 1; private static final int VERIFICATION_WIN32_SAFE = 2; // inside quotes not allowed private static final int VERIFICATION_LEGACY = 3; // See Command shell overview for documentation of special characters. @@ -384,12 +383,6 @@ final class ProcessImpl extends Process { return (upName.endsWith(".EXE") || upName.indexOf('.') < 0); } - // Old version that can be bypassed - private boolean isShellFile(String executablePath) { - String upPath = executablePath.toUpperCase(Locale.ROOT); - return (upPath.endsWith(".CMD") || upPath.endsWith(".BAT")); - } - private String quoteString(String arg) { StringBuilder argbuf = new StringBuilder(arg.length() + 2); return argbuf.append('"').append(arg).append('"').toString(); @@ -472,12 +465,10 @@ final class ProcessImpl extends Process { // Quotation protects from interpretation of the [path] argument as // start of longer path with spaces. Quotation has no influence to // [.exe] extension heuristic. - boolean isShell = allowAmbiguousCommands ? isShellFile(executablePath) - : !isExe(executablePath); + boolean isShell = !isExe(executablePath); cmdstr = createCommandLine( // We need the extended verification procedures - isShell ? VERIFICATION_CMD_BAT - : (allowAmbiguousCommands ? VERIFICATION_WIN32 : VERIFICATION_WIN32_SAFE), + isShell ? VERIFICATION_CMD_BAT : VERIFICATION_WIN32_SAFE, quoteString(executablePath), cmd); } diff --git a/src/java.base/windows/native/libjava/WinNTFileSystem_md.c b/src/java.base/windows/native/libjava/WinNTFileSystem_md.c index ccf89eb71e0..974d5c11d7e 100644 --- a/src/java.base/windows/native/libjava/WinNTFileSystem_md.c +++ b/src/java.base/windows/native/libjava/WinNTFileSystem_md.c @@ -60,7 +60,6 @@ Java_java_io_WinNTFileSystem_initIDs(JNIEnv *env, jclass cls) /* -- Path operations -- */ extern int wcanonicalize(const WCHAR *path, WCHAR *out, int len); -extern int wcanonicalizeWithPrefix(const WCHAR *canonicalPrefix, const WCHAR *pathWithCanonicalPrefix, WCHAR *out, int len); /** * Retrieves the fully resolved (final) path for the given path or NULL @@ -296,41 +295,6 @@ Java_java_io_WinNTFileSystem_canonicalize0(JNIEnv *env, jobject this, } -JNIEXPORT jstring JNICALL -Java_java_io_WinNTFileSystem_canonicalizeWithPrefix0(JNIEnv *env, jobject this, - jstring canonicalPrefixString, - jstring pathWithCanonicalPrefixString) -{ - jstring rv = NULL; - WCHAR canonicalPath[MAX_PATH_LENGTH]; - WITH_UNICODE_STRING(env, canonicalPrefixString, canonicalPrefix) { - WITH_UNICODE_STRING(env, pathWithCanonicalPrefixString, pathWithCanonicalPrefix) { - int len = (int)wcslen(canonicalPrefix) + MAX_PATH; - if (len > MAX_PATH_LENGTH) { - WCHAR *cp = (WCHAR*)malloc(len * sizeof(WCHAR)); - if (cp != NULL) { - if (wcanonicalizeWithPrefix(canonicalPrefix, - pathWithCanonicalPrefix, - cp, len) >= 0) { - rv = (*env)->NewString(env, cp, (jsize)wcslen(cp)); - } - free(cp); - } else { - JNU_ThrowOutOfMemoryError(env, "native memory allocation failed"); - } - } else if (wcanonicalizeWithPrefix(canonicalPrefix, - pathWithCanonicalPrefix, - canonicalPath, MAX_PATH_LENGTH) >= 0) { - rv = (*env)->NewString(env, canonicalPath, (jsize)wcslen(canonicalPath)); - } - } END_UNICODE_STRING(env, pathWithCanonicalPrefix); - } END_UNICODE_STRING(env, canonicalPrefix); - if (rv == NULL && !(*env)->ExceptionCheck(env)) { - JNU_ThrowIOExceptionWithLastError(env, "Bad pathname"); - } - return rv; -} - JNIEXPORT jstring JNICALL Java_java_io_WinNTFileSystem_getFinalPath0(JNIEnv* env, jobject this, jstring pathname) { jstring rv = NULL; diff --git a/src/java.base/windows/native/libjava/canonicalize_md.c b/src/java.base/windows/native/libjava/canonicalize_md.c index 7e567c7fbb4..3719ec75d11 100644 --- a/src/java.base/windows/native/libjava/canonicalize_md.c +++ b/src/java.base/windows/native/libjava/canonicalize_md.c @@ -268,64 +268,6 @@ wcanonicalize(WCHAR *orig_path, WCHAR *result, int size) return -1; } -/* Convert a pathname to canonical form. The input prefix is assumed - to be in canonical form already, and the trailing filename must not - contain any wildcard, dot/double dot, or other "tricky" characters - that are rejected by the canonicalize() routine above. This - routine is present to allow the canonicalization prefix cache to be - used while still returning canonical names with the correct - capitalization. */ -int -wcanonicalizeWithPrefix(WCHAR *canonicalPrefix, WCHAR *pathWithCanonicalPrefix, WCHAR *result, int size) -{ - WIN32_FIND_DATAW fd; - HANDLE h; - WCHAR *src, *dst, *dend; - WCHAR *pathbuf; - int pathlen; - - src = pathWithCanonicalPrefix; - dst = result; /* Place results here */ - dend = dst + size; /* Don't go to or past here */ - - - if ((pathlen=(int)wcslen(pathWithCanonicalPrefix)) > MAX_PATH - 1) { - pathbuf = getPrefixed(pathWithCanonicalPrefix, pathlen); - h = FindFirstFileW(pathbuf, &fd); /* Look up prefix */ - free(pathbuf); - } else - h = FindFirstFileW(pathWithCanonicalPrefix, &fd); /* Look up prefix */ - if (h != INVALID_HANDLE_VALUE) { - /* Lookup succeeded; append true name to result and continue */ - FindClose(h); - if (!(dst = wcp(dst, dend, L'\0', - canonicalPrefix, - canonicalPrefix + wcslen(canonicalPrefix)))) { - return -1; - } - if (!(dst = wcp(dst, dend, L'\\', - fd.cFileName, - fd.cFileName + wcslen(fd.cFileName)))) { - return -1; - } - } else { - if (!lastErrorReportable()) { - if (!(dst = wcp(dst, dend, L'\0', src, src + wcslen(src)))) { - return -1; - } - } else { - return -1; - } - } - - if (dst >= dend) { - errno = ENAMETOOLONG; - return -1; - } - *dst = L'\0'; - return 0; -} - /* Non-Wide character version of canonicalize. Converts to wchar and delegates to wcanonicalize. */ JNIEXPORT int diff --git a/src/java.desktop/share/classes/com/sun/beans/WildcardTypeImpl.java b/src/java.desktop/share/classes/com/sun/beans/WildcardTypeImpl.java index 28e316c90ec..ebbc8d2cb26 100644 --- a/src/java.desktop/share/classes/com/sun/beans/WildcardTypeImpl.java +++ b/src/java.desktop/share/classes/com/sun/beans/WildcardTypeImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, 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 @@ -73,6 +73,7 @@ final class WildcardTypeImpl implements WildcardType { * @return an array of types representing * the upper bound(s) of this type variable */ + @Override public Type[] getUpperBounds() { return this.upperBounds.clone(); } @@ -87,6 +88,7 @@ final class WildcardTypeImpl implements WildcardType { * @return an array of types representing * the lower bound(s) of this type variable */ + @Override public Type[] getLowerBounds() { return this.lowerBounds.clone(); } diff --git a/src/java.desktop/share/classes/com/sun/beans/decoder/NullElementHandler.java b/src/java.desktop/share/classes/com/sun/beans/decoder/NullElementHandler.java index f865535e4fb..d5ac5368f9a 100644 --- a/src/java.desktop/share/classes/com/sun/beans/decoder/NullElementHandler.java +++ b/src/java.desktop/share/classes/com/sun/beans/decoder/NullElementHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, 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 @@ -61,6 +61,7 @@ class NullElementHandler extends ElementHandler implements ValueObject { * * @return {@code null} by default */ + @Override public Object getValue() { return null; } @@ -70,6 +71,7 @@ class NullElementHandler extends ElementHandler implements ValueObject { * * @return {@code false} always */ + @Override public final boolean isVoid() { return false; } diff --git a/src/java.desktop/share/classes/com/sun/beans/decoder/ValueObjectImpl.java b/src/java.desktop/share/classes/com/sun/beans/decoder/ValueObjectImpl.java index 6fa46c93fa8..54c73381191 100644 --- a/src/java.desktop/share/classes/com/sun/beans/decoder/ValueObjectImpl.java +++ b/src/java.desktop/share/classes/com/sun/beans/decoder/ValueObjectImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 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 @@ -72,6 +72,7 @@ final class ValueObjectImpl implements ValueObject { * * @return the result of method execution */ + @Override public Object getValue() { return this.value; } @@ -82,6 +83,7 @@ final class ValueObjectImpl implements ValueObject { * @return {@code true} if value should be ignored, * {@code false} otherwise */ + @Override public boolean isVoid() { return this.isVoid; } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/BooleanEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/BooleanEditor.java index 69aca3238c9..79900b5deb1 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/BooleanEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/BooleanEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2012, 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 @@ -34,6 +34,7 @@ import java.beans.*; public class BooleanEditor extends PropertyEditorSupport { + @Override public String getJavaInitializationString() { Object value = getValue(); return (value != null) @@ -41,6 +42,7 @@ public class BooleanEditor extends PropertyEditorSupport { : "null"; } + @Override public String getAsText() { Object value = getValue(); return (value instanceof Boolean) @@ -48,6 +50,7 @@ public class BooleanEditor extends PropertyEditorSupport { : null; } + @Override public void setAsText(String text) throws java.lang.IllegalArgumentException { if (text == null) { setValue(null); @@ -60,6 +63,7 @@ public class BooleanEditor extends PropertyEditorSupport { } } + @Override public String[] getTags() { return new String[] {getValidName(true), getValidName(false)}; } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/ByteEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/ByteEditor.java index 2f4f342774f..fe927fda74d 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/ByteEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/ByteEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -34,6 +34,7 @@ import java.beans.*; public class ByteEditor extends NumberEditor { + @Override public String getJavaInitializationString() { Object value = getValue(); return (value != null) @@ -41,6 +42,7 @@ public class ByteEditor extends NumberEditor { : "null"; } + @Override public void setAsText(String text) throws IllegalArgumentException { setValue((text == null) ? null : Byte.decode(text)); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/ColorEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/ColorEditor.java index 3c3207ccd15..a5cf00923dd 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/ColorEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/ColorEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2021, 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 @@ -79,16 +79,19 @@ public class ColorEditor extends Panel implements PropertyEditor { resize(ourWidth,40); } + @Override public void setValue(Object o) { Color c = (Color)o; changeColor(c); } + @Override @SuppressWarnings("deprecation") public Dimension preferredSize() { return new Dimension(ourWidth, 40); } + @Override @SuppressWarnings("deprecation") public boolean keyUp(Event e, int key) { if (e.target == text) { @@ -101,6 +104,7 @@ public class ColorEditor extends Panel implements PropertyEditor { return (false); } + @Override public void setAsText(String s) throws java.lang.IllegalArgumentException { if (s == null) { changeColor(null); @@ -124,6 +128,7 @@ public class ColorEditor extends Panel implements PropertyEditor { } + @Override @SuppressWarnings("deprecation") public boolean action(Event e, Object arg) { if (e.target == chooser) { @@ -132,6 +137,7 @@ public class ColorEditor extends Panel implements PropertyEditor { return false; } + @Override public String getJavaInitializationString() { return (this.color != null) ? "new java.awt.Color(" + this.color.getRGB() + ",true)" @@ -165,14 +171,17 @@ public class ColorEditor extends Panel implements PropertyEditor { support.firePropertyChange("", null, null); } + @Override public Object getValue() { return color; } + @Override public boolean isPaintable() { return true; } + @Override public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) { Color oldColor = gfx.getColor(); gfx.setColor(Color.black); @@ -182,28 +191,34 @@ public class ColorEditor extends Panel implements PropertyEditor { gfx.setColor(oldColor); } + @Override public String getAsText() { return (this.color != null) ? this.color.getRed() + "," + this.color.getGreen() + "," + this.color.getBlue() : null; } + @Override public String[] getTags() { return null; } + @Override public java.awt.Component getCustomEditor() { return this; } + @Override public boolean supportsCustomEditor() { return true; } + @Override public void addPropertyChangeListener(PropertyChangeListener l) { support.addPropertyChangeListener(l); } + @Override public void removePropertyChangeListener(PropertyChangeListener l) { support.removePropertyChangeListener(l); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/DoubleEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/DoubleEditor.java index 55d5a0528a4..3803cca7d7c 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/DoubleEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/DoubleEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -34,6 +34,7 @@ import java.beans.*; public class DoubleEditor extends NumberEditor { + @Override public void setAsText(String text) throws IllegalArgumentException { setValue((text == null) ? null : Double.valueOf(text)); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/EnumEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/EnumEditor.java index b7f5ada0d1f..b5316a04d65 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/EnumEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/EnumEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2014, 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 @@ -63,10 +63,12 @@ public final class EnumEditor implements PropertyEditor { } } + @Override public Object getValue() { return this.value; } + @Override public void setValue( Object value ) { if ( ( value != null ) && !this.type.isInstance( value ) ) { throw new IllegalArgumentException( "Unsupported value: " + value ); @@ -92,12 +94,14 @@ public final class EnumEditor implements PropertyEditor { } } + @Override public String getAsText() { return ( this.value != null ) ? ( ( Enum )this.value ).name() : null; } + @Override public void setAsText( String text ) { @SuppressWarnings("unchecked") Object tmp = ( text != null ) @@ -106,10 +110,12 @@ public final class EnumEditor implements PropertyEditor { setValue(tmp); } + @Override public String[] getTags() { return this.tags.clone(); } + @Override public String getJavaInitializationString() { String name = getAsText(); return ( name != null ) @@ -117,27 +123,33 @@ public final class EnumEditor implements PropertyEditor { : "null"; } + @Override public boolean isPaintable() { return false; } + @Override public void paintValue( Graphics gfx, Rectangle box ) { } + @Override public boolean supportsCustomEditor() { return false; } + @Override public Component getCustomEditor() { return null; } + @Override public void addPropertyChangeListener( PropertyChangeListener listener ) { synchronized ( this.listeners ) { this.listeners.add( listener ); } } + @Override public void removePropertyChangeListener( PropertyChangeListener listener ) { synchronized ( this.listeners ) { this.listeners.remove( listener ); diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/FloatEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/FloatEditor.java index 4723c489cc0..5820c00d82e 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/FloatEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/FloatEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -34,6 +34,7 @@ import java.beans.*; public class FloatEditor extends NumberEditor { + @Override public String getJavaInitializationString() { Object value = getValue(); return (value != null) @@ -41,6 +42,7 @@ public class FloatEditor extends NumberEditor { : "null"; } + @Override public void setAsText(String text) throws IllegalArgumentException { setValue((text == null) ? null : Float.valueOf(text)); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/FontEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/FontEditor.java index cf2fdd26307..26d4ab2b182 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/FontEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/FontEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2021, 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 @@ -78,11 +78,13 @@ public class FontEditor extends Panel implements java.beans.PropertyEditor { } + @Override @SuppressWarnings("deprecation") public Dimension preferredSize() { return new Dimension(300, 40); } + @Override public void setValue(Object o) { font = (Font) o; if (this.font == null) @@ -130,10 +132,12 @@ public class FontEditor extends Panel implements java.beans.PropertyEditor { support.firePropertyChange("", null, null); } + @Override public Object getValue() { return (font); } + @Override public String getJavaInitializationString() { if (this.font == null) return "null"; @@ -142,6 +146,7 @@ public class FontEditor extends Panel implements java.beans.PropertyEditor { font.getStyle() + ", " + font.getSize() + ")"; } + @Override @SuppressWarnings("deprecation") public boolean action(Event e, Object arg) { String family = familyChoser.getSelectedItem(); @@ -158,10 +163,12 @@ public class FontEditor extends Panel implements java.beans.PropertyEditor { } + @Override public boolean isPaintable() { return true; } + @Override public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) { // Silent noop. Font oldFont = gfx.getFont(); @@ -172,6 +179,7 @@ public class FontEditor extends Panel implements java.beans.PropertyEditor { gfx.setFont(oldFont); } + @Override public String getAsText() { if (this.font == null) { return null; @@ -195,26 +203,32 @@ public class FontEditor extends Panel implements java.beans.PropertyEditor { return sb.toString(); } + @Override public void setAsText(String text) throws IllegalArgumentException { setValue((text == null) ? null : Font.decode(text)); } + @Override public String[] getTags() { return null; } + @Override public java.awt.Component getCustomEditor() { return this; } + @Override public boolean supportsCustomEditor() { return true; } + @Override public void addPropertyChangeListener(PropertyChangeListener l) { support.addPropertyChangeListener(l); } + @Override public void removePropertyChangeListener(PropertyChangeListener l) { support.removePropertyChangeListener(l); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/IntegerEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/IntegerEditor.java index 066b7143ac6..65b4d1dcf19 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/IntegerEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/IntegerEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2012, 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 @@ -35,6 +35,7 @@ import java.beans.*; public class IntegerEditor extends NumberEditor { + @Override public void setAsText(String text) throws IllegalArgumentException { setValue((text == null) ? null : Integer.decode(text)); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/LongEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/LongEditor.java index 3a8efbba53c..ed4d12ac505 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/LongEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/LongEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -34,6 +34,7 @@ import java.beans.*; public class LongEditor extends NumberEditor { + @Override public String getJavaInitializationString() { Object value = getValue(); return (value != null) @@ -41,6 +42,7 @@ public class LongEditor extends NumberEditor { : "null"; } + @Override public void setAsText(String text) throws IllegalArgumentException { setValue((text == null) ? null : Long.decode(text)); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/NumberEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/NumberEditor.java index 9097546d2e0..3c0c5bb6c9f 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/NumberEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/NumberEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -34,6 +34,7 @@ import java.beans.*; public abstract class NumberEditor extends PropertyEditorSupport { + @Override public String getJavaInitializationString() { Object value = getValue(); return (value != null) diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/ShortEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/ShortEditor.java index cf82eef215d..6be5b14b90f 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/ShortEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/ShortEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -35,6 +35,7 @@ import java.beans.*; public class ShortEditor extends NumberEditor { + @Override public String getJavaInitializationString() { Object value = getValue(); return (value != null) @@ -42,6 +43,7 @@ public class ShortEditor extends NumberEditor { : "null"; } + @Override public void setAsText(String text) throws IllegalArgumentException { setValue((text == null) ? null : Short.decode(text)); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/StringEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/StringEditor.java index 2f1cde46ea0..b064ccbddbb 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/StringEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/StringEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -30,6 +30,7 @@ import java.beans.*; public class StringEditor extends PropertyEditorSupport { + @Override public String getJavaInitializationString() { Object value = getValue(); if (value == null) @@ -67,6 +68,7 @@ public class StringEditor extends PropertyEditorSupport { return sb.toString(); } + @Override public void setAsText(String text) { setValue(text); } diff --git a/src/java.desktop/share/classes/com/sun/beans/infos/ComponentBeanInfo.java b/src/java.desktop/share/classes/com/sun/beans/infos/ComponentBeanInfo.java index 1514b005074..39d7cbb2146 100644 --- a/src/java.desktop/share/classes/com/sun/beans/infos/ComponentBeanInfo.java +++ b/src/java.desktop/share/classes/com/sun/beans/infos/ComponentBeanInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -34,6 +34,7 @@ import java.beans.*; public class ComponentBeanInfo extends SimpleBeanInfo { private static final Class beanClass = java.awt.Component.class; + @Override public PropertyDescriptor[] getPropertyDescriptors() { try { PropertyDescriptor diff --git a/src/java.desktop/share/classes/com/sun/beans/util/Cache.java b/src/java.desktop/share/classes/com/sun/beans/util/Cache.java index 2cb21791416..58151e3a56f 100644 --- a/src/java.desktop/share/classes/com/sun/beans/util/Cache.java +++ b/src/java.desktop/share/classes/com/sun/beans/util/Cache.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 @@ -405,11 +405,13 @@ public abstract class Cache { */ public static enum Kind { STRONG { + @Override Ref create(Object owner, T value, ReferenceQueue queue) { return new Strong<>(owner, value); } }, SOFT { + @Override Ref create(Object owner, T referent, ReferenceQueue queue) { return (referent == null) ? new Strong<>(owner, referent) @@ -417,6 +419,7 @@ public abstract class Cache { } }, WEAK { + @Override Ref create(Object owner, T referent, ReferenceQueue queue) { return (referent == null) ? new Strong<>(owner, referent) @@ -463,6 +466,7 @@ public abstract class Cache { * * @return the owner of the reference or {@code null} if the owner is unknown */ + @Override public Object getOwner() { return this.owner; } @@ -472,6 +476,7 @@ public abstract class Cache { * * @return the referred object */ + @Override public T getReferent() { return this.referent; } @@ -481,6 +486,7 @@ public abstract class Cache { * * @return {@code true} if the referred object was collected */ + @Override public boolean isStale() { return false; } @@ -488,6 +494,7 @@ public abstract class Cache { /** * Marks this reference as removed from the cache. */ + @Override public void removeOwner() { this.owner = null; } @@ -522,6 +529,7 @@ public abstract class Cache { * * @return the owner of the reference or {@code null} if the owner is unknown */ + @Override public Object getOwner() { return this.owner; } @@ -531,6 +539,7 @@ public abstract class Cache { * * @return the referred object or {@code null} if it was collected */ + @Override public T getReferent() { return get(); } @@ -540,6 +549,7 @@ public abstract class Cache { * * @return {@code true} if the referred object was collected */ + @Override public boolean isStale() { return null == get(); } @@ -547,6 +557,7 @@ public abstract class Cache { /** * Marks this reference as removed from the cache. */ + @Override public void removeOwner() { this.owner = null; } @@ -581,6 +592,7 @@ public abstract class Cache { * * @return the owner of the reference or {@code null} if the owner is unknown */ + @Override public Object getOwner() { return this.owner; } @@ -590,6 +602,7 @@ public abstract class Cache { * * @return the referred object or {@code null} if it was collected */ + @Override public T getReferent() { return get(); } @@ -599,6 +612,7 @@ public abstract class Cache { * * @return {@code true} if the referred object was collected */ + @Override public boolean isStale() { return null == get(); } @@ -606,6 +620,7 @@ public abstract class Cache { /** * Marks this reference as removed from the cache. */ + @Override public void removeOwner() { this.owner = null; } diff --git a/src/java.desktop/share/classes/java/awt/Component.java b/src/java.desktop/share/classes/java/awt/Component.java index e78cab2a14c..e48255aaf00 100644 --- a/src/java.desktop/share/classes/java/awt/Component.java +++ b/src/java.desktop/share/classes/java/awt/Component.java @@ -6287,7 +6287,7 @@ public abstract class Component implements ImageObserver, MenuContainer, * and paint (and update) events. * For mouse move events the last event is always returned, causing * intermediate moves to be discarded. For paint events, the new - * event is coalesced into a complex {@code RepaintArea} in the peer. + * event is coalesced into a complex repaint area in the peer. * The new {@code AWTEvent} is always returned. * * @param existingEvent the event already on the {@code EventQueue} diff --git a/src/java.desktop/share/classes/java/awt/GridBagConstraints.java b/src/java.desktop/share/classes/java/awt/GridBagConstraints.java index 30f8bc4bf50..0008f5ac780 100644 --- a/src/java.desktop/share/classes/java/awt/GridBagConstraints.java +++ b/src/java.desktop/share/classes/java/awt/GridBagConstraints.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 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 @@ -575,7 +575,7 @@ public class GridBagConstraints implements Cloneable, java.io.Serializable { private static final long serialVersionUID = -1000070633030801713L; /** - * Creates a {@code GridBagConstraint} object with + * Creates a {@code GridBagConstraints} object with * all of its fields set to their default value. */ public GridBagConstraints () { diff --git a/src/java.desktop/share/classes/java/awt/Robot.java b/src/java.desktop/share/classes/java/awt/Robot.java index 957e30126e1..e91cc582c4c 100644 --- a/src/java.desktop/share/classes/java/awt/Robot.java +++ b/src/java.desktop/share/classes/java/awt/Robot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, 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 @@ -717,8 +717,8 @@ public class Robot { * Sleeps for the specified time. *

      * If the invoking thread is interrupted while waiting, then it will return - * immediately with the interrupt status set. If the interrupted status is - * already set, this method returns immediately with the interrupt status + * immediately with the interrupted status set. If the interrupted status is + * already set, this method returns immediately with the interrupted status * set. * * @apiNote It is recommended to avoid calling this method on @@ -736,7 +736,7 @@ public class Robot { try { Thread.sleep(ms); } catch (final InterruptedException ignored) { - thread.interrupt(); // Preserve interrupt status + thread.interrupt(); // Preserve interrupted status } } } 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 @@ public final class NumericShaper implements java.io.Serializable { 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 @@ public final class NumericShaper implements java.io.Serializable { // 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 final class NumericShaper implements java.io.Serializable { 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 @@ public final class NumericShaper implements java.io.Serializable { * @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/java/awt/image/BandedSampleModel.java b/src/java.desktop/share/classes/java/awt/image/BandedSampleModel.java index bd955e35870..bad9abc6130 100644 --- a/src/java.desktop/share/classes/java/awt/image/BandedSampleModel.java +++ b/src/java.desktop/share/classes/java/awt/image/BandedSampleModel.java @@ -141,12 +141,9 @@ public final class BandedSampleModel extends ComponentSampleModel * @param h the height of the resulting {@code BandedSampleModel} * @return a new {@code BandedSampleModel} with the specified * width and height. - * @throws IllegalArgumentException if {@code w} or - * {@code h} equals either - * {@code Integer.MAX_VALUE} or - * {@code Integer.MIN_VALUE} - * @throws IllegalArgumentException if {@code dataType} is not - * one of the supported data types + * @throws IllegalArgumentException if the product of {@code w} + * and {@code h} is greater than {@code Integer.MAX_VALUE} + * or {@code w} or {@code h} is not greater than 0. */ public SampleModel createCompatibleSampleModel(int w, int h) { int[] bandOffs; @@ -172,8 +169,8 @@ public final class BandedSampleModel extends ComponentSampleModel * of the original BandedSampleModel/DataBuffer combination. * @throws RasterFormatException if the number of bands is greater than * the number of banks in this sample model. - * @throws IllegalArgumentException if {@code dataType} is not - * one of the supported data types + * @throws IllegalArgumentException if the number of bands is not greater than 0 + * @throws ArrayIndexOutOfBoundsException if any of the bank indices is out of bounds */ public SampleModel createSubsetSampleModel(int[] bands) { if (bands.length > bankIndices.length) diff --git a/src/java.desktop/share/classes/java/awt/image/renderable/ContextualRenderedImageFactory.java b/src/java.desktop/share/classes/java/awt/image/renderable/ContextualRenderedImageFactory.java index 94a2aa14bf5..8df3895a021 100644 --- a/src/java.desktop/share/classes/java/awt/image/renderable/ContextualRenderedImageFactory.java +++ b/src/java.desktop/share/classes/java/awt/image/renderable/ContextualRenderedImageFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2008, 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 @@ -41,7 +41,7 @@ import java.awt.image.RenderedImage; * ContextualRenderedImageFactory provides an interface for the * functionality that may differ between instances of * RenderableImageOp. Thus different operations on RenderableImages - * may be performed by a single class such as RenderedImageOp through + * may be performed by a single class such as RenderableImageOp through * the use of multiple instances of ContextualRenderedImageFactory. * The name ContextualRenderedImageFactory is commonly shortened to * "CRIF." diff --git a/src/java.desktop/share/classes/java/awt/image/renderable/RenderableImageOp.java b/src/java.desktop/share/classes/java/awt/image/renderable/RenderableImageOp.java index 19bf3b39323..f2f0d458ba1 100644 --- a/src/java.desktop/share/classes/java/awt/image/renderable/RenderableImageOp.java +++ b/src/java.desktop/share/classes/java/awt/image/renderable/RenderableImageOp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2014, 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 @@ -58,7 +58,7 @@ public class RenderableImageOp implements RenderableImage { /** - * Constructs a RenderedImageOp given a + * Constructs a {@code RenderableImageOp} given a * ContextualRenderedImageFactory object, and * a ParameterBlock containing RenderableImage sources and other * parameters. Any RenderedImage sources referenced by the diff --git a/src/java.desktop/share/classes/javax/swing/ScrollPaneLayout.java b/src/java.desktop/share/classes/javax/swing/ScrollPaneLayout.java index 0b8d8576f14..50cb25fe4f8 100644 --- a/src/java.desktop/share/classes/javax/swing/ScrollPaneLayout.java +++ b/src/java.desktop/share/classes/javax/swing/ScrollPaneLayout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, 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 @@ -38,8 +38,8 @@ import java.io.Serializable; /** - * The layout manager used by JScrollPane. - * JScrollPaneLayout is + * The layout manager used by {@code JScrollPane}. + * {@code ScrollPaneLayout} is * responsible for nine components: a viewport, two scrollbars, * a row header, a column header, and four "corner" components. *

      diff --git a/src/java.desktop/share/classes/javax/swing/text/GlyphView.java b/src/java.desktop/share/classes/javax/swing/text/GlyphView.java index 792d99fd7db..ef9d1bbe1ea 100644 --- a/src/java.desktop/share/classes/javax/swing/text/GlyphView.java +++ b/src/java.desktop/share/classes/javax/swing/text/GlyphView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, 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 @@ -391,7 +391,7 @@ public class GlyphView extends View implements TabableView, Cloneable { } if (bg != null) { g.setColor(bg); - g.fillRect(alloc.x, alloc.y, alloc.width, alloc.height); + g.fillRect(alloc.x, alloc.y, alloc.width, (int)painter.getHeight(this)); } if (c instanceof JTextComponent) { JTextComponent tc = (JTextComponent) c; 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 @@ public final class WindowsCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI { 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.Color; 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 final class WindowsMenuItemUI extends BasicMenuItemUI { 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 @@ public final class WindowsMenuItemUI extends BasicMenuItemUI { 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 @@ public final class WindowsMenuItemUI extends BasicMenuItemUI { } 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 @@ public final class WindowsMenuUI extends BasicMenuUI { 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 @@ public final class WindowsRadioButtonMenuItemUI extends BasicRadioButtonMenuItem 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/java/net/http/HttpClient.java b/src/java.net.http/share/classes/java/net/http/HttpClient.java index 4ce77486e70..889ea56531e 100644 --- a/src/java.net.http/share/classes/java/net/http/HttpClient.java +++ b/src/java.net.http/share/classes/java/net/http/HttpClient.java @@ -933,7 +933,7 @@ public abstract class HttpClient implements AutoCloseable { *

      If interrupted while waiting, this method may attempt to stop all * operations by calling {@link #shutdownNow()}. It then continues to wait * until all actively executing operations have completed. - * The interrupt status will be re-asserted before this method returns. + * The interrupted status will be re-asserted before this method returns. * *

      If already terminated, invoking this method has no effect. * diff --git a/src/java.net.http/share/classes/java/net/http/HttpResponse.java b/src/java.net.http/share/classes/java/net/http/HttpResponse.java index 9843e4c7c5b..1889d9d7300 100644 --- a/src/java.net.http/share/classes/java/net/http/HttpResponse.java +++ b/src/java.net.http/share/classes/java/net/http/HttpResponse.java @@ -1330,7 +1330,7 @@ public interface HttpResponse { * @implNote The {@code read} method of the {@code InputStream} * returned by the default implementation of this method will * throw an {@code IOException} with the {@linkplain Thread#isInterrupted() - * thread interrupt status set} if the thread is interrupted + * thread interrupted status set} if the thread is interrupted * while blocking on read. In that case, the request will also be * cancelled and the {@code InputStream} will be closed. * diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java index b73b92add63..6ea196a4d1c 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java @@ -60,6 +60,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.ExecutionException; @@ -75,6 +76,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantLock; import java.util.function.BiConsumer; import java.util.function.BooleanSupplier; +import java.util.function.Function; import java.util.stream.Stream; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -974,6 +976,12 @@ final class HttpClientImpl extends HttpClient implements Trackable { } throw ie; } catch (ExecutionException e) { + // Exceptions are often thrown from asynchronous code, and the + // stacktrace may not always contain the application classes. That + // makes it difficult to trace back to the application code which + // invoked the `HttpClient`. Here we instantiate/recreate the + // exceptions to capture the application's calling code in the + // stacktrace of the thrown exception. final Throwable throwable = e.getCause(); final String msg = throwable.getMessage(); @@ -1104,6 +1112,8 @@ final class HttpClientImpl extends HttpClient implements Trackable { res = registerPending(pending, res); if (exchangeExecutor != null) { + // We're called by `sendAsync()` - make sure we translate exceptions + res = translateSendAsyncExecFailure(res); // makes sure that any dependent actions happen in the CF default // executor. This is only needed for sendAsync(...), when // exchangeExecutor is non-null. @@ -1121,6 +1131,31 @@ final class HttpClientImpl extends HttpClient implements Trackable { } } + /** + * {@return a new {@code CompletableFuture} wrapping the + * {@link #sendAsync(HttpRequest, BodyHandler, PushPromiseHandler, Executor) sendAsync()} + * execution failures with, as per specification, {@link IOException}, if necessary} + */ + private static CompletableFuture> translateSendAsyncExecFailure( + CompletableFuture> responseFuture) { + return responseFuture + .handle((response, exception) -> { + if (exception == null) { + return MinimalFuture.completedFuture(response); + } + var unwrappedException = Utils.getCompletionCause(exception); + // Except `Error` and `CancellationException`, wrap failures inside an `IOException`. + // This is required to comply with the specification of `HttpClient::sendAsync`. + var translatedException = unwrappedException instanceof Error + || unwrappedException instanceof CancellationException + || unwrappedException instanceof IOException + ? unwrappedException + : new IOException(unwrappedException); + return MinimalFuture.>failedFuture(translatedException); + }) + .thenCompose(Function.identity()); + } + // Main loop for this client's selector private static final class SelectorManager extends Thread { 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 @@ public final class Utils { 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.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java index 7df3d8d2de0..b118c9ee215 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, 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 @@ -37,6 +37,7 @@ import java.io.InvalidObjectException; import java.io.IOException; import java.io.ObjectInputStream; import java.io.OutputStream; +import java.io.Serial; import java.security.*; import javax.security.auth.Subject; import javax.security.auth.kerberos.KerberosCredMessage; @@ -1332,6 +1333,7 @@ class Krb5Context implements GSSContextSpi { * The session key returned by inquireSecContext(KRB5_INQ_SSPI_SESSION_KEY) */ static class KerberosSessionKey implements Key { + @Serial private static final long serialVersionUID = 699307378954123869L; @SuppressWarnings("serial") // Not statically typed as Serializable @@ -1369,7 +1371,7 @@ class Krb5Context implements GSSContextSpi { * @throws IOException if an I/O error occurs * @throws ClassNotFoundException if a serialized class cannot be loaded */ - @java.io.Serial + @Serial private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { throw new InvalidObjectException diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java index 29176ba3c2b..7aaa8975185 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java @@ -402,7 +402,7 @@ public class Krb5InitCredential * @throws IOException if an I/O error occurs * @throws ClassNotFoundException if a serialized class cannot be loaded */ - @java.io.Serial + @Serial private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { throw new InvalidObjectException("Krb5InitCredential not deserializable"); diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/Asn1Exception.java b/src/java.security.jgss/share/classes/sun/security/krb5/Asn1Exception.java index 7899a571589..8fe5591ffbc 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/Asn1Exception.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/Asn1Exception.java @@ -30,8 +30,11 @@ package sun.security.krb5; +import java.io.Serial; + public class Asn1Exception extends KrbException { + @Serial private static final long serialVersionUID = 8291288984575084132L; public Asn1Exception(int i) { diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/KrbCryptoException.java b/src/java.security.jgss/share/classes/sun/security/krb5/KrbCryptoException.java index eda5fcec397..24cda1849ff 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbCryptoException.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbCryptoException.java @@ -30,6 +30,8 @@ package sun.security.krb5; +import java.io.Serial; + /** * KrbCryptoException is a wrapper exception for exceptions thrown by JCE. * @@ -37,6 +39,7 @@ package sun.security.krb5; */ public class KrbCryptoException extends KrbException { + @Serial private static final long serialVersionUID = -1657367919979982250L; public KrbCryptoException(String s) { diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/KrbException.java b/src/java.security.jgss/share/classes/sun/security/krb5/KrbException.java index 3fae5c7c2c5..434a0b401dd 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbException.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbException.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 @@ -31,6 +31,7 @@ package sun.security.krb5; +import java.io.Serial; import java.util.Objects; import sun.security.krb5.internal.Krb5; @@ -38,6 +39,7 @@ import sun.security.krb5.internal.KRBError; public class KrbException extends Exception { + @Serial private static final long serialVersionUID = -4993302876451928596L; private int returnCode; diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/RealmException.java b/src/java.security.jgss/share/classes/sun/security/krb5/RealmException.java index 124d1b63ed4..461da49c757 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/RealmException.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/RealmException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, 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 @@ -31,8 +31,11 @@ package sun.security.krb5; +import java.io.Serial; + public class RealmException extends KrbException { + @Serial private static final long serialVersionUID = -9100385213693792864L; public RealmException(int i) { diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java index db6192ce9ee..46c733824c5 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, 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 @@ -40,6 +40,7 @@ import sun.security.krb5.RealmException; import sun.security.util.*; import java.io.IOException; import java.io.ObjectInputStream; +import java.io.Serial; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; @@ -85,7 +86,8 @@ import static sun.security.krb5.internal.Krb5.DEBUG; */ public class KRBError implements java.io.Serializable { - static final long serialVersionUID = 3643809337475284503L; + @Serial + private static final long serialVersionUID = 3643809337475284503L; private transient int pvno; private transient int msgType; @@ -112,7 +114,7 @@ public class KRBError implements java.io.Serializable { * @throws IOException if an I/O error occurs * @throws ClassNotFoundException if a serialized class cannot be loaded */ - @java.io.Serial + @Serial private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { try { @@ -123,6 +125,7 @@ public class KRBError implements java.io.Serializable { } } + @Serial private void writeObject(ObjectOutputStream os) throws IOException { try { diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KdcErrException.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KdcErrException.java index c55670f4b23..744ba9ba2ac 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KdcErrException.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KdcErrException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2004, 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 @@ -31,8 +31,11 @@ package sun.security.krb5.internal; +import java.io.Serial; + public class KdcErrException extends sun.security.krb5.KrbException { + @Serial private static final long serialVersionUID = -8788186031117310306L; public KdcErrException(int i) { diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbApErrException.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbApErrException.java index 04048cb73bb..4fd0aec5fa8 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbApErrException.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbApErrException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2003, 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 @@ -31,8 +31,11 @@ package sun.security.krb5.internal; +import java.io.Serial; + public class KrbApErrException extends sun.security.krb5.KrbException { + @Serial private static final long serialVersionUID = 7545264413323118315L; public KrbApErrException(int i) { diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbErrException.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbErrException.java index 62e84959ca9..8e5a49dd802 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbErrException.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbErrException.java @@ -30,8 +30,11 @@ package sun.security.krb5.internal; +import java.io.Serial; + public class KrbErrException extends sun.security.krb5.KrbException { + @Serial private static final long serialVersionUID = 2186533836785448317L; public KrbErrException(int i) { diff --git a/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Ktab.java b/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Ktab.java index ffe2e3196c1..bf1ceed8d22 100644 --- a/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Ktab.java +++ b/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Ktab.java @@ -36,6 +36,7 @@ import java.io.IOException; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.Reader; +import java.io.Serial; import java.nio.charset.Charset; import java.text.DateFormat; import java.util.Arrays; @@ -82,8 +83,8 @@ public class Ktab { } private static class ExitException extends RuntimeException { - @java.io.Serial - static final long serialVersionUID = 0L; + @Serial + private static final long serialVersionUID = 0L; private final int errorCode; public ExitException(int errorCode) { this.errorCode = errorCode; 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 @@ import org.xml.sax.SAXNotSupportedException; /** * @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/xerces/internal/parsers/DOMParserImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/DOMParserImpl.java index 45fdbf878cb..9ca148b618b 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/DOMParserImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/DOMParserImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -926,7 +926,7 @@ extends AbstractDOMParser implements LSParser, DOMConfiguration { parse (source); fBusy = false; if (abortNow && currentThread.isInterrupted()) { - //reset interrupt state + //reset interrupted state abortNow = false; Thread.interrupted(); } @@ -983,7 +983,7 @@ extends AbstractDOMParser implements LSParser, DOMConfiguration { parse (xmlInputSource); fBusy = false; if (abortNow && currentThread.isInterrupted()) { - //reset interrupt state + //reset interrupted state abortNow = false; Thread.interrupted(); } 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 @@ import jdk.xml.internal.*; * * @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 class XPathFactoryImpl extends XPathFactory { JdkXmlConfig config = JdkXmlConfig.getInstance(false); _xmlSecMgr = config.getXMLSecurityManager(true); _featureManager = config.getXMLFeatures(true); + _xmlSecPropMgr = config.getXMLSecurityPropertyManager(true); } /** @@ -129,7 +131,7 @@ public class XPathFactoryImpl extends XPathFactory { */ public javax.xml.xpath.XPath newXPath() { return new XPathImpl(xPathVariableResolver, xPathFunctionResolver, - !_isNotSecureProcessing, _featureManager, _xmlSecMgr); + !_isNotSecureProcessing, _featureManager, _xmlSecMgr, _xmlSecPropMgr); } /** @@ -183,6 +185,7 @@ public class XPathFactoryImpl extends XPathFactory { 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 class XPathFactoryImpl extends XPathFactory { 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 javax.xml.xpath.XPathVariableResolver; 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 @@ import org.xml.sax.InputSource; * 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.axes.LocPathIterator; 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 javax.xml.xpath.XPathVariableResolver; 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 @@ import org.xml.sax.SAXException; * 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 @@ class XPathImplUtil { // // 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 class JdkXmlUtils { * @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 class JdkXmlUtils { } 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 final class XMLSecurityManager implements Cloneable { */ 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 @@ public final class XMLSecurityManager implements Cloneable { * 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 final class XMLSecurityManager implements Cloneable { 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/Annotate.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java index 9f56bec4cca..f5fdc1578b8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java @@ -332,6 +332,13 @@ public class Annotate { Assert.checkNonNull(c, "Failed to create annotation"); + if (env.info.isAnonymousNewClass) { + // Annotations on anonymous class instantiations should be attributed, + // but not attached to the enclosing element. They will be visited + // separately and attached to the synthetic class declaration. + continue; + } + if (a.type.isErroneous() || a.type.tsym.isAnnotationType()) { if (annotated.containsKey(a.type.tsym)) { ListBuffer l = annotated.get(a.type.tsym); @@ -1144,8 +1151,11 @@ public class Annotate { public void visitNewClass(JCNewClass tree) { scan(tree.encl); scan(tree.typeargs); - if (tree.def == null) { + try { + env.info.isAnonymousNewClass = tree.def != null; scan(tree.clazz); + } finally { + env.info.isAnonymousNewClass = false; } scan(tree.args); // the anonymous class instantiation if any will be visited separately. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index f780df025bd..617d8ca7077 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -1183,7 +1183,7 @@ public class Attr extends JCTree.Visitor { Errors.DefaultAllowedInIntfAnnotationMember); } if (isDefaultMethod || (tree.sym.flags() & (ABSTRACT | NATIVE)) == 0) - log.error(tree.pos(), Errors.MissingMethBodyOrDeclAbstract); + log.error(tree.pos(), Errors.MissingMethBodyOrDeclAbstract(tree.sym, owner)); } else { if ((tree.sym.flags() & (ABSTRACT|DEFAULT|PRIVATE)) == ABSTRACT) { if ((owner.flags() & INTERFACE) != 0) { @@ -2777,16 +2777,9 @@ public class Attr extends JCTree.Visitor { // Attribute clazz expression and store // symbol + type back into the attributed tree. - Type clazztype; - - try { - env.info.isAnonymousNewClass = tree.def != null; - clazztype = TreeInfo.isEnumInit(env.tree) ? - attribIdentAsEnumType(env, (JCIdent)clazz) : - attribType(clazz, env); - } finally { - env.info.isAnonymousNewClass = false; - } + Type clazztype = TreeInfo.isEnumInit(env.tree) ? + attribIdentAsEnumType(env, (JCIdent)clazz) : + attribType(clazz, env); clazztype = chk.checkDiamond(tree, clazztype); chk.validate(clazz, localEnv); @@ -5256,8 +5249,7 @@ public class Attr extends JCTree.Visitor { Type underlyingType = attribType(tree.underlyingType, env); Type annotatedType = underlyingType.preannotatedType(); - if (!env.info.isAnonymousNewClass) - annotate.annotateTypeSecondStage(tree, tree.annotations, annotatedType); + annotate.annotateTypeSecondStage(tree, tree.annotations, annotatedType); result = tree.type = annotatedType; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 3d9eff107da..da7db138604 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -2080,7 +2080,7 @@ public class Check { } } log.error(pos, - explicitOverride ? (m.isStatic() ? Errors.StaticMethodsCannotBeAnnotatedWithOverride : Errors.MethodDoesNotOverrideSuperclass) : + explicitOverride ? (m.isStatic() ? Errors.StaticMethodsCannotBeAnnotatedWithOverride(m, m.enclClass()) : Errors.MethodDoesNotOverrideSuperclass(m, m.enclClass())) : Errors.AnonymousDiamondMethodDoesNotOverrideSuperclass(Fragments.DiamondAnonymousMethodsImplicitlyOverride)); } } 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 index 7994d08a62a..74524b70426 100644 --- 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 @@ -273,30 +273,36 @@ public class ExhaustivenessComputer { Set permitted = allPermittedSubTypes(clazz, isPossibleSubtypePredicate(selectorType)); int permittedSubtypes = permitted.size(); + //the set of pending permitted subtypes needed to cover clazz: + Set pendingPermitted = new HashSet<>(permitted); + for (PatternDescription pdOther : patterns) { if (pdOther instanceof BindingPattern bpOther) { - Set currentPermittedSubTypes = - allPermittedSubTypes(bpOther.type.tsym, s -> true); + //remove all types from pendingPermitted that we can + //cover using bpOther: - PERMITTED: for (Iterator it = permitted.iterator(); it.hasNext();) { - Symbol perm = it.next(); + //all types that are permitted subtypes of bpOther's type: + pendingPermitted.removeIf(pending -> isSubtypeErasure(pending.type, + bpOther.type)); - for (Symbol currentPermitted : currentPermittedSubTypes) { - if (isSubtypeErasure(currentPermitted.type, - perm.type)) { - it.remove(); - continue PERMITTED; - } - } - if (isSubtypeErasure(perm.type, - bpOther.type)) { - it.remove(); - } + if (bpOther.type.tsym.isAbstract()) { + //all types that are in a diamond hierarchy with bpOther's type + //i.e. there's a common subtype of the given type and bpOther's type: + Predicate check = + pending -> permitted.stream() + .filter(perm -> isSubtypeErasure(perm.type, + bpOther.type)) + .filter(perm -> isSubtypeErasure(perm.type, + pending.type)) + .findAny() + .isPresent(); + + pendingPermitted.removeIf(check); } } } - if (permitted.isEmpty()) { + if (pendingPermitted.isEmpty()) { toAdd.add(new BindingPattern(clazz.type, permittedSubtypes, Set.of())); } } 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 64f77e44756..4e1b36a42e6 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 supertype 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 supertype 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 @@ -905,14 +905,17 @@ compiler.err.limit.string.overflow=\ compiler.err.malformed.fp.lit=\ malformed floating-point literal +# 0: symbol, 1: symbol compiler.err.method.does.not.override.superclass=\ - method does not override or implement a method from a supertype + {0} in {1} does not override or implement a method from a supertype +# 0: symbol, 1: symbol compiler.err.static.methods.cannot.be.annotated.with.override=\ - static methods cannot be annotated with @Override + static method {0} in {1} cannot be annotated with @Override +# 0: symbol, 1: symbol compiler.err.missing.meth.body.or.decl.abstract=\ - missing method body, or declare abstract + method {0} in {1} is missing a method body, or should be declared abstract compiler.err.missing.ret.stmt=\ missing return statement diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/legacy.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/legacy.properties deleted file mode 100644 index c8e433e67c0..00000000000 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/legacy.properties +++ /dev/null @@ -1,570 +0,0 @@ -# -# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. -# 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. -# - -com.sun.accessibility.internal.resources = tiger legacy -com.sun.awt = tiger legacy -com.sun.beans = tiger legacy -com.sun.corba.se.impl.activation = tiger legacy -com.sun.corba.se.impl.copyobject = tiger legacy -com.sun.corba.se.impl.corba = tiger legacy -com.sun.corba.se.impl.dynamicany = tiger legacy -com.sun.corba.se.impl.encoding = tiger legacy -com.sun.corba.se.impl.interceptors = tiger legacy -com.sun.corba.se.impl.io = tiger legacy -com.sun.corba.se.impl.ior = tiger legacy -com.sun.corba.se.impl.ior.iiop = tiger legacy -com.sun.corba.se.impl.javax.rmi = tiger legacy -com.sun.corba.se.impl.javax.rmi.CORBA = tiger legacy -com.sun.corba.se.impl.legacy.connection = tiger legacy -com.sun.corba.se.impl.logging = tiger legacy -com.sun.corba.se.impl.monitoring = tiger legacy -com.sun.corba.se.impl.naming.cosnaming = tiger legacy -com.sun.corba.se.impl.naming.namingutil = tiger legacy -com.sun.corba.se.impl.naming.pcosnaming = tiger legacy -com.sun.corba.se.impl.oa = tiger legacy -com.sun.corba.se.impl.oa.poa = tiger legacy -com.sun.corba.se.impl.oa.toa = tiger legacy -com.sun.corba.se.impl.orb = tiger legacy -com.sun.corba.se.impl.orbutil = tiger legacy -com.sun.corba.se.impl.orbutil.closure = tiger legacy -com.sun.corba.se.impl.orbutil.concurrent = tiger legacy -com.sun.corba.se.impl.orbutil.fsm = tiger legacy -com.sun.corba.se.impl.orbutil.graph = tiger legacy -com.sun.corba.se.impl.orbutil.resources = tiger legacy -com.sun.corba.se.impl.orbutil.threadpool = tiger legacy -com.sun.corba.se.impl.presentation.rmi = tiger legacy -com.sun.corba.se.impl.protocol = tiger legacy -com.sun.corba.se.impl.protocol.giopmsgheaders = tiger legacy -com.sun.corba.se.impl.resolver = tiger legacy -com.sun.corba.se.impl.transport = tiger legacy -com.sun.corba.se.impl.util = tiger legacy -com.sun.corba.se.internal.CosNaming = tiger legacy -com.sun.corba.se.internal.Interceptors = tiger legacy -com.sun.corba.se.internal.POA = tiger legacy -com.sun.corba.se.internal.corba = tiger legacy -com.sun.corba.se.internal.iiop = tiger legacy -com.sun.corba.se.org.omg.CORBA = tiger legacy -com.sun.corba.se.pept.broker = tiger legacy -com.sun.corba.se.pept.encoding = tiger legacy -com.sun.corba.se.pept.protocol = tiger legacy -com.sun.corba.se.pept.transport = tiger legacy -com.sun.corba.se.spi.activation = tiger legacy -com.sun.corba.se.spi.activation.InitialNameServicePackage = tiger legacy -com.sun.corba.se.spi.activation.LocatorPackage = tiger legacy -com.sun.corba.se.spi.activation.RepositoryPackage = tiger legacy -com.sun.corba.se.spi.copyobject = tiger legacy -com.sun.corba.se.spi.encoding = tiger legacy -com.sun.corba.se.spi.extension = tiger legacy -com.sun.corba.se.spi.ior = tiger legacy -com.sun.corba.se.spi.ior.iiop = tiger legacy -com.sun.corba.se.spi.legacy.connection = tiger legacy -com.sun.corba.se.spi.legacy.interceptor = tiger legacy -com.sun.corba.se.spi.logging = tiger legacy -com.sun.corba.se.spi.monitoring = tiger legacy -com.sun.corba.se.spi.oa = tiger legacy -com.sun.corba.se.spi.orb = tiger legacy -com.sun.corba.se.spi.orbutil.closure = tiger legacy -com.sun.corba.se.spi.orbutil.fsm = tiger legacy -com.sun.corba.se.spi.orbutil.proxy = tiger legacy -com.sun.corba.se.spi.orbutil.threadpool = tiger legacy -com.sun.corba.se.spi.presentation.rmi = tiger legacy -com.sun.corba.se.spi.protocol = tiger legacy -com.sun.corba.se.spi.resolver = tiger legacy -com.sun.corba.se.spi.servicecontext = tiger legacy -com.sun.corba.se.spi.transport = tiger legacy -com.sun.imageio.metadata = tiger legacy -com.sun.imageio.plugins.bmp = tiger legacy -com.sun.imageio.plugins.common = tiger legacy -com.sun.imageio.plugins.gif = tiger legacy -com.sun.imageio.plugins.jpeg = tiger legacy -com.sun.imageio.plugins.png = tiger legacy -com.sun.imageio.plugins.wbmp = tiger legacy -com.sun.imageio.spi = tiger legacy -com.sun.java.swing = tiger legacy -com.sun.java.swing.plaf.gtk = tiger legacy -com.sun.java.swing.plaf.gtk.icons = tiger legacy -com.sun.java.swing.plaf.gtk.resources = tiger legacy -com.sun.java.swing.plaf.gtk.resources.metacity.SwingFallbackTheme.metacity-1 = tiger legacy -com.sun.java.swing.plaf.motif = tiger legacy -com.sun.java.swing.plaf.motif.icons = tiger legacy -com.sun.java.swing.plaf.motif.resources = tiger legacy -com.sun.java.swing.plaf.nimbus = tiger legacy -com.sun.java.swing.plaf.windows = tiger legacy -com.sun.java.swing.plaf.windows.icons = tiger legacy -com.sun.java.swing.plaf.windows.resources = tiger legacy -com.sun.java.util.jar.pack = tiger legacy -com.sun.java_cup.internal = tiger legacy -com.sun.java_cup.internal.runtime = tiger legacy -com.sun.jlex.internal = tiger legacy -com.sun.jmx.defaults = tiger legacy -com.sun.jmx.interceptor = tiger legacy -com.sun.jmx.mbeanserver = tiger legacy -com.sun.jmx.remote.internal = tiger legacy -com.sun.jmx.remote.protocol.iiop = tiger legacy -com.sun.jmx.remote.protocol.rmi = tiger legacy -com.sun.jmx.remote.security = tiger legacy -com.sun.jmx.remote.util = tiger legacy -com.sun.jmx.snmp = tiger legacy -com.sun.jmx.snmp.IPAcl = tiger legacy -com.sun.jmx.snmp.agent = tiger legacy -com.sun.jmx.snmp.daemon = tiger legacy -com.sun.jmx.snmp.defaults = tiger legacy -com.sun.jmx.snmp.internal = tiger legacy -com.sun.jmx.snmp.mpm = tiger legacy -com.sun.jmx.snmp.tasks = tiger legacy -com.sun.jmx.trace = tiger legacy -com.sun.jndi.cosnaming = tiger legacy -com.sun.jndi.dns = tiger legacy -com.sun.jndi.ldap = tiger legacy -com.sun.jndi.ldap.ext = tiger legacy -com.sun.jndi.ldap.pool = tiger legacy -com.sun.jndi.ldap.sasl = tiger legacy -com.sun.jndi.rmi.registry = tiger legacy -com.sun.jndi.toolkit.corba = tiger legacy -com.sun.jndi.toolkit.ctx = tiger legacy -com.sun.jndi.toolkit.dir = tiger legacy -com.sun.jndi.toolkit.url = tiger legacy -com.sun.jndi.url.corbaname = tiger legacy -com.sun.jndi.url.dns = tiger legacy -com.sun.jndi.url.iiop = tiger legacy -com.sun.jndi.url.iiopname = tiger legacy -com.sun.jndi.url.ldap = tiger legacy -com.sun.jndi.url.ldaps = tiger legacy -com.sun.jndi.url.rmi = tiger legacy -com.sun.management = tiger legacy -com.sun.management.jmx = tiger legacy -com.sun.media.sound = tiger legacy -com.sun.naming.internal = tiger legacy -com.sun.net.ssl = tiger legacy -com.sun.net.ssl.internal.ssl = tiger legacy -com.sun.net.ssl.internal.www.protocol.https = tiger legacy -com.sun.org.apache.bcel.internal = tiger legacy -com.sun.org.apache.bcel.internal.classfile = tiger legacy -com.sun.org.apache.bcel.internal.generic = tiger legacy -com.sun.org.apache.bcel.internal.util = tiger legacy -com.sun.org.apache.bcel.internal.verifier = tiger legacy -com.sun.org.apache.bcel.internal.verifier.exc = tiger legacy -com.sun.org.apache.bcel.internal.verifier.statics = tiger legacy -com.sun.org.apache.bcel.internal.verifier.structurals = tiger legacy -com.sun.org.apache.html.internal.dom = tiger legacy -com.sun.org.apache.regexp.internal = tiger legacy -com.sun.org.apache.wml.internal = tiger legacy -com.sun.org.apache.wml.internal.dom = tiger legacy -com.sun.org.apache.xalan.internal = tiger legacy -com.sun.org.apache.xalan.internal.client = tiger legacy -com.sun.org.apache.xalan.internal.extensions = tiger legacy -com.sun.org.apache.xalan.internal.lib = tiger legacy -com.sun.org.apache.xalan.internal.res = tiger legacy -com.sun.org.apache.xalan.internal.templates = tiger legacy -com.sun.org.apache.xalan.internal.xslt = tiger legacy -com.sun.org.apache.xalan.internal.xsltc = tiger legacy -com.sun.org.apache.xalan.internal.xsltc.cmdline = tiger legacy -com.sun.org.apache.xalan.internal.xsltc.cmdline.getopt = tiger legacy -com.sun.org.apache.xalan.internal.xsltc.compiler = tiger legacy -com.sun.org.apache.xalan.internal.xsltc.compiler.util = tiger legacy -com.sun.org.apache.xalan.internal.xsltc.dom = tiger legacy -com.sun.org.apache.xalan.internal.xsltc.runtime = tiger legacy -com.sun.org.apache.xalan.internal.xsltc.runtime.output = tiger legacy -com.sun.org.apache.xalan.internal.xsltc.trax = tiger legacy -com.sun.org.apache.xalan.internal.xsltc.util = tiger legacy -com.sun.org.apache.xerces.internal.dom = tiger legacy -com.sun.org.apache.xerces.internal.dom.events = tiger legacy -com.sun.org.apache.xerces.internal.dom3.as = tiger legacy -com.sun.org.apache.xerces.internal.impl = tiger legacy -com.sun.org.apache.xerces.internal.impl.dtd = tiger legacy -com.sun.org.apache.xerces.internal.impl.dtd.models = tiger legacy -com.sun.org.apache.xerces.internal.impl.dv = tiger legacy -com.sun.org.apache.xerces.internal.impl.dv.dtd = tiger legacy -com.sun.org.apache.xerces.internal.impl.dv.util = tiger legacy -com.sun.org.apache.xerces.internal.impl.dv.xs = tiger legacy -com.sun.org.apache.xerces.internal.impl.io = tiger legacy -com.sun.org.apache.xerces.internal.impl.msg = tiger legacy -com.sun.org.apache.xerces.internal.impl.validation = tiger legacy -com.sun.org.apache.xerces.internal.impl.xpath = tiger legacy -com.sun.org.apache.xerces.internal.impl.xpath.regex = tiger legacy -com.sun.org.apache.xerces.internal.impl.xs = tiger legacy -com.sun.org.apache.xerces.internal.impl.xs.dom = tiger legacy -com.sun.org.apache.xerces.internal.impl.xs.identity = tiger legacy -com.sun.org.apache.xerces.internal.impl.xs.models = tiger legacy -com.sun.org.apache.xerces.internal.impl.xs.opti = tiger legacy -com.sun.org.apache.xerces.internal.impl.xs.traversers = tiger legacy -com.sun.org.apache.xerces.internal.impl.xs.util = tiger legacy -com.sun.org.apache.xerces.internal.jaxp = tiger legacy -com.sun.org.apache.xerces.internal.jaxp.datatype = tiger legacy -com.sun.org.apache.xerces.internal.jaxp.validation = tiger legacy -com.sun.org.apache.xerces.internal.jaxp.validation.xs = tiger legacy -com.sun.org.apache.xerces.internal.parsers = tiger legacy -com.sun.org.apache.xerces.internal.util = tiger legacy -com.sun.org.apache.xerces.internal.xinclude = tiger legacy -com.sun.org.apache.xerces.internal.xni = tiger legacy -com.sun.org.apache.xerces.internal.xni.grammars = tiger legacy -com.sun.org.apache.xerces.internal.xni.parser = tiger legacy -com.sun.org.apache.xerces.internal.xs = tiger legacy -com.sun.org.apache.xml.internal.dtm = tiger legacy -com.sun.org.apache.xml.internal.dtm.ref = tiger legacy -com.sun.org.apache.xml.internal.dtm.ref.dom2dtm = tiger legacy -com.sun.org.apache.xml.internal.dtm.ref.sax2dtm = tiger legacy -com.sun.org.apache.xml.internal.res = tiger legacy -com.sun.org.apache.xml.internal.serialize = tiger legacy -com.sun.org.apache.xml.internal.serializer = tiger legacy -com.sun.org.apache.xml.internal.utils = tiger legacy -com.sun.org.apache.xml.internal.utils.res = tiger legacy -com.sun.org.apache.xml.internal.utils.synthetic = tiger legacy -com.sun.org.apache.xml.internal.utils.synthetic.reflection = tiger legacy -com.sun.org.apache.xpath.internal = tiger legacy -com.sun.org.apache.xpath.internal.axes = tiger legacy -com.sun.org.apache.xpath.internal.compiler = tiger legacy -com.sun.org.apache.xpath.internal.functions = tiger legacy -com.sun.org.apache.xpath.internal.jaxp = tiger legacy -com.sun.org.apache.xpath.internal.objects = tiger legacy -com.sun.org.apache.xpath.internal.operations = tiger legacy -com.sun.org.apache.xpath.internal.patterns = tiger legacy -com.sun.org.apache.xpath.internal.res = tiger legacy -com.sun.org.omg.CORBA = tiger legacy -com.sun.org.omg.CORBA.ValueDefPackage = tiger legacy -com.sun.org.omg.CORBA.portable = tiger legacy -com.sun.org.omg.SendingContext = tiger legacy -com.sun.org.omg.SendingContext.CodeBasePackage = tiger legacy -com.sun.rmi.rmid = tiger legacy -com.sun.rowset = tiger legacy -com.sun.rowset.internal = tiger legacy -com.sun.rowset.providers = tiger legacy -com.sun.security.auth = tiger legacy -com.sun.security.auth.callback = tiger legacy -com.sun.security.auth.login = tiger legacy -com.sun.security.auth.module = tiger legacy -com.sun.security.cert.internal.x509 = tiger legacy -com.sun.security.jgss = tiger legacy -com.sun.security.sasl = tiger legacy -com.sun.security.sasl.digest = tiger legacy -com.sun.security.sasl.gsskerb = tiger legacy -com.sun.security.sasl.util = tiger legacy -com.sun.swing.internal.plaf.basic.resources = tiger legacy -com.sun.swing.internal.plaf.metal.resources = tiger legacy -com.sun.swing.internal.plaf.synth.resources = tiger legacy -com.sun.tracing = tiger legacy -com.sun.tracing.dtrace = tiger legacy -java.applet = tiger legacy -java.awt = tiger legacy -java.awt.color = tiger legacy -java.awt.datatransfer = tiger legacy -java.awt.dnd = tiger legacy -java.awt.dnd.peer = tiger legacy -java.awt.event = tiger legacy -java.awt.font = tiger legacy -java.awt.geom = tiger legacy -java.awt.im = tiger legacy -java.awt.im.spi = tiger legacy -java.awt.image = tiger legacy -java.awt.image.renderable = tiger legacy -java.awt.peer = tiger legacy -java.awt.print = tiger legacy -java.beans = tiger legacy -java.beans.beancontext = tiger legacy -java.io = tiger legacy -java.lang = tiger legacy -java.lang.annotation = tiger legacy -java.lang.instrument = tiger legacy -java.lang.management = tiger legacy -java.lang.ref = tiger legacy -java.lang.reflect = tiger legacy -java.math = tiger legacy -java.net = tiger legacy -java.nio = tiger legacy -java.nio.channels = tiger legacy -java.nio.channels.spi = tiger legacy -java.nio.charset = tiger legacy -java.nio.charset.spi = tiger legacy -java.rmi = tiger legacy -java.rmi.activation = tiger legacy -java.rmi.dgc = tiger legacy -java.rmi.registry = tiger legacy -java.rmi.server = tiger legacy -java.security = tiger legacy -java.security.acl = tiger legacy -java.security.cert = tiger legacy -java.security.interfaces = tiger legacy -java.security.spec = tiger legacy -java.sql = tiger legacy -java.text = tiger legacy -java.util = tiger legacy -java.util.concurrent = tiger legacy -java.util.concurrent.atomic = tiger legacy -java.util.concurrent.locks = tiger legacy -java.util.jar = tiger legacy -java.util.logging = tiger legacy -java.util.prefs = tiger legacy -java.util.regex = tiger legacy -java.util.zip = tiger legacy -javax.accessibility = tiger legacy -javax.activity = tiger legacy -javax.imageio = tiger legacy -javax.imageio.event = tiger legacy -javax.imageio.metadata = tiger legacy -javax.imageio.plugins.bmp = tiger legacy -javax.imageio.plugins.jpeg = tiger legacy -javax.imageio.spi = tiger legacy -javax.imageio.stream = tiger legacy -javax.management = tiger legacy -javax.management.loading = tiger legacy -javax.management.modelmbean = tiger legacy -javax.management.monitor = tiger legacy -javax.management.openmbean = tiger legacy -javax.management.relation = tiger legacy -javax.management.remote = tiger legacy -javax.management.remote.rmi = tiger legacy -javax.management.timer = tiger legacy -javax.naming = tiger legacy -javax.naming.directory = tiger legacy -javax.naming.event = tiger legacy -javax.naming.ldap = tiger legacy -javax.naming.spi = tiger legacy -javax.net = tiger legacy -javax.net.ssl = tiger legacy -javax.print = tiger legacy -javax.print.attribute = tiger legacy -javax.print.attribute.standard = tiger legacy -javax.print.event = tiger legacy -javax.rmi = tiger legacy -javax.rmi.CORBA = tiger legacy -javax.rmi.ssl = tiger legacy -javax.security.auth = tiger legacy -javax.security.auth.callback = tiger legacy -javax.security.auth.kerberos = tiger legacy -javax.security.auth.login = tiger legacy -javax.security.auth.spi = tiger legacy -javax.security.auth.x500 = tiger legacy -javax.security.cert = tiger legacy -javax.security.sasl = tiger legacy -javax.sound.midi = tiger legacy -javax.sound.midi.spi = tiger legacy -javax.sound.sampled = tiger legacy -javax.sound.sampled.spi = tiger legacy -javax.sql = tiger legacy -javax.sql.rowset = tiger legacy -javax.sql.rowset.serial = tiger legacy -javax.sql.rowset.spi = tiger legacy -javax.swing = tiger legacy -javax.swing.border = tiger legacy -javax.swing.colorchooser = tiger legacy -javax.swing.event = tiger legacy -javax.swing.filechooser = tiger legacy -javax.swing.plaf = tiger legacy -javax.swing.plaf.basic = tiger legacy -javax.swing.plaf.basic.icons = tiger legacy -javax.swing.plaf.metal = tiger legacy -javax.swing.plaf.metal.icons = tiger legacy -javax.swing.plaf.metal.icons.ocean = tiger legacy -javax.swing.plaf.metal.sounds = tiger legacy -javax.swing.plaf.multi = tiger legacy -javax.swing.plaf.nimbus = tiger legacy -javax.swing.plaf.synth = tiger legacy -javax.swing.table = tiger legacy -javax.swing.text = tiger legacy -javax.swing.text.html = tiger legacy -javax.swing.text.html.icons = tiger legacy -javax.swing.text.html.parser = tiger legacy -javax.swing.text.rtf = tiger legacy -javax.swing.text.rtf.charsets = tiger legacy -javax.swing.tree = tiger legacy -javax.swing.undo = tiger legacy -javax.transaction = tiger legacy -javax.transaction.xa = tiger legacy -javax.xml = tiger legacy -javax.xml.datatype = tiger legacy -javax.xml.namespace = tiger legacy -javax.xml.parsers = tiger legacy -javax.xml.transform = tiger legacy -javax.xml.transform.dom = tiger legacy -javax.xml.transform.sax = tiger legacy -javax.xml.transform.stream = tiger legacy -javax.xml.validation = tiger legacy -javax.xml.xpath = tiger legacy -org.ietf.jgss = tiger legacy -org.omg.CORBA = tiger legacy -org.omg.CORBA.DynAnyPackage = tiger legacy -org.omg.CORBA.ORBPackage = tiger legacy -org.omg.CORBA.TypeCodePackage = tiger legacy -org.omg.CORBA.portable = tiger legacy -org.omg.CORBA_2_3 = tiger legacy -org.omg.CORBA_2_3.portable = tiger legacy -org.omg.CosNaming = tiger legacy -org.omg.CosNaming.NamingContextExtPackage = tiger legacy -org.omg.CosNaming.NamingContextPackage = tiger legacy -org.omg.Dynamic = tiger legacy -org.omg.DynamicAny = tiger legacy -org.omg.DynamicAny.DynAnyFactoryPackage = tiger legacy -org.omg.DynamicAny.DynAnyPackage = tiger legacy -org.omg.IOP = tiger legacy -org.omg.IOP.CodecFactoryPackage = tiger legacy -org.omg.IOP.CodecPackage = tiger legacy -org.omg.Messaging = tiger legacy -org.omg.PortableInterceptor = tiger legacy -org.omg.PortableInterceptor.ORBInitInfoPackage = tiger legacy -org.omg.PortableServer = tiger legacy -org.omg.PortableServer.CurrentPackage = tiger legacy -org.omg.PortableServer.POAManagerPackage = tiger legacy -org.omg.PortableServer.POAPackage = tiger legacy -org.omg.PortableServer.ServantLocatorPackage = tiger legacy -org.omg.PortableServer.portable = tiger legacy -org.omg.SendingContext = tiger legacy -org.omg.stub.java.rmi = tiger legacy -org.omg.stub.javax.management.remote.rmi = tiger legacy -org.w3c.dom = tiger legacy -org.w3c.dom.bootstrap = tiger legacy -org.w3c.dom.css = tiger legacy -org.w3c.dom.events = tiger legacy -org.w3c.dom.html = tiger legacy -org.w3c.dom.ls = tiger legacy -org.w3c.dom.ranges = tiger legacy -org.w3c.dom.stylesheets = tiger legacy -org.w3c.dom.traversal = tiger legacy -org.w3c.dom.views = tiger legacy -org.xml.sax = tiger legacy -org.xml.sax.ext = tiger legacy -org.xml.sax.helpers = tiger legacy -sun.applet = tiger legacy -sun.applet.resources = tiger legacy -sun.audio = tiger legacy -sun.awt = tiger legacy -sun.awt.X11 = tiger legacy -sun.awt.color = tiger legacy -sun.awt.datatransfer = tiger legacy -sun.awt.dnd = tiger legacy -sun.awt.geom = tiger legacy -sun.awt.im = tiger legacy -sun.awt.image = tiger legacy -sun.awt.image.codec = tiger legacy -sun.awt.motif = tiger legacy -sun.awt.resources = tiger legacy -sun.awt.shell = tiger legacy -sun.awt.windows = tiger legacy -sun.beans.editors = tiger legacy -sun.beans.infos = tiger legacy -sun.corba = tiger legacy -sun.dc.path = tiger legacy -sun.dc.pr = tiger legacy -sun.font = tiger legacy -sun.instrument = tiger legacy -sun.io = tiger legacy -sun.java2d = tiger legacy -sun.java2d.loops = tiger legacy -sun.java2d.opengl = tiger legacy -sun.java2d.pipe = tiger legacy -sun.jdbc.odbc = tiger legacy -sun.jdbc.odbc.ee = tiger legacy -sun.management = tiger legacy -sun.management.counter = tiger legacy -sun.management.counter.perf = tiger legacy -sun.management.jmxremote = tiger legacy -sun.management.resources = tiger legacy -sun.management.snmp = tiger legacy -sun.management.snmp.jvminstr = tiger legacy -sun.management.snmp.jvmmib = tiger legacy -sun.management.snmp.util = tiger legacy -sun.misc = tiger legacy -sun.misc.resources = tiger legacy -sun.net = tiger legacy -sun.net.dns = tiger legacy -sun.net.ftp = tiger legacy -sun.net.smtp = tiger legacy -sun.net.spi = tiger legacy -sun.net.spi.nameservice = tiger legacy -sun.net.util = tiger legacy -sun.net.www = tiger legacy -sun.net.www.content.audio = tiger legacy -sun.net.www.content.image = tiger legacy -sun.net.www.content.text = tiger legacy -sun.net.www.http = tiger legacy -sun.net.www.protocol.doc = tiger legacy -sun.net.www.protocol.file = tiger legacy -sun.net.www.protocol.ftp = tiger legacy -sun.net.www.protocol.gopher = tiger legacy -sun.net.www.protocol.http = tiger legacy -sun.net.www.protocol.https = tiger legacy -sun.net.www.protocol.jar = tiger legacy -sun.net.www.protocol.mailto = tiger legacy -sun.net.www.protocol.netdoc = tiger legacy -sun.net.www.protocol.systemresource = tiger legacy -sun.net.www.protocol.verbatim = tiger legacy -sun.nio = tiger legacy -sun.nio.ch = tiger legacy -sun.nio.cs = tiger legacy -sun.print = tiger legacy -sun.print.resources = tiger legacy -sun.reflect = tiger legacy -sun.reflect.annotation = tiger legacy -sun.reflect.generics.factory = tiger legacy -sun.reflect.generics.parser = tiger legacy -sun.reflect.generics.reflectiveObjects = tiger legacy -sun.reflect.generics.repository = tiger legacy -sun.reflect.generics.scope = tiger legacy -sun.reflect.generics.tree = tiger legacy -sun.reflect.generics.visitor = tiger legacy -sun.rmi.log = tiger legacy -sun.rmi.registry = tiger legacy -sun.rmi.registry.resources = tiger legacy -sun.rmi.rmid.resources = tiger legacy -sun.rmi.runtime = tiger legacy -sun.rmi.server = tiger legacy -sun.rmi.transport = tiger legacy -sun.rmi.transport.proxy = tiger legacy -sun.rmi.transport.tcp = tiger legacy -sun.security.action = tiger legacy -sun.security.jca = tiger legacy -sun.security.jgss = tiger legacy -sun.security.jgss.krb5 = tiger legacy -sun.security.jgss.spi = tiger legacy -sun.security.krb5 = tiger legacy -sun.security.krb5.internal = tiger legacy -sun.security.krb5.internal.ccache = tiger legacy -sun.security.krb5.internal.crypto = tiger legacy -sun.security.krb5.internal.crypto.dk = tiger legacy -sun.security.krb5.internal.ktab = tiger legacy -sun.security.krb5.internal.rcache = tiger legacy -sun.security.krb5.internal.tools = tiger legacy -sun.security.krb5.internal.util = tiger legacy -sun.security.pkcs = tiger legacy -sun.security.provider = tiger legacy -sun.security.provider.certpath = tiger legacy -sun.security.rsa = tiger legacy -sun.security.timestamp = tiger legacy -sun.security.tools = tiger legacy -sun.security.util = tiger legacy -sun.security.validator = tiger legacy -sun.security.x509 = tiger legacy -sun.swing = tiger legacy -sun.swing.plaf.synth = tiger legacy -sun.text = tiger legacy -sun.text.resources = tiger legacy -sun.tools.hprof = tiger legacy -sun.tools.jar = tiger legacy -sun.tools.jar.resources = tiger legacy -sun.util = tiger legacy -sun.util.calendar = tiger legacy -sun.util.locale = tiger legacy -sun.util.logging.resources = tiger legacy -sunw.io = tiger legacy -sunw.util = tiger legacy 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 class PPC64Frame extends Frame { } 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 @@ public class PPC64Frame extends Frame { 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.httpserver/share/classes/module-info.java b/src/jdk.httpserver/share/classes/module-info.java index 15e9e2cc36d..ac147582b14 100644 --- a/src/jdk.httpserver/share/classes/module-info.java +++ b/src/jdk.httpserver/share/classes/module-info.java @@ -23,6 +23,8 @@ * questions. */ +import com.sun.net.httpserver.*; + /** * Defines the JDK-specific HTTP server API, and provides the jwebserver tool * for running a minimal HTTP server. @@ -109,6 +111,14 @@ * and implementation of the server does not intend to be a full-featured, high performance * HTTP server. * + * @implNote + * Prior to JDK 26, in the JDK default implementation, the {@link HttpExchange} attribute map was + * shared with the enclosing {@link HttpContext}. + * Since JDK 26, by default, exchange attributes are per-exchange and the context attributes must + * be accessed by calling {@link HttpExchange#getHttpContext() getHttpContext()}{@link + * HttpContext#getAttributes() .getAttributes()}.
      + * A new system property, {@systemProperty jdk.httpserver.attributes} (default value: {@code ""}) + * allows to revert this new behavior. Set this property to "context" to restore the pre JDK 26 behavior. * @toolGuide jwebserver * * @uses com.sun.net.httpserver.spi.HttpServerProvider diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java index 1119d1e386b..0899952b495 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, 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 @@ -29,6 +29,7 @@ import java.io.*; import java.net.*; import javax.net.ssl.*; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.text.*; @@ -59,6 +60,9 @@ class ExchangeImpl { /* for formatting the Date: header */ private static final DateTimeFormatter FORMATTER; + private static final boolean perExchangeAttributes = + !System.getProperty("jdk.httpserver.attributes", "") + .equals("context"); static { String pattern = "EEE, dd MMM yyyy HH:mm:ss zzz"; FORMATTER = DateTimeFormatter.ofPattern(pattern, Locale.US) @@ -76,7 +80,7 @@ class ExchangeImpl { PlaceholderOutputStream uos_orig; boolean sentHeaders; /* true after response headers sent */ - Map attributes; + final Map attributes; int rcode = -1; HttpPrincipal principal; ServerImpl server; @@ -91,6 +95,9 @@ class ExchangeImpl { this.uri = u; this.connection = connection; this.reqContentLen = len; + this.attributes = perExchangeAttributes + ? new ConcurrentHashMap<>() + : getHttpContext().getAttributes(); /* ros only used for headers, body written directly to stream */ this.ros = req.outputStream(); this.ris = req.inputStream(); @@ -361,26 +368,15 @@ class ExchangeImpl { } public Object getAttribute (String name) { - if (name == null) { - throw new NullPointerException ("null name parameter"); - } - if (attributes == null) { - attributes = getHttpContext().getAttributes(); - } - return attributes.get (name); + return attributes.get(Objects.requireNonNull(name, "null name parameter")); } public void setAttribute (String name, Object value) { - if (name == null) { - throw new NullPointerException ("null name parameter"); - } - if (attributes == null) { - attributes = getHttpContext().getAttributes(); - } + var key = Objects.requireNonNull(name, "null name parameter"); if (value != null) { - attributes.put (name, value); + attributes.put(key, value); } else { - attributes.remove (name); + attributes.remove(key); } } 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 @@ public class HtmlDoclet extends AbstractDoclet { 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 @@ public class TableOfContents { .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 class DocPaths { /** 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 java.lang.management.*; 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 @@ public class ProxyClient implements JConsoleContext { this.advancedUrl = url; this.connectionName = getConnectionName(url, userName); this.displayName = connectionName; - setParameters(new JMXServiceURL(url), userName, password); + JMXServiceURL jmxServiceURL = new JMXServiceURL(url); + setParameters(jmxServiceURL, userName, password); + if ("rmi".equals(jmxServiceURL.getProtocol())) { + String path = jmxServiceURL.getURLPath(); + if (path.startsWith("/jndi/")) { + int end = path.indexOf(';'); + if (end < 0) end = path.length(); + String registryURIStr = path.substring(6, end); + URI registryURI = URI.create(registryURIStr); + if ("rmi".equals(registryURI.getScheme()) + && "/jmxrmi".equals(registryURI.getPath())) { + this.registryHostName = registryURI.getHost(); + this.registryPort = registryURI.getPort(); + this.vmConnector = true; + checkSslConfig(); + } + } + } } private ProxyClient(LocalVirtualMachine lvm) throws IOException { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java index 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 @@ public final class EventDirectoryStream extends AbstractEventStream { } 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 @@ public final class EventFileStream extends AbstractEventStream { 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 @@ public final class EventFileStream extends AbstractEventStream { Arrays.sort(cacheSorted, 0, index, EVENT_COMPARATOR); for (int i = 0; i < index; i++) { c.dispatch(cacheSorted[i]); + cacheSorted[i] = null; } - onFlush(); } private void processUnordered(Dispatcher c) throws IOException { diff --git a/src/jdk.jlink/share/man/jlink.md b/src/jdk.jlink/share/man/jlink.md index 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/linux/classes/jdk/jpackage/internal/DesktopIntegration.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java index 790d5d877aa..dbaa5e3eec6 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java @@ -26,12 +26,10 @@ package jdk.jpackage.internal; import static jdk.jpackage.internal.ApplicationImageUtils.createLauncherIconResource; import static jdk.jpackage.internal.model.LauncherShortcut.toRequest; -import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; -import java.io.UncheckedIOException; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; @@ -82,22 +80,12 @@ final class DesktopIntegration extends ShellCustomAction { // - user explicitly requested to create a shortcut boolean withDesktopFile = !associations.isEmpty() || toRequest(launcher.shortcut()).orElse(false); - var curIconResource = createLauncherIconResource(pkg.app(), launcher, - env::createResource); - - if (curIconResource.isEmpty()) { + if (!launcher.hasIcon()) { // This is additional launcher with explicit `no icon` configuration. withDesktopFile = false; - } else { - try { - if (curIconResource.get().saveToFile((Path)null) != OverridableResource.Source.DefaultResource) { - // This launcher has custom icon configured. - withDesktopFile = true; - } - } catch (IOException ex) { - // Should never happen as `saveToFile((Path)null)` should not perform any actual I/O operations. - throw new UncheckedIOException(ex); - } + } else if (launcher.hasCustomIcon()) { + // This launcher has custom icon configured. + withDesktopFile = true; } desktopFileResource = env.createResource("template.desktop") @@ -119,17 +107,12 @@ final class DesktopIntegration extends ShellCustomAction { if (withDesktopFile) { desktopFile = Optional.of(createDesktopFile(desktopFileName)); iconFile = Optional.of(createDesktopFile(escapedAppFileName + ".png")); - - if (curIconResource.isEmpty()) { - // Create default icon. - curIconResource = createLauncherIconResource(pkg.app(), pkg.app().mainLauncher().orElseThrow(), env::createResource); - } } else { desktopFile = Optional.empty(); iconFile = Optional.empty(); } - iconResource = curIconResource; + iconResource = createLauncherIconResource(launcher, env::createResource); desktopFileData = createDataForDesktopFile(); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java index 992ec630c0e..fe8d6bcf34f 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java @@ -25,13 +25,15 @@ package jdk.jpackage.internal; +import java.util.Optional; + public class LinuxAppBundler extends AppImageBundler { public LinuxAppBundler() { setAppImageSupplier((params, output) -> { // Order is important! var app = LinuxFromParams.APPLICATION.fetchFrom(params); var env = BuildEnvFromParams.BUILD_ENV.fetchFrom(params); - LinuxPackagingPipeline.build() + LinuxPackagingPipeline.build(Optional.empty()) .excludeDirFromCopying(output.getParent()) .create().execute(BuildEnv.withAppImageDir(env, output), app); }); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java index 9e2ea63cc32..76a08519b48 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java @@ -27,6 +27,7 @@ package jdk.jpackage.internal; import java.nio.file.Path; import java.util.Map; +import java.util.Optional; import jdk.jpackage.internal.model.LinuxDebPackage; import jdk.jpackage.internal.model.PackagerException; import jdk.jpackage.internal.model.StandardPackageType; @@ -51,12 +52,14 @@ public class LinuxDebBundler extends LinuxPackageBundler { @Override public Path execute(Map params, Path outputParentDir) throws PackagerException { + var pkg = LinuxFromParams.DEB_PACKAGE.fetchFrom(params); + return Packager.build().outputDir(outputParentDir) - .pkg(LinuxFromParams.DEB_PACKAGE.fetchFrom(params)) + .pkg(pkg) .env(BuildEnvFromParams.BUILD_ENV.fetchFrom(params)) - .pipelineBuilderMutatorFactory((env, pkg, outputDir) -> { + .pipelineBuilderMutatorFactory((env, _, outputDir) -> { return new LinuxDebPackager(env, pkg, outputDir, sysEnv.orElseThrow()); - }).execute(LinuxPackagingPipeline.build()); + }).execute(LinuxPackagingPipeline.build(Optional.of(pkg))); } @Override diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java index a8d109220dc..e9d1416b5c3 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java @@ -43,6 +43,7 @@ import jdk.jpackage.internal.model.LinuxDebPackage; import jdk.jpackage.internal.model.LinuxLauncher; import jdk.jpackage.internal.model.LinuxLauncherMixin; import jdk.jpackage.internal.model.LinuxRpmPackage; +import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.StandardPackageType; final class LinuxFromParams { @@ -55,7 +56,9 @@ final class LinuxFromParams { final var launcher = launcherFromParams.create(launcherParams); final var shortcut = findLauncherShortcut(LINUX_SHORTCUT_HINT, params, launcherParams); return LinuxLauncher.create(launcher, new LinuxLauncherMixin.Stub(shortcut)); - }), APPLICATION_LAYOUT).create(); + }), (LinuxLauncher linuxLauncher, Launcher launcher) -> { + return LinuxLauncher.create(launcher, linuxLauncher); + }, APPLICATION_LAYOUT).create(); return LinuxApplication.create(app); } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackagingPipeline.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackagingPipeline.java index dd29338655d..6f6013b3091 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackagingPipeline.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackagingPipeline.java @@ -25,17 +25,20 @@ package jdk.jpackage.internal; import static jdk.jpackage.internal.ApplicationImageUtils.createLauncherIconResource; -import jdk.jpackage.internal.PackagingPipeline.AppImageBuildEnv; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Optional; +import jdk.jpackage.internal.PackagingPipeline.AppImageBuildEnv; import jdk.jpackage.internal.PackagingPipeline.BuildApplicationTaskID; import jdk.jpackage.internal.PackagingPipeline.PrimaryTaskID; import jdk.jpackage.internal.PackagingPipeline.TaskID; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLayout; +import jdk.jpackage.internal.model.Launcher; +import jdk.jpackage.internal.model.LinuxPackage; import jdk.jpackage.internal.resources.ResourceLocator; final class LinuxPackagingPipeline { @@ -45,14 +48,20 @@ final class LinuxPackagingPipeline { LAUNCHER_ICONS } - static PackagingPipeline.Builder build() { - return PackagingPipeline.buildStandard() + static PackagingPipeline.Builder build(Optional pkg) { + var builder = PackagingPipeline.buildStandard() .task(LinuxAppImageTaskID.LAUNCHER_LIB) .addDependent(PrimaryTaskID.BUILD_APPLICATION_IMAGE) .applicationAction(LinuxPackagingPipeline::writeLauncherLib).add() .task(LinuxAppImageTaskID.LAUNCHER_ICONS) .addDependent(BuildApplicationTaskID.CONTENT) .applicationAction(LinuxPackagingPipeline::writeLauncherIcons).add(); + + pkg.ifPresent(_ -> { + builder.task(LinuxAppImageTaskID.LAUNCHER_ICONS).noaction().add(); + }); + + return builder; } private static void writeLauncherLib( @@ -68,8 +77,8 @@ final class LinuxPackagingPipeline { private static void writeLauncherIcons( AppImageBuildEnv env) throws IOException { - for (var launcher : env.app().launchers()) { - createLauncherIconResource(env.app(), launcher, env.env()::createResource).ifPresent(iconResource -> { + env.app().launchers().stream().filter(Launcher::hasCustomIcon).forEach(launcher -> { + createLauncherIconResource(launcher, env.env()::createResource).ifPresent(iconResource -> { String iconFileName = launcher.executableName() + ".png"; Path iconTarget = env.resolvedLayout().desktopIntegrationDirectory().resolve(iconFileName); try { @@ -78,7 +87,7 @@ final class LinuxPackagingPipeline { throw new UncheckedIOException(ex); } }); - } + }); } static final LinuxApplicationLayout APPLICATION_LAYOUT = LinuxApplicationLayout.create( diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java index 2337a286011..c134aa91d6a 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java @@ -27,6 +27,7 @@ package jdk.jpackage.internal; import java.nio.file.Path; import java.util.Map; +import java.util.Optional; import jdk.jpackage.internal.model.LinuxRpmPackage; import jdk.jpackage.internal.model.PackagerException; import jdk.jpackage.internal.model.StandardPackageType; @@ -52,12 +53,14 @@ public class LinuxRpmBundler extends LinuxPackageBundler { @Override public Path execute(Map params, Path outputParentDir) throws PackagerException { + var pkg = LinuxFromParams.RPM_PACKAGE.fetchFrom(params); + return Packager.build().outputDir(outputParentDir) - .pkg(LinuxFromParams.RPM_PACKAGE.fetchFrom(params)) + .pkg(pkg) .env(BuildEnvFromParams.BUILD_ENV.fetchFrom(params)) - .pipelineBuilderMutatorFactory((env, pkg, outputDir) -> { + .pipelineBuilderMutatorFactory((env, _, outputDir) -> { return new LinuxRpmPackager(env, pkg, outputDir, sysEnv.orElseThrow()); - }).execute(LinuxPackagingPipeline.build()); + }).execute(LinuxPackagingPipeline.build(Optional.of(pkg))); } @Override 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 @@ final class AppImageSigner { throw new IllegalArgumentException(); } - app = normalizeAppImageLayout(app); + app = copyWithUnresolvedAppImageLayout(app); final var fileFilter = new SignFilter(app, appImage); @@ -243,7 +243,7 @@ final class AppImageSigner { } } - 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 abstract class MacBaseInstallerBundler extends AbstractBundler { 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..72c33ef6475 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java @@ -92,14 +92,16 @@ final class MacFromParams { final var superAppBuilder = createApplicationBuilder(params, toFunction(launcherParams -> { var launcher = launcherFromParams.create(launcherParams); return MacLauncher.create(launcher); - }), APPLICATION_LAYOUT, RUNTIME_BUNDLE_LAYOUT, predefinedRuntimeLayout.map(RuntimeLayout::unresolve)); + }), (MacLauncher _, Launcher launcher) -> { + return MacLauncher.create(launcher); + }, APPLICATION_LAYOUT, RUNTIME_BUNDLE_LAYOUT, predefinedRuntimeLayout.map(RuntimeLayout::unresolve)); if (hasPredefinedAppImage(params)) { // Set the main launcher start up info. // 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 +124,7 @@ final class MacFromParams { 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 @@ final class MacPackageBuilder { .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 @@ final class MacPkgPackageBuilder { } 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 @@ final class MacPkgPackageBuilder { } } + 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..76a5fc1a50c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java @@ -32,7 +32,9 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.function.BiFunction; import java.util.function.Function; +import java.util.function.Predicate; import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLaunchers; @@ -40,7 +42,9 @@ import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.ExternalApplication; import jdk.jpackage.internal.model.ExternalApplication.LauncherInfo; import jdk.jpackage.internal.model.Launcher; +import jdk.jpackage.internal.model.LauncherIcon; import jdk.jpackage.internal.model.LauncherStartupInfo; +import jdk.jpackage.internal.model.ResourceDirLauncherIcon; import jdk.jpackage.internal.model.RuntimeBuilder; final class ApplicationBuilder { @@ -86,6 +90,9 @@ final class ApplicationBuilder { ApplicationBuilder initFromExternalApplication(ExternalApplication app, Function mapper) { + + externalApp = Objects.requireNonNull(app); + if (version == null) { version = app.getAppVersion(); } @@ -112,6 +119,19 @@ final class ApplicationBuilder { 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; @@ -152,6 +172,97 @@ final class ApplicationBuilder { return this; } + static ApplicationLaunchers normalizeIcons( + ApplicationLaunchers appLaunchers, Optional resourceDir, BiFunction launcherOverrideCtor) { + + Objects.requireNonNull(resourceDir); + + return normalizeLauncherProperty(appLaunchers, Launcher::hasDefaultIcon, (T launcher) -> { + return resourceDir.flatMap(dir -> { + var resource = LauncherBuilder.createLauncherIconResource(launcher, _ -> { + return new OverridableResource() + .setResourceDir(dir) + .setSourceOrder(OverridableResource.Source.ResourceDir); + }); + if (resource.probe() == OverridableResource.Source.ResourceDir) { + return Optional.of(ResourceDirLauncherIcon.create(resource.getPublicName().toString())); + } else { + return Optional.empty(); + } + }); + }, launcher -> { + return launcher.icon().orElseThrow(); + }, (launcher, icon) -> { + return launcherOverrideCtor.apply(launcher, overrideIcon(launcher, icon)); + }); + } + + static ApplicationLaunchers normalizeLauncherProperty( + ApplicationLaunchers appLaunchers, + Predicate needsNormalization, + Function> normalizedPropertyValueFinder, + BiFunction propertyOverrider) { + + return normalizeLauncherProperty( + appLaunchers, + needsNormalization, + normalizedPropertyValueFinder, + launcher -> { + return normalizedPropertyValueFinder.apply(launcher).orElseThrow(); + }, + propertyOverrider); + } + + static ApplicationLaunchers normalizeLauncherProperty( + ApplicationLaunchers appLaunchers, + Predicate needsNormalization, + Function> normalizedPropertyValueFinder, + Function normalizedPropertyValueGetter, + BiFunction propertyOverrider) { + + Objects.requireNonNull(appLaunchers); + Objects.requireNonNull(needsNormalization); + Objects.requireNonNull(normalizedPropertyValueFinder); + Objects.requireNonNull(normalizedPropertyValueGetter); + Objects.requireNonNull(propertyOverrider); + + boolean[] modified = new boolean[1]; + + @SuppressWarnings("unchecked") + var newLaunchers = appLaunchers.asList().stream().map(launcher -> { + return (U)launcher; + }).map(launcher -> { + if (needsNormalization.test(launcher)) { + return normalizedPropertyValueFinder.apply(launcher).map(normalizedPropertyValue -> { + modified[0] = true; + return propertyOverrider.apply(launcher, normalizedPropertyValue); + }).orElse(launcher); + } else { + return launcher; + } + }).toList(); + + var newMainLauncher = newLaunchers.getFirst(); + if (!needsNormalization.test(newMainLauncher)) { + // The main launcher doesn't require normalization. + newLaunchers = newLaunchers.stream().map(launcher -> { + if (needsNormalization.test(launcher)) { + var normalizedPropertyValue = normalizedPropertyValueGetter.apply(newMainLauncher); + modified[0] = true; + return propertyOverrider.apply(launcher, normalizedPropertyValue); + } else { + return launcher; + } + }).toList(); + } + + if (modified[0]) { + return ApplicationLaunchers.fromList(newLaunchers).orElseThrow(); + } else { + return appLaunchers; + } + } + static Launcher overrideLauncherStartupInfo(Launcher launcher, LauncherStartupInfo startupInfo) { return new Launcher.Stub( launcher.name(), @@ -179,6 +290,18 @@ final class ApplicationBuilder { app.extraAppImageFileData()); } + private static Launcher overrideIcon(Launcher launcher, LauncherIcon icon) { + return new Launcher.Stub( + launcher.name(), + launcher.startupInfo(), + launcher.fileAssociations(), + launcher.isService(), + launcher.description(), + Optional.of(icon), + launcher.defaultIconResourceName(), + launcher.extraAppImageFileData()); + } + record MainLauncherStartupInfo(String qualifiedClassName) implements LauncherStartupInfo { @Override public List javaOptions() { @@ -208,6 +331,7 @@ final class ApplicationBuilder { 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..3d2ffbfdc7c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java @@ -26,12 +26,12 @@ package jdk.jpackage.internal; import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; -import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import java.io.IOException; 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; @@ -42,44 +42,36 @@ import jdk.jpackage.internal.PackagingPipeline.ApplicationImageTaskAction; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.model.CustomLauncherIcon; +import jdk.jpackage.internal.model.DefaultLauncherIcon; import jdk.jpackage.internal.model.Launcher; +import jdk.jpackage.internal.model.ResourceDirLauncherIcon; import jdk.jpackage.internal.util.FileUtils; -import jdk.jpackage.internal.util.PathUtils; final class ApplicationImageUtils { - static Optional createLauncherIconResource(Application app, - Launcher launcher, + static Optional createLauncherIconResource(Launcher launcher, Function resourceSupplier) { - final String defaultIconName = launcher.defaultIconResourceName(); - final String resourcePublicName = launcher.executableName() + PathUtils.getSuffix(Path.of(defaultIconName)); - if (!launcher.hasIcon()) { - return Optional.empty(); - } + return launcher.icon().map(icon -> { + var resource = LauncherBuilder.createLauncherIconResource(launcher, resourceSupplier); - OverridableResource resource = resourceSupplier.apply(defaultIconName) - .setCategory("icon") - .setPublicName(resourcePublicName); - - launcher.icon().flatMap(CustomLauncherIcon::fromLauncherIcon).map(CustomLauncherIcon::path).ifPresent(resource::setExternal); - - if (launcher.hasDefaultIcon() && app.mainLauncher().orElseThrow() != launcher) { - // No icon explicitly configured for this launcher. - // Dry-run resource creation to figure out its source. - final Path nullPath = null; - if (toSupplier(() -> resource.saveToFile(nullPath)).get() != OverridableResource.Source.ResourceDir) { - // No icon in resource dir for this launcher, inherit icon - // configured for the main launcher. - return createLauncherIconResource( - app, app.mainLauncher().orElseThrow(), - resourceSupplier - ).map(r -> r.setLogPublicName(resourcePublicName)); + switch (icon) { + case DefaultLauncherIcon _ -> { + resource.setSourceOrder(OverridableResource.Source.DefaultResource); + } + case ResourceDirLauncherIcon v -> { + resource.setSourceOrder(OverridableResource.Source.ResourceDir); + resource.setPublicName(v.name()); + } + case CustomLauncherIcon v -> { + resource.setSourceOrder(OverridableResource.Source.External); + resource.setExternal(v.path()); + } } - } - return Optional.of(resource); + return resource; + }); } static ApplicationImageTaskAction createWriteRuntimeAction() { @@ -148,6 +140,7 @@ final class ApplicationImageUtils { } } - 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 class Arguments { 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..df9cc528439 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java @@ -47,6 +47,7 @@ import static jdk.jpackage.internal.StandardBundlerParam.NAME; 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.PREDEFINED_RUNTIME_IMAGE; +import static jdk.jpackage.internal.StandardBundlerParam.RESOURCE_DIR; import static jdk.jpackage.internal.StandardBundlerParam.SOURCE_DIR; import static jdk.jpackage.internal.StandardBundlerParam.VENDOR; import static jdk.jpackage.internal.StandardBundlerParam.VERSION; @@ -60,6 +61,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.BiFunction; import java.util.function.Function; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLaunchers; @@ -76,14 +78,16 @@ import jdk.jpackage.internal.util.function.ThrowingFunction; final class FromParams { - static ApplicationBuilder createApplicationBuilder(Map params, + static ApplicationBuilder createApplicationBuilder(Map params, Function, Launcher> launcherMapper, + BiFunction launcherOverrideCtor, ApplicationLayout appLayout) throws ConfigException, IOException { - return createApplicationBuilder(params, launcherMapper, appLayout, RuntimeLayout.DEFAULT, Optional.of(RuntimeLayout.DEFAULT)); + return createApplicationBuilder(params, launcherMapper, launcherOverrideCtor, appLayout, RuntimeLayout.DEFAULT, Optional.of(RuntimeLayout.DEFAULT)); } - static ApplicationBuilder createApplicationBuilder(Map params, + static ApplicationBuilder createApplicationBuilder(Map params, Function, Launcher> launcherMapper, + BiFunction launcherOverrideCtor, ApplicationLayout appLayout, RuntimeLayout runtimeLayout, Optional predefinedRuntimeLayout) throws ConfigException, IOException { @@ -120,7 +124,7 @@ final class FromParams { 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() @@ -133,7 +137,9 @@ final class FromParams { jlinkOptionsBuilder.apply(); }); - appBuilder.launchers(launchers).runtimeBuilder(runtimeBuilderBuilder.create()); + final var normalizedLaunchers = ApplicationBuilder.normalizeIcons(launchers, RESOURCE_DIR.findIn(params), launcherOverrideCtor); + + appBuilder.launchers(normalizedLaunchers).runtimeBuilder(runtimeBuilderBuilder.create()); } } 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 @@ final class IOUtils { 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 @@ final class IOUtils { } } - 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 @@ final class IOUtils { } } - 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.ModuleDescriptor; 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 @@ final class JLinkRuntimeBuilder implements RuntimeBuilder { options, startupInfos)); } + /** + * Returns a list of paths that includes the location where the "java.base" + * module can be found. + *

      + * 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 @@ final class JLinkRuntimeBuilder implements RuntimeBuilder { } 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 @@ final class JLinkRuntimeBuilder implements RuntimeBuilder { static final ToolProvider JLINK_TOOL = ToolProvider.findFirst( "jlink").orElseThrow(); - }; + } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherBuilder.java index 5ce98165a4a..0f6b5d6ac8d 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherBuilder.java @@ -32,14 +32,18 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.function.Function; import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.CustomLauncherIcon; +import jdk.jpackage.internal.model.DefaultLauncherIcon; import jdk.jpackage.internal.model.FileAssociation; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.Launcher.Stub; +import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.model.LauncherIcon; import jdk.jpackage.internal.model.LauncherStartupInfo; +import jdk.jpackage.internal.model.ResourceDirLauncherIcon; final class LauncherBuilder { @@ -110,6 +114,15 @@ final class LauncherBuilder { return Optional.ofNullable(name).orElseGet(() -> startupInfo.simpleClassName()); } + static OverridableResource createLauncherIconResource(Launcher launcher, + Function resourceSupplier) { + + var defaultIconResourceName = launcher.defaultIconResourceName(); + return resourceSupplier.apply(defaultIconResourceName) + .setPublicName(launcher.executableName() + PathUtils.getSuffix(Path.of(defaultIconResourceName))) + .setCategory("icon"); + } + static void validateIcon(Path icon) throws ConfigException { switch (OperatingSystem.current()) { case WINDOWS -> { 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.IOException; 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 @@ final class OverridableResource { 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 java.util.stream.Stream; 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 @@ import static jdk.jpackage.internal.model.RuntimeBuilder.getDefaultModulePath; */ 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 record ApplicationLaunchers(Launcher mainLauncher, List additio }).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/CustomLauncherIcon.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/CustomLauncherIcon.java index 13216535680..5819c42fcda 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/CustomLauncherIcon.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/CustomLauncherIcon.java @@ -29,11 +29,11 @@ import java.util.Objects; import java.util.Optional; /** - * Custom application launcher icon. + * Custom application launcher icon sourced from an external file. *

      * Use {@link #create(Path)} method to create an instance of this type. */ -public interface CustomLauncherIcon extends LauncherIcon { +public sealed interface CustomLauncherIcon extends LauncherIcon { /** * Returns path to icon file. diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/DefaultLauncherIcon.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/DefaultLauncherIcon.java index 4275b84ba80..51847349458 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/DefaultLauncherIcon.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/DefaultLauncherIcon.java @@ -29,11 +29,11 @@ import java.util.Optional; /** * Default application launcher icon. *

      - * Default icon is either loaded from the resources of {@link jdk.jpackage} module or picked from the resource directory. + * Default icon is loaded from the resources of {@link jdk.jpackage} module. *

      * Use {@link #INSTANCE} field to get an instance of this type. */ -public interface DefaultLauncherIcon extends LauncherIcon { +public sealed interface DefaultLauncherIcon extends LauncherIcon { /** * Returns the given icon as {@link DefaultLauncherIcon} type or an empty {@link Optional} instance @@ -53,5 +53,9 @@ public interface DefaultLauncherIcon extends LauncherIcon { /** * Singleton. */ - public static DefaultLauncherIcon INSTANCE = new DefaultLauncherIcon() {}; + public static DefaultLauncherIcon INSTANCE = new Details.Impl(); + + static final class Details { + private static final class Impl implements DefaultLauncherIcon {} + } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Launcher.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Launcher.java index ac60b503fe4..4c729072839 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Launcher.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Launcher.java @@ -151,16 +151,18 @@ public interface Launcher { } /** - * Returns true if this launcher has a custom icon. + * Returns true if this launcher has non-default icon. + *

      + * A custom icon can be sourced from an external file or from the resource directory. * - * @return true if this launcher has a custom icon + * @return true if this launcher has non-default icon * @see CustomLauncherIcon * @see #icon() * @see #hasDefaultIcon() * @see #hasIcon() */ default boolean hasCustomIcon() { - return icon().flatMap(CustomLauncherIcon::fromLauncherIcon).isPresent(); + return !hasDefaultIcon() && icon().isPresent(); } /** diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherIcon.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherIcon.java index bd7a9955b90..dee20013c31 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherIcon.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherIcon.java @@ -27,5 +27,5 @@ package jdk.jpackage.internal.model; /** * Application launcher icon. */ -public interface LauncherIcon { +public sealed interface LauncherIcon permits DefaultLauncherIcon, ResourceDirLauncherIcon, CustomLauncherIcon { } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ResourceDirLauncherIcon.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ResourceDirLauncherIcon.java new file mode 100644 index 00000000000..835fcb4b6bd --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ResourceDirLauncherIcon.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.model; + +import java.util.Objects; +import java.util.Optional; + +/** + * Custom application launcher icon sourced from the resource directory. + *

      + * Use {@link #create(String)} method to create an instance of this type. + */ +public sealed interface ResourceDirLauncherIcon extends LauncherIcon { + + /** + * Returns name of the resource referencing an icon file in the resource directory. + * @return name of the resource referencing an icon file in the resource directory + */ + String name(); + + /** + * Returns the given icon as {@link ResourceDirLauncherIcon} type or an empty {@link Optional} instance + * if the given icon object is not an instance of {@link ResourceDirLauncherIcon} type. + * + * @param icon application launcher icon object or null + * @return the given icon as {@link ResourceDirLauncherIcon} type or an empty {@link Optional} instance + */ + public static Optional fromLauncherIcon(LauncherIcon icon) { + if (icon instanceof ResourceDirLauncherIcon customIcon) { + return Optional.of(customIcon); + } else { + return Optional.empty(); + } + } + + /** + * Creates object of type {@link ResourceDirLauncherIcon} from the name of the resource referencing an icon file in the resource directory. + * @param name name of the resource referencing an icon file in the resource directory + * @return {@link ResourceDirLauncherIcon} instance + */ + public static ResourceDirLauncherIcon create(String name) { + Objects.requireNonNull(name); + return new Stub(name); + } + + /** + * Default implementation of {@link ResourceDirLauncherIcon} type. + */ + record Stub(String name) implements ResourceDirLauncherIcon { + } +} 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/CompositeProxy.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CompositeProxy.java index 4fe5fe9039e..39a9d319468 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CompositeProxy.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CompositeProxy.java @@ -25,14 +25,17 @@ package jdk.jpackage.internal.util; import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.toSet; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -291,26 +294,119 @@ public final class CompositeProxy { return proxy; } - private static Map, Object> createInterfaceDispatch(Class[] interfaces, Object[] slices) { + private record InterfaceDispatchBuilder(Set> interfaces, Collection slices) { - final Map, Object> interfaceDispatch = Stream.of(interfaces).collect(toMap(x -> x, iface -> { - return Stream.of(slices).filter(obj -> { - return Set.of(obj.getClass().getInterfaces()).contains(iface); - }).reduce((a, b) -> { - throw new IllegalArgumentException( - String.format("both [%s] and [%s] slices implement %s", a, b, iface)); - }).orElseThrow(() -> createInterfaceNotImplementedException(List.of(iface))); - })); + InterfaceDispatchBuilder { + Objects.requireNonNull(interfaces); + Objects.requireNonNull(slices); - if (interfaceDispatch.size() != interfaces.length) { - final List> missingInterfaces = new ArrayList<>(Set.of(interfaces)); - missingInterfaces.removeAll(interfaceDispatch.keySet()); - throw createInterfaceNotImplementedException(missingInterfaces); + if (interfaces.isEmpty()) { + throw new IllegalArgumentException("No interfaces to dispatch"); + } + + if (slices.isEmpty()) { + throw createInterfaceNotImplementedException(interfaces); + } } - return Stream.of(interfaces).flatMap(iface -> { + InterfaceDispatchBuilder(Result result) { + this(result.unservedInterfaces(), result.unusedSlices()); + } + + Map, List> createDispatchGroups() { + return interfaces.stream().collect(toMap(x -> x, iface -> { + return slices.stream().filter(obj -> { + return Stream.of(obj.getClass().getInterfaces()).flatMap(sliceIface -> { + return unfoldInterface(sliceIface); + }).anyMatch(Predicate.isEqual(iface)); + }).toList(); + })); + } + + Result createDispatch() { + var groups = createDispatchGroups(); + + var dispatch = groups.entrySet().stream().filter(e -> { + return e.getValue().size() == 1; + }).collect(toMap(Map.Entry::getKey, e -> { + return e.getValue().getFirst(); + })); + + var unservedInterfaces = groups.entrySet().stream().filter(e -> { + return e.getValue().size() != 1; + }).map(Map.Entry::getKey).collect(toSet()); + + var usedSliceIdentities = dispatch.values().stream() + .map(IdentityWrapper::new) + .collect(toSet()); + + var unusedSliceIdentities = new HashSet<>(toIdentitySet(slices)); + unusedSliceIdentities.removeAll(usedSliceIdentities); + + return new Result(dispatch, unservedInterfaces, unusedSliceIdentities.stream().map(IdentityWrapper::value).toList()); + } + + private record Result(Map, Object> dispatch, Set> unservedInterfaces, Collection unusedSlices) { + + Result { + Objects.requireNonNull(dispatch); + Objects.requireNonNull(unservedInterfaces); + Objects.requireNonNull(unusedSlices); + + if (!Collections.disjoint(dispatch.keySet(), unservedInterfaces)) { + throw new IllegalArgumentException(); + } + + if (!Collections.disjoint(toIdentitySet(dispatch.values()), toIdentitySet(unusedSlices))) { + throw new IllegalArgumentException(); + } + } + } + + private static Collection> toIdentitySet(Collection v) { + return v.stream().map(IdentityWrapper::new).collect(toSet()); + } + } + + private static Map, Object> createInterfaceDispatch(Class[] interfaces, Object[] slices) { + + if (interfaces.length == 0) { + return Collections.emptyMap(); + } + + Map, Object> dispatch = new HashMap<>(); + + var builder = new InterfaceDispatchBuilder(Set.of(interfaces), List.of(slices)); + for (;;) { + var result = builder.createDispatch(); + if (result.dispatch().isEmpty()) { + var unserved = builder.createDispatchGroups(); + for (var e : unserved.entrySet()) { + var iface = e.getKey(); + var ifaceSlices = e.getValue(); + if (ifaceSlices.size() > 1) { + throw new IllegalArgumentException( + String.format("multiple slices %s implement %s", ifaceSlices, iface)); + } + } + + var unservedInterfaces = unserved.entrySet().stream().filter(e -> { + return e.getValue().isEmpty(); + }).map(Map.Entry::getKey).toList(); + throw createInterfaceNotImplementedException(unservedInterfaces); + } else { + dispatch.putAll(result.dispatch()); + if (result.unservedInterfaces().isEmpty()) { + break; + } + } + + builder = new InterfaceDispatchBuilder(result); + } + + return dispatch.keySet().stream().flatMap(iface -> { return unfoldInterface(iface).map(unfoldedIface -> { - return Map.entry(unfoldedIface, interfaceDispatch.get(iface)); + return Map.entry(unfoldedIface, dispatch.get(iface)); }); }).collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); } @@ -321,7 +417,7 @@ public final class CompositeProxy { } private static IllegalArgumentException createInterfaceNotImplementedException( - Collection> missingInterfaces) { + Collection> missingInterfaces) { return new IllegalArgumentException(String.format("none of the slices implement %s", missingInterfaces)); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.java index 8ac88c13e1d..cb1bcaa51b1 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.java @@ -30,6 +30,7 @@ import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.List; @@ -106,6 +107,17 @@ public final class FileUtils { private static record CopyAction(Path src, Path dest) { void apply(CopyOption... options) throws IOException { + if (List.of(options).contains(StandardCopyOption.REPLACE_EXISTING)) { + // They requested copying with replacing the existing content. + if (src == null && Files.isRegularFile(dest)) { + // This copy action creates a directory, but a file at the same path already exists, so delete it. + Files.deleteIfExists(dest); + } else if (src != null && Files.isDirectory(dest)) { + // This copy action copies a file, but a directory at the same path exists already, so delete it. + deleteRecursive(dest); + } + } + if (src == null) { Files.createDirectories(dest); } else { 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 @@ final class PackageScripts & Supplier> { } 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 class WinExeBundler extends AbstractBundler { @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/WinFromParams.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromParams.java index 29c1b665f86..2d4225f5a5b 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromParams.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromParams.java @@ -41,6 +41,7 @@ import java.io.IOException; import java.util.Map; import java.util.UUID; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.WinApplication; import jdk.jpackage.internal.model.WinExePackage; import jdk.jpackage.internal.model.WinLauncher; @@ -66,7 +67,9 @@ final class WinFromParams { return WinLauncher.create(launcher, new WinLauncherMixin.Stub(isConsole, startMenuShortcut, desktopShortcut)); - }), APPLICATION_LAYOUT).create(); + }), (WinLauncher winLauncher, Launcher launcher) -> { + return WinLauncher.create(launcher, winLauncher); + }, APPLICATION_LAYOUT).create(); return WinApplication.create(app); } 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 static jdk.jpackage.internal.model.ConfigException.rethrowConfigException 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 class WinMsiBundler extends AbstractBundler { } @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/WinPackagingPipeline.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinPackagingPipeline.java index 2fa7dd895c3..f359a61ce7b 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinPackagingPipeline.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinPackagingPipeline.java @@ -56,7 +56,7 @@ final class WinPackagingPipeline { private static void rebrandLaunchers(AppImageBuildEnv env) throws IOException, PackagerException { for (var launcher : env.app().launchers()) { - final var iconTarget = createLauncherIconResource(env.app(), launcher, env.env()::createResource).map(iconResource -> { + final var iconTarget = createLauncherIconResource(launcher, env.env()::createResource).map(iconResource -> { var iconDir = env.env().buildRoot().resolve("icons"); var theIconTarget = iconDir.resolve(launcher.executableName() + ".ico"); try { 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 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder { 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 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder { } 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 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder { 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 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder { 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 @@ final class WixPipeline { 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.Arrays; 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 @@ final class WixSourceConverter { 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 @@ final class WixSourceConverter { } } - private final Map resources = new HashMap<>(); + private final Map resources = new TreeMap<>(); private final WixToolsetType wixToolsetType; } diff --git a/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpChannel.java b/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpChannel.java index 30920864c98..312a143a8e4 100644 --- a/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpChannel.java +++ b/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2024, 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 @@ -202,7 +202,7 @@ public abstract class SctpChannel * If another thread interrupts the current thread * while the connect operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws java.nio.channels.UnresolvedAddressException * If the given remote address is not fully resolved @@ -422,7 +422,7 @@ public abstract class SctpChannel * If another thread interrupts the current thread * while the connect operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws java.nio.channels.UnresolvedAddressException * If the given remote address is not fully resolved @@ -483,7 +483,7 @@ public abstract class SctpChannel * If another thread interrupts the current thread * while the connect operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws java.nio.channels.UnresolvedAddressException * If the given remote address is not fully resolved @@ -552,7 +552,7 @@ public abstract class SctpChannel * If another thread interrupts the current thread * while the connect operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws IOException * If some other I/O error occurs @@ -776,7 +776,7 @@ public abstract class SctpChannel * If another thread interrupts the current thread * while the read operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws java.nio.channels.NotYetConnectedException * If this channel is not yet connected @@ -843,7 +843,7 @@ public abstract class SctpChannel * If another thread interrupts the current thread * while the read operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws java.nio.channels.NotYetConnectedException * If this channel is not yet connected diff --git a/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpMultiChannel.java b/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpMultiChannel.java index a44a5d9c142..54b5bb796b0 100644 --- a/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpMultiChannel.java +++ b/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpMultiChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2024, 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 @@ -596,7 +596,7 @@ public abstract class SctpMultiChannel * If another thread interrupts the current thread * while the read operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws NotYetBoundException * If this channel is not yet bound @@ -682,7 +682,7 @@ public abstract class SctpMultiChannel * If another thread interrupts the current thread * while the read operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws IOException * If some other I/O error occurs diff --git a/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpServerChannel.java b/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpServerChannel.java index 27abbc71b29..816289bb380 100644 --- a/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpServerChannel.java +++ b/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpServerChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2024, 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 @@ -132,7 +132,7 @@ public abstract class SctpServerChannel * If another thread interrupts the current thread * while the accept operation is in progress, thereby * closing the channel and setting the current thread's - * interrupt status + * interrupted status * * @throws java.nio.channels.NotYetBoundException * If this channel's socket has not yet been bound diff --git a/src/utils/IdealGraphVisualizer/Coordinator/src/main/java/com/sun/hotspot/igv/coordinator/GraphNode.java b/src/utils/IdealGraphVisualizer/Coordinator/src/main/java/com/sun/hotspot/igv/coordinator/GraphNode.java index 8cd94675bca..abc1784d8d9 100644 --- a/src/utils/IdealGraphVisualizer/Coordinator/src/main/java/com/sun/hotspot/igv/coordinator/GraphNode.java +++ b/src/utils/IdealGraphVisualizer/Coordinator/src/main/java/com/sun/hotspot/igv/coordinator/GraphNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2015, 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 @@ -29,6 +29,8 @@ import com.sun.hotspot.igv.data.Properties; import com.sun.hotspot.igv.data.services.GraphViewer; import com.sun.hotspot.igv.util.PropertiesSheet; import com.sun.hotspot.igv.util.StringUtils; +import com.sun.hotspot.igv.view.EditorTopComponent; + import java.awt.Image; import javax.swing.Action; import org.openide.actions.OpenAction; @@ -86,7 +88,7 @@ public class GraphNode extends AbstractNode { @Override public String getDisplayName() { - return graph.getDisplayName(); + return EditorTopComponent.getGraphDisplayName(graph); } private GraphNode(InputGraph graph, InstanceContent content) { diff --git a/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputGraph.java b/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputGraph.java index 987c0e5576e..94c0b2ec446 100644 --- a/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputGraph.java +++ b/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputGraph.java @@ -257,11 +257,8 @@ public class InputGraph extends Properties.Entity implements FolderElement { @Override public String getDisplayName() { - if (isDiffGraph) { - return firstGraph.getDisplayName() + " Δ " + secondGraph.getDisplayName(); - } else { - return getIndex()+1 + ". " + getName(); - } + assert false : "Use EditorTopComponent::getGraphDisplayName() instead"; + return ""; } public int getIndex() { diff --git a/src/utils/IdealGraphVisualizer/Settings/src/main/java/com/sun/hotspot/igv/settings/Settings.java b/src/utils/IdealGraphVisualizer/Settings/src/main/java/com/sun/hotspot/igv/settings/Settings.java index 480fb43ea5b..b1b2c94361c 100644 --- a/src/utils/IdealGraphVisualizer/Settings/src/main/java/com/sun/hotspot/igv/settings/Settings.java +++ b/src/utils/IdealGraphVisualizer/Settings/src/main/java/com/sun/hotspot/igv/settings/Settings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, 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 @@ -46,6 +46,8 @@ public class Settings { public static final String NODE_SHORT_TEXT_DEFAULT = "[idx] [name]"; public static final String NODE_TINY_TEXT = "nodeTinyText"; public static final String NODE_TINY_TEXT_DEFAULT = "[idx]"; + public static final String GRAPH_NAME_SUFFIX = "graphNameSuffix"; + public static final String GRAPH_NAME_SUFFIX_DEFAULT = "(map: [map], block #[block] at [method])"; public static final String DEFAULT_VIEW = "defaultView"; public static final int DEFAULT_VIEW_DEFAULT = DefaultView.SEA_OF_NODES; public static final String PORT = "port"; diff --git a/src/utils/IdealGraphVisualizer/Settings/src/main/java/com/sun/hotspot/igv/settings/ViewPanel.form b/src/utils/IdealGraphVisualizer/Settings/src/main/java/com/sun/hotspot/igv/settings/ViewPanel.form index 75240c9a150..466bb2a82db 100644 --- a/src/utils/IdealGraphVisualizer/Settings/src/main/java/com/sun/hotspot/igv/settings/ViewPanel.form +++ b/src/utils/IdealGraphVisualizer/Settings/src/main/java/com/sun/hotspot/igv/settings/ViewPanel.form @@ -28,7 +28,7 @@ - + @@ -47,14 +47,16 @@ + - + - + - + + @@ -70,24 +72,30 @@ - + - + - - - + + + - - - - + + + + + + + + + + @@ -133,9 +141,6 @@ - - - @@ -149,9 +154,6 @@ - - - @@ -173,6 +175,19 @@ + + + + + + + + + + + + + diff --git a/src/utils/IdealGraphVisualizer/Settings/src/main/java/com/sun/hotspot/igv/settings/ViewPanel.java b/src/utils/IdealGraphVisualizer/Settings/src/main/java/com/sun/hotspot/igv/settings/ViewPanel.java index c2293820c31..123131d7914 100644 --- a/src/utils/IdealGraphVisualizer/Settings/src/main/java/com/sun/hotspot/igv/settings/ViewPanel.java +++ b/src/utils/IdealGraphVisualizer/Settings/src/main/java/com/sun/hotspot/igv/settings/ViewPanel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, 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 @@ -56,6 +56,8 @@ final class ViewPanel extends javax.swing.JPanel { jLabel5 = new javax.swing.JLabel(); nodeTinyTextField = new javax.swing.JTextField(); defaultViewComboBox = new javax.swing.JComboBox<>(); + graphNameSuffixField = new javax.swing.JTextField(); + jLabel6 = new javax.swing.JLabel(); org.openide.awt.Mnemonics.setLocalizedText(jLabel1, "Node Text"); @@ -70,17 +72,19 @@ final class ViewPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(jLabel4, "Short Node Text"); - nodeShortTextField.setBackground(new java.awt.Color(255, 255, 255)); nodeShortTextField.setToolTipText("Single-line format string for nodes in edge tooltips, slot tooltips, and node search bar. Properties are specified with brackets (example: \"[idx]\")."); org.openide.awt.Mnemonics.setLocalizedText(jLabel5, "Tiny Node Text"); - nodeTinyTextField.setBackground(new java.awt.Color(255, 255, 255)); nodeTinyTextField.setToolTipText("Single-line format string for node input lists. Properties are specified with brackets (example: \"[idx]\")."); defaultViewComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Sea of nodes", "Clustered sea of nodes", "Control-flow graph" })); defaultViewComboBox.setToolTipText("View shown by default when a graph is opened."); + graphNameSuffixField.setToolTipText("Single-line format string for node input lists. Properties are specified with brackets (example: \"[method]\")."); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel6, "Graph Name Suffix"); + org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( @@ -92,14 +96,16 @@ final class ViewPanel extends javax.swing.JPanel { .add(jLabel3) .add(jLabel2) .add(jLabel4) - .add(jLabel5)) - .add(39, 39, 39) + .add(jLabel5) + .add(jLabel6)) + .add(18, 18, 18) .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING, false) .add(nodeShortTextField) - .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 470, Short.MAX_VALUE) + .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 663, Short.MAX_VALUE) .add(portSpinner) - .add(nodeTinyTextField) - .add(defaultViewComboBox, 0, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .add(defaultViewComboBox, 0, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(graphNameSuffixField) + .add(nodeTinyTextField)) .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); jPanel1Layout.setVerticalGroup( @@ -111,20 +117,25 @@ final class ViewPanel extends javax.swing.JPanel { .add(jLabel1)) .add(27, 27, 27) .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) - .add(jLabel4) - .add(nodeShortTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .add(nodeShortTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .add(jLabel4, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 18, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 27, Short.MAX_VALUE) .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) - .add(jLabel5) + .add(jLabel5, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 24, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(nodeTinyTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) .add(27, 27, 27) - .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) - .add(jLabel2) - .add(defaultViewComboBox, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 27, Short.MAX_VALUE) - .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(graphNameSuffixField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .add(jLabel6, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 18, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .add(27, 27, 27) + .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(defaultViewComboBox, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .add(jLabel2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 18, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .add(27, 27, 27) + .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(portSpinner, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .add(jLabel3))) + .add(jLabel3, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 18, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .addContainerGap()) ); org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); @@ -141,13 +152,14 @@ final class ViewPanel extends javax.swing.JPanel { .add(layout.createSequentialGroup() .addContainerGap() .add(jPanel1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addContainerGap(29, Short.MAX_VALUE)) + .addContainerGap(15, Short.MAX_VALUE)) ); }// //GEN-END:initComponents void load() { nodeTextArea.setText(Settings.get().get(Settings.NODE_TEXT, Settings.NODE_TEXT_DEFAULT)); nodeShortTextField.setText(Settings.get().get(Settings.NODE_SHORT_TEXT, Settings.NODE_SHORT_TEXT_DEFAULT)); nodeTinyTextField.setText(Settings.get().get(Settings.NODE_TINY_TEXT, Settings.NODE_TINY_TEXT_DEFAULT)); + graphNameSuffixField.setText(Settings.get().get(Settings.GRAPH_NAME_SUFFIX, Settings.GRAPH_NAME_SUFFIX_DEFAULT)); defaultViewComboBox.setSelectedIndex(Settings.get().getInt(Settings.DEFAULT_VIEW, Settings.DefaultView.SEA_OF_NODES)); portSpinner.setValue(Integer.parseInt(Settings.get().get(Settings.PORT, Settings.PORT_DEFAULT))); } @@ -156,6 +168,7 @@ final class ViewPanel extends javax.swing.JPanel { Settings.get().put(Settings.NODE_TEXT, nodeTextArea.getText()); Settings.get().put(Settings.NODE_SHORT_TEXT, nodeShortTextField.getText()); Settings.get().put(Settings.NODE_TINY_TEXT, nodeTinyTextField.getText()); + Settings.get().put(Settings.GRAPH_NAME_SUFFIX, graphNameSuffixField.getText()); Settings.get().putInt(Settings.DEFAULT_VIEW, defaultViewComboBox.getSelectedIndex()); Settings.get().put(Settings.PORT, portSpinner.getValue().toString()); } @@ -165,11 +178,13 @@ final class ViewPanel extends javax.swing.JPanel { } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JComboBox defaultViewComboBox; + private javax.swing.JTextField graphNameSuffixField; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JLabel jLabel3; private javax.swing.JLabel jLabel4; private javax.swing.JLabel jLabel5; + private javax.swing.JLabel jLabel6; private javax.swing.JPanel jPanel1; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JTextField nodeShortTextField; diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/EditorTopComponent.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/EditorTopComponent.java index 35e440322d8..f1e6616a44f 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/EditorTopComponent.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/EditorTopComponent.java @@ -28,8 +28,10 @@ import com.sun.hotspot.igv.data.Group; import com.sun.hotspot.igv.data.InputGraph; import com.sun.hotspot.igv.data.InputLiveRange; import com.sun.hotspot.igv.data.InputNode; +import com.sun.hotspot.igv.data.Properties; import com.sun.hotspot.igv.data.services.InputGraphProvider; import com.sun.hotspot.igv.graph.Figure; +import com.sun.hotspot.igv.settings.Settings; import com.sun.hotspot.igv.util.LookupHistory; import com.sun.hotspot.igv.util.RangeSlider; import com.sun.hotspot.igv.util.StringUtils; @@ -37,8 +39,14 @@ import com.sun.hotspot.igv.view.actions.*; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; import java.util.List; -import java.util.*; +import java.util.Set; + import javax.swing.*; import javax.swing.border.Border; import org.openide.actions.RedoAction; @@ -132,7 +140,7 @@ public final class EditorTopComponent extends TopComponent implements TopCompone } diagramViewModel.addTitleCallback(changedGraph -> { - setDisplayName(changedGraph.getDisplayName()); + setDisplayName(getGraphDisplayName(changedGraph)); setToolTipText(diagramViewModel.getGroup().getDisplayName()); }); @@ -252,11 +260,25 @@ public final class EditorTopComponent extends TopComponent implements TopCompone } private void graphChanged(DiagramViewModel model) { - setDisplayName(model.getGraph().getDisplayName()); + setDisplayName(getGraphDisplayName(model.getGraph())); setToolTipText(model.getGroup().getDisplayName()); graphContent.set(Collections.singletonList(new EditorInputGraphProvider(this)), null); } + public static String getGraphDisplayName(InputGraph graph) { + if (graph.isDiffGraph()) { + return getGraphDisplayName(graph.getFirstGraph()) + " Δ " + + getGraphDisplayName(graph.getSecondGraph()); + } else { + String suffixTemplate = Settings.get().get(Settings.GRAPH_NAME_SUFFIX, + Settings.GRAPH_NAME_SUFFIX_DEFAULT); + String suffix = graph.getProperties().resolveString(suffixTemplate); + String emptySuffix = new Properties().resolveString(suffixTemplate); + String graphNameSuffix = suffix.equals(emptySuffix) ? "" : " " + suffix; + return graph.getIndex() + 1 + ". " + graph.getName() + graphNameSuffix; + } + } + public DiagramViewModel getModel() { return scene.getModel(); } diff --git a/test/benchmarks/micros-javac/README.md b/test/benchmarks/micros-javac/README.md new file mode 100644 index 00000000000..66ca073a763 --- /dev/null +++ b/test/benchmarks/micros-javac/README.md @@ -0,0 +1,61 @@ +# Javac microbenchmarks + +The Javac Microbenchmarks is a collection of microbenchmarks for measuring +the performance of Javac API using the +[JMH](http://openjdk.java.net/projects/code-tools/jmh/) framework. + + +## Building and running the project + +Currently, the project can be built and run with JDK 9 and later. This is +a Maven project and is built by: + + $ mvn clean install + +After building, the executable jar is target/micros-javac-[version].jar. +Run the benchmarks with: + + $ java -jar target/micros-javac-*.jar [optional jmh parameters] + +See the entire list of benchmarks using: + + $ java -jar target/micros-javacs-*.jar -l [optional regex to select benchmarks] + +For example: + + $ java -jar target/micros-javac-1.0-SNAPSHOT.jar -l + Benchmarks: + org.openjdk.bench.langtools.javac.GroupJavacBenchmark.coldGroup + org.openjdk.bench.langtools.javac.GroupJavacBenchmark.hotGroup + org.openjdk.bench.langtools.javac.SingleJavacBenchmark.compileCold + org.openjdk.bench.langtools.javac.SingleJavacBenchmark.compileHot + +And the same regex syntax works to run some test: + + $ java -jar target/micros-javac-1.0-SNAPSHOT.jar SingleJavacBenchmark.compileHot + +## Troubleshooting + +### Build of micros-javac module got stuck + +If you build got stuck on `[get] Getting: https://download.java.net/openjdk/jdk11/ri/openjdk-11+28_windows-x64_bin.zip` then you are probably experiencing some networking or web proxy obstacles. + +One solution is to download required reference JDK from [https://download.java.net/openjdk/jdk11/ri/openjdk-11+28_windows-x64_bin.zip](https://download.java.net/openjdk/jdk11/ri/openjdk-11+28_windows-x64_bin.zip) manually and then build the project with property pointing to the local copy: + + $ mvn clean install -Djavac.benchmark.openjdk.zip.download.url=file:////openjdk-11+28_windows-x64_bin.zip + +Note: Please use `openjdk-11+28_windows-x64_bin.zip` to build the project no matter what target platform is. + +Another solution might be to add proxy settings: + + $ mvn -Dhttps.proxyHost=... -Dhttps.proxyPort=... clean install + +### Execution of micros-javac benchmarks takes several hours + +micros-javac benchmarks consist of two sets of benchmarks: + * `SingleJavacBenchmark` (which is parametrized) measures each single javac compilation stage in an isolated run. This benchmark is designed for exact automated performance regression testing and it takes several hours to execute completely. + * `GroupJavacBenchmark` is grouping the measurements of all javac compilation stages into one run and its execution should take less than 30 minutes on a regular developers computer. + +Solution to speed up javac benchmarking is to select only `GroupJavacBenchmark` for execution using following command line: + + $ java -jar target/micros-javac-1.0-SNAPSHOT.jar .*GroupJavacBenchmark.* diff --git a/test/benchmarks/micros-javac/pom.xml b/test/benchmarks/micros-javac/pom.xml new file mode 100644 index 00000000000..5a8a40d7a9a --- /dev/null +++ b/test/benchmarks/micros-javac/pom.xml @@ -0,0 +1,139 @@ + + + 4.0.0 + org.openjdk + micros-javac + jar + 1.0-SNAPSHOT + OpenJDK Microbenchmark of Java Compile + + https://download.java.net/openjdk/jdk11/ri/openjdk-11+28_windows-x64_bin.zip + UTF-8 + 1.36 + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.1 + + + package + + shade + + + + + org.openjdk.jmh.Main + + + META-INF/BenchmarkList + + + META-INF/CompilerHints + + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.1 + + 1.8 + 1.8 + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + + + + + + org.apache.maven.plugins + maven-release-plugin + 3.1.1 + + + maven-deploy-plugin + 3.1.4 + + + maven-antrun-plugin + 3.1.0 + + + process-resources + + + + + + + + + + + + + +------------------------------------------------- +Bundling JDK sources with following release info: +------------------------------------------------- +${release.info} +------------------------------------------------- + + + + + run + + + + + + + + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + provided + + + diff --git a/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/GroupJavacBenchmark.java b/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/GroupJavacBenchmark.java new file mode 100644 index 00000000000..5ca3f0bf648 --- /dev/null +++ b/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/GroupJavacBenchmark.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * 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 org.openjdk.bench.langtools.javac; + +import java.io.IOException; +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.Group; +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; +import org.openjdk.jmh.infra.Blackhole; + +@State(Scope.Benchmark) +public class GroupJavacBenchmark extends JavacBenchmark { + + public static final String COLD_GROUP_NAME = "coldGroup"; + public static final int COLD_ITERATION_WARMUPS = 0; + public static final int COLD_ITERATIONS = 1; + public static final int COLD_FORK_WARMUPS = 1; + public static final int COLD_FORKS = 15; + + public static final String HOT_GROUP_NAME = "hotGroup"; + public static final int HOT_ITERATION_WARMUPS = 8; + public static final int HOT_ITERATIONS = 10; + public static final int HOT_FORK_WARMUPS = 0; + public static final int HOT_FORKS = 1; + + @Benchmark + @Group(COLD_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = COLD_ITERATION_WARMUPS) + @Measurement(iterations = COLD_ITERATIONS) + @Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void cold1_Init() throws InterruptedException { + Stage.Init.waitFor(); + } + + @Benchmark + @Group(COLD_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = COLD_ITERATION_WARMUPS) + @Measurement(iterations = COLD_ITERATIONS) + @Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void cold2_Parse() throws InterruptedException { + Stage.Parse.waitFor(); + } + + @Benchmark + @Group(COLD_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = COLD_ITERATION_WARMUPS) + @Measurement(iterations = COLD_ITERATIONS) + @Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void cold3_InitModules() throws InterruptedException { + Stage.InitModules.waitFor(); + } + + @Benchmark + @Group(COLD_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = COLD_ITERATION_WARMUPS) + @Measurement(iterations = COLD_ITERATIONS) + @Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void cold4_Enter() throws InterruptedException { + Stage.Enter.waitFor(); + } + + @Benchmark + @Group(COLD_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = COLD_ITERATION_WARMUPS) + @Measurement(iterations = COLD_ITERATIONS) + @Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void cold5_Attribute() throws InterruptedException { + Stage.Attribute.waitFor(); + } + + @Benchmark + @Group(COLD_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = COLD_ITERATION_WARMUPS) + @Measurement(iterations = COLD_ITERATIONS) + @Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void cold6_Flow() throws InterruptedException { + Stage.Flow.waitFor(); + } + + @Benchmark + @Group(COLD_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = COLD_ITERATION_WARMUPS) + @Measurement(iterations = COLD_ITERATIONS) + @Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void cold7_Desugar() throws InterruptedException { + Stage.Desugar.waitFor(); + } + + @Benchmark + @Group(COLD_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = COLD_ITERATION_WARMUPS) + @Measurement(iterations = COLD_ITERATIONS) + @Fork(warmups = COLD_FORK_WARMUPS, value = COLD_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void cold8_Generate(Blackhole bh) throws IOException { + compile(bh, Stage.Generate); + } + + @Benchmark + @Group(HOT_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = HOT_ITERATION_WARMUPS) + @Measurement(iterations = HOT_ITERATIONS) + @Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void hot1_Init() throws InterruptedException { + Stage.Init.waitFor(); + } + + @Benchmark + @Group(HOT_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = HOT_ITERATION_WARMUPS) + @Measurement(iterations = HOT_ITERATIONS) + @Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void hot2_Parse() throws InterruptedException { + Stage.Parse.waitFor(); + } + + @Benchmark + @Group(HOT_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = HOT_ITERATION_WARMUPS) + @Measurement(iterations = HOT_ITERATIONS) + @Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void hot3_InitModules() throws InterruptedException { + Stage.InitModules.waitFor(); + } + + @Benchmark + @Group(HOT_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = HOT_ITERATION_WARMUPS) + @Measurement(iterations = HOT_ITERATIONS) + @Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void hot4_Enter() throws InterruptedException { + Stage.Enter.waitFor(); + } + + @Benchmark + @Group(HOT_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = HOT_ITERATION_WARMUPS) + @Measurement(iterations = HOT_ITERATIONS) + @Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void hot5_Attribute() throws InterruptedException { + Stage.Attribute.waitFor(); + } + + @Benchmark + @Group(HOT_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = HOT_ITERATION_WARMUPS) + @Measurement(iterations = HOT_ITERATIONS) + @Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void hot6_Flow() throws InterruptedException { + Stage.Flow.waitFor(); + } + + @Benchmark + @Group(HOT_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = HOT_ITERATION_WARMUPS) + @Measurement(iterations = HOT_ITERATIONS) + @Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void hot7_Desugar() throws InterruptedException { + Stage.Desugar.waitFor(); + } + + @Benchmark + @Group(HOT_GROUP_NAME) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = HOT_ITERATION_WARMUPS) + @Measurement(iterations = HOT_ITERATIONS) + @Fork(warmups = HOT_FORK_WARMUPS, value = HOT_FORKS, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void hot8_Generate(Blackhole bh) throws IOException { + compile(bh, Stage.Generate); + } +} diff --git a/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/JavacBenchmark.java b/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/JavacBenchmark.java new file mode 100644 index 00000000000..1afffc6d044 --- /dev/null +++ b/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/JavacBenchmark.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 org.openjdk.bench.langtools.javac; + +import com.sun.tools.javac.comp.AttrContext; +import com.sun.tools.javac.comp.Env; +import com.sun.tools.javac.file.JavacFileManager; +import com.sun.tools.javac.main.JavaCompiler; +import com.sun.tools.javac.main.Main; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.Context.Factory; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Pair; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.Queue; +import static java.util.logging.Level.FINE; +import static java.util.logging.Level.CONFIG; +import java.util.logging.Logger; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileObject; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.infra.Blackhole; + +@State(Scope.Benchmark) +public class JavacBenchmark { + + static final Logger LOG = Logger.getLogger(JavacBenchmark.class.getName()); + + public enum Stage { + Init, Parse, InitModules, Enter, Attribute, Flow, Desugar, Generate; + + public synchronized void waitFor() throws InterruptedException { + wait(); + } + public synchronized void notifyDone() { + notifyAll(); + LOG.log(FINE, "{0} finished.", this.name()); + } + public boolean isAfter(Stage other) { + return ordinal() > other.ordinal(); + } + } + + private Path root; + private Path srcList; + + @Setup(Level.Trial) + public void setup(Blackhole bh) throws IOException, InterruptedException { + LOG.log(CONFIG, "Release info of the sources to be compiled by the benchmark:\n{0}", new String(JavacBenchmark.class.getResourceAsStream("/release").readAllBytes(), StandardCharsets.UTF_8)); + root = Files.createTempDirectory("JavacBenchmarkRoot"); + srcList = root.resolve("sources.list"); + int i = 0; + try (PrintStream srcListOut = new PrintStream(srcList.toFile())) { + try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(JavacBenchmark.class.getResourceAsStream("/src.zip")))) { + for (ZipEntry entry; (entry = zis.getNextEntry()) != null;) { + final String ename = entry.getName(); + if (!ename.startsWith("java.desktop") && !ename.startsWith("jdk.internal.vm.compiler") && !ename.startsWith("jdk.aot") && !ename.startsWith("jdk.accessibility")) { + if (!entry.isDirectory() && ename.endsWith(".java")) { + Path dst = root.resolve(ename); + Files.createDirectories(dst.getParent()); + Files.copy(zis, dst); + Files.readAllBytes(dst); //reads all the file back to exclude antivirus scanning time from following measurements + srcListOut.println(dst.toString()); + i++; + } + } + } + } + } + Files.walk(root).map(Path::toFile).forEach(File::deleteOnExit); //mark all files and folders for deletion on JVM exit for cases when tearDown is not executed + Thread.sleep(10000); //give some more time for the system to catch a breath for more precise measurement + LOG.log(FINE, "Extracted {0} sources.", i); + } + + @TearDown(Level.Trial) + public void tearDown() throws IOException { + Files.walk(root).sorted(Comparator.reverseOrder()).map(Path::toFile).forEachOrdered(File::delete); + LOG.fine("Sources deleted."); + } + + protected void compile(Blackhole bh, final Stage stopAt) throws IOException { + final OutputStream bhos = new OutputStream() { + @Override + public void write(int b) throws IOException { + bh.consume(b); + } + @Override + public void write(byte[] b, int off, int len) throws IOException { + bh.consume(b); + } + }; + final Context ctx = new Context(); + //inject JavaCompiler wrapping all measured methods so they directly report to the benchmark + ctx.put(JavaCompiler.compilerKey, (Factory)(c) -> { + return new JavaCompiler(c) { + @Override + public List parseFiles(Iterable fileObjects) { + Stage.Init.notifyDone(); + return stopAt.isAfter(Stage.Init) ? super.parseFiles(fileObjects) : List.nil(); + } + + @Override + public List initModules(List roots) { + Stage.Parse.notifyDone(); + return stopAt.isAfter(Stage.Parse) ? super.initModules(roots) : List.nil(); + } + + @Override + public List enterTrees(List roots) { + Stage.InitModules.notifyDone(); + return stopAt.isAfter(Stage.InitModules) ? super.enterTrees(roots) : List.nil(); + } + + @Override + public Queue> attribute(Queue> envs) { + Stage.Enter.notifyDone(); + return stopAt.isAfter(Stage.Enter) ? super.attribute(envs) : new ListBuffer<>(); + } + + @Override + public Queue> flow(Queue> envs) { + Stage.Attribute.notifyDone(); + return stopAt.isAfter(Stage.Attribute) ? super.flow(envs) : new ListBuffer<>(); + } + + @Override + public Queue, JCTree.JCClassDecl>> desugar(Queue> envs) { + Stage.Flow.notifyDone(); + return stopAt.isAfter(Stage.Flow) ? super.desugar(envs) : new ListBuffer<>(); + } + + @Override + public void generate(Queue, JCTree.JCClassDecl>> queue) { + Stage.Desugar.notifyDone(); + if (stopAt.isAfter(Stage.Desugar)) super.generate(queue); + } + }; + }); + //JavaFileManager directing all writes to a Blackhole to avoid measurement fluctuations due to delayed filesystem writes + try (JavacFileManager mngr = new JavacFileManager(ctx, true, null) { + @Override + public JavaFileObject getJavaFileForOutput(JavaFileManager.Location arg0, String arg1, JavaFileObject.Kind arg2, FileObject arg3) throws IOException { + return new ForwardingJavaFileObject(super.getJavaFileForOutput(arg0, arg1, arg2, arg3)) { + @Override + public OutputStream openOutputStream() throws IOException { + return bhos; + } + }; + } + }) { + String[] cmdLine = new String[] {"-XDcompilePolicy=simple", "-implicit:none", "-nowarn", "--module-source-path", root.toString(), "-d", root.toString(), "-XDignore.symbol.file=true", "@" + srcList.toString()}; + if (new Main("javac").compile(cmdLine, ctx).exitCode != 0) { + throw new IOException("compilation failed"); + } + } + LOG.fine("Compilation finished."); + } +} diff --git a/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/SingleJavacBenchmark.java b/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/SingleJavacBenchmark.java new file mode 100644 index 00000000000..2cc5c1a7c83 --- /dev/null +++ b/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/SingleJavacBenchmark.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * 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 org.openjdk.bench.langtools.javac; + +import java.io.IOException; +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.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +@State(Scope.Benchmark) +public class SingleJavacBenchmark extends JavacBenchmark { + + @Param + public Stage stopStage; + + @Benchmark + @Threads(1) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = 0) + @Measurement(iterations = 1) + @Fork(warmups = 1, value = 15, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void compileCold(Blackhole bh) throws IOException { + compile(bh, stopStage); + } + + @Benchmark + @Threads(1) + @BenchmarkMode(Mode.SingleShotTime) + @Warmup(iterations = 8) + @Measurement(iterations = 10) + @Fork(warmups = 0, value = 1, jvmArgsPrepend = { "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" }) + @OutputTimeUnit(TimeUnit.SECONDS) + public void compileHot(Blackhole bh) throws IOException { + compile(bh, stopStage); + } +} 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 @@ generate(SpecialCases, [["ccmn", "__ ccmn(zr, zr, 3u, Assembler::LE);", ["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..5752d6df75d 100644 --- a/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp +++ b/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp @@ -23,6 +23,7 @@ */ #include "memory/allocation.hpp" +#include "memory/arena.hpp" #include "nmt/memTracker.hpp" #include "runtime/os.hpp" #include "sanitizers/address.hpp" @@ -142,6 +143,21 @@ DEFINE_TEST(test_corruption_on_realloc_growing, COMMON_NMT_HEAP_CORRUPTION_MESSA static void test_corruption_on_realloc_shrinking() { test_corruption_on_realloc(0x11, 0x10); } DEFINE_TEST(test_corruption_on_realloc_shrinking, COMMON_NMT_HEAP_CORRUPTION_MESSAGE_PREFIX); +static void test_chunkpool_lock() { + if (!MemTracker::enabled()) { + tty->print_cr("Skipped"); + return; + } + PrintNMTStatistics = true; + { + ChunkPoolLocker cpl; + char* mem = (char*)os::malloc(100, mtTest); + memset(mem - 16, 0, 100 + 16 + 2); + os::free(mem); + } +} +DEFINE_TEST(test_chunkpool_lock, COMMON_NMT_HEAP_CORRUPTION_MESSAGE_PREFIX); + /////// // realloc is the trickiest of the bunch. Test that realloc works and correctly takes over @@ -164,4 +180,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/gtest/runtime/test_os_windows.cpp b/test/hotspot/gtest/runtime/test_os_windows.cpp index ad270848077..2d9a7e00b39 100644 --- a/test/hotspot/gtest/runtime/test_os_windows.cpp +++ b/test/hotspot/gtest/runtime/test_os_windows.cpp @@ -756,7 +756,6 @@ TEST_VM(os_windows, large_page_init_multiple_sizes) { size_t decided_large_page_size = os::win32::large_page_init_decide_size(); EXPECT_GT(decided_large_page_size, default_page_size) << "Large page size should be greater than the default page size for LargePageSizeInBytes = 4 * min_size"; -#if !defined(IA32) size_t page_size_count = 0; size_t page_size = os::page_sizes().largest(); @@ -773,7 +772,6 @@ TEST_VM(os_windows, large_page_init_multiple_sizes) { EXPECT_TRUE(page_size % min_size == 0) << "Each page size should be a multiple of the minimum large page size."; EXPECT_LE(page_size, large_page_size) << "Page size should not exceed the determined large page size."; } -#endif } TEST_VM(os_windows, large_page_init_decide_size) { @@ -809,11 +807,11 @@ TEST_VM(os_windows, large_page_init_decide_size) { EXPECT_EQ(decided_size, 2 * M) << "Expected decided size to be 2M when large page is 1M and OS reported size is 2M"; } -#if defined(IA32) || defined(AMD64) +#if defined(AMD64) FLAG_SET_CMDLINE(LargePageSizeInBytes, 5 * M); // Set large page size to 5MB if (!EnableAllLargePageSizesForWindows) { decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size - EXPECT_EQ(decided_size, 0) << "Expected decided size to be 0 for large pages bigger than 4mb on IA32 or AMD64"; + EXPECT_EQ(decided_size, 0) << "Expected decided size to be 0 for large pages bigger than 4mb on AMD64"; } #endif 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 f02ba70ba87..3dac8821165 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -79,16 +79,7 @@ compiler/c2/TestVerifyConstraintCasts.java 8355574 generic-all compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64 -compiler/whitebox/DeoptimizeRelocatedNMethod.java#G1 8369147 generic-all -compiler/whitebox/DeoptimizeRelocatedNMethod.java#Parallel 8369147 generic-all -compiler/whitebox/DeoptimizeRelocatedNMethod.java#Serial 8369147 generic-all -compiler/whitebox/DeoptimizeRelocatedNMethod.java#ZGC 8369147 generic-all -compiler/whitebox/RelocateNMethod.java#G1 8369147 generic-all -compiler/whitebox/RelocateNMethod.java#Parallel 8369147 generic-all -compiler/whitebox/RelocateNMethod.java#Serial 8369147 generic-all -compiler/whitebox/RelocateNMethod.java#ZGC 8369147 generic-all -compiler/whitebox/StressNMethodRelocation.java 8369147,8369148,8369149 generic-all -serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java 8369150,8369151 generic-all +serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java 8369150 generic-all ############################################################################# @@ -133,7 +124,7 @@ containers/docker/TestJcmdWithSideCar.java 8341518 linux-x64 # :hotspot_serviceability # 8239062 and 8270326 only affects macosx-x64,macosx-aarch64 -serviceability/sa/sadebugd/DebugdConnectTest.java 8239062,8270326,8344261 generic-all +serviceability/sa/sadebugd/DebugdConnectTest.java 8239062,8270326 generic-all serviceability/sa/TestRevPtrsForInvokeDynamic.java 8241235 generic-all serviceability/jvmti/vthread/GetThreadStateMountedTest/GetThreadStateMountedTest.java 8318090,8318729 generic-all @@ -153,10 +144,6 @@ serviceability/sa/ClhsdbThreadContext.java 8356704 windows-x64 serviceability/jvmti/stress/StackTrace/NotSuspended/GetStackTraceNotSuspendedStressTest.java 8315980 linux-all,windows-x64 -serviceability/sa/JhsdbThreadInfoTest.java 8344261 generic-all -serviceability/sa/TestJhsdbJstackLock.java 8344261 generic-all -serviceability/attach/RemovingUnixDomainSocketTest.java 8344261 generic-all - ############################################################################# # :hotspot_misc diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index fd9dd6eb4c0..2f58670b90e 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -563,6 +563,7 @@ hotspot_aot_classlinking = \ -runtime/cds/appcds/resolvedConstants/AOTLinkedVarHandles.java \ -runtime/cds/appcds/resolvedConstants/ResolvedConstants.java \ -runtime/cds/appcds/RewriteBytecodesTest.java \ + -runtime/cds/appcds/SignedJar.java \ -runtime/cds/appcds/SpecifySysLoaderProp.java \ -runtime/cds/appcds/StaticArchiveWithLambda.java \ -runtime/cds/appcds/TestEpsilonGCWithCDS.java \ @@ -570,6 +571,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/arraycopy/TestCloneUnknownClassAtParseTime.java b/test/hotspot/jtreg/compiler/arraycopy/TestCloneUnknownClassAtParseTime.java new file mode 100644 index 00000000000..1cd7cc21611 --- /dev/null +++ b/test/hotspot/jtreg/compiler/arraycopy/TestCloneUnknownClassAtParseTime.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2025 IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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 8339526 + * @summary C2: store incorrectly removed for clone() transformed to series of loads/stores + * @run main/othervm -XX:-BackgroundCompilation compiler.arraycopy.TestCloneUnknownClassAtParseTime + * @run main compiler.arraycopy.TestCloneUnknownClassAtParseTime + */ + +package compiler.arraycopy; + +public class TestCloneUnknownClassAtParseTime { + private static volatile int volatileField; + static A field; + + public static void main(String[] args) throws CloneNotSupportedException { + A a = new A(); + for (int i = 0; i < 20_000; i++) { + B b = (B)test1(-1); + if (b.field1 != 42 || b.field2 != 42|| b.field3 != 42) { + throw new RuntimeException("Clone wrongly initialized"); + } + inlined1(42); + field = a; + inlined2(); + } + } + + private static A test1(int i) throws CloneNotSupportedException { + int[] nonEscapingArray = new int[1]; + field = new B(42, 42, 42); + + if (i > 0) { + throw new RuntimeException("never taken"); + } + inlined1(i); + + nonEscapingArray[0] = 42; + return inlined2(); + } + + private static A inlined2() throws CloneNotSupportedException { + A a = field; + return (A)a.clone(); + } + + private static void inlined1(int i) { + if (i > 0) { + volatileField = 42; + } + } + + private static class A implements Cloneable { + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + } + + private static class B extends A { + int field1; + int field2; + int field3; + + B(int v1, int v2, int v3) { + field1 = v1; + field2 = v2; + field3 = v3; + } + } +} 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 @@ import jdk.test.lib.process.ProcessTools; * @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/LShiftINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/LShiftINodeIdealizationTests.java index 7db56e2a878..c7a35a5a265 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/LShiftINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/LShiftINodeIdealizationTests.java @@ -65,6 +65,7 @@ public class LShiftINodeIdealizationTests { "testLShiftOfAndOfRShift", "testLShiftOfAndOfURShift", "testLShiftOfAndOfCon", + "testShiftOfSubConstant", }) public void runMethod() { int a = RunInfo.getRandom().nextInt(); @@ -122,6 +123,7 @@ public class LShiftINodeIdealizationTests { Asserts.assertEQ((a + a) << 31, testLargeShiftOfAddSameInput(a)); Asserts.assertEQ(((a + 1) << 1) + 1, testShiftOfAddConstant(a)); Asserts.assertEQ((a & ((1 << (32 - 10)) -1)) << 10, testLShiftOfAndOfCon(a)); + Asserts.assertEQ(((1 - a) << 1) + 1, testShiftOfSubConstant(a)); assertDoubleShiftResult(a); } @@ -359,17 +361,45 @@ public class LShiftINodeIdealizationTests { @Test @IR(counts = { IRNode.ADD_I, "1"} , failOn = { IRNode.LSHIFT_I, IRNode.RSHIFT_I } ) @Arguments( values = { Argument.NUMBER_42 }) - public void testStoreShort(int x) { + public void testStoreShort1(int x) { shortField = (short)(x + x); } @Test @IR(counts = { IRNode.ADD_I, "1"} , failOn = { IRNode.LSHIFT_I, IRNode.RSHIFT_I } ) @Arguments( values = { Argument.NUMBER_42 }) - public void testStoreByte(int x) { + public void testStoreByte1(int x) { byteField = (byte)(x + x); } + @Test + @IR(counts = { IRNode.ADD_I, "1"} , failOn = { IRNode.LSHIFT_I, IRNode.RSHIFT_I } ) + @Arguments( values = { Argument.NUMBER_42 }) + public void testStoreShort2(int x) { + shortField = (short)(x + 1); + } + + @Test + @IR(counts = { IRNode.ADD_I, "1"} , failOn = { IRNode.LSHIFT_I, IRNode.RSHIFT_I } ) + @Arguments( values = { Argument.NUMBER_42 }) + public void testStoreByte2(int x) { + byteField = (byte)(x + 1); + } + + @Test + @IR(counts = { IRNode.SUB_I, "1"} , failOn = { IRNode.LSHIFT_I, IRNode.RSHIFT_I } ) + @Arguments( values = { Argument.NUMBER_42 }) + public void testStoreShort3(int x) { + shortField = (short)(1 - x); + } + + @Test + @IR(counts = { IRNode.SUB_I, "1"} , failOn = { IRNode.LSHIFT_I, IRNode.RSHIFT_I } ) + @Arguments( values = { Argument.NUMBER_42 }) + public void testStoreByte3(int x) { + byteField = (byte)(1 - x); + } + static int otherInput; @Test @@ -409,4 +439,10 @@ public class LShiftINodeIdealizationTests { public int testLShiftOfAndOfCon(int x) { return (x & ((1 << (32 - 10)) -1)) << 10; } + + @Test + @IR(counts = { IRNode.LSHIFT_I, "1", IRNode.SUB_I, "1" }, failOn = { IRNode.ADD_I }) + public int testShiftOfSubConstant(int x) { + return ((1 - x) << 1) + 1; + } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/LShiftLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/LShiftLNodeIdealizationTests.java index f489a348eef..6e58d6a04fe 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/LShiftLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/LShiftLNodeIdealizationTests.java @@ -63,6 +63,7 @@ public class LShiftLNodeIdealizationTests { "testLShiftOfAndOfRShift", "testLShiftOfAndOfURShift", "testLShiftOfAndOfCon", + "testShiftOfSubConstant", }) public void runMethod() { long a = RunInfo.getRandom().nextLong(); @@ -118,6 +119,7 @@ public class LShiftLNodeIdealizationTests { Asserts.assertEQ((a + a) << 63, testLargeShiftOfAddSameInput(a)); Asserts.assertEQ(((a + 1) << 1) + 1, testShiftOfAddConstant(a)); Asserts.assertEQ((a & ((1L << (64 - 10)) -1)) << 10, testLShiftOfAndOfCon(a)); + Asserts.assertEQ(((1L - a) << 1) + 1, testShiftOfSubConstant(a)); assertDoubleShiftResult(a); } @@ -358,4 +360,10 @@ public class LShiftLNodeIdealizationTests { public long testLShiftOfAndOfCon(long x) { return (x & ((1L << (64 - 10)) -1)) << 10; } + + @Test + @IR(counts = { IRNode.LSHIFT_L, "1", IRNode.SUB_L, "1" }, failOn = { IRNode.ADD_L }) + public long testShiftOfSubConstant(long x) { + return ((1 - x) << 1) + 1; + } } 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 @@ import compiler.lib.ir_framework.*; * @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 @@ import compiler.lib.ir_framework.*; * @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/c2/irTests/TestVectorConditionalMove.java b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorConditionalMove.java index ade409d01e5..6da8bd89245 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorConditionalMove.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorConditionalMove.java @@ -145,6 +145,7 @@ public class TestVectorConditionalMove { } // Extension: Compare 2 ILFD values, and pick from 2 ILFD values + // Signed comparison: I/L private int cmoveIGTforI(int a, int b, int c, int d) { return (a > b) ? c : d; } @@ -177,6 +178,144 @@ public class TestVectorConditionalMove { return (a > b) ? c : d; } + // Unsigned comparison: I/L + // I for I + private int cmoveUIGTforI(int a, int b, int c, int d) { + return Integer.compareUnsigned(a, b) > 0 ? c : d; + } + + private int cmoveUIGEforI(int a, int b, int c, int d) { + return Integer.compareUnsigned(a, b) >= 0 ? c : d; + } + + private int cmoveUILTforI(int a, int b, int c, int d) { + return Integer.compareUnsigned(a, b) < 0 ? c : d; + } + + private int cmoveUILEforI(int a, int b, int c, int d) { + return Integer.compareUnsigned(a, b) <= 0 ? c : d; + } + + // I for L + private long cmoveUIGTforL(int a, int b, long c, long d) { + return Integer.compareUnsigned(a, b) > 0 ? c : d; + } + + private long cmoveUIGEforL(int a, int b, long c, long d) { + return Integer.compareUnsigned(a, b) >= 0 ? c : d; + } + + private long cmoveUILTforL(int a, int b, long c, long d) { + return Integer.compareUnsigned(a, b) < 0 ? c : d; + } + + private long cmoveUILEforL(int a, int b, long c, long d) { + return Integer.compareUnsigned(a, b) <= 0 ? c : d; + } + + // I for F + private float cmoveUIGTforF(int a, int b, float c, float d) { + return Integer.compareUnsigned(a, b) > 0 ? c : d; + } + + private float cmoveUIGEforF(int a, int b, float c, float d) { + return Integer.compareUnsigned(a, b) >= 0 ? c : d; + } + + private float cmoveUILTforF(int a, int b, float c, float d) { + return Integer.compareUnsigned(a, b) < 0 ? c : d; + } + + private float cmoveUILEforF(int a, int b, float c, float d) { + return Integer.compareUnsigned(a, b) <= 0 ? c : d; + } + + // I for D + private double cmoveUIGTforD(int a, int b, double c, double d) { + return Integer.compareUnsigned(a, b) > 0 ? c : d; + } + + private double cmoveUIGEforD(int a, int b, double c, double d) { + return Integer.compareUnsigned(a, b) >= 0 ? c : d; + } + + private double cmoveUILTforD(int a, int b, double c, double d) { + return Integer.compareUnsigned(a, b) < 0 ? c : d; + } + + private double cmoveUILEforD(int a, int b, double c, double d) { + return Integer.compareUnsigned(a, b) <= 0 ? c : d; + } + + // L for I + private int cmoveULGTforI(long a, long b, int c, int d) { + return Long.compareUnsigned(a, b) > 0 ? c : d; + } + + private int cmoveULGEforI(long a, long b, int c, int d) { + return Long.compareUnsigned(a, b) >= 0 ? c : d; + } + + private int cmoveULLTforI(long a, long b, int c, int d) { + return Long.compareUnsigned(a, b) < 0 ? c : d; + } + + private int cmoveULLEforI(long a, long b, int c, int d) { + return Long.compareUnsigned(a, b) <= 0 ? c : d; + } + + // L for L + private long cmoveULGTforL(long a, long b, long c, long d) { + return Long.compareUnsigned(a, b) > 0 ? c : d; + } + + private long cmoveULGEforL(long a, long b, long c, long d) { + return Long.compareUnsigned(a, b) >= 0 ? c : d; + } + + private long cmoveULLTforL(long a, long b, long c, long d) { + return Long.compareUnsigned(a, b) < 0 ? c : d; + } + + private long cmoveULLEforL(long a, long b, long c, long d) { + return Long.compareUnsigned(a, b) <= 0 ? c : d; + } + + // L for F + private float cmoveULGTforF(long a, long b, float c, float d) { + return Long.compareUnsigned(a, b) > 0 ? c : d; + } + + private float cmoveULGEforF(long a, long b, float c, float d) { + return Long.compareUnsigned(a, b) >= 0 ? c : d; + } + + private float cmoveULLTforF(long a, long b, float c, float d) { + return Long.compareUnsigned(a, b) < 0 ? c : d; + } + + private float cmoveULLEforF(long a, long b, float c, float d) { + return Long.compareUnsigned(a, b) <= 0 ? c : d; + } + + // L for D + private double cmoveULGTforD(long a, long b, double c, double d) { + return Long.compareUnsigned(a, b) > 0 ? c : d; + } + + private double cmoveULGEforD(long a, long b, double c, double d) { + return Long.compareUnsigned(a, b) >= 0 ? c : d; + } + + private double cmoveULLTforD(long a, long b, double c, double d) { + return Long.compareUnsigned(a, b) < 0 ? c : d; + } + + private double cmoveULLEforD(long a, long b, double c, double d) { + return Long.compareUnsigned(a, b) <= 0 ? c : d; + } + + // Float comparison private int cmoveFGTforI(float a, float b, int c, int d) { return (a > b) ? c : d; } @@ -595,6 +734,7 @@ public class TestVectorConditionalMove { // do not float down into the branches, I compute a value, and store it to r2 (same as r, except that the // compilation does not know that). // So far, vectorization only works for CMoveF/D, with same data-width comparison (F/I for F, D/L for D). + // Signed comparison: I/L @Test @IR(failOn = {IRNode.STORE_VECTOR}) private static void testCMoveIGTforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { @@ -694,6 +834,411 @@ public class TestVectorConditionalMove { } } + // Unsigned comparison: I/L + // I fo I + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveUIGTforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { + for (int i = 0; i < a.length; i++) { + int cc = c[i]; + int dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) > 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveUIGEforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { + for (int i = 0; i < a.length; i++) { + int cc = c[i]; + int dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) >= 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveUILTforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { + for (int i = 0; i < a.length; i++) { + int cc = c[i]; + int dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) < 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveUILEforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { + for (int i = 0; i < a.length; i++) { + int cc = c[i]; + int dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) <= 0 ? cc : dd; + } + } + + // I fo L + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveUIGTforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { + for (int i = 0; i < a.length; i++) { + long cc = c[i]; + long dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) > 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveUIGEforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { + for (int i = 0; i < a.length; i++) { + long cc = c[i]; + long dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) >= 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveUILTforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { + for (int i = 0; i < a.length; i++) { + long cc = c[i]; + long dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) < 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveUILEforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { + for (int i = 0; i < a.length; i++) { + long cc = c[i]; + long dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) <= 0 ? cc : dd; + } + } + + // I fo F + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.LOAD_VECTOR_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.STORE_VECTOR, ">0"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + private static void testCMoveUIGTforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { + for (int i = 0; i < a.length; i++) { + float cc = c[i]; + float dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) > 0 ? cc : dd; + } + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.LOAD_VECTOR_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.STORE_VECTOR, ">0"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + private static void testCMoveUIGEforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { + for (int i = 0; i < a.length; i++) { + float cc = c[i]; + float dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) >= 0 ? cc : dd; + } + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.LOAD_VECTOR_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.STORE_VECTOR, ">0"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + private static void testCMoveUILTforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { + for (int i = 0; i < a.length; i++) { + float cc = c[i]; + float dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) < 0 ? cc : dd; + } + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.LOAD_VECTOR_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", + IRNode.STORE_VECTOR, ">0"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + private static void testCMoveUILEforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { + for (int i = 0; i < a.length; i++) { + float cc = c[i]; + float dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) <= 0 ? cc : dd; + } + } + + // I fo D + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveUIGTforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { + for (int i = 0; i < a.length; i++) { + double cc = c[i]; + double dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) > 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveUIGEforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { + for (int i = 0; i < a.length; i++) { + double cc = c[i]; + double dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) >= 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveUILTforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { + for (int i = 0; i < a.length; i++) { + double cc = c[i]; + double dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) < 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveUILEforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { + for (int i = 0; i < a.length; i++) { + double cc = c[i]; + double dd = d[i]; + r2[i] = cc + dd; + r[i] = Integer.compareUnsigned(a[i], b[i]) <= 0 ? cc : dd; + } + } + + // L fo I + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveULGTforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { + for (int i = 0; i < a.length; i++) { + int cc = c[i]; + int dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) > 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveULGEforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { + for (int i = 0; i < a.length; i++) { + int cc = c[i]; + int dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) >= 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveULLTforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { + for (int i = 0; i < a.length; i++) { + int cc = c[i]; + int dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) < 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveULLEforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { + for (int i = 0; i < a.length; i++) { + int cc = c[i]; + int dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) <= 0 ? cc : dd; + } + } + + // L fo L + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveULGTforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { + for (int i = 0; i < a.length; i++) { + long cc = c[i]; + long dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) > 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveULGEforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { + for (int i = 0; i < a.length; i++) { + long cc = c[i]; + long dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) >= 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveULLTforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { + for (int i = 0; i < a.length; i++) { + long cc = c[i]; + long dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) < 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveULLEforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { + for (int i = 0; i < a.length; i++) { + long cc = c[i]; + long dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) <= 0 ? cc : dd; + } + } + + // L fo F + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveULGTforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { + for (int i = 0; i < a.length; i++) { + float cc = c[i]; + float dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) > 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveULGEforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { + for (int i = 0; i < a.length; i++) { + float cc = c[i]; + float dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) >= 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveULLTforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { + for (int i = 0; i < a.length; i++) { + float cc = c[i]; + float dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) < 0 ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + private static void testCMoveULLEforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { + for (int i = 0; i < a.length; i++) { + float cc = c[i]; + float dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) <= 0 ? cc : dd; + } + } + + // L fo D + @Test + @IR(counts = {IRNode.LOAD_VECTOR_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.LOAD_VECTOR_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.STORE_VECTOR, ">0"}, + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. + private static void testCMoveULGTforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { + for (int i = 0; i < a.length; i++) { + double cc = c[i]; + double dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) > 0 ? cc : dd; + } + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.LOAD_VECTOR_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.STORE_VECTOR, ">0"}, + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. + private static void testCMoveULGEforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { + for (int i = 0; i < a.length; i++) { + double cc = c[i]; + double dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) >= 0 ? cc : dd; + } + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.LOAD_VECTOR_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.STORE_VECTOR, ">0"}, + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. + private static void testCMoveULLTforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { + for (int i = 0; i < a.length; i++) { + double cc = c[i]; + double dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) < 0 ? cc : dd; + } + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.LOAD_VECTOR_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", + IRNode.STORE_VECTOR, ">0"}, + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. + private static void testCMoveULLEforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { + for (int i = 0; i < a.length; i++) { + double cc = c[i]; + double dd = d[i]; + r2[i] = cc + dd; + r[i] = Long.compareUnsigned(a[i], b[i]) <= 0 ? cc : dd; + } + } + @Test @IR(failOn = {IRNode.STORE_VECTOR}) private static void testCMoveFGTforI(float[] a, float[] b, int[] c, int[] d, int[] r, int[] r2) { @@ -977,7 +1522,8 @@ public class TestVectorConditionalMove { } @Warmup(0) - @Run(test = {"testCMoveIGTforI", + @Run(test = {// Signed + "testCMoveIGTforI", "testCMoveIGTforL", "testCMoveIGTforF", "testCMoveIGTforD", @@ -985,6 +1531,48 @@ public class TestVectorConditionalMove { "testCMoveLGTforL", "testCMoveLGTforF", "testCMoveLGTforD", + // Unsigned + // I for I + "testCMoveUIGTforI", + "testCMoveUIGEforI", + "testCMoveUILTforI", + "testCMoveUILEforI", + // I for L + "testCMoveUIGTforL", + "testCMoveUIGEforL", + "testCMoveUILTforL", + "testCMoveUILEforL", + // I for F + "testCMoveUIGTforF", + "testCMoveUIGEforF", + "testCMoveUILTforF", + "testCMoveUILEforF", + // I for D + "testCMoveUIGTforD", + "testCMoveUIGEforD", + "testCMoveUILTforD", + "testCMoveUILEforD", + // L for I + "testCMoveULGTforI", + "testCMoveULGEforI", + "testCMoveULLTforI", + "testCMoveULLEforI", + // L for L + "testCMoveULGTforL", + "testCMoveULGEforL", + "testCMoveULLTforL", + "testCMoveULLEforL", + // L for F + "testCMoveULGTforF", + "testCMoveULGEforF", + "testCMoveULLTforF", + "testCMoveULLEforF", + // L for D + "testCMoveULGTforD", + "testCMoveULGEforD", + "testCMoveULLTforD", + "testCMoveULLEforD", + // Float "testCMoveFGTforI", "testCMoveFGTforL", "testCMoveFGTforF", @@ -1034,6 +1622,7 @@ public class TestVectorConditionalMove { init(cD); init(dD); + // Signed testCMoveIGTforI(aI, bI, cI, dI, rI, rI); for (int i = 0; i < SIZE; i++) { Asserts.assertEquals(rI[i], cmoveIGTforI(aI[i], bI[i], cI[i], dI[i])); @@ -1074,6 +1663,176 @@ public class TestVectorConditionalMove { Asserts.assertEquals(rD[i], cmoveLGTforD(aL[i], bL[i], cD[i], dD[i])); } + // Unsigned + // I for I + testCMoveUIGTforI(aI, bI, cI, dI, rI, rI); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rI[i], cmoveUIGTforI(aI[i], bI[i], cI[i], dI[i])); + } + + testCMoveUIGEforI(aI, bI, cI, dI, rI, rI); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rI[i], cmoveUIGEforI(aI[i], bI[i], cI[i], dI[i])); + } + + testCMoveUILTforI(aI, bI, cI, dI, rI, rI); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rI[i], cmoveUILTforI(aI[i], bI[i], cI[i], dI[i])); + } + + testCMoveUILEforI(aI, bI, cI, dI, rI, rI); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rI[i], cmoveUILEforI(aI[i], bI[i], cI[i], dI[i])); + } + + // I for L + testCMoveUIGTforL(aI, bI, cL, dL, rL, rL); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rL[i], cmoveUIGTforL(aI[i], bI[i], cL[i], dL[i])); + } + + testCMoveUIGEforL(aI, bI, cL, dL, rL, rL); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rL[i], cmoveUIGEforL(aI[i], bI[i], cL[i], dL[i])); + } + + testCMoveUILTforL(aI, bI, cL, dL, rL, rL); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rL[i], cmoveUILTforL(aI[i], bI[i], cL[i], dL[i])); + } + + testCMoveUILEforL(aI, bI, cL, dL, rL, rL); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rL[i], cmoveUILEforL(aI[i], bI[i], cL[i], dL[i])); + } + + // I for F + testCMoveUIGTforF(aI, bI, cF, dF, rF, rF); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rF[i], cmoveUIGTforF(aI[i], bI[i], cF[i], dF[i])); + } + + testCMoveUIGEforF(aI, bI, cF, dF, rF, rF); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rF[i], cmoveUIGEforF(aI[i], bI[i], cF[i], dF[i])); + } + + testCMoveUILTforF(aI, bI, cF, dF, rF, rF); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rF[i], cmoveUILTforF(aI[i], bI[i], cF[i], dF[i])); + } + + testCMoveUILEforF(aI, bI, cF, dF, rF, rF); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rF[i], cmoveUILEforF(aI[i], bI[i], cF[i], dF[i])); + } + + // I for D + testCMoveUIGTforD(aI, bI, cD, dD, rD, rD); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rD[i], cmoveUIGTforD(aI[i], bI[i], cD[i], dD[i])); + } + + testCMoveUIGEforD(aI, bI, cD, dD, rD, rD); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rD[i], cmoveUIGEforD(aI[i], bI[i], cD[i], dD[i])); + } + + testCMoveUILTforD(aI, bI, cD, dD, rD, rD); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rD[i], cmoveUILTforD(aI[i], bI[i], cD[i], dD[i])); + } + + testCMoveUILEforD(aI, bI, cD, dD, rD, rD); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rD[i], cmoveUILEforD(aI[i], bI[i], cD[i], dD[i])); + } + + // L for I + testCMoveULGTforI(aL, bL, cI, dI, rI, rI); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rI[i], cmoveULGTforI(aL[i], bL[i], cI[i], dI[i])); + } + + testCMoveULGEforI(aL, bL, cI, dI, rI, rI); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rI[i], cmoveULGEforI(aL[i], bL[i], cI[i], dI[i])); + } + + testCMoveULLTforI(aL, bL, cI, dI, rI, rI); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rI[i], cmoveULLTforI(aL[i], bL[i], cI[i], dI[i])); + } + + testCMoveULLEforI(aL, bL, cI, dI, rI, rI); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rI[i], cmoveULLEforI(aL[i], bL[i], cI[i], dI[i])); + } + + // L for L + testCMoveULGTforL(aL, bL, cL, dL, rL, rL); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rL[i], cmoveULGTforL(aL[i], bL[i], cL[i], dL[i])); + } + + testCMoveULGEforL(aL, bL, cL, dL, rL, rL); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rL[i], cmoveULGEforL(aL[i], bL[i], cL[i], dL[i])); + } + + testCMoveULLTforL(aL, bL, cL, dL, rL, rL); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rL[i], cmoveULLTforL(aL[i], bL[i], cL[i], dL[i])); + } + + testCMoveULLEforL(aL, bL, cL, dL, rL, rL); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rL[i], cmoveULLEforL(aL[i], bL[i], cL[i], dL[i])); + } + + // L for F + testCMoveULGTforF(aL, bL, cF, dF, rF, rF); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rF[i], cmoveULGTforF(aL[i], bL[i], cF[i], dF[i])); + } + + testCMoveULGEforF(aL, bL, cF, dF, rF, rF); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rF[i], cmoveULGEforF(aL[i], bL[i], cF[i], dF[i])); + } + + testCMoveULLTforF(aL, bL, cF, dF, rF, rF); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rF[i], cmoveULLTforF(aL[i], bL[i], cF[i], dF[i])); + } + + testCMoveULLEforF(aL, bL, cF, dF, rF, rF); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rF[i], cmoveULLEforF(aL[i], bL[i], cF[i], dF[i])); + } + + // L for D + testCMoveULGTforD(aL, bL, cD, dD, rD, rD); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rD[i], cmoveULGTforD(aL[i], bL[i], cD[i], dD[i])); + } + + testCMoveULGEforD(aL, bL, cD, dD, rD, rD); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rD[i], cmoveULGEforD(aL[i], bL[i], cD[i], dD[i])); + } + + testCMoveULLTforD(aL, bL, cD, dD, rD, rD); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rD[i], cmoveULLTforD(aL[i], bL[i], cD[i], dD[i])); + } + + testCMoveULLEforD(aL, bL, cD, dD, rD, rD); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rD[i], cmoveULLEforD(aL[i], bL[i], cD[i], dD[i])); + } + + // Float testCMoveFGTforI(aF, bF, cI, dI, rI, rI); for (int i = 0; i < SIZE; i++) { Asserts.assertEquals(rI[i], cmoveFGTforI(aF[i], bF[i], cI[i], dI[i])); diff --git a/test/hotspot/jtreg/compiler/codegen/aes/TestAESMain.java b/test/hotspot/jtreg/compiler/codegen/aes/TestAESMain.java index 36ee43a7732..608b979e8f1 100644 --- a/test/hotspot/jtreg/compiler/codegen/aes/TestAESMain.java +++ b/test/hotspot/jtreg/compiler/codegen/aes/TestAESMain.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022, 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 @@ -155,7 +155,7 @@ public class TestAESMain { public static void main(String[] args) { String mode = System.getProperty("mode", "CBC"); if ((mode.equals("CBC") || mode.equals("ECB")) && - !Compiler.isIntrinsicAvailable(CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "com.sun.crypto.provider.AESCrypt", "implEncryptBlock", byte[].class, int.class, byte[].class, int.class)) { + !Compiler.isIntrinsicAvailable(CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "com.sun.crypto.provider.AES_Crypt", "implEncryptBlock", byte[].class, int.class, byte[].class, int.class)) { throw new SkippedException("AES intrinsic is not available"); } if (mode.equals("GCM") && diff --git a/test/hotspot/jtreg/compiler/cpuflags/AESIntrinsicsBase.java b/test/hotspot/jtreg/compiler/cpuflags/AESIntrinsicsBase.java index 2ed3fd81e1c..9f36aae4579 100644 --- a/test/hotspot/jtreg/compiler/cpuflags/AESIntrinsicsBase.java +++ b/test/hotspot/jtreg/compiler/cpuflags/AESIntrinsicsBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, 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 @@ -31,7 +31,7 @@ public abstract class AESIntrinsicsBase { + ".provider\\.CipherBlockChaining::" + "(implEncrypt|implDecrypt) \\([0-9]+ bytes\\)\\s+\\(intrinsic[,\\)]"; public static final String AES_INTRINSIC = "com\\.sun\\.crypto\\" - + ".provider\\.AESCrypt::(implEncryptBlock|implDecryptBlock) \\([0-9]+ " + + ".provider\\.AES_Crypt::(implEncryptBlock|implDecryptBlock) \\([0-9]+ " + "bytes\\)\\s+\\(intrinsic[,\\)]"; public static final String USE_AES = "UseAES"; public static final String USE_AES_INTRINSICS = "UseAESIntrinsics"; 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 01e015d50cb..c1e2dd7a112 100644 --- a/test/hotspot/jtreg/compiler/gcbarriers/TestG1BarrierGeneration.java +++ b/test/hotspot/jtreg/compiler/gcbarriers/TestG1BarrierGeneration.java @@ -880,6 +880,16 @@ public class TestG1BarrierGeneration { 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(); @@ -889,6 +899,16 @@ public class TestG1BarrierGeneration { 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/intrinsics/TestReturnOopSetForJFRWriteCheckpoint.java b/test/hotspot/jtreg/compiler/intrinsics/TestReturnOopSetForJFRWriteCheckpoint.java new file mode 100644 index 00000000000..fd80b1630f3 --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/TestReturnOopSetForJFRWriteCheckpoint.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. + */ + +package compiler.intrinsics; + +import compiler.lib.ir_framework.*; + +import jdk.jfr.Event; +import jdk.jfr.Recording; + +/** + * @test + * @summary Tests that the getEventWriter call to write_checkpoint correctly + * reports returning an oop + * @bug 8347463 + * @requires vm.hasJFR + * @library /test/lib / + * @run driver compiler.intrinsics.TestReturnOopSetForJFRWriteCheckpoint + */ +public class TestReturnOopSetForJFRWriteCheckpoint { + + private static class TestEvent extends Event { + } + + public static void main(String... args) { + TestFramework.run(); + } + + // Crash was due to the return_oop field not being set + // for the write_checkpoint call. Instead of explicitly checking for + // it, we look for an non-void return type (which comes hand-in-hand + // with the return_oop information). + @Test + @IR(failOn = { IRNode.STATIC_CALL_OF_METHOD, "write_checkpoint.*void"}) + public void testWriteCheckpointReturnType() { + try (Recording r = new Recording()) { + r.start(); + emitEvent(); + } + } + + @ForceInline + public void emitEvent() { + TestEvent t = new TestEvent(); + t.commit(); + } +} diff --git a/test/hotspot/jtreg/compiler/jsr292/MHInlineTest.java b/test/hotspot/jtreg/compiler/jsr292/MHInlineTest.java index a8e76f5797d..4638c27fe59 100644 --- a/test/hotspot/jtreg/compiler/jsr292/MHInlineTest.java +++ b/test/hotspot/jtreg/compiler/jsr292/MHInlineTest.java @@ -51,7 +51,8 @@ public class MHInlineTest { "-XX:+IgnoreUnrecognizedVMOptions", "-showversion", "-XX:-TieredCompilation", "-Xbatch", "-XX:+PrintCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining", - "-XX:CompileCommand=dontinline,compiler.jsr292.MHInlineTest::test*", + "-XX:CompileCommand=quiet", + "-XX:CompileCommand=compileonly,compiler.jsr292.MHInlineTest::test*", Launcher.class.getName()); OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); @@ -60,13 +61,17 @@ public class MHInlineTest { // The test is applicable only to C2 (present in Server VM). if (analyzer.getStderr().contains("Server VM")) { + analyzer.shouldContain("compiler.jsr292.MHInlineTest$A::package_final_x (3 bytes) inline (hot)"); + analyzer.shouldContain("compiler.jsr292.MHInlineTest$A::private_x (3 bytes) inline (hot)"); + analyzer.shouldContain("compiler.jsr292.MHInlineTest$A::package_static_x (3 bytes) inline (hot)"); + analyzer.shouldContain("compiler.jsr292.MHInlineTest$B::public_x (3 bytes) inline (hot)"); analyzer.shouldContain("compiler.jsr292.MHInlineTest$B::protected_x (3 bytes) inline (hot)"); analyzer.shouldContain("compiler.jsr292.MHInlineTest$B::package_x (3 bytes) inline (hot)"); - analyzer.shouldContain("compiler.jsr292.MHInlineTest$A::package_final_x (3 bytes) inline (hot)"); analyzer.shouldContain("compiler.jsr292.MHInlineTest$B::private_x (3 bytes) inline (hot)"); analyzer.shouldContain("compiler.jsr292.MHInlineTest$B::private_static_x (3 bytes) inline (hot)"); - analyzer.shouldContain("compiler.jsr292.MHInlineTest$A::package_static_x (3 bytes) inline (hot)"); + + analyzer.shouldNotContain("failed to inline"); } else { throw new SkippedException("The test is applicable only to C2 (present in Server VM)"); } @@ -75,23 +80,24 @@ public class MHInlineTest { static class A { public static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); - public Class public_x() { return A.class; } - protected Class protected_x() { return A.class; } - Class package_x() { return A.class; } - final Class package_final_x() { return A.class; } + public Class public_x() { return A.class; } + protected Class protected_x() { return A.class; } + /*package*/ Class package_x() { return A.class; } + /*package*/ final Class package_final_x() { return A.class; } + private Class private_x() { return A.class; } - static Class package_static_x() { return A.class; } + /*package*/ static Class package_static_x() { return A.class; } } static class B extends A { public static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); - @Override public Class public_x() { return B.class; } - @Override protected Class protected_x() { return B.class; } - @Override Class package_x() { return B.class; } + @Override public Class public_x() { return B.class; } + @Override protected Class protected_x() { return B.class; } + @Override /*package*/ Class package_x() { return B.class; } - private Class private_x() { return B.class; } - static Class private_static_x() { return B.class; } + private Class private_x() { return B.class; } + private static Class private_static_x() { return B.class; } } static final MethodHandle A_PUBLIC_X; @@ -99,6 +105,7 @@ public class MHInlineTest { static final MethodHandle A_PACKAGE_X; static final MethodHandle A_PACKAGE_STATIC_X; static final MethodHandle A_PACKAGE_FINAL_X; + static final MethodHandle A_PRIVATE_X; static final MethodHandle B_PRIVATE_X; static final MethodHandle B_PRIVATE_STATIC_X; @@ -117,6 +124,8 @@ public class MHInlineTest { A.class, "package_final_x", MethodType.methodType(Class.class)); A_PACKAGE_STATIC_X = LOOKUP.findStatic( A.class, "package_static_x", MethodType.methodType(Class.class)); + A_PRIVATE_X = LOOKUP.findVirtual( + A.class, "private_x", MethodType.methodType(Class.class)); B_PRIVATE_X = B.LOOKUP.findVirtual( B.class, "private_x", MethodType.methodType(Class.class)); @@ -129,7 +138,18 @@ public class MHInlineTest { static final A a = new B(); - private static void testPublicMH() { + /* ============== public Class public_x() ============== */ + + private static void testPublicMH(A recv) { + try { + Class r = (Class)A_PUBLIC_X.invokeExact(recv); + assertEquals(r, B.class); + } catch (Throwable throwable) { + throw new Error(throwable); + } + } + + private static void testPublicMHConst() { try { Class r = (Class)A_PUBLIC_X.invokeExact(a); assertEquals(r, B.class); @@ -138,7 +158,18 @@ public class MHInlineTest { } } - private static void testProtectedMH() { + /* ============== protected Class protected_x() ============== */ + + private static void testProtectedMH(A recv) { + try { + Class r = (Class)A_PROTECTED_X.invokeExact(recv); + assertEquals(r, B.class); + } catch (Throwable throwable) { + throw new Error(throwable); + } + } + + private static void testProtectedMHConst() { try { Class r = (Class)A_PROTECTED_X.invokeExact(a); assertEquals(r, B.class); @@ -147,7 +178,18 @@ public class MHInlineTest { } } - private static void testPackageMH() { + /* ============== Class package_x() ============== */ + + private static void testPackageMH(A recv) { + try { + Class r = (Class)A_PACKAGE_X.invokeExact(recv); + assertEquals(r, B.class); + } catch (Throwable throwable) { + throw new Error(throwable); + } + } + + private static void testPackageMHConst() { try { Class r = (Class)A_PACKAGE_X.invokeExact(a); assertEquals(r, B.class); @@ -156,7 +198,9 @@ public class MHInlineTest { } } - private static void testPackageFinalMH() { + /* ============== final Class package_final_x() ============== */ + + private static void testPackageFinalMH(A recv) { try { Class r = (Class)A_PACKAGE_FINAL_X.invokeExact(a); assertEquals(r, A.class); @@ -165,16 +209,45 @@ public class MHInlineTest { } } - private static void testPackageStaticMH() { + private static void testPackageFinalMHConst() { try { - Class r = (Class)A_PACKAGE_STATIC_X.invokeExact(); + Class r = (Class)A_PACKAGE_FINAL_X.invokeExact(a); assertEquals(r, A.class); } catch (Throwable throwable) { throw new Error(throwable); } } - private static void testPrivateMH() { + /* ============== private Class private_x() ============== */ + + private static void testPrivateA_MH(A recv) { + try { + Class r = (Class)A_PRIVATE_X.invokeExact(recv); + assertEquals(r, A.class); + } catch (Throwable throwable) { + throw new Error(throwable); + } + } + + private static void testPrivateA_MHConst() { + try { + Class r = (Class)A_PRIVATE_X.invokeExact(a); + assertEquals(r, A.class); + } catch (Throwable throwable) { + throw new Error(throwable); + } + } + + private static void testPrivateB_MH(B recv) { + try { + Class r = (Class)B_PRIVATE_X.invokeExact(recv); + assertEquals(r, B.class); + } catch (Throwable throwable) { + throw new Error(throwable); + } + } + + private static void testPrivateB_MHConst() { try { Class r = (Class)B_PRIVATE_X.invokeExact((B)a); assertEquals(r, B.class); @@ -183,7 +256,19 @@ public class MHInlineTest { } } - private static void testPrivateStaticMH() { + /* ============== static ============== */ + + private static void testPackageStaticMHConst() { + try { + Class r = (Class)A_PACKAGE_STATIC_X.invokeExact(); + assertEquals(r, A.class); + } catch (Throwable throwable) { + throw new Error(throwable); + } + } + + + private static void testPrivateStaticMHConst() { try { Class r = (Class)B_PRIVATE_STATIC_X.invokeExact(); assertEquals(r, B.class); @@ -192,28 +277,20 @@ public class MHInlineTest { } } + /* ====================================================================== */ + static class Launcher { public static void main(String[] args) throws Exception { for (int i = 0; i < 20_000; i++) { - testPublicMH(); - } - for (int i = 0; i < 20_000; i++) { - testProtectedMH(); - } - for (int i = 0; i < 20_000; i++) { - testPackageMH(); - } - for (int i = 0; i < 20_000; i++) { - testPackageFinalMH(); - } - for (int i = 0; i < 20_000; i++) { - testPackageStaticMH(); - } - for (int i = 0; i < 20_000; i++) { - testPrivateMH(); - } - for (int i = 0; i < 20_000; i++) { - testPrivateStaticMH(); + testPublicMH(a); testPublicMHConst(); + testProtectedMH(a); testProtectedMHConst(); + testPackageMH(a); testPackageMHConst(); + testPackageFinalMH(a); testPackageFinalMHConst(); + testPrivateA_MH(a); testPrivateA_MHConst(); + testPrivateB_MH((B)a); testPrivateB_MHConst(); + + testPackageStaticMHConst(); + testPrivateStaticMHConst(); } } } diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/TestHotSpotJVMCIRuntime.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/TestHotSpotJVMCIRuntime.java index e5d1da81309..e62de99f366 100644 --- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/TestHotSpotJVMCIRuntime.java +++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/TestHotSpotJVMCIRuntime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, 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 @@ -114,7 +114,7 @@ public class TestHotSpotJVMCIRuntime { VirtualObjectLayoutTest.class, TestHotSpotJVMCIRuntime.class)); try { - classes.add(Class.forName("com.sun.crypto.provider.AESCrypt")); + classes.add(Class.forName("com.sun.crypto.provider.AES_Crypt")); classes.add(Class.forName("com.sun.crypto.provider.CipherBlockChaining")); } catch (ClassNotFoundException e) { // Extension classes not available diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 99a289476ec..f0f7aaf3836 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -2840,6 +2840,36 @@ public class IRNode { vectorNode(EXPAND_BITS_VL, "ExpandBitsV", TYPE_LONG); } + public static final String COMPRESS_VB = VECTOR_PREFIX + "COMPRESS_VB" + POSTFIX; + static { + vectorNode(COMPRESS_VB, "CompressV", TYPE_BYTE); + } + + public static final String COMPRESS_VS = VECTOR_PREFIX + "COMPRESS_VS" + POSTFIX; + static { + vectorNode(COMPRESS_VS, "CompressV", TYPE_SHORT); + } + + public static final String COMPRESS_VI = VECTOR_PREFIX + "COMPRESS_VI" + POSTFIX; + static { + vectorNode(COMPRESS_VI, "CompressV", TYPE_INT); + } + + public static final String COMPRESS_VL = VECTOR_PREFIX + "COMPRESS_VL" + POSTFIX; + static { + vectorNode(COMPRESS_VL, "CompressV", TYPE_LONG); + } + + public static final String COMPRESS_VF = VECTOR_PREFIX + "COMPRESS_VF" + POSTFIX; + static { + vectorNode(COMPRESS_VF, "CompressV", TYPE_FLOAT); + } + + public static final String COMPRESS_VD = VECTOR_PREFIX + "COMPRESS_VD" + POSTFIX; + static { + vectorNode(COMPRESS_VD, "CompressV", TYPE_DOUBLE); + } + public static final String EXPAND_VB = VECTOR_PREFIX + "EXPAND_VB" + POSTFIX; static { vectorNode(EXPAND_VB, "ExpandV", TYPE_BYTE); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java index 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 @@ import compiler.lib.template_framework.library.TestFrameworkClass; * 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/TestMemorySegment_8359688.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment_SubOfShift.java similarity index 86% rename from test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment_8359688.java rename to test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment_SubOfShift.java index cac69901a32..34741d0f2a7 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment_8359688.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment_SubOfShift.java @@ -30,17 +30,17 @@ import compiler.lib.ir_framework.*; /* * @test - * @bug 8324751 + * @bug 8324751 8369435 * @summary Reported issue: JDK-8359688: C2 SuperWord: missing RCE with MemorySegment * The examples are generated from TestAliasingFuzzer.java * So if you see something change here, you may want to investigate if we * can also tighten up the IR rules there. * @library /test/lib / - * @run driver compiler.loopopts.superword.TestMemorySegment_8359688 + * @run driver compiler.loopopts.superword.TestMemorySegment_SubOfShift */ -public class TestMemorySegment_8359688 { +public class TestMemorySegment_SubOfShift { public static MemorySegment b = MemorySegment.ofArray(new long[4 * 30_000]); @@ -61,17 +61,13 @@ public class TestMemorySegment_8359688 { @Test @Arguments(setup = "setup") - @IR(counts = {IRNode.STORE_VECTOR, "= 0", - IRNode.REPLICATE_L, "= 0", + @IR(counts = {IRNode.STORE_VECTOR, "> 0", + IRNode.REPLICATE_L, "= 1", ".*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"}) - // Does not manage to remove all RangeChecks -> no vectorization - // If you see this IR rule fail: investigate JDK-8359688, possibly close it and fix this IR rule! - // Also: consider renaming the file to something more descriptive: what have you fixed with this? - // And: you may now be able to tighten IR rules in TestAliasingFuzzer.java public static void test1(MemorySegment b, int ivLo, int ivHi, int invar) { for (int i = ivLo; i < ivHi; i++) { b.setAtIndex(ValueLayout.JAVA_LONG_UNALIGNED, 30_000L - (long)i + (long)invar, 42); 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/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/whitebox/DeoptimizeRelocatedNMethod.java b/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java index 25314051fdd..42f29044e8c 100644 --- a/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java +++ b/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java @@ -23,67 +23,14 @@ */ /* - * @test id=Serial + * @test * @bug 8316694 * @library /test/lib / * @modules java.base/jdk.internal.misc java.management * @requires vm.opt.DeoptimizeALot != true - * @requires vm.gc.Serial * @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 -Xbatch -XX:+SegmentedCodeCache -XX:+UseSerialGC - * -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.DeoptimizeRelocatedNMethod - */ - -/* - * @test id=Parallel - * @bug 8316694 - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * @requires vm.opt.DeoptimizeALot != true - * @requires vm.gc.Parallel - * @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 -Xbatch -XX:+SegmentedCodeCache -XX:+UseParallelGC - * -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.DeoptimizeRelocatedNMethod - */ - -/* - * @test id=G1 - * @bug 8316694 - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * @requires vm.opt.DeoptimizeALot != true - * @requires vm.gc.G1 - * @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 -Xbatch -XX:+SegmentedCodeCache -XX:+UseG1GC - * -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.DeoptimizeRelocatedNMethod - */ - -/* - * @test id=Shenandoah - * @bug 8316694 - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * @requires vm.opt.DeoptimizeALot != true - * @requires vm.gc.Shenandoah - * @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 -Xbatch -XX:+SegmentedCodeCache -XX:+UseShenandoahGC - * -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.DeoptimizeRelocatedNMethod - */ - -/* - * @test id=ZGC - * @bug 8316694 - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * @requires vm.opt.DeoptimizeALot != true - * @requires vm.gc.Z - * @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 -Xbatch -XX:+SegmentedCodeCache -XX:+UseZGC + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache * -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.DeoptimizeRelocatedNMethod */ @@ -118,7 +65,7 @@ public class DeoptimizeRelocatedNMethod { NMethod origNmethod = NMethod.get(method, false); // Relocate nmethod and mark old for cleanup - WHITE_BOX.relocateNMethodFromMethod(method, BlobType.MethodProfiled.id); + WHITE_BOX.relocateNMethodFromMethod(method, BlobType.MethodNonProfiled.id); // Trigger GC to clean up old nmethod WHITE_BOX.fullGC(); diff --git a/test/hotspot/jtreg/compiler/whitebox/RelocateNMethod.java b/test/hotspot/jtreg/compiler/whitebox/RelocateNMethod.java index c18a8afa400..7c625496b8a 100644 --- a/test/hotspot/jtreg/compiler/whitebox/RelocateNMethod.java +++ b/test/hotspot/jtreg/compiler/whitebox/RelocateNMethod.java @@ -23,84 +23,18 @@ */ /* - * @test id=Serial + * @test * @bug 8316694 * @summary test that nmethod::relocate() correctly creates a new nmethod * @library /test/lib / * @modules java.base/jdk.internal.misc java.management - * * @requires vm.opt.DeoptimizeALot != true - * @requires vm.gc.Serial - * * @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 -Xbatch -XX:+SegmentedCodeCache - * -XX:+UseSerialGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod + * -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod */ -/* - * @test id=Parallel - * @bug 8316694 - * @summary test that nmethod::relocate() correctly creates a new nmethod - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * - * @requires vm.opt.DeoptimizeALot != true - * @requires vm.gc.Parallel - * - * @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 -Xbatch -XX:+SegmentedCodeCache - * -XX:+UseParallelGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod - */ - -/* - * @test id=G1 - * @bug 8316694 - * @summary test that nmethod::relocate() correctly creates a new nmethod - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * - * @requires vm.opt.DeoptimizeALot != true - * @requires vm.gc.G1 - * - * @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 -Xbatch -XX:+SegmentedCodeCache - * -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod - */ - -/* - * @test id=Shenandoah - * @bug 8316694 - * @summary test that nmethod::relocate() correctly creates a new nmethod - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * - * @requires vm.opt.DeoptimizeALot != true - * @requires vm.gc.Shenandoah - * - * @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 -Xbatch -XX:+SegmentedCodeCache - * -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod - */ - -/* - * @test id=ZGC - * @bug 8316694 - * @summary test that nmethod::relocate() correctly creates a new nmethod - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * - * @requires vm.opt.DeoptimizeALot != true - * @requires vm.gc.Z - * - * @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 -Xbatch -XX:+SegmentedCodeCache - * -XX:+UseZGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod - */ package compiler.whitebox; @@ -132,7 +66,7 @@ public class RelocateNMethod extends CompilerWhiteBoxTest { checkCompiled(); NMethod origNmethod = NMethod.get(method, false); - WHITE_BOX.relocateNMethodFromMethod(method, BlobType.MethodProfiled.id); + WHITE_BOX.relocateNMethodFromMethod(method, BlobType.MethodNonProfiled.id); WHITE_BOX.fullGC(); diff --git a/test/hotspot/jtreg/compiler/whitebox/RelocateNMethodMultiplePaths.java b/test/hotspot/jtreg/compiler/whitebox/RelocateNMethodMultiplePaths.java index 49be3eff8c2..2a2a342da52 100644 --- a/test/hotspot/jtreg/compiler/whitebox/RelocateNMethodMultiplePaths.java +++ b/test/hotspot/jtreg/compiler/whitebox/RelocateNMethodMultiplePaths.java @@ -23,10 +23,9 @@ */ /* - * @test id=SerialC1 + * @test id=C1 * @bug 8316694 * @requires vm.debug == true - * @requires vm.gc.Serial * @summary test that relocated nmethod is correctly deoptimized * @library /test/lib / * @modules java.base/jdk.internal.misc java.management @@ -34,15 +33,14 @@ * @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 -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1 - * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseSerialGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation + * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation * compiler.whitebox.RelocateNMethodMultiplePaths */ /* - * @test id=SerialC2 + * @test id=C2 * @bug 8316694 * @requires vm.debug == true - * @requires vm.gc.Serial * @summary test that relocated nmethod is correctly deoptimized * @library /test/lib / * @modules java.base/jdk.internal.misc java.management @@ -50,135 +48,7 @@ * @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 -Xbatch -XX:+TieredCompilation - * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseSerialGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation - * compiler.whitebox.RelocateNMethodMultiplePaths - */ - -/* - * @test id=ParallelC1 - * @bug 8316694 - * @requires vm.debug == true - * @requires vm.gc.Parallel - * @summary test that relocated nmethod is correctly deoptimized - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * - * @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 -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1 - * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseParallelGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation - * compiler.whitebox.RelocateNMethodMultiplePaths - */ - -/* - * @test id=ParallelC2 - * @bug 8316694 - * @requires vm.debug == true - * @requires vm.gc.Parallel - * @summary test that relocated nmethod is correctly deoptimized - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * - * @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 -Xbatch -XX:+TieredCompilation - * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseParallelGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation - * compiler.whitebox.RelocateNMethodMultiplePaths - */ - -/* - * @test id=G1C1 - * @bug 8316694 - * @requires vm.debug == true - * @requires vm.gc.G1 - * @summary test that relocated nmethod is correctly deoptimized - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * - * @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 -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1 - * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation - * compiler.whitebox.RelocateNMethodMultiplePaths - */ - -/* - * @test id=G1C2 - * @bug 8316694 - * @requires vm.debug == true - * @requires vm.gc.G1 - * @summary test that relocated nmethod is correctly deoptimized - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * - * @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 -Xbatch -XX:+TieredCompilation - * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation - * compiler.whitebox.RelocateNMethodMultiplePaths - */ - -/* - * @test id=ShenandoahC1 - * @bug 8316694 - * @requires vm.debug == true - * @requires vm.gc.Shenandoah - * @summary test that relocated nmethod is correctly deoptimized - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * - * @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 -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1 - * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation - * compiler.whitebox.RelocateNMethodMultiplePaths - */ - -/* - * @test id=ShenandoahC2 - * @bug 8316694 - * @requires vm.debug == true - * @requires vm.gc.Shenandoah - * @summary test that relocated nmethod is correctly deoptimized - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * - * @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 -Xbatch -XX:+TieredCompilation - * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation - * compiler.whitebox.RelocateNMethodMultiplePaths - */ - -/* - * @test id=ZGCC1 - * @bug 8316694 - * @requires vm.debug == true - * @requires vm.gc.Z - * @summary test that relocated nmethod is correctly deoptimized - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * - * @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 -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1 - * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseZGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation - * compiler.whitebox.RelocateNMethodMultiplePaths - */ - -/* - * @test id=ZGCC2 - * @bug 8316694 - * @requires vm.debug == true - * @requires vm.gc.Z - * @summary test that relocated nmethod is correctly deoptimized - * @library /test/lib / - * @modules java.base/jdk.internal.misc java.management - * - * @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 -Xbatch -XX:+TieredCompilation - * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseZGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation + * -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation * compiler.whitebox.RelocateNMethodMultiplePaths */ diff --git a/test/hotspot/jtreg/compiler/whitebox/StressNMethodRelocation.java b/test/hotspot/jtreg/compiler/whitebox/StressNMethodRelocation.java index 3b397f48306..efcc9f03fb9 100644 --- a/test/hotspot/jtreg/compiler/whitebox/StressNMethodRelocation.java +++ b/test/hotspot/jtreg/compiler/whitebox/StressNMethodRelocation.java @@ -28,11 +28,11 @@ * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management + * @requires vm.compiler2.enabled & vm.opt.SegmentedCodeCache != false & vm.opt.TieredCompilation != false * @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:+SegmentedCodeCache -XX:+UnlockExperimentalVMOptions - * -XX:+NMethodRelocation compiler.whitebox.StressNMethodRelocation + * -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.StressNMethodRelocation */ package compiler.whitebox; @@ -45,27 +45,27 @@ import jdk.test.whitebox.code.NMethod; import jdk.test.lib.compiler.InMemoryJavaCompiler; import java.lang.reflect.Method; -import java.util.Arrays; +import java.util.ArrayList; import java.util.EnumSet; import java.util.Random; public class StressNMethodRelocation { private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); private static final int C2_LEVEL = 4; - private static final int ACTIVE_METHODS = 1024; - private static TestMethod[] methods; + private static ArrayList methods; private static byte[] num1; private static byte[] num2; - private static long DURATION = 60_000; + private static long COMPILE_DURATION = 30_000; + private static long RUN_DURATION = 60_000; public static void main(String[] args) throws Exception { // Initialize defaults initNums(); // Generate compiled code - methods = new TestMethod[ACTIVE_METHODS]; + methods = new ArrayList<>(); generateCode(methods); // Create thread that runs compiled methods @@ -108,13 +108,15 @@ public class StressNMethodRelocation { num2 = genNum(random, digitCount); } - private static void generateCode(TestMethod[] m) throws Exception { + private static void generateCode(ArrayList m) throws Exception { byte[] result = new byte[num1.length + 1]; - for (int i = 0; i < ACTIVE_METHODS; ++i) { - m[i] = new TestMethod(); - m[i].profile(num1, num2, result); - m[i].compileWithC2(); + long startTime = System.currentTimeMillis(); + while (System.currentTimeMillis() - startTime < COMPILE_DURATION) { + TestMethod testMethod = new TestMethod(); + testMethod.profile(num1, num2, result); + testMethod.compileWithC2(); + m.add(testMethod); } } @@ -194,7 +196,7 @@ public class StressNMethodRelocation { // Move nmethod back and forth between NonProfiled and Profiled code heaps public void run() { long startTime = System.currentTimeMillis(); - while (System.currentTimeMillis() - startTime < DURATION) { + while (System.currentTimeMillis() - startTime < RUN_DURATION) { // Relocate NonProfiled to Profiled CodeBlob[] nonProfiledBlobs = CodeBlob.getCodeBlobs(BlobType.MethodNonProfiled); for (CodeBlob blob : nonProfiledBlobs) { @@ -220,7 +222,7 @@ public class StressNMethodRelocation { public void run() { try { long startTime = System.currentTimeMillis(); - while (System.currentTimeMillis() - startTime < DURATION) { + while (System.currentTimeMillis() - startTime < RUN_DURATION) { callMethods(); } } catch (Exception e) { diff --git a/test/hotspot/jtreg/gc/NativeWrapperCollection/NativeWrapperCollection.java b/test/hotspot/jtreg/gc/NativeWrapperCollection/NativeWrapperCollection.java new file mode 100644 index 00000000000..d963b25e331 --- /dev/null +++ b/test/hotspot/jtreg/gc/NativeWrapperCollection/NativeWrapperCollection.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package gc.NativeWrapperCollection; + +/* + * @test NativeWrapperCollection + * @summary Test that native wrappers are collected after becoming not entrant + * @requires vm.compiler1.enabled + * @library /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm/native -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * gc.NativeWrapperCollection.NativeWrapperCollection + */ + +import java.lang.reflect.Method; +import java.util.Iterator; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.dcmd.JMXExecutor; +import jdk.test.lib.dcmd.CommandExecutor; +import jdk.test.whitebox.WhiteBox; + +public class NativeWrapperCollection { + + static { + System.loadLibrary("nativeWrapperCollection"); + } + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + static native void method(); + static native void callRegisterNatives(int index); + + public static void main(String[] args) throws Exception { + Method method = NativeWrapperCollection.class.getDeclaredMethod("method"); + + callRegisterNatives(0); + + WB.enqueueMethodForCompilation(method, 1 /* compLevel */); + while (WB.isMethodQueuedForCompilation(method)) { + Thread.sleep(50 /* ms */); + } + + callRegisterNatives(1); + + WB.enqueueMethodForCompilation(method, 1 /* compLevel */); + while (WB.isMethodQueuedForCompilation(method)) { + Thread.sleep(50 /* ms */); + } + + WB.fullGC(); // mark the nmethod as not on stack + WB.fullGC(); // reclaim the nmethod + + OutputAnalyzer output = new JMXExecutor().execute("Compiler.codelist"); + Iterator lines = output.asLines().iterator(); + + boolean foundOne = false; + while (lines.hasNext()) { + String line = lines.next(); + if (!line.contains("NativeWrapperCollection.method")) { + continue; + } + if (foundOne) { + throw new AssertionError("Expected one CodeCache entry for " + + "'NativeWrapperCollection.method', found at least 2"); + } + + String[] parts = line.split(" "); + int codeState = Integer.parseInt(parts[2]); + if (codeState == 1 /* not_entrant */) { + throw new AssertionError("Unexpected not-entrant entry for " + + "'NativeWrapperCollection.method'"); + } + + // Found one NativeWrapperCollection.method, exactly one is + // expected + foundOne = true; + } + } +} diff --git a/test/hotspot/jtreg/gc/NativeWrapperCollection/libnativeWrapperCollection.cpp b/test/hotspot/jtreg/gc/NativeWrapperCollection/libnativeWrapperCollection.cpp new file mode 100644 index 00000000000..0626cf8d401 --- /dev/null +++ b/test/hotspot/jtreg/gc/NativeWrapperCollection/libnativeWrapperCollection.cpp @@ -0,0 +1,44 @@ +/* + * 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" + +static void method0(JNIEnv* env, jclass cls) {} +static void method1(JNIEnv* env, jclass cls) {} + +extern "C" { + +JNIEXPORT void JNICALL +Java_gc_NativeWrapperCollection_NativeWrapperCollection_callRegisterNatives +(JNIEnv *env, jclass cls, jint index) { + JNINativeMethod nativeMethods[] = { + { + (char*) "method", // name + (char*) "()V", // sig + (void*) (index == 0 ? method0 : method1) // native method ptr + } + }; + env->RegisterNatives(cls, nativeMethods, 1); +} + +} diff --git a/test/hotspot/jtreg/gc/TestUseGCOverheadLimit.java b/test/hotspot/jtreg/gc/TestUseGCOverheadLimit.java new file mode 100644 index 00000000000..bc4c6bd6278 --- /dev/null +++ b/test/hotspot/jtreg/gc/TestUseGCOverheadLimit.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package gc; + +/* + * @test id=Parallel + * @requires vm.gc.Parallel + * @requires !vm.debug + * @summary Verifies that the UseGCOverheadLimit functionality works in Parallel GC. + * @library /test/lib + * @run driver gc.TestUseGCOverheadLimit Parallel + */ + +/* + * @test id=G1 + * @requires vm.gc.G1 + * @requires !vm.debug + * @summary Verifies that the UseGCOverheadLimit functionality works in G1 GC. + * @library /test/lib + * @run driver gc.TestUseGCOverheadLimit G1 + */ + +import java.util.Arrays; +import java.util.stream.Stream; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestUseGCOverheadLimit { + public static void main(String args[]) throws Exception { + String[] parallelArgs = { + "-XX:+UseParallelGC", + "-XX:NewSize=122m", + "-XX:SurvivorRatio=99", + "-XX:GCHeapFreeLimit=10" + }; + String[] g1Args = { + "-XX:+UseG1GC", + "-XX:GCHeapFreeLimit=5" + }; + + String[] selectedArgs = args[0].equals("G1") ? g1Args : parallelArgs; + + final String[] commonArgs = { + "-XX:-UseCompactObjectHeaders", // Object sizes are calculated such that the heap is tight. + "-XX:ParallelGCThreads=1", // Make GCs take longer. + "-XX:+UseGCOverheadLimit", + "-Xlog:gc=debug", + "-XX:GCTimeLimit=90", // Ease the CPU requirement a little. + "-Xmx128m", + Allocating.class.getName() + }; + + String[] vmArgs = Stream.concat(Arrays.stream(selectedArgs), Arrays.stream(commonArgs)).toArray(String[]::new); + OutputAnalyzer output = ProcessTools.executeLimitedTestJava(vmArgs); + output.shouldNotHaveExitValue(0); + + System.out.println(output.getStdout()); + + output.stdoutShouldContain("GC Overhead Limit exceeded too often (5)."); + } + + static class Allocating { + public static void main(String[] args) { + Object[] cache = new Object[1024 * 1024 * 2]; + + // Allocate random objects, keeping around data, causing garbage + // collections. + for (int i = 0; i < 1024* 1024 * 30; i++) { + Object[] obj = new Object[10]; + cache[i % cache.length] = obj; + } + + System.out.println(cache); + } + } +} diff --git a/test/hotspot/jtreg/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 @@ package gc.arguments; * @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 @@ package gc.g1; * 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/gc/shenandoah/generational/TestOldGrowthTriggers.java b/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java index b5d191e33ba..5182d4a9ea3 100644 --- a/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java +++ b/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java @@ -25,6 +25,7 @@ /* * @test id=generational * @summary Test that growth of old-gen triggers old-gen marking + * @key intermittent * @requires vm.gc.Shenandoah * @library /test/lib * @run driver TestOldGrowthTriggers diff --git a/test/hotspot/jtreg/gc/shenandoah/options/TestPassiveModeWithCardBarrier.java b/test/hotspot/jtreg/gc/shenandoah/options/TestPassiveModeWithCardBarrier.java new file mode 100644 index 00000000000..64e0a0b2a93 --- /dev/null +++ b/test/hotspot/jtreg/gc/shenandoah/options/TestPassiveModeWithCardBarrier.java @@ -0,0 +1,59 @@ +/* + * 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. + * + */ + +/* + * @test + * @summary Test passive mode with card barrier with a gc heavy app. A simple hello world in TestSelectiveBarrierFlags + * does not always surface crashes + * @requires vm.gc.Shenandoah + * @library /test/lib + * + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+UseShenandoahGC -Xmx128m -XX:ShenandoahGCMode=passive -XX:+ShenandoahCardBarrier TestPassiveModeWithCardBarrier + */ + +import java.util.*; +import java.util.concurrent.*; + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +public class TestPassiveModeWithCardBarrier { + public static void main(String[] args) throws Exception { + List junk = new ArrayList<>(); + int junkLength = 1000; + int totalRounds = 10; + int round = 0; + + while (round++ < totalRounds) { + for (int i = 0; i < junkLength; i++) { + junk.add(new byte[1024]); + } + + System.out.println(junk.hashCode()); + } + + // trigger a full gc in case it was all degen + System.gc(); + } +} diff --git a/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java b/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java index 411c2b1003d..348aa5367e9 100644 --- a/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java +++ b/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java @@ -37,18 +37,30 @@ import jdk.test.lib.process.OutputAnalyzer; public class TestWrongBarrierEnable { public static void main(String[] args) throws Exception { - String[] concurrent = { "ShenandoahSATBBarrier" }; + String[] concurrent = { + "ShenandoahLoadRefBarrier", + "ShenandoahSATBBarrier", + "ShenandoahCASBarrier", + "ShenandoahCloneBarrier", + "ShenandoahStackWatermarkBarrier", + }; String[] generational = { "ShenandoahCardBarrier" }; - String[] all = { "ShenandoahSATBBarrier", "ShenandoahCardBarrier" }; + String[] all = { + "ShenandoahLoadRefBarrier", + "ShenandoahSATBBarrier", + "ShenandoahCASBarrier", + "ShenandoahCloneBarrier", + "ShenandoahStackWatermarkBarrier", + "ShenandoahCardBarrier" + }; shouldPassAll("-XX:ShenandoahGCHeuristics=adaptive", concurrent); shouldPassAll("-XX:ShenandoahGCHeuristics=static", concurrent); shouldPassAll("-XX:ShenandoahGCHeuristics=compact", concurrent); shouldPassAll("-XX:ShenandoahGCHeuristics=aggressive", concurrent); - shouldPassAll("-XX:ShenandoahGCMode=passive", concurrent); + shouldPassAll("-XX:ShenandoahGCMode=passive", all); shouldPassAll("-XX:ShenandoahGCMode=generational", all); shouldFailAll("-XX:ShenandoahGCMode=satb", generational); - shouldFailAll("-XX:ShenandoahGCMode=passive", generational); } private static void shouldFailAll(String h, String[] barriers) throws Exception { diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java b/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java index 0e36fdce7d8..a9d957bac18 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java @@ -142,7 +142,11 @@ public class AccessZeroNKlassHitsProtectionZone { for (forceBase = start; forceBase < end; forceBase += step) { String thisBaseString = String.format("0x%016X", forceBase).toLowerCase(); output = run_test(COH, CDS, thisBaseString); - if (output.contains("CompressedClassSpaceBaseAddress=" + thisBaseString + " given, but reserving class space failed.")) { + if (output.contains("CompressedClassSpaceBaseAddress=" + thisBaseString + " given, but reserving class space failed.") || + output.matches ("CompressedClassSpaceBaseAddress=" + thisBaseString + " given with shift .*, cannot be used to encode class pointers")) { + // possible output: + // CompressedClassSpaceBaseAddress=0x0000000c00000000 given, but reserving class space failed. + // CompressedClassSpaceBaseAddress=0x0000000d00000000 given with shift 6, cannot be used to encode class pointers // try next one } else if (output.contains("Successfully forced class space address to " + thisBaseString)) { break; diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/AsanReportTest.java b/test/hotspot/jtreg/runtime/ErrorHandling/AsanReportTest.java new file mode 100644 index 00000000000..211df563e6c --- /dev/null +++ b/test/hotspot/jtreg/runtime/ErrorHandling/AsanReportTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2025, IBM Corporation. All rights reserved. + * 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 Test that we get ASAN-reports and hs-err files on ASAN error + * @library /test/lib + * @requires vm.asan + * @requires vm.flagless + * @requires vm.debug == true & os.family == "linux" + * @modules java.base/jdk.internal.misc + * java.management + * @run driver AsanReportTest + */ + +// Note: this test can only run on debug since it relies on VMError::controlled_crash() which +// only exists in debug builds. +import java.io.File; +import java.util.regex.Pattern; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class AsanReportTest { + + private static void do_test() throws Exception { + + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-Xmx64M", "-XX:CompressedClassSpaceSize=64M", + // Default ASAN options should prevent core file generation, which should overrule +CreateCoredumpOnCrash. + // We test below. + "-XX:+CreateCoredumpOnCrash", + "-Xlog:asan", + // Switch off NMT since it can alter the error ASAN sees; we want the pure double free error + "-XX:NativeMemoryTracking=off", + // Causes double-free in controlled_crash + "-XX:ErrorHandlerTest=18", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + output.shouldNotHaveExitValue(0); + + // ASAN error should appear on stderr + output.shouldContain("CreateCoredumpOnCrash overruled"); + output.shouldContain("JVM caught ASAN Error"); + output.shouldMatch("AddressSanitizer.*double-free"); + output.shouldMatch("# +A fatal error has been detected by the Java Runtime Environment"); + output.shouldMatch("# +fatal error: ASAN"); + output.shouldNotContain("Aborted (core dumped)"); + + File hs_err_file = HsErrFileUtils.openHsErrFileFromOutput(output); + Pattern[] pat = new Pattern[] { + Pattern.compile(".*A S A N.*"), + Pattern.compile(".*AddressSanitizer.*double-free.*"), + Pattern.compile(".*(crash_with_segfault|controlled_crash).*") + }; + HsErrFileUtils.checkHsErrFileContent(hs_err_file, pat, false); + } + + public static void main(String[] args) throws Exception { + do_test(); + } + +} + diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/MachCodeFramesInErrorFile.java b/test/hotspot/jtreg/runtime/ErrorHandling/MachCodeFramesInErrorFile.java index 5717a576e65..74cedae5f1a 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/NMT/CheckForProperDetailStackTrace.java b/test/hotspot/jtreg/runtime/NMT/CheckForProperDetailStackTrace.java index 398d6b523ad..28af0692721 100644 --- a/test/hotspot/jtreg/runtime/NMT/CheckForProperDetailStackTrace.java +++ b/test/hotspot/jtreg/runtime/NMT/CheckForProperDetailStackTrace.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, 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 @@ -26,11 +26,14 @@ * @bug 8133747 8218458 * @summary Running with NMT detail should produce expected stack traces. * @library /test/lib + * @library / * @modules java.base/jdk.internal.misc * java.management * @requires vm.debug + * @build jdk.test.whitebox.WhiteBox * @compile ../modules/CompilerUtils.java - * @run driver CheckForProperDetailStackTrace + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI CheckForProperDetailStackTrace */ import jdk.test.lib.Platform; @@ -40,6 +43,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.regex.Matcher; import java.util.regex.Pattern; +import jdk.test.whitebox.WhiteBox; /** * We are checking for details that should be seen with NMT detail enabled. @@ -59,7 +63,10 @@ public class CheckForProperDetailStackTrace { private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); private static final Path MODS_DIR = Paths.get(TEST_CLASSES, "mods"); - private static final boolean expectSourceInformation = Platform.isLinux() || Platform.isWindows(); + // Windows has source information only in full pdbs, not in stripped pdbs + private static boolean expectSourceInformation = Platform.isLinux() || Platform.isWindows(); + + static WhiteBox wb = WhiteBox.getWhiteBox(); /* The stack trace we look for by default. Note that :: has been replaced by .* to make sure it matches even if the symbol is not unmangled. @@ -138,6 +145,10 @@ public class CheckForProperDetailStackTrace { throw new RuntimeException("Expected stack trace missing from output"); } + if (wb.hasExternalSymbolsStripped()) { + expectSourceInformation = false; + } + System.out.println("Looking for source information:"); if (expectSourceInformation) { if (!stackTraceMatches(".*moduleEntry.cpp.*", output)) { diff --git a/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java b/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java new file mode 100644 index 00000000000..7f0f1be929b --- /dev/null +++ b/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java @@ -0,0 +1,116 @@ +/* + * 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:-CreateCoredumpOnCrash", + "-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/appcds/aotCache/SharedSecretsTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/SharedSecretsTest.java new file mode 100644 index 00000000000..5d6e909b751 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/SharedSecretsTest.java @@ -0,0 +1,92 @@ +/* + * 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 Try to AOT-initialize SharedSecrets accessors that are not allowed. + * @bug 8368199 + * @requires vm.cds.supports.aot.class.linking + * @requires vm.debug + * @library /test/lib + * @build SharedSecretsTest + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar MyTestApp + * @run driver SharedSecretsTest AOT + */ + +import java.io.ObjectInputFilter; +import jdk.test.lib.cds.CDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.OutputAnalyzer; + +public class SharedSecretsTest { + static final String appJar = ClassFileInstaller.getJarPath("app.jar"); + static final String mainClass = "MyTestApp"; + + public static void main(String[] args) throws Exception { + Tester t = new Tester(mainClass); + t.setCheckExitValue(false); + t.runAOTAssemblyWorkflow(); + } + + static class Tester extends CDSAppTester { + public Tester(String name) { + super(name); + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] vmArgs(RunMode runMode) { + return new String[] { "-XX:AOTInitTestClass=" + mainClass}; + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] { + mainClass, + }; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) { + if (runMode == RunMode.ASSEMBLY) { + out.shouldMatch("jdk.internal.access.SharedSecrets::javaObjectInputFilterAccess .* must be stateless"); + out.shouldNotHaveExitValue(0); + } else { + out.shouldHaveExitValue(0); + } + } + } +} + +// We use -XX:AOTInitTestClass to force this class to be AOT-initialized in the assembly phase. It will +// cause the SharedSecrets::javaObjectInputFilterAccess to be initialized, which is not allowed. +class MyTestApp { + static Object foo = ObjectInputFilter.Config.createFilter(""); + public static void main(String args[]) { + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java index bc2ac9db2ab..ea2c6fc88b4 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java @@ -58,6 +58,22 @@ * @run main/othervm -Dcds.app.tester.workflow=DYNAMIC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. ResolvedConstants DYNAMIC */ +/* + * @test id=aot + * @summary Dump time resolution of constant pool entries (AOT workflow). + * @requires vm.cds + * @requires vm.cds.supports.aot.class.linking + * @requires vm.compMode != "Xcomp" + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes/ + * @build OldProvider OldClass OldConsumer StringConcatTestOld + * @build ResolvedConstants + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar + * ResolvedConstantsApp ResolvedConstantsFoo ResolvedConstantsBar + * MyInterface InterfaceWithClinit NormalClass + * OldProvider OldClass OldConsumer SubOfOldClass + * StringConcatTest StringConcatTestOld + * @run driver ResolvedConstants AOT --two-step-training + */ import java.util.function.Consumer; import jdk.test.lib.cds.CDSOptions; import jdk.test.lib.cds.CDSTestUtils; @@ -122,12 +138,15 @@ public class ResolvedConstants { .shouldMatch(ALWAYS("field.* ResolvedConstantsBar => ResolvedConstantsBar.a:I")) .shouldMatch(ALWAYS("field.* ResolvedConstantsBar => ResolvedConstantsFoo.a:I")) .shouldMatch(ALWAYS("field.* ResolvedConstantsFoo => ResolvedConstantsFoo.a:I")) + .shouldMatch(ALWAYS("field.* ResolvedConstantsApp => ResolvedConstantsApp.static_i:I")) // Resolve field references to child classes ONLY when using -XX:+AOTClassLinking + .shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsFoo => ResolvedConstantsBar.static_b:I")) .shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsFoo => ResolvedConstantsBar.a:I")) .shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsFoo => ResolvedConstantsBar.b:I")) // Resolve field references to unrelated classes ONLY when using -XX:+AOTClassLinking + .shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsApp => ResolvedConstantsBar.static_b:I")) .shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsApp => ResolvedConstantsBar.a:I")) .shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsApp => ResolvedConstantsBar.b:I")); @@ -150,8 +169,8 @@ public class ResolvedConstants { .shouldMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.privateInstanceCall:")) .shouldMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.publicInstanceCall:")) - // Should not resolve references to static method - .shouldNotMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.staticCall:")) + // Should resolve references to static method + .shouldMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.staticCall:")) // Should resolve references to method in super type .shouldMatch(ALWAYS("method.*: ResolvedConstantsBar ResolvedConstantsFoo.doBar:")) @@ -164,7 +183,12 @@ public class ResolvedConstants { .shouldMatch(AOTLINK_ONLY("method.*: ResolvedConstantsApp java/io/PrintStream.println:")) .shouldMatch(AOTLINK_ONLY("method.*: ResolvedConstantsBar java/lang/Class.getName:")) - // Resole resolve methods in unrelated classes ONLY when using -XX:+AOTClassLinking + // Resolve method references to child classes ONLY when using -XX:+AOTClassLinking + .shouldMatch(AOTLINK_ONLY("method.* ResolvedConstantsFoo ResolvedConstantsBar.static_doit")) + .shouldMatch(AOTLINK_ONLY("method.* ResolvedConstantsFoo ResolvedConstantsBar.doit2")) + + // Resolve methods in unrelated classes ONLY when using -XX:+AOTClassLinking + .shouldMatch(AOTLINK_ONLY("method.*: ResolvedConstantsApp ResolvedConstantsBar.static_doit:")) .shouldMatch(AOTLINK_ONLY("method.*: ResolvedConstantsApp ResolvedConstantsBar.doit:")) // End --- @@ -174,14 +198,9 @@ public class ResolvedConstants { // 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"); } } @@ -208,11 +227,14 @@ class ResolvedConstantsApp implements Runnable { System.out.println("Hello ResolvedConstantsApp"); ResolvedConstantsApp app = new ResolvedConstantsApp(); ResolvedConstantsApp.staticCall(); + ResolvedConstantsApp.static_i ++; app.privateInstanceCall(); app.publicInstanceCall(); Object a = app; ((Runnable)a).run(); + ResolvedConstantsBar.static_b += 10; + ResolvedConstantsBar.static_doit(); ResolvedConstantsFoo foo = new ResolvedConstantsFoo(); ResolvedConstantsBar bar = new ResolvedConstantsBar(); bar.a ++; @@ -223,6 +245,7 @@ class ResolvedConstantsApp implements Runnable { StringConcatTest.test(); StringConcatTestOld.main(null); } + private static int static_i = 10; private static void staticCall() {} private void privateInstanceCall() {} public void publicInstanceCall() {} @@ -318,13 +341,21 @@ class ResolvedConstantsFoo { } void doBar(ResolvedConstantsBar bar) { + ResolvedConstantsBar.static_b += 1; + ResolvedConstantsBar.static_doit(); + bar.a ++; bar.b ++; + bar.doit2(); } } class ResolvedConstantsBar extends ResolvedConstantsFoo { + public static int static_b = 10; int b = 2; + public static void static_doit() { + } + void doit() { System.out.println("Hello ResolvedConstantsBar and " + ResolvedConstantsFoo.class.getName()); System.out.println("a = " + a); @@ -335,4 +366,8 @@ class ResolvedConstantsBar extends ResolvedConstantsFoo { ((ResolvedConstantsFoo)this).doBar(this); } + + void doit2() { + + } } 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/NMethodRelocation/NMethodRelocationTest.java b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java index b12e2c3455c..6c465b357d7 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java @@ -27,6 +27,7 @@ * @bug 8316694 * @summary Verify that nmethod relocation posts the correct JVMTI events * @requires vm.jvmti + * @requires vm.gc == "null" | vm.gc == "Serial" * @library /test/lib /test/hotspot/jtreg * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java index 9ec9ca1b8c2..7f524f80267 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, 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 @@ -105,7 +105,7 @@ class GetThreadStateTest { int expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE; check(thread, expected); - // re-test with interrupt status set + // re-test with interrupted status set thread.interrupt(); check(thread, expected | JVMTI_THREAD_STATE_INTERRUPTED); } finally { @@ -143,7 +143,7 @@ class GetThreadStateTest { int expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; await(thread, expected); - // re-test with interrupt status set + // re-test with interrupted status set thread.interrupt(); check(thread, expected | JVMTI_THREAD_STATE_INTERRUPTED); } @@ -192,7 +192,7 @@ class GetThreadStateTest { expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; check(thread, expected); - // re-test with interrupt status set + // re-test with interrupted status set thread.interrupt(); check(thread, expected | JVMTI_THREAD_STATE_INTERRUPTED); } @@ -244,7 +244,7 @@ class GetThreadStateTest { expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; check(thread, expected); - // re-test with interrupt status set + // re-test with interrupted status set thread.interrupt(); check(thread, expected | JVMTI_THREAD_STATE_INTERRUPTED); } 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 @@ public class ThreadStateTest { 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 class ThreadStateTest { 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/TestJhsdbJstackWithVirtualThread.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackWithVirtualThread.java index acea00a190b..6d7921c7ed8 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackWithVirtualThread.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackWithVirtualThread.java @@ -37,7 +37,6 @@ import jdk.test.lib.process.OutputAnalyzer; * @test * @bug 8369505 * @requires vm.hasSA - * @requires (os.arch == "amd64" | os.arch == "x86_64" | os.arch == "aarch64" | os.arch == "riscv64") * @library /test/lib * @run driver TestJhsdbJstackWithVirtualThread */ diff --git a/test/hotspot/jtreg/vmTestbase/gc/vector/CircularListLow/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/vector/CircularListLow/TestDescription.java index 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 @@ public class PerformChecksHelper { } } } + } 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/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/InterruptThread/intrpthrd001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/InterruptThread/intrpthrd001/TestDescription.java index f74b8cb6c78..f878dbede82 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/InterruptThread/intrpthrd001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/InterruptThread/intrpthrd001/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 @@ -36,7 +36,7 @@ * - sleeping on Thread.sleep() * then agent part of the test calls InterruptThread for these threads * and the debugee checks that: - * - the running thread get interrupt status + * - the running thread get interrupted status * - the waiting and sleeping threads get InterruptedException * COMMENTS * diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/bcinstr/BI04/bi04t002/newclass02/java.base/java/lang/Object.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/bcinstr/BI04/bi04t002/newclass02/java.base/java/lang/Object.java index 47e030945ab..a52a76895f5 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/bcinstr/BI04/bi04t002/newclass02/java.base/java/lang/Object.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/bcinstr/BI04/bi04t002/newclass02/java.base/java/lang/Object.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 @@ -383,7 +383,7 @@ public class Object { try { wait0(timeoutMillis); } catch (InterruptedException e) { - // virtual thread's interrupt status needs to be cleared + // virtual thread's interrupted status needs to be cleared vthread.getAndClearInterrupt(); throw e; } 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 class tc03t001 extends DebugeeClass { /* =================================================================== */ 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 class tc03t002 extends DebugeeClass { /* =================================================================== */ 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/jvmti/scenarios/sampling/SP01/sp01t002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/sampling/SP01/sp01t002/TestDescription.java index c8003af65ee..e095d4dc8a0 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/sampling/SP01/sp01t002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/sampling/SP01/sp01t002/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 @@ -51,7 +51,7 @@ * COMMENTS * Converted the test to use GetThreadState instead of GetThreadStatus. * Test fixed according to test bug: - * 4935244 TEST BUG: wrong interrupt status flag tests. + * 4935244 TEST BUG: wrong interrupted status flag tests. * Fixed according to test bug: * 6405644 TEST_BUG: no proper sync with agent thread in sp02t001/sp02t003 * diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/sampling/SP01/sp01t003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/sampling/SP01/sp01t003/TestDescription.java index 99509bf5107..04e5ff8ad35 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/sampling/SP01/sp01t003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/sampling/SP01/sp01t003/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 @@ -52,7 +52,7 @@ * COMMENTS * Converted the test to use GetThreadState instead of GetThreadStatus. * Test fixed according to test bug: - * 4935244 TEST BUG: wrong interrupt status flag tests. + * 4935244 TEST BUG: wrong interrupted status flag tests. * Fixed according to test bug: * 6405644 TEST_BUG: no proper sync with agent thread in sp02t001/sp02t003 * diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/AllDiag.java b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/AllDiag.java index e9f9b42955e..ec93f3df2c0 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/AllDiag.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/AllDiag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2018, 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 @@ -41,7 +41,7 @@ public class AllDiag implements Runnable { public void run() { AllMemoryObject.dumpStatistics(); - // Ensure that interrupt status is not lost + // Ensure that interrupted status is not lost if (Thread.currentThread().isInterrupted()) return; try { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/FinDiag.java b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/FinDiag.java index 58cda02b24f..1864687e122 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/FinDiag.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/FinDiag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2018, 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 @@ -41,7 +41,7 @@ public class FinDiag implements Runnable { public void run() { FinMemoryObject.dumpStatistics(); - // Ensure that interrupt status is not lost + // Ensure that interrupted status is not lost if (Thread.currentThread().isInterrupted()) return; try { diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/AbstractDebuggeeTest.java b/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/AbstractDebuggeeTest.java index 86d5d7ecd24..74516fd5a69 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/AbstractDebuggeeTest.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/AbstractDebuggeeTest.java @@ -175,13 +175,6 @@ public class AbstractDebuggeeTest { } } - static public void sleep1sec() { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - } - } - private StateTestThread stateTestThread; public static final String COMMAND_QUIT = "quit"; @@ -354,9 +347,6 @@ public class AbstractDebuggeeTest { eatMemory(); } - public void voidValueMethod() { - } - public void unexpectedException(Throwable t) { setSuccess(false); t.printStackTrace(log.getOutStream()); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/runner/MemDiag.java b/test/hotspot/jtreg/vmTestbase/nsk/share/runner/MemDiag.java index f361c62b5ae..19dce3d8c39 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/runner/MemDiag.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/runner/MemDiag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -39,7 +39,7 @@ public class MemDiag implements Runnable { public void run() { System.out.println(Runtime.getRuntime().freeMemory()); - // Ensure that interrupt status is not lost + // Ensure that interrupted status is not lost if (Thread.currentThread().isInterrupted()) return; try { diff --git a/test/hotspot/jtreg/vmTestbase/vm/gc/compact/Humongous_NonbranchyTree5M/TestDescription.java b/test/hotspot/jtreg/vmTestbase/vm/gc/compact/Humongous_NonbranchyTree5M/TestDescription.java index 1d5a6f0cf11..a539fb7cea2 100644 --- a/test/hotspot/jtreg/vmTestbase/vm/gc/compact/Humongous_NonbranchyTree5M/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/vm/gc/compact/Humongous_NonbranchyTree5M/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, 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 @@ -36,7 +36,7 @@ * * @library /vmTestbase * /test/lib - * @run main/othervm + * @run main/othervm/timeout=480 * -XX:-UseGCOverheadLimit * vm.gc.compact.Compact * -gp nonbranchyTree(high) diff --git a/test/jdk/ProblemList-Virtual.txt b/test/jdk/ProblemList-Virtual.txt index dcd4dbac310..dffbd4a952e 100644 --- a/test/jdk/ProblemList-Virtual.txt +++ b/test/jdk/ProblemList-Virtual.txt @@ -38,24 +38,3 @@ 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 diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 0e69446ae35..d1644a56100 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -152,7 +152,7 @@ java/awt/event/InputEvent/EventWhenTest/EventWhenTest.java 8168646 generic-all java/awt/List/KeyEventsTest/KeyEventsTest.java 8201307 linux-all java/awt/Paint/ListRepaint.java 8201307 linux-all java/awt/Mixing/AWT_Mixing/HierarchyBoundsListenerMixingTest.java 8049405 macosx-all -java/awt/Mixing/AWT_Mixing/OpaqueOverlapping.java 8294264 windows-x64 +java/awt/Mixing/AWT_Mixing/OpaqueOverlapping.java 8370584 windows-x64 java/awt/Mixing/AWT_Mixing/OpaqueOverlappingChoice.java 8048171 generic-all java/awt/Mixing/AWT_Mixing/JMenuBarOverlapping.java 8159451 linux-all,windows-all,macosx-all java/awt/Mixing/AWT_Mixing/JSplitPaneOverlapping.java 6986109 generic-all @@ -587,6 +587,7 @@ java/net/MulticastSocket/Test.java 7145658,8308807 # jdk_nio java/nio/channels/Channels/SocketChannelStreams.java 8317838 aix-ppc64 +java/nio/channels/AsyncCloseAndInterrupt.java 8368290 macosx-26.0.1 java/nio/channels/DatagramChannel/AdaptorMulticasting.java 8308807,8144003 aix-ppc64,macosx-all java/nio/channels/DatagramChannel/AfterDisconnect.java 8308807 aix-ppc64 @@ -636,6 +637,8 @@ sun/security/smartcardio/TestTransmit.java 8039280 generic- sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java 8316183 linux-ppc64le +sun/security/ssl/SSLLogger/DebugPropertyValuesTest.java 8370852 generic-all + ############################################################################ # jdk_sound @@ -676,7 +679,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 diff --git a/test/jdk/build/AbsPathsInImage.java b/test/jdk/build/AbsPathsInImage.java index 1aa7e59941e..7b2c60c3dda 100644 --- a/test/jdk/build/AbsPathsInImage.java +++ b/test/jdk/build/AbsPathsInImage.java @@ -21,6 +21,7 @@ * questions. */ +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.FileVisitResult; @@ -35,6 +36,8 @@ import java.util.Properties; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import static java.util.Comparator.comparing; + /* * @test * @bug 8226346 @@ -42,7 +45,7 @@ import java.util.zip.ZipInputStream; * @requires !vm.debug * @comment ASAN keeps the 'unwanted' paths in the binaries because of its build options * @requires !vm.asan - * @run main/othervm -Xmx900m AbsPathsInImage + * @run main AbsPathsInImage */ public class AbsPathsInImage { @@ -51,9 +54,14 @@ public class AbsPathsInImage { public static final String DIR_PROPERTY = "jdk.test.build.AbsPathsInImage.dir"; private static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase().contains("windows"); private static final boolean IS_LINUX = System.getProperty("os.name").toLowerCase().contains("linux"); + private static final int DEFAULT_BUFFER_SIZE = 8192; + private static List searchPatterns = new ArrayList<>(); + private static List prefixTables = new ArrayList<>(); private boolean matchFound = false; + record Match(int begin, int end) { } + public static void main(String[] args) throws Exception { String jdkPathString = System.getProperty("test.jdk"); Path jdkHome = Paths.get(jdkPathString); @@ -107,9 +115,9 @@ public class AbsPathsInImage { throw new Error("Output root is not an absolute path: " + buildOutputRoot); } - List searchPatterns = new ArrayList<>(); - expandPatterns(searchPatterns, buildWorkspaceRoot); - expandPatterns(searchPatterns, buildOutputRoot); + expandPatterns(buildWorkspaceRoot); + expandPatterns(buildOutputRoot); + createPrefixTables(); System.out.println("Looking for:"); for (byte[] searchPattern : searchPatterns) { @@ -118,7 +126,7 @@ public class AbsPathsInImage { System.out.println(); AbsPathsInImage absPathsInImage = new AbsPathsInImage(); - absPathsInImage.scanFiles(dirToScan, searchPatterns); + absPathsInImage.scanFiles(dirToScan); if (absPathsInImage.matchFound) { throw new Exception("Test failed"); @@ -129,7 +137,7 @@ public class AbsPathsInImage { * Add path pattern to list of patterns to search for. Create all possible * variants depending on platform. */ - private static void expandPatterns(List searchPatterns, String pattern) { + private static void expandPatterns(String pattern) { if (IS_WINDOWS) { String forward = pattern.replace('\\', '/'); String back = pattern.replace('/', '\\'); @@ -151,7 +159,42 @@ public class AbsPathsInImage { } } - private void scanFiles(Path root, List searchPatterns) throws IOException { + /** + * The failure function for KMP. Returns the correct index in the pattern to jump + * back to when encountering a mismatched character. Used in both + * createPrefixTables (pre-processing) and scanBytes (matching). + */ + private static int getPrefixIndex(int patternIdx, int state, byte match) { + if (state == 0) { + return 0; + } + byte[] searchPattern = searchPatterns.get(patternIdx); + int[] prefixTable = prefixTables.get(patternIdx); + int i = prefixTable[state - 1]; + while (i > 0 && searchPattern[i] != match) { + i = prefixTable[i - 1]; + } + return searchPattern[i] == match ? i + 1 : i; + } + + /** + * Pre-processing string patterns for Knuth–Morris–Pratt (KMP) search algorithm. + * Lookup tables of longest prefixes at each given index are created for each + * search pattern string. These tables are later used in scanBytes during matching + * as lookups for failure state transitions. + */ + private static void createPrefixTables() { + for (int patternIdx = 0; patternIdx < searchPatterns.size(); patternIdx++) { + int patternLen = searchPatterns.get(patternIdx).length; + int[] prefixTable = new int[patternLen]; + prefixTables.add(prefixTable); + for (int i = 1; i < patternLen; i++) { + prefixTable[i] = getPrefixIndex(patternIdx, i, searchPatterns.get(patternIdx)[i]); + } + } + } + + private void scanFiles(Path root) throws IOException { Files.walkFileTree(root, new SimpleFileVisitor<>() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { @@ -170,84 +213,128 @@ public class AbsPathsInImage { } else if ((fileName.endsWith(".debuginfo") && !IS_LINUX) || fileName.endsWith(".pdb")) { // Do nothing } else if (fileName.endsWith(".zip")) { - scanZipFile(file, searchPatterns); + scanZipFile(file); } else { - scanFile(file, searchPatterns); + scanFile(file); } return super.visitFile(file, attrs); } }); } - private void scanFile(Path file, List searchPatterns) throws IOException { - List matches = scanBytes(Files.readAllBytes(file), searchPatterns); - if (matches.size() > 0) { - matchFound = true; - System.out.println(file + ":"); - for (String match : matches) { - System.out.println(match); - } - System.out.println(); + private void scanFile(Path file) throws IOException { + List matches; + try (InputStream inputStream = Files.newInputStream(file)) { + matches = scanBytes(inputStream); + } + // test succeeds + if (matches.size() == 0) { + return; + } + // test fails; pay penalty and re-scan file for debug output + try (InputStream inputStream = Files.newInputStream(file)) { + printDebugOutput(inputStream, matches, file + ":"); } } - private void scanZipFile(Path zipFile, List searchPatterns) throws IOException { + private void scanZipFile(Path zipFile) throws IOException { + List> entryMatches = new ArrayList<>(); + boolean found = false; + ZipEntry zipEntry; try (ZipInputStream zipInputStream = new ZipInputStream(Files.newInputStream(zipFile))) { - ZipEntry zipEntry; while ((zipEntry = zipInputStream.getNextEntry()) != null) { - List matches = scanBytes(zipInputStream.readAllBytes(), searchPatterns); + List matches = scanBytes(zipInputStream); if (matches.size() > 0) { - matchFound = true; - System.out.println(zipFile + ", " + zipEntry.getName() + ":"); - for (String match : matches) { - System.out.println(match); - } - System.out.println(); + entryMatches.add(matches); + found = true; + } else { + entryMatches.add(null); + } + } + } + // test succeeds + if (!found) { + return; + } + // test fails + try (ZipInputStream zipInputStream = new ZipInputStream(Files.newInputStream(zipFile))) { + int i = 0; + while ((zipEntry = zipInputStream.getNextEntry()) != null) { + List matches = entryMatches.get(i); + i++; + if (matches != null) { + printDebugOutput(zipInputStream, matches, zipFile + ", " + zipEntry.getName() + ":"); } } } } - private List scanBytes(byte[] data, List searchPatterns) { - List matches = new ArrayList<>(); - for (int i = 0; i < data.length; i++) { - for (byte[] searchPattern : searchPatterns) { - boolean found = true; - for (int j = 0; j < searchPattern.length; j++) { - if ((i + j >= data.length || data[i + j] != searchPattern[j])) { - found = false; + /** + * Scans each byte until encounters a match with one of searchPatterns. Uses KMP to + * perform matches. Keep track of current matched index (states) for each search + * pattern. At each given byte, update states accordingly (increment if match or + * failure function transition if mismatch). Returns a list of Match objects. + */ + private List scanBytes(InputStream input) throws IOException { + List matches = new ArrayList<>(); + byte[] buf = new byte[DEFAULT_BUFFER_SIZE]; + int[] states = new int[searchPatterns.size()]; + int fileIdx = 0; + int bytesRead, patternLen; + while ((bytesRead = input.read(buf)) != -1) { + for (int bufIdx = 0; bufIdx < bytesRead; bufIdx++, fileIdx++) { + byte datum = buf[bufIdx]; + for (int i = 0; i < searchPatterns.size(); i++) { + patternLen = searchPatterns.get(i).length; + if (datum != searchPatterns.get(i)[states[i]]) { + states[i] = getPrefixIndex(i, states[i], datum); + } else if (++states[i] == patternLen) { + // technically at last match, state should reset according to failure function + // but in original test, matching didn't search same string for multiple matches + states[i] = 0; + matches.add(new Match(fileIdx - patternLen + 1, fileIdx)); break; } } - if (found) { - matches.add(new String(data, charsStart(data, i), charsOffset(data, i, searchPattern.length))); - // No need to search the same string for multiple patterns - break; - } } } return matches; } - private int charsStart(byte[] data, int startIndex) { - int index = startIndex; - while (--index > 0) { - byte datum = data[index]; - if (datum < 32 || datum > 126) { - break; + /** + * In original test, failed test output would backtrack to last non-ascii byte on + * matched pattern. This is incompatible with the new buffered approach (and a + * proper solution requires a 2nd dynamic buffer). Instead, on failed test case, + * files are scanned a 2nd time to print debug output. Failed runs will pay + * additional performance/space penalty, but passing runs are faster. + */ + private void printDebugOutput(InputStream input, List matches, final String HEADER) throws IOException{ + matchFound = true; + System.out.println(HEADER); + matches.sort(comparing(Match::begin)); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + byte[] buf = new byte[DEFAULT_BUFFER_SIZE]; + int matchIdx = 0; + int fileIdx = 0; + int bytesRead; + while (matchIdx < matches.size() && (bytesRead = input.read(buf)) != -1) { + for (int i = 0; matchIdx < matches.size() && i < bytesRead; i++, fileIdx++) { + byte datum = buf[i]; + if (datum >= 32 && datum <= 126) { + output.write(datum); + } else if (fileIdx < matches.get(matchIdx).begin()) { + output.reset(); + } else if (fileIdx > matches.get(matchIdx).end()) { + System.out.println(output.toString()); + output.reset(); + // This imperfect as incorrect in edge cases with patterns containing non-ascii? + // but high-accuracy not priority + output still legible and useful + for (; matchIdx < matches.size() && matches.get(matchIdx).end() < fileIdx; matchIdx++); + } else { + output.write(datum); + } } } - return index + 1; - } - - private int charsOffset(byte[] data, int startIndex, int startOffset) { - int offset = startOffset; - while (startIndex + ++offset < data.length) { - byte datum = data[startIndex + offset]; - if (datum < 32 || datum > 126) { - break; - } - } - return offset; + System.out.println(); } } 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/TwoThreadsTest.java b/test/jdk/com/sun/jdi/TwoThreadsTest.java index e38783f3ea6..e5732ffd8d1 100644 --- a/test/jdk/com/sun/jdi/TwoThreadsTest.java +++ b/test/jdk/com/sun/jdi/TwoThreadsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, 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 @@ -55,6 +55,13 @@ class TwoThreadsTarg implements Runnable { t1.start(); t2.start(); + // The threads might be virtual and daemon, so wait until completion. + try { + t1.join(); + t2.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } } diff --git a/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java b/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java index 2ce3dfd016d..e7bea2814db 100644 --- a/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java +++ b/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.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,6 +27,9 @@ * @summary Tests for HttpExchange set/getAttribute * @library /test/lib * @run junit/othervm ExchangeAttributeTest + * @run junit/othervm -Djdk.httpserver.attributes=context ExchangeAttributeTest + * @run junit/othervm -Djdk.httpserver.attributes=random-string ExchangeAttributeTest + * @run junit/othervm -Djdk.httpserver.attributes ExchangeAttributeTest */ import com.sun.net.httpserver.HttpExchange; @@ -71,7 +74,7 @@ public class ExchangeAttributeTest { public void testExchangeAttributes() throws Exception { var handler = new AttribHandler(); var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR,0), 10); - server.createContext("/", handler); + server.createContext("/", handler).getAttributes().put("attr", "context-val"); server.start(); try { var client = HttpClient.newBuilder().proxy(NO_PROXY).build(); @@ -101,8 +104,17 @@ public class ExchangeAttributeTest { @java.lang.Override public void handle(HttpExchange exchange) throws IOException { try { - exchange.setAttribute("attr", "val"); - assertEquals("val", exchange.getAttribute("attr")); + if ("context".equals(System.getProperty("jdk.httpserver.attributes"))) { + exchange.setAttribute("attr", "val"); + assertEquals("val", exchange.getAttribute("attr")); + assertEquals("val", exchange.getHttpContext().getAttributes().get("attr")); + } else { + assertNull(exchange.getAttribute("attr")); + assertEquals("context-val", exchange.getHttpContext().getAttributes().get("attr")); + exchange.setAttribute("attr", "val"); + assertEquals("val", exchange.getAttribute("attr")); + assertEquals("context-val", exchange.getHttpContext().getAttributes().get("attr")); + } exchange.setAttribute("attr", null); assertNull(exchange.getAttribute("attr")); exchange.sendResponseHeaders(200, -1); diff --git a/test/jdk/com/sun/nio/sctp/SctpServerChannel/Accept.java b/test/jdk/com/sun/nio/sctp/SctpServerChannel/Accept.java index 56522a85f6b..c68bfd28945 100644 --- a/test/jdk/com/sun/nio/sctp/SctpServerChannel/Accept.java +++ b/test/jdk/com/sun/nio/sctp/SctpServerChannel/Accept.java @@ -190,7 +190,7 @@ public class Accept { /* TEST 5: AsynchronousCloseException */ debug("TEST 5: AsynchronousCloseException"); - /* reset thread interrupt status */ + /* reset thread interrupted status */ Thread.currentThread().interrupted(); ssc = SctpServerChannel.open().bind(null); 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 @@ import jdk.test.lib.Platform; 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/awt/image/BandedSampleModel/BSMCreateCompatibleSMTest.java b/test/jdk/java/awt/image/BandedSampleModel/BSMCreateCompatibleSMTest.java new file mode 100644 index 00000000000..a47659300e8 --- /dev/null +++ b/test/jdk/java/awt/image/BandedSampleModel/BSMCreateCompatibleSMTest.java @@ -0,0 +1,100 @@ +/* + * 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 6453640 + * @summary Verify BandedSampleModel.createCompatibleSampleModel + * and createSubsetSampleModel behaviour + * @run main BSMCreateCompatibleSMTest + */ + +import java.awt.image.BandedSampleModel; +import java.awt.image.DataBuffer; +import java.awt.image.RasterFormatException; + +public class BSMCreateCompatibleSMTest { + + public static void main(String[] args) { + + // These should all be OK + BandedSampleModel bsm = new BandedSampleModel(DataBuffer.TYPE_BYTE, 1, 1, 1); + bsm.createCompatibleSampleModel(20_000, 20_000); + int[] bands = { 0 } ; + bsm.createSubsetSampleModel(bands); + + // These should all throw an exception + try { + bsm.createCompatibleSampleModel(-1, 1); + throw new RuntimeException("No exception for illegal w"); + } catch (IllegalArgumentException e) { + System.out.println(e); + } + + try { + bsm.createCompatibleSampleModel(1, 0); + throw new RuntimeException("No exception for illegal h"); + } catch (IllegalArgumentException e) { + System.out.println(e); + } + + try { + bsm.createCompatibleSampleModel(-1, -1); + throw new RuntimeException("No exception for illegal w+h"); + } catch (IllegalArgumentException e) { + System.out.println(e); + } + + try { + bsm.createCompatibleSampleModel(50_000, 50_000); + throw new RuntimeException("No exception for too large dims"); + } catch (IllegalArgumentException e) { + System.out.println(e); + } + + try { + int[] bands0 = { } ; + bsm.createSubsetSampleModel(bands0); + throw new RuntimeException("No exception for empty bands[]"); + } catch (IllegalArgumentException e) { + System.out.println(e); + } + + try { + int[] bands1 = { 1 } ; + bsm.createSubsetSampleModel(bands1); + throw new RuntimeException("No exception for out of bounds band"); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(e); + } + + try { + int[] bands2 = { 0, 0 } ; + bsm.createSubsetSampleModel(bands2); + throw new RuntimeException("No exception for too many bands"); + } catch (RasterFormatException e) { + System.out.println(e); + } + } + +} diff --git a/test/jdk/java/awt/print/PrinterJob/PageRanges.java b/test/jdk/java/awt/print/PrinterJob/PageRanges.java index accde99ae95..e80330bae6c 100644 --- a/test/jdk/java/awt/print/PrinterJob/PageRanges.java +++ b/test/jdk/java/awt/print/PrinterJob/PageRanges.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,58 +21,63 @@ * questions. */ -/** +/* * @test * @bug 6575331 * @key printer * @summary The specified pages should be printed. - * @run main/manual=yesno PageRanges + * @library /java/awt/regtesthelpers + * @library /test/lib + * @build PassFailJFrame + * @build jtreg.SkippedException + * @run main/manual PageRanges */ -import java.awt.*; -import java.awt.print.*; +import java.awt.Graphics; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import jtreg.SkippedException; public class PageRanges implements Printable { - - static String[] instr = { - "This test prints two jobs, and tests that the specified range", - "of pages is printed. You must have a printer installed for this test.", - "In the first dialog, select a page range of 2 to 3, and press OK", - "In the second dialog, select ALL, to print all pages (in total 5 pages).", - "Collect the two print outs and confirm the jobs printed correctly", - }; + private static final String INSTRUCTIONS = """ + This test prints two jobs and tests that the specified range + of pages is printed. + In the first dialog, select a page range of 2 to 3, and press OK. + In the second dialog, select ALL, to print all pages (in total 5 pages). + Collect the two print outs and confirm the jobs are printed correctly. + """; public static void main(String args[]) throws Exception { - for (int i=0;i= 5) { return NO_SUCH_PAGE; } g.drawString("Page : " + (pi+1), 200, 200); - return PAGE_EXISTS; } } diff --git a/test/jdk/java/awt/print/PrinterJob/PolylinePrintingTest.java b/test/jdk/java/awt/print/PrinterJob/PolylinePrintingTest.java index 7d8568c01f9..c9dcdfdd450 100644 --- a/test/jdk/java/awt/print/PrinterJob/PolylinePrintingTest.java +++ b/test/jdk/java/awt/print/PrinterJob/PolylinePrintingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,31 +21,56 @@ * questions. */ -/** +/* + * @test * @bug 8041902 * @key printer * @summary Test printing of wide poly lines. - * @run main/manual=yesno PolylinePrintingTest + * @library /java/awt/regtesthelpers + * @library /test/lib + * @build PassFailJFrame + * @build jtreg.SkippedException + * @run main/manual PolylinePrintingTest */ -import java.awt.Dialog; -import java.awt.Frame; -import java.awt.TextArea; import java.awt.BasicStroke; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.Path2D; import java.awt.print.PageFormat; -import java.awt.print.Paper; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; +import jtreg.SkippedException; public class PolylinePrintingTest implements Printable { + private static final String INSTRUCTIONS = """ + Press OK in the print dialog and collect the printed page. + Passing test : Output should show two identical chevrons. + Failing test : The line joins will appear different. + """; + + public static void main(String[] args) throws Exception { + PrinterJob job = PrinterJob.getPrinterJob(); + if (job.getPrintService() == null) { + throw new SkippedException("Printer not configured or available."); + } + + PassFailJFrame passFailJFrame = PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(45) + .build(); + + job.setPrintable(new PolylinePrintingTest()); + if (job.printDialog()) { + job.print(); + } + + passFailJFrame.awaitAndCheck(); + } public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { - if (pageIndex > 0) { return NO_SUCH_PAGE; } @@ -66,7 +91,6 @@ public class PolylinePrintingTest implements Printable { private void drawPolylineGOOD(Graphics2D g2d, int[] x2Points, int[] y2Points) { - Path2D polyline = new Path2D.Float(Path2D.WIND_EVEN_ODD, x2Points.length); @@ -83,141 +107,4 @@ public class PolylinePrintingTest implements Printable { g.translate(0, offset); g.drawPolyline(xp, yp, xp.length); } - - public PolylinePrintingTest() throws PrinterException { - PrinterJob job = PrinterJob.getPrinterJob(); - PageFormat pf = job.defaultPage(); - Paper p = pf.getPaper(); - p.setImageableArea(0,0,p.getWidth(), p.getHeight()); - pf.setPaper(p); - job.setPrintable(this, pf); - if (job.printDialog()) { - job.print(); - } - } - - public static void main(String[] args) throws PrinterException { - String[] instructions = { - "You must have a printer available to perform this test.", - "OK the print dialog, and collect the printed page.", - "Passing test : Output should show two identical chevrons.", - "Failing test : The line joins will appear different." - }; - Sysout.createDialog(); - Sysout.printInstructions(instructions); - new PolylinePrintingTest(); - } } - -class Sysout { - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog { - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - show(); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - } - -}// TestDialog class - diff --git a/test/jdk/java/awt/print/PrinterJob/SwingUIText.java b/test/jdk/java/awt/print/PrinterJob/SwingUIText.java index 5fcd5e39158..6ef5064fc30 100644 --- a/test/jdk/java/awt/print/PrinterJob/SwingUIText.java +++ b/test/jdk/java/awt/print/PrinterJob/SwingUIText.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,44 +21,70 @@ * questions. */ -/** +/* * @test * @bug 6488219 6560738 7158350 8017469 * @key printer * @summary Test that text printed in Swing UI measures and looks OK. - * @run main/manual=yesno PrintTextTest + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @run main/manual SwingUIText */ -import java.awt.*; -import javax.swing.*; -import java.awt.print.*; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.GridLayout; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterJob; +import javax.swing.JButton; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import jtreg.SkippedException; public class SwingUIText implements Printable { + private static JFrame frame; + private static final String INSTRUCTIONS = """ + This test checks that when a Swing UI is printed, + the text in each component aligns with the component’s length as seen on-screen. + It also ensures the text spacing is reasonably even, though this is subjective. + The comparison should be made with JDK 1.5 GA or JDK 1.6 GA. - static String[] instructions = { - "This tests that when a Swing UI is printed, that the text", - "in each component properly matches the length of the component", - "as seen on-screen, and that the spacing of the text is of", - "reasonable even-ness. This latter part is very subjective and", - "the comparison has to be with JDK1.5 GA, or JDK 1.6 GA", - }; + Steps: + 1. Press the "Print" or "OK" button on the Print dialog. + This will print the content of the "Swing UI Text Printing Test" JFrame. + 2. Compare the printout with the content of the JFrame. + 3. If they match, press Pass; otherwise, press Fail. + """; - static JFrame frame; + public static void main(String args[]) throws Exception { + PrinterJob job = PrinterJob.getPrinterJob(); + if (job.getPrintService() == null) { + throw new SkippedException("Printer not configured or available."); + } - public static void main(String args[]) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - createUI(); - } - }); + PassFailJFrame passFailJFrame = PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(SwingUIText::createTestUI) + .build(); + + job.setPrintable(new SwingUIText()); + if (job.printDialog()) { + job.print(); + } + + passFailJFrame.awaitAndCheck(); } - public static void createUI() { - - Sysout.createDialogWithInstructions(instructions); - + public static JFrame createTestUI() { + frame = new JFrame(); JPanel panel = new JPanel(); - panel.setLayout(new GridLayout(4,1)); + panel.setLayout(new GridLayout(4, 1)); String text = "marvelous suspicious solving"; displayText(panel, text); @@ -89,24 +115,12 @@ public class SwingUIText implements Printable { frame = new JFrame("Swing UI Text Printing Test"); frame.getContentPane().add(panel); frame.pack(); - frame.setVisible(true); - - PrinterJob job = PrinterJob.getPrinterJob(); - PageFormat pf = job.defaultPage(); - job.setPrintable(new SwingUIText(), pf); - if (job.printDialog()) { - try { job.print(); } - catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } + return frame; } - static void displayText(JPanel p, String text) { JPanel panel = new JPanel(); - panel.setLayout(new GridLayout(2,1)); + panel.setLayout(new GridLayout(2, 1)); JPanel row = new JPanel(); Font font = new Font("Dialog", Font.PLAIN, 12); @@ -114,7 +128,7 @@ public class SwingUIText implements Printable { label.setFont(font); row.add(label); - JButton button = new JButton("Print "+text); + JButton button = new JButton("Print " + text); button.setMnemonic('P'); button.setFont(font); row.add(button); @@ -133,132 +147,14 @@ public class SwingUIText implements Printable { p.add(panel); } - public int print(Graphics g, PageFormat pf, int pageIndex) - throws PrinterException { - + public int print(Graphics g, PageFormat pf, int pageIndex) { if (pageIndex >= 1) { return Printable.NO_SUCH_PAGE; } + g.translate((int)pf.getImageableX(), (int)pf.getImageableY()); frame.printAll(g); - return Printable.PAGE_EXISTS; } } - -class Sysout - { - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - - }// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog - { - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 10, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("South", messageText); - - pack(); - - show(); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - } - -}// TestDialog class diff --git a/test/jdk/java/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.ByteArrayOutputStream; 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 class WriteToReleasesCarrier { 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 @@ public class WriteToReleasesCarrier { 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/lang/Double/ToHexString.java b/test/jdk/java/lang/Double/ToHexString.java index 912835b7aeb..c408f74fa63 100644 --- a/test/jdk/java/lang/Double/ToHexString.java +++ b/test/jdk/java/lang/Double/ToHexString.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -174,6 +175,26 @@ public class ToHexString { {"+4.9e-324", "0000000000000001"}, {"-4.9e-324", "8000000000000001"}, + // Test cases for trailing zeros in significand + // These test the removal of trailing zeros in the hexadecimal representation + // The comments indicate the number of trailing zeros removed from the significand + // For "0x1.0p1", there are 13 trailing zeros in the significand, but only 12 are removed + // as we always keep at least one hex digit in the significand + {"0x1.0p1", "4000000000000000"}, // 12 trailing zeros removed (13 total, but only 12 removed) + {"0x1.1p1", "4001000000000000"}, // 12 trailing zeros removed (all zeros after '1') + {"0x1.01p1", "4000100000000000"}, // 11 trailing zeros removed + {"0x1.001p1", "4000010000000000"}, // 10 trailing zeros removed + {"0x1.0001p1", "4000001000000000"}, // 9 trailing zeros removed + {"0x1.00001p1", "4000000100000000"}, // 8 trailing zeros removed + {"0x1.000001p1", "4000000010000000"}, // 7 trailing zeros removed + {"0x1.0000001p1", "4000000001000000"}, // 6 trailing zeros removed + {"0x1.00000001p1", "4000000000100000"}, // 5 trailing zeros removed + {"0x1.000000001p1", "4000000000010000"}, // 4 trailing zeros removed + {"0x1.0000000001p1", "4000000000001000"}, // 3 trailing zeros removed + {"0x1.00000000001p1", "4000000000000100"}, // 2 trailing zeros removed + {"0x1.000000000001p1", "4000000000000010"}, // 1 trailing zero removed (minimum) + {"0x1.0000000000001p1", "4000000000000001"}, // 0 trailing zeros removed (no trailing zeros to remove) + // fdlibm k_sin.c {"+5.00000000000000000000e-01", "3FE0000000000000"}, {"-1.66666666666666324348e-01", "BFC5555555555549"}, diff --git a/test/jdk/java/lang/Math/WorstCaseTests.java b/test/jdk/java/lang/Math/WorstCaseTests.java index a479c6a3444..664958da158 100644 --- a/test/jdk/java/lang/Math/WorstCaseTests.java +++ b/test/jdk/java/lang/Math/WorstCaseTests.java @@ -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 @@ -32,7 +32,7 @@ */ /** - * This test contains two distinct kinds of worst-case inputs: + * This test contains three distinct kinds of worst-case inputs: * * 1) Exact numerical results that are nearly half-way between * representable numbers or very close to a representable @@ -43,6 +43,9 @@ * 2) Worst-case errors as observed empirically across different * implementations that are not correctly rounded. * + * 3) Worst-case values found in the Julia environment, which uses a + * fork of FDLIBM. + * * For the first category, the "Table Maker's Dilemma" results from * Jean-Michel Muller and Vincent Lefèvre, are used. * See https://perso.ens-lyon.fr/jean-michel.muller/TMD.html for original @@ -70,6 +73,8 @@ * Extended, and Quadruple Precision" by Brian Gladman, Vincenzo * Innocente and Paul Zimmermann. * + * For the third category, see the preprint https://arxiv.org/abs/2509.05666 + * * From https://openlibm.org/, "The OpenLibm code derives from the * FreeBSD msun and OpenBSD libm implementations, which in turn derive * from FDLIBM 5.3." Java's StrictMath libraries use the FDLIBM 5.3 @@ -143,6 +148,8 @@ public class WorstCaseTests { // Worst-case observed error for OpenLibm {+0x1.2e8f20cf3cbe7p+8, 0x1.6a2a59cc78bf7p436}, + // Julia worst-case observed error + {-0x1.6251620687bf3p9, 0x0.c980224219398p-1022}, // Other worst-case observed errors {-0x1.49f33ad2c1c58p+9, 0x1.f3ccc815431b5p-953}, {+0x1.fce66609f7428p+5, 0x1.b59724cb0bc4cp91}, @@ -188,7 +195,9 @@ public class WorstCaseTests { {+0x1.DE7CD6751029Ap16, +0x1.76E7E5D7B6EABp+3}, // Worst-case observed error for OpenLibm - {+0x1.48ae5a67204f5p+0, 0x1.ffd10abffc3fep-3}, + {+0x1.48ae5a67204f5p+0, +0x1.ffd10abffc3fep-3}, + // Julia worst-case observed error + {+0x1.14fad2c09e275p0, +0x1.42a13ec2691dbp-4}, // Other worst-case observed errors {+0x1.1211bef8f68e9p+0, +0x1.175caeca67f84p-4}, {+0x1.008000db2e8bep+0, +0x1.ff83959f5cc1fp-10}, @@ -238,6 +247,8 @@ public class WorstCaseTests { // Worst-case observed error for OpenLibm {+0x1.4d84db080b9fdp+21, +0x1.6e21c4ff6aec3p-1}, + // Worst-case observed error for Julia + {+0x1.5a8e729e7934p102, +0x1.6deadddde6752p-1}, // Other worst-case observed errors {-0x1.f8b791cafcdefp+4, -0x1.073ca87470df9p-3 }, {-0x1.0e16eb809a35dp+944, +0x1.b5e361ed01dacp-2}, @@ -285,7 +296,10 @@ public class WorstCaseTests { {+0x1.E264357EA0E29p-1, +0x1.3AA301F6EBB1Dp+0}, // Worst-case observed error for OpenLibm - {-0x1.004d1c5a9400bp-1, -0x1.0c6e322e8a28bp-1}, + {-0x1.004d1c5a9400bp-1, -0x1.0c6e322e8a28bp-1}, + // Julia worst-case observed error + {-0x1.012d405d9408ep-1, -0x1.0d7142df4968fp-1}, + // Other worst-case observed errors {-0x1.0000045b2c904p-3, -0x1.00abe5252746cp-3}, {+0x1.6c042a6378102p-1, +0x1.94eda53f72c5ap-1}, @@ -334,6 +348,8 @@ public class WorstCaseTests { // Worst-case observed error for OpenLibm {-0x1.34e729fd08086p+21, +0x1.6a6a0d6a17f0fp-1}, + // Julia worst-case observed error + {-0x1.4e4cb79b5b5a2p930, 0x1.70f851fbdea52p-1}, // Other worst-case observed errors {-0x1.7120161c92674p+0, +0x1.0741fb7683849p-3}, {-0x1.d19ebc5567dcdp+311, -0x1.b5d2f45f68958p-2}, @@ -374,6 +390,8 @@ public class WorstCaseTests { // Worst-case observed error for OpenLibm {-0x1.0068b067c6feep-1, +0x1.0c335e2f0726fp1}, + // Julia worst-case observed error + {-0x1.0b7c63033d6cp-1, +0x1.0f6c7f5db3b93p1}, // Other worst-case observed errors {+0x1.dffffb3488a4p-1, 0x1.6bf3a4a4f4dcbp-2}, {+0x1.6c05eb219ec46p-1, 0x1.8f4f472807261p-1}, @@ -418,6 +436,9 @@ public class WorstCaseTests { // Worst-case observed error for OpenLibm, outside of 1 ulp error // {0x1.3f9605aaeb51bp+21, -0x1.9678ee5d64934p-1}, // 1.02 + + // Worst-case observed error for Julia, outside of 1 ulp error + // {0x1.e608f1390d9fp293, -0x1.9942a10545924p-1}, // 1.04 }; for(double[] testCase: testCases) { @@ -456,6 +477,8 @@ public class WorstCaseTests { // Worst-case observed error {0x1.62ff6a1682c25p-1, +0x1.3666b15c8756ap-1}, + // Julia worst-case observed error + {0x1.66340e55ce1adp-1, +0x1.388f4792eaa82p-1}, // Other worst-case observed errors {+0x1.f9004c4fef9eap-4, 0x1.f67727f5618f2p-4}, {-0x1.ffff8020d3d1dp-7, -0x1.fff4d5e4886c7p-7}, @@ -554,6 +577,10 @@ public class WorstCaseTests { {+0x1.E07E71BFCF06Fp+5, +0x1.91EC4412C344Fp+85}, {+0x1.54CD1FEA7663Ap+7, +0x1.C90810D354618p+244}, {+0x1.D6479EBA7C971p+8, +0x1.62A88613629B5p+677}, + + // Julia worst-case observed error, 1.9 ulps; + // added to hyperbolics testing in StrictMath. + // {-0x1.633c654fee2bap9, -0x1.fdf25fc26e7cp1023}, }; for(double[] testCase: testCases) { @@ -582,6 +609,10 @@ public class WorstCaseTests { {+0x1.A6031CD5F93BAp-1, +0x1.5BFF041B260FDp+0}, {+0x1.104B648F113A1p+0, +0x1.9EFDCA62B7009p+0}, {+0x1.EA5F2F2E4B0C5p+1, +0x17.10DB0CD0FED5p+0}, + + // Julia worst-case observed error, 1.9 ulps; + // added to hyperbolics testing in StrictMath. + // {-0x1.633c654fee2bap9, 0x1.fdf25fc26e7cp1023}, }; for(double[] testCase: testCases) { 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 @@ import jdk.test.lib.RandomFactory; * @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/StrictMath/CubeRootTests.java b/test/jdk/java/lang/StrictMath/CubeRootTests.java index 474a7760984..6275b8a8b44 100644 --- a/test/jdk/java/lang/StrictMath/CubeRootTests.java +++ b/test/jdk/java/lang/StrictMath/CubeRootTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, 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 @@ -471,6 +471,7 @@ public class CubeRootTests { {0x0.0000ffffffap-1022, 0x1.ffffffcp-347}, {0x0.0000ffffffff8p-1022, 0x1.ffffffffaaaabp-347}, {0x0.0fffffffffffbp-1022, 0x1.fffffffffffcbp-343}, + {-0x1.0edb6c7fa500fp-531, -0x1.04dc0b189b6cep-177}, // next down from Julia value // Empirical worst-case points in other libraries with // larger worst-case errors than FDLIBM diff --git a/test/jdk/java/lang/StrictMath/HyperbolicTests.java b/test/jdk/java/lang/StrictMath/HyperbolicTests.java index 68e6a27124c..1f570ce9efd 100644 --- a/test/jdk/java/lang/StrictMath/HyperbolicTests.java +++ b/test/jdk/java/lang/StrictMath/HyperbolicTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, 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 @@ -340,6 +340,10 @@ public class HyperbolicTests { {0x1.fffffffffff68p4, 0x1.1f43fcc4b5b83p45}, {0x1.fffffffffffd4p4, 0x1.1f43fcc4b6316p45}, {0x1.0p5, 0x1.1f43fcc4b662cp45}, + + // Julia worst-case input + {-0x1.633c654fee2bap9, -0x1.fdf25fc26e7cp1023}, + // Empirical worst-case points in other libraries with // larger worst-case errors than FDLIBM {-0x1.633c654fee2bap+9, -0x1.fdf25fc26e7cp1023}, @@ -386,6 +390,10 @@ public class HyperbolicTests { {0x1.0p4, 0x1.0f2ebd0a8005cp22}, {0x1.fffffffffffd4p4, 0x1.1f43fcc4b6316p45}, {0x1.0p5, 0x1.1f43fcc4b662cp45}, + + // Julia worst-case input + {-0x1.633c654fee2bap9, 0x1.fdf25fc26e7cp1023}, + // Empirical worst-case points in other libraries with // larger worst-case errors than FDLIBM {-0x1.633c654fee2bap+9, 0x1.fdf25fc26e7cp1023}, @@ -462,6 +470,10 @@ public class HyperbolicTests { {0x1.fffffffffffe1p0, 0x1.ed9505e1bc3cfp-1}, {0x1.ffffffffffed8p1, 0x1.ffa81708a0b4p-1}, {0x1.fffffffffff92p1, 0x1.ffa81708a0b41p-1}, + + // Julia worst-case input + {0x1.0108b83c4bbc8p-1, 0x1.dad53a45da5b0p-2}, + // Empirical worst-case points in other libraries with // larger worst-case errors than FDLIBM {-0x1.c41e527b70f43p-3, -0x1.bcea047cc736cp-3}, diff --git a/test/jdk/java/lang/StrictMath/Log10Tests.java b/test/jdk/java/lang/StrictMath/Log10Tests.java index 6cd8427c438..537d13d9cb4 100644 --- a/test/jdk/java/lang/StrictMath/Log10Tests.java +++ b/test/jdk/java/lang/StrictMath/Log10Tests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, 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 @@ -722,6 +722,9 @@ public class Log10Tests { {0x1.3fffffffffebdp25, 0x1.e7d9a8edb47a2p2}, {0x1.4p25, 0x1.e7d9a8edb47bfp2}, + // Julia worst-case + {0x1.10f12374877e3p0, 0x1.c7f8d2e32f5e9p-6}, + // Empirical worst-case points in other libraries with // larger worst-case errors than FDLIBM {0x1.de02157073b31p-1, -0x1.e8cfabf160ec6p-6}, diff --git a/test/jdk/java/lang/StrictMath/Log1pTests.java b/test/jdk/java/lang/StrictMath/Log1pTests.java index b8a8592812f..c31dc946136 100644 --- a/test/jdk/java/lang/StrictMath/Log1pTests.java +++ b/test/jdk/java/lang/StrictMath/Log1pTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, 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 @@ -209,6 +209,7 @@ public class Log1pTests { {0x1.7688bb5394bd3p325, 0x1.c34e8276daa48p7}, {0x1.d42aea2878b45p328, 0x1.c7e96ee5c7f87p7}, {0x1.249ad2594989p332, 0x1.cc845b54b54a6p7}, + {0x1.300240b87b096p-4, 0x1.25417bd05ba95p-4}, // nextUp from Julia value // Empirical worst-case points in other libraries with // larger worst-case errors than FDLIBM diff --git a/test/jdk/java/lang/StrictMath/TrigTests.java b/test/jdk/java/lang/StrictMath/TrigTests.java index c4f7dab547f..e758762a97d 100644 --- a/test/jdk/java/lang/StrictMath/TrigTests.java +++ b/test/jdk/java/lang/StrictMath/TrigTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, 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 @@ -271,6 +271,10 @@ public class TrigTests { {0x1.000000000001cp300, -0x1.b30fc9f73002cp-1}, {0x1.0000000000013p500, -0x1.c4e46751be12cp-1}, {0x1.00000000000ep1023, -0x1.d52c4ec04f108p-2}, + + // Julia worst-case input + {0x1.e608f1390d9fp293, -0x1.9942a10545925p-1}, + // Empirical worst-case points in other libraries with // larger worst-case errors than FDLIBM {+0x1.371a47b7e4eb2p+11, 0x1.9ded57c9ff46ap-1}, diff --git a/test/jdk/java/lang/Thread/JoinWithDuration.java b/test/jdk/java/lang/Thread/JoinWithDuration.java index 0eae0daeb78..5eebacf2107 100644 --- a/test/jdk/java/lang/Thread/JoinWithDuration.java +++ b/test/jdk/java/lang/Thread/JoinWithDuration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, 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 @@ -100,7 +100,7 @@ class JoinWithDuration { } /** - * Test invoking join with interrupt status set. + * Test invoking join with interrupted status set. */ @Test void testJoinWithInterruptStatusSet() throws Exception { @@ -141,7 +141,7 @@ class JoinWithDuration { thread.join(Duration.ofMinutes(1)); fail(); } catch (InterruptedException e) { - // interrupt status should be cleared + // interrupted status should be cleared assertFalse(thread.isInterrupted()); } finally { LockSupport.unpark(thread); 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/SleepWithDuration.java b/test/jdk/java/lang/Thread/SleepWithDuration.java index 4db64957f64..00704cdcb7c 100644 --- a/test/jdk/java/lang/Thread/SleepWithDuration.java +++ b/test/jdk/java/lang/Thread/SleepWithDuration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, 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 @@ -53,7 +53,7 @@ class SleepWithDuration { } /** - * Test Thread.sleep with interrupt status set. + * Test Thread.sleep with interrupted status set. */ @Test void testSleepWithInterruptStatusSet() throws Exception { @@ -94,7 +94,7 @@ class SleepWithDuration { Thread.sleep(Duration.ofSeconds(60)); fail(); } catch (InterruptedException e) { - // interrupt status should be cleared + // interrupted status should be cleared assertFalse(Thread.interrupted()); } finally { wakerThread.join(); 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 @@ import org.junit.jupiter.params.provider.MethodSource; * @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/Thread/virtual/CustomScheduler.java b/test/jdk/java/lang/Thread/virtual/CustomScheduler.java index 4289add0a3a..1b0a6b28e96 100644 --- a/test/jdk/java/lang/Thread/virtual/CustomScheduler.java +++ b/test/jdk/java/lang/Thread/virtual/CustomScheduler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, 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 @@ -204,7 +204,7 @@ class CustomScheduler { } /** - * Test running task with the carrier interrupt status set. + * Test running task with the carrier's interrupted status set. */ @Test void testRunWithInterruptSet() throws Exception { diff --git a/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java b/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java index 47089d9d6df..5aecc659a31 100644 --- a/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java +++ b/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java @@ -419,7 +419,7 @@ class MonitorWaitNotify { } /** - * Testing invoking Object.wait with interrupt status set. + * Testing invoking Object.wait with interrupted status set. */ @ParameterizedTest @ValueSource(ints = { 0, 30000, Integer.MAX_VALUE }) diff --git a/test/jdk/java/lang/Thread/virtual/Parking.java b/test/jdk/java/lang/Thread/virtual/Parking.java index c337f36e7c2..4ee320ca99f 100644 --- a/test/jdk/java/lang/Thread/virtual/Parking.java +++ b/test/jdk/java/lang/Thread/virtual/Parking.java @@ -169,7 +169,7 @@ class Parking { } /** - * Park with interrupt status set. + * Park with interrupted status set. */ @Test void testPark8() throws Exception { @@ -196,7 +196,7 @@ class Parking { } /** - * Park while holding monitor and with interrupt status set. + * Park while holding monitor and with interrupted status set. */ @Test void testPark10() throws Exception { @@ -318,7 +318,7 @@ class Parking { } /** - * Park with parkNanos and interrupt status set. + * Park with parkNanos and interrupted status set. */ @Test void testParkNanos8() throws Exception { @@ -345,7 +345,7 @@ class Parking { } /** - * Park with parkNanos while holding monitor and with interrupt status set. + * Park with parkNanos while holding monitor and with interrupted status set. */ @Test void testParkNanos10() throws Exception { diff --git a/test/jdk/java/lang/Thread/virtual/ThreadAPI.java b/test/jdk/java/lang/Thread/virtual/ThreadAPI.java index 397514c3852..1089081d186 100644 --- a/test/jdk/java/lang/Thread/virtual/ThreadAPI.java +++ b/test/jdk/java/lang/Thread/virtual/ThreadAPI.java @@ -403,7 +403,7 @@ class ThreadAPI { } /** - * Test platform thread invoking Thread.join with interrupt status set. + * Test platform thread invoking Thread.join with interrupted status set. */ @Test void testJoin15() throws Exception { @@ -419,7 +419,7 @@ class ThreadAPI { } /** - * Test virtual thread invoking Thread.join with interrupt status set. + * Test virtual thread invoking Thread.join with interrupted status set. */ @Test void testJoin16() throws Exception { @@ -427,7 +427,7 @@ class ThreadAPI { } /** - * Test platform thread invoking timed-Thread.join with interrupt status set. + * Test platform thread invoking timed-Thread.join with interrupted status set. */ @Test void testJoin17() throws Exception { @@ -443,7 +443,7 @@ class ThreadAPI { } /** - * Test virtual thread invoking timed-Thread.join with interrupt status set. + * Test virtual thread invoking timed-Thread.join with interrupted status set. */ @Test void testJoin18() throws Exception { @@ -451,7 +451,7 @@ class ThreadAPI { } /** - * Test platform thread invoking timed-Thread.join with interrupt status set. + * Test platform thread invoking timed-Thread.join with interrupted status set. */ @Test void testJoin19() throws Exception { @@ -468,7 +468,7 @@ class ThreadAPI { } /** - * Test virtual thread invoking timed-Thread.join with interrupt status set. + * Test virtual thread invoking timed-Thread.join with interrupted status set. */ @Test void testJoin20() throws Exception { @@ -593,7 +593,7 @@ class ThreadAPI { } /** - * Test virtual thread with interrupt status set calling Thread.join to wait + * Test virtual thread with interrupted status set calling Thread.join to wait * for platform thread to terminate. */ @Test @@ -732,7 +732,7 @@ class ThreadAPI { assertFalse(me.isInterrupted()); me.interrupt(); assertTrue(me.isInterrupted()); - Thread.interrupted(); // clear interrupt status + Thread.interrupted(); // clear interrupted status assertFalse(me.isInterrupted()); me.interrupt(); }); @@ -760,7 +760,7 @@ class ThreadAPI { } /** - * Test termination with interrupt status set. + * Test termination with interrupted status set. */ @Test void testInterrupt4() throws Exception { @@ -805,7 +805,7 @@ class ThreadAPI { Thread.sleep(60*1000); fail("sleep not interrupted"); } catch (InterruptedException e) { - // interrupt status should be reset + // interrupted status should be reset assertFalse(Thread.interrupted()); } } catch (Exception e) { @@ -839,7 +839,7 @@ class ThreadAPI { } /** - * Test trying to park with interrupt status set. + * Test trying to park with interrupted status set. */ @Test void testInterrupt8() throws Exception { @@ -852,7 +852,7 @@ class ThreadAPI { } /** - * Test trying to wait with interrupt status set. + * Test trying to wait with interrupted status set. */ @Test void testInterrupt9() throws Exception { @@ -871,7 +871,7 @@ class ThreadAPI { } /** - * Test trying to block with interrupt status set. + * Test trying to block with interrupted status set. */ @Test void testInterrupt10() throws Exception { @@ -1237,7 +1237,7 @@ class ThreadAPI { } /** - * Test Thread.sleep with interrupt status set. + * Test Thread.sleep with interrupted status set. */ @ParameterizedTest @MethodSource("sleepers") @@ -1256,7 +1256,7 @@ class ThreadAPI { } /** - * Test Thread.sleep with interrupt status set and a negative duration. + * Test Thread.sleep with interrupted status set and a negative duration. */ @Test void testSleep4() throws Exception { @@ -1292,7 +1292,7 @@ class ThreadAPI { sleeper.run(); fail("sleep was not interrupted"); } catch (InterruptedException e) { - // interrupt status should be cleared + // interrupted status should be cleared assertFalse(t.isInterrupted()); } }); @@ -1356,7 +1356,7 @@ class ThreadAPI { } /** - * Test Thread.sleep when pinned and with interrupt status set. + * Test Thread.sleep when pinned and with interrupted status set. */ @Test void testSleep9() throws Exception { @@ -1389,7 +1389,7 @@ class ThreadAPI { }); fail("sleep not interrupted"); } catch (InterruptedException e) { - // interrupt status should be cleared + // interrupted status should be cleared assertFalse(t.isInterrupted()); } }); 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/math/BigDecimal/SquareRootTests.java b/test/jdk/java/math/BigDecimal/SquareRootTests.java index 7d9fce4caa0..1a685c8483a 100644 --- a/test/jdk/java/math/BigDecimal/SquareRootTests.java +++ b/test/jdk/java/math/BigDecimal/SquareRootTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, 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 @@ -348,7 +348,7 @@ public class SquareRootTests { for (int scale = 0; scale <= 4; scale++) { BigDecimal scaledSquare = square.setScale(scale, RoundingMode.UNNECESSARY); - int expectedScale = scale/2; + int expectedScale = Math.ceilDiv(scale, 2); for (int precision = 0; precision <= 5; precision++) { for (RoundingMode rm : RoundingMode.values()) { MathContext mc = new MathContext(precision, rm); @@ -582,7 +582,8 @@ public class SquareRootTests { // The code below favors relative simplicity over checking // for special cases that could run faster. - int preferredScale = bd.scale()/2; + int preferredScale = Math.ceilDiv(bd.scale(), 2); + BigDecimal zeroWithFinalPreferredScale = BigDecimal.valueOf(0L, preferredScale); diff --git a/test/jdk/java/math/BigInteger/BigIntegerTest.java b/test/jdk/java/math/BigInteger/BigIntegerTest.java index 0e847f5ff6c..84aa8d15676 100644 --- a/test/jdk/java/math/BigInteger/BigIntegerTest.java +++ b/test/jdk/java/math/BigInteger/BigIntegerTest.java @@ -24,7 +24,7 @@ /* * @test * @bug 4181191 4161971 4227146 4194389 4823171 4624738 4812225 4837946 4026465 - * 8074460 8078672 8032027 8229845 8077587 8367365 + * 8074460 8078672 8032027 8229845 8077587 8367365 8370628 * @summary tests methods in BigInteger (use -Dseed=X to set PRNG seed) * @key randomness * @library /test/lib @@ -232,7 +232,7 @@ public class BigIntegerTest { failCount1++; failCount1 += checkResult(x.signum() < 0 && power % 2 == 0 ? x.negate() : x, - y.nthRoot(power), "BigInteger.pow() inconsistent with BigInteger.nthRoot()"); + y.rootn(power), "BigInteger.pow() inconsistent with BigInteger.rootn()"); } report("pow for " + order + " bits", failCount1); } @@ -414,7 +414,7 @@ public class BigIntegerTest { BigInteger.valueOf(x)).collect(Collectors.summingInt(g))); } - private static void nthRootSmall() { + private static void rootnSmall() { int failCount = 0; // A non-positive degree should cause an exception. @@ -422,10 +422,10 @@ public class BigIntegerTest { BigInteger x = BigInteger.ONE; BigInteger s; try { - s = x.nthRoot(n); - // If nthRoot() does not throw an exception that is a failure. + s = x.rootn(n); + // If rootn() does not throw an exception that is a failure. failCount++; - printErr("nthRoot() of non-positive degree did not throw an exception"); + printErr("rootn() of non-positive degree did not throw an exception"); } catch (ArithmeticException expected) { // Not a failure } @@ -434,41 +434,41 @@ public class BigIntegerTest { n = 4; x = BigInteger.valueOf(-1); try { - s = x.nthRoot(n); - // If nthRoot() does not throw an exception that is a failure. + s = x.rootn(n); + // If rootn() does not throw an exception that is a failure. failCount++; - printErr("nthRoot() of negative number and even degree did not throw an exception"); + printErr("rootn() of negative number and even degree did not throw an exception"); } catch (ArithmeticException expected) { // Not a failure } - // A negative value with odd degree should return -nthRoot(-x, n) + // A negative value with odd degree should return -rootn(-x, n) n = 3; x = BigInteger.valueOf(-8); - failCount += checkResult(x.negate().nthRoot(n).negate(), x.nthRoot(n), - "nthRoot(" + x + ", " + n + ") != -nthRoot(" + x.negate() + ", " + n + ")"); + failCount += checkResult(x.negate().rootn(n).negate(), x.rootn(n), + "rootn(" + x + ", " + n + ") != -rootn(" + x.negate() + ", " + n + ")"); // A zero value should return BigInteger.ZERO. - failCount += checkResult(BigInteger.ZERO, BigInteger.ZERO.nthRoot(n), - "nthRoot(0, " + n + ") != 0"); + failCount += checkResult(BigInteger.ZERO, BigInteger.ZERO.rootn(n), + "rootn(0, " + n + ") != 0"); // A one degree should return x. x = BigInteger.TWO; - failCount += checkResult(x, x.nthRoot(1), "nthRoot(" + x + ", 1) != " + x); + failCount += checkResult(x, x.rootn(1), "rootn(" + x + ", 1) != " + x); n = 8; // 1 <= value < 2^n should return BigInteger.ONE. int end = 1 << n; for (int i = 1; i < end; i++) { failCount += checkResult(BigInteger.ONE, - BigInteger.valueOf(i).nthRoot(n), "nthRoot(" + i + ", " + n + ") != 1"); + BigInteger.valueOf(i).rootn(n), "rootn(" + i + ", " + n + ") != 1"); } - report("nthRootSmall", failCount); + report("rootnSmall", failCount); } - public static void nthRoot() { - nthRootSmall(); + public static void rootn() { + rootnSmall(); ToIntFunction f = (x) -> { int n = random.nextInt(x.bitLength()) + 2; @@ -476,28 +476,28 @@ public class BigIntegerTest { // nth root of x^n -> x BigInteger xN = x.pow(n); - failCount += checkResult(x, xN.nthRoot(n), "nthRoot() x^n -> x"); + failCount += checkResult(x, xN.rootn(n), "rootn() x^n -> x"); // nth root of x^n + 1 -> x BigInteger xNup = xN.add(BigInteger.ONE); - failCount += checkResult(x, xNup.nthRoot(n), "nthRoot() x^n + 1 -> x"); + failCount += checkResult(x, xNup.rootn(n), "rootn() x^n + 1 -> x"); // nth root of (x + 1)^n - 1 -> x BigInteger up = x.add(BigInteger.ONE).pow(n).subtract(BigInteger.ONE); - failCount += checkResult(x, up.nthRoot(n), "nthRoot() (x + 1)^n - 1 -> x"); + failCount += checkResult(x, up.rootn(n), "rootn() (x + 1)^n - 1 -> x"); - // nthRoot(x, n)^n <= x - BigInteger r = x.nthRoot(n); + // rootn(x, n)^n <= x + BigInteger r = x.rootn(n); if (r.pow(n).compareTo(x) > 0) { failCount++; - printErr("nthRoot(x, n)^n > x for x = " + x + ", n = " + n); + printErr("rootn(x, n)^n > x for x = " + x + ", n = " + n); } - // (nthRoot(x, n) + 1)^n > x + // (rootn(x, n) + 1)^n > x if (r.add(BigInteger.ONE).pow(n).compareTo(x) <= 0) { failCount++; - printErr("(nthRoot(x, n) + 1)^n <= x for x = " + x + ", n = " + n); + printErr("(rootn(x, n) + 1)^n <= x for x = " + x + ", n = " + n); } return failCount; @@ -513,52 +513,52 @@ public class BigIntegerTest { } sb.add((new BigDecimal(Double.MAX_VALUE)).toBigInteger()); sb.add((new BigDecimal(Double.MAX_VALUE)).toBigInteger().add(BigInteger.ONE)); - report("nthRoot for 2^N, 2^N - 1 and 2^N + 1, 1 <= N <= " + maxExponent, + report("rootn for 2^N, 2^N - 1 and 2^N + 1, 1 <= N <= " + maxExponent, sb.build().collect(Collectors.summingInt(f))); IntStream ints = random.ints(SIZE, 2, Integer.MAX_VALUE); - report("nthRoot for int", ints.mapToObj(x -> + report("rootn for int", ints.mapToObj(x -> BigInteger.valueOf(x)).collect(Collectors.summingInt(f))); LongStream longs = random.longs(SIZE, Integer.MAX_VALUE + 1L, Long.MAX_VALUE); - report("nthRoot for long", longs.mapToObj(x -> + report("rootn for long", longs.mapToObj(x -> BigInteger.valueOf(x)).collect(Collectors.summingInt(f))); DoubleStream doubles = random.doubles(SIZE, 0x1p63, Math.scalb(1.0, maxExponent)); - report("nthRoot for double", doubles.mapToObj(x -> + report("rootn for double", doubles.mapToObj(x -> BigDecimal.valueOf(x).toBigInteger()).collect(Collectors.summingInt(f))); } - public static void nthRootAndRemainder() { + public static void rootnAndRemainder() { ToIntFunction g = (x) -> { int failCount = 0; int n = random.nextInt(x.bitLength()) + 2; BigInteger xN = x.pow(n); // nth root of x^n -> x - BigInteger[] actual = xN.nthRootAndRemainder(n); - failCount += checkResult(x, actual[0], "nthRootAndRemainder()[0]"); - failCount += checkResult(BigInteger.ZERO, actual[1], "nthRootAndRemainder()[1]"); + BigInteger[] actual = xN.rootnAndRemainder(n); + failCount += checkResult(x, actual[0], "rootnAndRemainder()[0]"); + failCount += checkResult(BigInteger.ZERO, actual[1], "rootnAndRemainder()[1]"); // nth root of x^n + 1 -> x BigInteger xNup = xN.add(BigInteger.ONE); - actual = xNup.nthRootAndRemainder(n); - failCount += checkResult(x, actual[0], "nthRootAndRemainder()[0]"); - failCount += checkResult(BigInteger.ONE, actual[1], "nthRootAndRemainder()[1]"); + actual = xNup.rootnAndRemainder(n); + failCount += checkResult(x, actual[0], "rootnAndRemainder()[0]"); + failCount += checkResult(BigInteger.ONE, actual[1], "rootnAndRemainder()[1]"); // nth root of (x + 1)^n - 1 -> x BigInteger up = x.add(BigInteger.ONE).pow(n).subtract(BigInteger.ONE); - actual = up.nthRootAndRemainder(n); - failCount += checkResult(x, actual[0], "nthRootAndRemainder()[0]"); + actual = up.rootnAndRemainder(n); + failCount += checkResult(x, actual[0], "rootnAndRemainder()[0]"); BigInteger r = up.subtract(xN); - failCount += checkResult(r, actual[1], "nthRootAndRemainder()[1]"); + failCount += checkResult(r, actual[1], "rootnAndRemainder()[1]"); return failCount; }; IntStream bits = random.ints(SIZE, 3, Short.MAX_VALUE); - report("nthRootAndRemainder", bits.mapToObj(x -> + report("rootnAndRemainder", bits.mapToObj(x -> BigInteger.valueOf(x)).collect(Collectors.summingInt(g))); } @@ -1472,8 +1472,8 @@ public class BigIntegerTest { squareRoot(); squareRootAndRemainder(); - nthRoot(); - nthRootAndRemainder(); + rootn(); + rootnAndRemainder(); } if (failure) diff --git a/test/jdk/java/net/Socket/Timeouts.java b/test/jdk/java/net/Socket/Timeouts.java index f8fcfb86d0f..17f3d9db809 100644 --- a/test/jdk/java/net/Socket/Timeouts.java +++ b/test/jdk/java/net/Socket/Timeouts.java @@ -442,7 +442,7 @@ class Timeouts { } /** - * Test timed accept with the thread interrupt status set. + * Test timed accept with the thread interrupted status set. */ @Test void testTimedAccept8() throws IOException { @@ -461,7 +461,7 @@ class Timeouts { checkDuration(startMillis, timeout-100, timeout+20_000); assertTrue(Thread.currentThread().isInterrupted()); } finally { - Thread.interrupted(); // clear interrupt status + Thread.interrupted(); // clear interrupted status } } } @@ -488,7 +488,7 @@ class Timeouts { assertTrue(Thread.currentThread().isInterrupted()); } finally { interrupter.cancel(true); - Thread.interrupted(); // clear interrupt status + Thread.interrupted(); // clear interrupted status } } } 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/CancelRequestTest.java b/test/jdk/java/net/httpclient/CancelRequestTest.java index bfc1eff9cf9..23e4a042179 100644 --- a/test/jdk/java/net/httpclient/CancelRequestTest.java +++ b/test/jdk/java/net/httpclient/CancelRequestTest.java @@ -607,7 +607,7 @@ public class CancelRequestTest implements HttpServerAdapters { } else if (failed instanceof IOException) { out.println(uriStr + ": got IOException: " + failed); // that could be OK if the main thread was interrupted - // from the main thread: the interrupt status could have + // from the main thread: the interrupted status could have // been caught by writing to the socket from the main // thread. if (interruptingThread.isDone() && interruptingThread.get() == main) { diff --git a/test/jdk/java/net/httpclient/HttpClientSendAsyncExceptionTest.java b/test/jdk/java/net/httpclient/HttpClientSendAsyncExceptionTest.java new file mode 100644 index 00000000000..f4a1f3cca82 --- /dev/null +++ b/test/jdk/java/net/httpclient/HttpClientSendAsyncExceptionTest.java @@ -0,0 +1,263 @@ +/* + * 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 org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import javax.net.ssl.SSLParameters; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpHeaders; +import java.net.http.HttpOption; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.UnsupportedProtocolVersionException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; + +import static java.net.http.HttpClient.Builder.NO_PROXY; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * @test + * @bug 8368249 + * @summary Verifies exceptions thrown by `HttpClient::sendAsync` + * @library /test/jdk/java/net/httpclient/lib /test/lib + * @run junit HttpClientSendAsyncExceptionTest + */ + +class HttpClientSendAsyncExceptionTest { + + @Test + void testClosedClient() { + var client = HttpClient.newHttpClient(); + client.close(); + var request = HttpRequest.newBuilder(URI.create("https://example.com")).GET().build(); + var responseBodyHandler = HttpResponse.BodyHandlers.discarding(); + var responseFuture = client.sendAsync(request, responseBodyHandler); + var exception = assertThrows(ExecutionException.class, responseFuture::get); + var cause = assertThrowableInstanceOf(IOException.class, exception.getCause()); + assertContains(cause.getMessage(), "closed"); + } + + @Test + void testH3IncompatClient() { + SSLParameters h3IncompatSslParameters = new SSLParameters(new String[0], new String[]{"foo"}); + try (var h3IncompatClient = HttpClient.newBuilder() + // Provide `SSLParameters` incompatible with QUIC's TLS requirements to disarm the HTTP/3 support + .sslParameters(h3IncompatSslParameters) + .build()) { + var h3Request = HttpRequest.newBuilder(URI.create("https://example.com")) + .GET() + .version(HttpClient.Version.HTTP_3) + .setOption(HttpOption.H3_DISCOVERY, HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY) + .build(); + var responseBodyHandler = HttpResponse.BodyHandlers.discarding(); + var responseFuture = h3IncompatClient.sendAsync(h3Request, responseBodyHandler); + var exception = assertThrows(ExecutionException.class, responseFuture::get); + var cause = assertThrowableInstanceOf(UnsupportedProtocolVersionException.class, exception.getCause()); + assertEquals("HTTP3 is not supported", cause.getMessage()); + } + } + + @Test + void testConnectMethod() { + try (var client = HttpClient.newHttpClient()) { + // The default `HttpRequest` builder does not allow `CONNECT`. + // Hence, we create our custom `HttpRequest` instance: + var connectRequest = new HttpRequest() { + + @Override + public Optional bodyPublisher() { + return Optional.empty(); + } + + @Override + public String method() { + return "CONNECT"; + } + + @Override + public Optional timeout() { + return Optional.empty(); + } + + @Override + public boolean expectContinue() { + return false; + } + + @Override + public URI uri() { + return URI.create("https://example.com"); + } + + @Override + public Optional version() { + return Optional.empty(); + } + + @Override + public HttpHeaders headers() { + return HttpHeaders.of(Collections.emptyMap(), (_, _) -> true); + } + + }; + var responseBodyHandler = HttpResponse.BodyHandlers.discarding(); + var exception = assertThrows( + IllegalArgumentException.class, + () -> client.sendAsync(connectRequest, responseBodyHandler)); + assertContains(exception.getMessage(), "Unsupported method CONNECT"); + } + } + + static List exceptionTestCases() { + + // `RuntimeException` + List testCases = new ArrayList<>(); + var runtimeException = new RuntimeException(); + testCases.add(new ExceptionTestCase( + "RuntimeException", + _ -> { throw runtimeException; }, + exception -> { + assertThrowableInstanceOf(IOException.class, exception); + assertThrowableSame(runtimeException, exception.getCause()); + })); + + // `Error` + var error = new Error(); + testCases.add(new ExceptionTestCase( + "Error", + _ -> { throw error; }, + exception -> assertThrowableSame(error, exception))); + + // `CancellationException` + var cancellationException = new CancellationException(); + testCases.add(new ExceptionTestCase( + "CancellationException", + _ -> { throw cancellationException; }, + exception -> assertThrowableSame(cancellationException, exception))); + + // `IOException` (needs sneaky throw) + var ioException = new IOException(); + testCases.add(new ExceptionTestCase( + "IOException", + _ -> { sneakyThrow(ioException); throw new AssertionError(); }, + exception -> assertThrowableSame(ioException, exception))); + + // `UncheckedIOException` + var uncheckedIOException = new UncheckedIOException(ioException); + testCases.add(new ExceptionTestCase( + "UncheckedIOException(IOException)", + _ -> { throw uncheckedIOException; }, + exception -> assertThrowableSame(uncheckedIOException, exception.getCause()))); + + return testCases; + + } + + private static T assertThrowableInstanceOf(Class expectedClass, Throwable actual) { + if (!expectedClass.isInstance(actual)) { + var message = "Was expecting `%s`".formatted(expectedClass.getCanonicalName()); + throw new AssertionError(message, actual); + } + return expectedClass.cast(actual); + } + + private static void assertThrowableSame(Throwable expected, Throwable actual) { + if (expected != actual) { + var message = "Was expecting `%s`".formatted(expected.getClass().getCanonicalName()); + throw new AssertionError(message, actual); + } + } + + private record ExceptionTestCase( + String description, + HttpResponse.BodyHandler throwingResponseBodyHandler, + Consumer exceptionVerifier) { + + @Override + public String toString() { + return description; + } + + } + + @SuppressWarnings("unchecked") + private static void sneakyThrow(Throwable throwable) throws T { + throw (T) throwable; + } + + @ParameterizedTest + @MethodSource("exceptionTestCases") + void testIOExceptionWrap(ExceptionTestCase testCase, TestInfo testInfo) throws Exception { + var version = HttpClient.Version.HTTP_1_1; + try (var server = HttpServerAdapters.HttpTestServer.create(version); + var client = HttpServerAdapters.createClientBuilderFor(version).proxy(NO_PROXY).build()) { + + // Configure the server to respond with 200 containing a single byte + var serverHandlerPath = "/%s/%s/".formatted( + testInfo.getTestClass().map(Class::getSimpleName).orElse("unknown-class"), + testInfo.getTestMethod().map(Method::getName).orElse("unknown-method")); + HttpServerAdapters.HttpTestHandler serverHandler = exchange -> { + try (exchange) { + exchange.sendResponseHeaders(200, 1); + exchange.getResponseBody().write(new byte[]{0}); + } + }; + server.addHandler(serverHandler, serverHandlerPath); + server.start(); + + // Verify the execution failure + var requestUri = URI.create("http://" + server.serverAuthority() + serverHandlerPath); + var request = HttpRequest.newBuilder(requestUri).version(version).build(); + // We need to make `sendAsync()` execution fail. + // There are several ways to achieve this. + // We choose to use a throwing response handler. + var responseFuture = client.sendAsync(request, testCase.throwingResponseBodyHandler); + var exception = assertThrows(ExecutionException.class, responseFuture::get); + testCase.exceptionVerifier.accept(exception.getCause()); + + } + + } + + private static void assertContains(String target, String expected) { + assertTrue(target.contains(expected), "does not contain `" + expected + "`: " + target); + } + +} 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 @@ import static org.junit.jupiter.api.Assertions.assertThrows; * @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 @@ public class OfByteArrayTest { */ 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 @@ public class OfByteArrayTest { 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/net/httpclient/http3/H3SimpleGet.java b/test/jdk/java/net/httpclient/http3/H3SimpleGet.java index bccd77e7e1d..e75ad04263a 100644 --- a/test/jdk/java/net/httpclient/http3/H3SimpleGet.java +++ b/test/jdk/java/net/httpclient/http3/H3SimpleGet.java @@ -26,6 +26,7 @@ * @bug 8087112 * @requires os.family != "windows" | ( os.name != "Windows 10" & os.name != "Windows Server 2016" * & os.name != "Windows Server 2019" ) + * @requires os.family != "aix" * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestUtil * jdk.httpclient.test.lib.http2.Http2TestServer @@ -45,6 +46,29 @@ * H3SimpleGet */ +/* + * @test id=with-continuations-aix + * @bug 8087112 + * @requires os.family == "aix" + * @library /test/lib /test/jdk/java/net/httpclient/lib + * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestUtil + * jdk.httpclient.test.lib.http2.Http2TestServer + * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * H3SimpleGet + * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * -Djdk.httpclient.retryOnStreamlimit=20 + * -Djdk.httpclient.redirects.retrylimit=21 + * -Dsimpleget.repeat=1 -Dsimpleget.chunks=1 -Dsimpleget.requests=1000 + * H3SimpleGet + * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * -Dsimpleget.requests=150 + * -Dsimpleget.chunks=16384 + * -Djdk.httpclient.retryOnStreamlimit=5 + * -Djdk.httpclient.redirects.retrylimit=6 + * -Djdk.httpclient.quic.defaultMTU=8192 + * H3SimpleGet + */ + /* * @test id=without-continuation * @bug 8087112 @@ -75,6 +99,7 @@ /* * @test id=useNioSelector * @bug 8087112 + * @requires os.family != "aix" * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestUtil * jdk.httpclient.test.lib.http2.Http2TestServer @@ -97,6 +122,32 @@ * H3SimpleGet */ +/* + * @test id=useNioSelector-aix + * @bug 8087112 + * @requires os.family == "aix" + * @library /test/lib /test/jdk/java/net/httpclient/lib + * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestUtil + * jdk.httpclient.test.lib.http2.Http2TestServer + * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * -Djdk.internal.httpclient.quic.useNioSelector=true + * H3SimpleGet + * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * -Djdk.internal.httpclient.quic.useNioSelector=true + * -Djdk.httpclient.retryOnStreamlimit=20 + * -Djdk.httpclient.redirects.retrylimit=21 + * -Dsimpleget.repeat=1 -Dsimpleget.chunks=1 -Dsimpleget.requests=1000 + * H3SimpleGet + * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * -Djdk.internal.httpclient.quic.useNioSelector=true + * -Dsimpleget.requests=150 + * -Dsimpleget.chunks=16384 + * -Djdk.httpclient.retryOnStreamlimit=5 + * -Djdk.httpclient.redirects.retrylimit=6 + * -Djdk.httpclient.quic.defaultMTU=8192 + * H3SimpleGet + */ + // Interesting additional settings for debugging and manual testing: // ----------------------------------------------------------------- // -Djdk.httpclient.HttpClient.log=requests,errors,quic:retransmit:control,http3 diff --git a/test/jdk/java/nio/channels/Channels/SocketChannelStreams.java b/test/jdk/java/nio/channels/Channels/SocketChannelStreams.java index 57142fb03d7..72c07392d7c 100644 --- a/test/jdk/java/nio/channels/Channels/SocketChannelStreams.java +++ b/test/jdk/java/nio/channels/Channels/SocketChannelStreams.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 @@ -130,7 +130,7 @@ public class SocketChannelStreams { } /** - * Test interrupt status set before read. + * Test interrupted status set before read. */ public void testRead7() throws Exception { withConnection((sc, peer) -> { @@ -220,7 +220,7 @@ public class SocketChannelStreams { } /** - * Test interrupt status set before write. + * Test interrupted status set before write. */ public void testWrite4() throws Exception { withConnection((sc, peer) -> { diff --git a/test/jdk/java/nio/channels/DatagramChannel/InterruptibleOrNot.java b/test/jdk/java/nio/channels/DatagramChannel/InterruptibleOrNot.java index e6fde147bd9..ace3e6343e8 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/InterruptibleOrNot.java +++ b/test/jdk/java/nio/channels/DatagramChannel/InterruptibleOrNot.java @@ -61,7 +61,7 @@ public class InterruptibleOrNot { } /** - * Call DatagramChannel.receive with the interrupt status set, the DatagramChannel + * Call DatagramChannel.receive with the interrupted status set, the DatagramChannel * is interruptible. */ @Test @@ -72,7 +72,7 @@ public class InterruptibleOrNot { assertThrows(ClosedByInterruptException.class, () -> dc.receive(buf)); assertFalse(dc.isOpen()); } finally { - Thread.interrupted(); // clear interrupt status + Thread.interrupted(); // clear interrupted status } } @@ -89,12 +89,12 @@ public class InterruptibleOrNot { assertThrows(ClosedByInterruptException.class, () -> dc.receive(buf)); assertFalse(dc.isOpen()); } finally { - Thread.interrupted(); // clear interrupt status + Thread.interrupted(); // clear interrupted status } } /** - * Call DatagramChannel.receive with the interrupt status set, the DatagramChannel + * Call DatagramChannel.receive with the interrupted status set, the DatagramChannel * is not interruptible. */ @Test @@ -111,7 +111,7 @@ public class InterruptibleOrNot { assertThrows(AsynchronousCloseException.class, () -> dc.receive(buf)); assertFalse(dc.isOpen()); } finally { - Thread.interrupted(); // clear interrupt status + Thread.interrupted(); // clear interrupted status } } @@ -136,12 +136,12 @@ public class InterruptibleOrNot { assertThrows(AsynchronousCloseException.class, () -> dc.receive(buf)); assertFalse(dc.isOpen()); } finally { - Thread.interrupted(); // clear interrupt status + Thread.interrupted(); // clear interrupted status } } /** - * Call DatagramChannel.send with the interrupt status set, the DatagramChannel + * Call DatagramChannel.send with the interrupted status set, the DatagramChannel * is interruptible. */ @Test @@ -158,7 +158,7 @@ public class InterruptibleOrNot { } /** - * Call DatagramChannel.send with the interrupt status set, the DatagramChannel + * Call DatagramChannel.send with the interrupted status set, the DatagramChannel * is not interruptible. */ @Test @@ -171,7 +171,7 @@ public class InterruptibleOrNot { assertEquals(100, n); assertTrue(dc.isOpen()); } finally { - Thread.interrupted(); // clear interrupt status + Thread.interrupted(); // clear interrupted status } } diff --git a/test/jdk/java/nio/channels/FileChannel/CloseDuringTransfer.java b/test/jdk/java/nio/channels/FileChannel/CloseDuringTransfer.java index 3b1973782d6..d4c61bfd9e2 100644 --- a/test/jdk/java/nio/channels/FileChannel/CloseDuringTransfer.java +++ b/test/jdk/java/nio/channels/FileChannel/CloseDuringTransfer.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 @@ -232,7 +232,7 @@ class CloseDuringTransfer { /** * Waits for the interrupt task submitted by scheduleInterrupt, and clears the - * current thread's interrupt status. + * current thread's interrupted status. */ private void finishInterrupt(Future interrupter) throws Exception { boolean done = false; diff --git a/test/jdk/java/nio/channels/FileChannel/ClosedByInterrupt.java b/test/jdk/java/nio/channels/FileChannel/ClosedByInterrupt.java index a14d111b6f0..66fbd57a05a 100644 --- a/test/jdk/java/nio/channels/FileChannel/ClosedByInterrupt.java +++ b/test/jdk/java/nio/channels/FileChannel/ClosedByInterrupt.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2011, 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 @@ -144,9 +144,9 @@ public class ClosedByInterrupt { } catch (ClosedByInterruptException e) { if (interruptible) { if (Thread.interrupted()) { - expected(e + " thrown and interrupt status set"); + expected(e + " thrown and interrupted status set"); } else { - unexpected(e + " thrown but interrupt status not set"); + unexpected(e + " thrown but interrupted status not set"); } } else { unexpected(e); diff --git a/test/jdk/java/nio/channels/Pipe/PipeInterrupt.java b/test/jdk/java/nio/channels/Pipe/PipeInterrupt.java index 53606b60487..b81823efd54 100644 --- a/test/jdk/java/nio/channels/Pipe/PipeInterrupt.java +++ b/test/jdk/java/nio/channels/Pipe/PipeInterrupt.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 @@ -55,7 +55,7 @@ public class PipeInterrupt { close(); if (interrupted) { if (!this.isInterrupted()) - exc = new RuntimeException("interrupt status reset"); + exc = new RuntimeException("interrupted status reset"); break; } } catch (IOException ioe) { diff --git a/test/jdk/java/nio/channels/Selector/LotsOfInterrupts.java b/test/jdk/java/nio/channels/Selector/LotsOfInterrupts.java index 98470dec160..5fea3d5c932 100644 --- a/test/jdk/java/nio/channels/Selector/LotsOfInterrupts.java +++ b/test/jdk/java/nio/channels/Selector/LotsOfInterrupts.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 @@ -64,7 +64,7 @@ public class LotsOfInterrupts { phaser.arriveAndAwaitAdvance(); sel.select(); - // clear interrupt status and consume wakeup + // clear interrupted status and consume wakeup Thread.interrupted(); sel.selectNow(); } diff --git a/test/jdk/java/nio/channels/Selector/SelectWithConsumer.java b/test/jdk/java/nio/channels/Selector/SelectWithConsumer.java index 41edb207239..1a2ada51bce 100644 --- a/test/jdk/java/nio/channels/Selector/SelectWithConsumer.java +++ b/test/jdk/java/nio/channels/Selector/SelectWithConsumer.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 @@ -360,7 +360,7 @@ public class SelectWithConsumer { } /** - * Test invoking select with interrupt status set + * Test invoking select with interrupted status set */ public void testInterruptBeforeSelect() throws Exception { // select(Consumer) @@ -371,7 +371,7 @@ public class SelectWithConsumer { assertTrue(Thread.currentThread().isInterrupted()); assertTrue(sel.isOpen()); } finally { - Thread.currentThread().interrupted(); // clear interrupt status + Thread.currentThread().interrupted(); // clear interrupted status } // select(Consumer, timeout) @@ -384,7 +384,7 @@ public class SelectWithConsumer { assertTrue(Thread.currentThread().isInterrupted()); assertTrue(sel.isOpen()); } finally { - Thread.currentThread().interrupted(); // clear interrupt status + Thread.currentThread().interrupted(); // clear interrupted status } } @@ -400,7 +400,7 @@ public class SelectWithConsumer { assertTrue(Thread.currentThread().isInterrupted()); assertTrue(sel.isOpen()); } finally { - Thread.currentThread().interrupted(); // clear interrupt status + Thread.currentThread().interrupted(); // clear interrupted status } // select(Consumer, timeout) @@ -411,7 +411,7 @@ public class SelectWithConsumer { assertTrue(Thread.currentThread().isInterrupted()); assertTrue(sel.isOpen()); } finally { - Thread.currentThread().interrupted(); // clear interrupt status + Thread.currentThread().interrupted(); // clear interrupted status } } diff --git a/test/jdk/java/nio/channels/Selector/WakeupAfterClose.java b/test/jdk/java/nio/channels/Selector/WakeupAfterClose.java index 92b1aef5c6d..965300c8a3a 100644 --- a/test/jdk/java/nio/channels/Selector/WakeupAfterClose.java +++ b/test/jdk/java/nio/channels/Selector/WakeupAfterClose.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2017, 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 @@ -23,7 +23,7 @@ /* @test * @bug 6524172 - * @summary Invoking wakeup on closed Selector can throw NPE if close resets interrupt status + * @summary Invoking wakeup on closed Selector can throw NPE if close resets interrupted status */ import java.io.IOException; diff --git a/test/jdk/java/nio/channels/SocketChannel/AdaptorStreams.java b/test/jdk/java/nio/channels/SocketChannel/AdaptorStreams.java index 846d5cd22b6..3487ae45de1 100644 --- a/test/jdk/java/nio/channels/SocketChannel/AdaptorStreams.java +++ b/test/jdk/java/nio/channels/SocketChannel/AdaptorStreams.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, 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 @@ -106,7 +106,7 @@ public class AdaptorStreams { } /** - * Test interrupt status set before read + * Test interrupted status set before read */ public void testRead6() throws Exception { withConnection((sc, peer) -> { @@ -203,7 +203,7 @@ public class AdaptorStreams { } /** - * Test interrupt status set before timed read + * Test interrupted status set before timed read */ public void testTimedRead5() throws Exception { withConnection((sc, peer) -> { @@ -257,7 +257,7 @@ public class AdaptorStreams { } /** - * Test interrupt status set before write + * Test interrupted status set before write */ public void testWrite2() throws Exception { withConnection((sc, peer) -> { diff --git a/test/jdk/java/nio/channels/vthread/SelectorOps.java b/test/jdk/java/nio/channels/vthread/SelectorOps.java index 0c86cae6185..81821a85791 100644 --- a/test/jdk/java/nio/channels/vthread/SelectorOps.java +++ b/test/jdk/java/nio/channels/vthread/SelectorOps.java @@ -253,7 +253,7 @@ class SelectorOps { } /** - * Test calling select with interrupt status set. + * Test calling select with interrupted status set. */ @Test public void testInterruptBeforeSelect() throws Exception { @@ -270,7 +270,7 @@ class SelectorOps { } /** - * Test calling select with interrupt status set and thread is pinned. + * Test calling select with interrupted status set and thread is pinned. */ @Test public void testInterruptBeforeSelectWhenPinned() throws Exception { diff --git a/test/jdk/java/nio/file/Files/CallWithInterruptSet.java b/test/jdk/java/nio/file/Files/CallWithInterruptSet.java index 313862e65f3..3d7bfd2c518 100644 --- a/test/jdk/java/nio/file/Files/CallWithInterruptSet.java +++ b/test/jdk/java/nio/file/Files/CallWithInterruptSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 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,7 +25,7 @@ * @test * @bug 8205612 * @run testng CallWithInterruptSet - * @summary Test invoking Files methods with the interrupt status set + * @summary Test invoking Files methods with the interrupted status set */ import java.io.InputStream; diff --git a/test/jdk/java/nio/file/Files/InterruptCopy.java b/test/jdk/java/nio/file/Files/InterruptCopy.java index f13a2877d91..81f5c0d86bc 100644 --- a/test/jdk/java/nio/file/Files/InterruptCopy.java +++ b/test/jdk/java/nio/file/Files/InterruptCopy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2021, 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 @@ -120,7 +120,7 @@ public class InterruptCopy { } catch (IOException e) { boolean interrupted = Thread.interrupted(); if (!interrupted) - throw new RuntimeException("Interrupt status was not set"); + throw new RuntimeException("Interrupted status was not set"); System.out.println("Copy failed (this is expected)."); } try { 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.X509CertSelector; 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 @@ public class NoExtensions { // 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 @@ public class NoExtensions { + "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 @@ public class NoExtensions { + "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 @@ public class NoExtensions { + "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"); + Collection entries = new HashSet<>(); - ByteArrayInputStream is; - - 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 @@ public final class StatusLoopDependency { 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 @@ public final class StatusLoopDependency { 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 @@ public class CircularCRLTwoLevel { 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 @@ public class CircularCRLTwoLevelRevoked { 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.SECONDS; 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 class TCKDuration extends AbstractTCKTest { 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/time/tck/java/time/TCKInstant.java b/test/jdk/java/time/tck/java/time/TCKInstant.java index c666a1340d3..d92d46d62bd 100644 --- a/test/jdk/java/time/tck/java/time/TCKInstant.java +++ b/test/jdk/java/time/tck/java/time/TCKInstant.java @@ -106,6 +106,7 @@ import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.Optional; import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -1225,6 +1226,83 @@ public class TCKInstant extends AbstractDateTimeTest { t.plusNanos(-1); } + @DataProvider(name = "PlusSaturating") + Object[][] provider_plusSaturating() { + return new Object[][]{ + // 1. {edge or constant instants} x {edge or constant durations} + {Instant.MIN, Duration.ofSeconds(Long.MIN_VALUE, 0), Optional.of(Instant.MIN)}, + {Instant.MIN, Duration.ofSeconds(Long.MIN_VALUE, 0), Optional.of(Instant.MIN)}, + {Instant.MIN, Duration.ZERO, Optional.empty()}, + {Instant.MIN, Duration.ofSeconds(Long.MAX_VALUE, 999_999_999), Optional.of(Instant.MAX)}, + {Instant.EPOCH, Duration.ofSeconds(Long.MIN_VALUE, 0), Optional.of(Instant.MIN)}, + {Instant.EPOCH, Duration.ZERO, Optional.empty()}, + {Instant.EPOCH, Duration.ofSeconds(Long.MAX_VALUE, 999_999_999), Optional.of(Instant.MAX)}, + {Instant.MAX, Duration.ofSeconds(Long.MIN_VALUE, 0), Optional.of(Instant.MIN)}, + {Instant.MAX, Duration.ZERO, Optional.empty()}, + {Instant.MAX, Duration.ofSeconds(Long.MAX_VALUE, 999_999_999), Optional.of(Instant.MAX)}, + // 2. {edge or constant instants} x {normal durations} + {Instant.MIN, Duration.ofDays(-32), Optional.of(Instant.MIN)}, + {Instant.MIN, Duration.ofDays(32), Optional.empty()}, + {Instant.EPOCH, Duration.ofDays(-32), Optional.empty()}, + {Instant.EPOCH, Duration.ofDays(32), Optional.empty()}, + {Instant.MAX, Duration.ofDays(-32), Optional.empty()}, + {Instant.MAX, Duration.ofDays(32), Optional.of(Instant.MAX)}, + // 3. {normal instants with both positive and negative epoch seconds} x {edge or constant durations} + {Instant.parse("1950-01-01T00:00:00Z"), Duration.ofSeconds(Long.MIN_VALUE, 0), Optional.of(Instant.MIN)}, + {Instant.parse("1950-01-01T00:00:00Z"), Duration.ZERO, Optional.empty()}, + {Instant.parse("1950-01-01T00:00:00Z"), Duration.ofSeconds(Long.MAX_VALUE, 999_999_999), Optional.of(Instant.MAX)}, + {Instant.parse("1990-01-01T00:00:00Z"), Duration.ofSeconds(Long.MIN_VALUE, 0), Optional.of(Instant.MIN)}, + {Instant.parse("1990-01-01T00:00:00Z"), Duration.ZERO, Optional.empty()}, + {Instant.parse("1990-01-01T00:00:00Z"), Duration.ofSeconds(Long.MAX_VALUE, 999_999_999), Optional.of(Instant.MAX)}, + // 4. {normal instants with both positive and negative epoch seconds} x {normal durations} + {Instant.parse("1950-01-01T00:00:00Z"), Duration.ofDays(-32), Optional.empty()}, + {Instant.parse("1950-01-01T00:00:00Z"), Duration.ofDays(32), Optional.empty()}, + {Instant.parse("1990-01-01T00:00:00Z"), Duration.ofDays(-32), Optional.empty()}, + {Instant.parse("1990-01-01T00:00:00Z"), Duration.ofDays(32), Optional.empty()}, + // 5. instant boundary + {Instant.MIN, Duration.between(Instant.MIN, Instant.MAX), Optional.of(Instant.MAX)}, + {Instant.EPOCH, Duration.between(Instant.EPOCH, Instant.MAX), Optional.of(Instant.MAX)}, + {Instant.EPOCH, Duration.between(Instant.EPOCH, Instant.MIN), Optional.of(Instant.MIN)}, + {Instant.MAX, Duration.between(Instant.MAX, Instant.MIN), Optional.of(Instant.MIN)} + }; + } + + @Test(dataProvider = "PlusSaturating") + public void plusSaturating(Instant i, Duration d, Optional value) { + var actual = i.plusSaturating(d); + try { + assertEquals(actual, i.plus(d)); + // If `value` is present, perform an additional check. It may be + // important to ensure that not only does the result of `plusSaturating` + // match that of `plus`, but that it also matches our expectation. + // Because if it doesn’t, then the test isn’t testing what we think + // it is, and needs to be fixed. + value.ifPresent(instant -> assertEquals(actual, instant)); + } catch (DateTimeException /* instant overflow */ + | ArithmeticException /* long overflow */ e) { + if (value.isEmpty()) { + throw new AssertionError(); + } + assertEquals(actual, value.get()); + } + } + + @DataProvider(name = "PlusSaturating_null") + Object[][] provider_plusSaturating_null() { + return new Object[][]{ + {Instant.MIN}, + {Instant.EPOCH}, + {Instant.MAX}, + // any non-random but also non-special instant + {Instant.parse("2025-10-13T20:47:50.369955Z")}, + }; + } + + @Test(expectedExceptions = NullPointerException.class, dataProvider = "PlusSaturating_null") + public void test_plusSaturating_null(Instant i) { + i.plusSaturating(null); + } + //----------------------------------------------------------------------- @DataProvider(name="Minus") Object[][] provider_minus() { 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.ObjectInputStream; 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.EmptySource; 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 class 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 class LocaleEnhanceTest { .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 class LocaleEnhanceTest { .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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { // 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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { // 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 class LocaleEnhanceTest { // 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" }, + // 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" }, + // 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" }, + // 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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { 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); + .setLanguageTag(source) + .build() + .toLanguageTag(); + assertEquals(target, result, "language"); - // 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"); }}; + // 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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { .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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { .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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { .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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { .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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { .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 class LocaleEnhanceTest { // 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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { @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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { } 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 class LocaleEnhanceTest { // 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 class LocaleEnhanceTest { // 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 class LocaleEnhanceTest { // 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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { 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 class LocaleEnhanceTest { 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/CompletableFuture/LostInterrupt.java b/test/jdk/java/util/concurrent/CompletableFuture/LostInterrupt.java index 0f5a462b630..e46b0d0ee5e 100644 --- a/test/jdk/java/util/concurrent/CompletableFuture/LostInterrupt.java +++ b/test/jdk/java/util/concurrent/CompletableFuture/LostInterrupt.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 @@ -30,7 +30,7 @@ import static java.util.concurrent.TimeUnit.DAYS; * @test * @bug 8254350 * @run main LostInterrupt - * @summary CompletableFuture.get may swallow interrupt status + * @summary CompletableFuture.get may swallow interrupted status * @key randomness */ @@ -38,9 +38,9 @@ import static java.util.concurrent.TimeUnit.DAYS; /** * Submits a task that completes immediately, then invokes CompletableFuture.get - * with the interrupt status set. CompletableFuture.get should either complete - * immediately with the interrupt status set, or else throw InterruptedException - * with the interrupt status cleared. + * with the interrupted status set. CompletableFuture.get should either complete + * immediately with the interrupted status set, or else throw InterruptedException + * with the interrupted status cleared. */ public class LostInterrupt { static final int ITERATIONS = 10_000; @@ -63,7 +63,7 @@ public class LostInterrupt { } catch (InterruptedException expected) { if (Thread.interrupted()) throw new AssertionError( - "interrupt status not cleared, run=" + i); + "interrupted status not cleared, run=" + i); } } } finally { diff --git a/test/jdk/java/util/concurrent/CompletableFuture/SwallowedInterruptedException.java b/test/jdk/java/util/concurrent/CompletableFuture/SwallowedInterruptedException.java index 93ae7305998..98ba7ce037c 100644 --- a/test/jdk/java/util/concurrent/CompletableFuture/SwallowedInterruptedException.java +++ b/test/jdk/java/util/concurrent/CompletableFuture/SwallowedInterruptedException.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 @@ -57,12 +57,12 @@ public class SwallowedInterruptedException { if (!Thread.currentThread().isInterrupted()) { fail.set(new AssertionError( - "Future.get completed with interrupt status not set")); + "Future.get completed with interrupted status not set")); } } catch (InterruptedException ex) { if (Thread.currentThread().isInterrupted()) { fail.set(new AssertionError( - "InterruptedException with interrupt status set")); + "InterruptedException with interrupted status set")); } } catch (Throwable ex) { fail.set(ex); diff --git a/test/jdk/java/util/concurrent/ExecutorService/CloseTest.java b/test/jdk/java/util/concurrent/ExecutorService/CloseTest.java index 162d4f7d1b4..129d388cf6d 100644 --- a/test/jdk/java/util/concurrent/ExecutorService/CloseTest.java +++ b/test/jdk/java/util/concurrent/ExecutorService/CloseTest.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 @@ -207,7 +207,7 @@ class CloseTest { } /** - * Test invoking close with interrupt status set. + * Test invoking close with interrupted status set. */ @ParameterizedTest @MethodSource("executors") @@ -225,7 +225,7 @@ class CloseTest { executor.close(); assertTrue(Thread.currentThread().isInterrupted()); } finally { - Thread.interrupted(); // clear interrupt status + Thread.interrupted(); // clear interrupted status } assertTrue(executor.isShutdown()); assertTrue(executor.isTerminated()); @@ -259,7 +259,7 @@ class CloseTest { executor.close(); assertTrue(Thread.currentThread().isInterrupted()); } finally { - Thread.interrupted(); // clear interrupt status + Thread.interrupted(); // clear interrupted status } assertTrue(executor.isShutdown()); assertTrue(executor.isTerminated()); diff --git a/test/jdk/java/util/concurrent/ExecutorService/InvokeTest.java b/test/jdk/java/util/concurrent/ExecutorService/InvokeTest.java index a9a4eb0d8d7..901291450fc 100644 --- a/test/jdk/java/util/concurrent/ExecutorService/InvokeTest.java +++ b/test/jdk/java/util/concurrent/ExecutorService/InvokeTest.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 @@ -284,7 +284,7 @@ class InvokeTest { } /** - * Test invokeAny with interrupt status set. + * Test invokeAny with interrupted status set. */ @ParameterizedTest @MethodSource("executors") @@ -530,7 +530,7 @@ class InvokeTest { } /** - * Test invokeAll with interrupt status set. + * Test invokeAll with interrupted status set. */ @ParameterizedTest @MethodSource("executors") @@ -555,7 +555,7 @@ class InvokeTest { } /** - * Test timed-invokeAll with interrupt status set. + * Test timed-invokeAll with interrupted status set. */ @ParameterizedTest @MethodSource("executors") diff --git a/test/jdk/java/util/concurrent/StructuredTaskScope/StructuredTaskScopeTest.java b/test/jdk/java/util/concurrent/StructuredTaskScope/StructuredTaskScopeTest.java index 79f4a935195..762f24dc89f 100644 --- a/test/jdk/java/util/concurrent/StructuredTaskScope/StructuredTaskScopeTest.java +++ b/test/jdk/java/util/concurrent/StructuredTaskScope/StructuredTaskScopeTest.java @@ -425,7 +425,7 @@ class StructuredTaskScopeTest { } /** - * Test join with interrupt status set. + * Test join with interrupted status set. */ @ParameterizedTest @MethodSource("factories") @@ -444,7 +444,7 @@ class StructuredTaskScopeTest { scope.join(); fail("join did not throw"); } catch (InterruptedException expected) { - assertFalse(Thread.interrupted()); // interrupt status should be cleared + assertFalse(Thread.interrupted()); // interrupted status should be cleared } } } @@ -470,7 +470,7 @@ class StructuredTaskScopeTest { scope.join(); fail("join did not throw"); } catch (InterruptedException expected) { - assertFalse(Thread.interrupted()); // interrupt status should be clear + assertFalse(Thread.interrupted()); // interrupted status should be clear } } } @@ -745,7 +745,7 @@ class StructuredTaskScopeTest { } /** - * Test close with interrupt status set. + * Test close with interrupted status set. */ @ParameterizedTest @MethodSource("factories") @@ -776,12 +776,12 @@ class StructuredTaskScopeTest { scope.join(); - // invoke close with interrupt status set + // invoke close with interrupted status set Thread.currentThread().interrupt(); try { scope.close(); } finally { - assertTrue(Thread.interrupted()); // clear interrupt status + assertTrue(Thread.interrupted()); // clear interrupted status assertTrue(done.get()); } } @@ -829,7 +829,7 @@ class StructuredTaskScopeTest { try { scope.close(); } finally { - assertTrue(Thread.interrupted()); // clear interrupt status + assertTrue(Thread.interrupted()); // clear interrupted status assertTrue(done.get()); } } diff --git a/test/jdk/java/util/concurrent/ThreadPerTaskExecutor/ThreadPerTaskExecutorTest.java b/test/jdk/java/util/concurrent/ThreadPerTaskExecutor/ThreadPerTaskExecutorTest.java index ff3b9566f5e..1427a72b931 100644 --- a/test/jdk/java/util/concurrent/ThreadPerTaskExecutor/ThreadPerTaskExecutorTest.java +++ b/test/jdk/java/util/concurrent/ThreadPerTaskExecutor/ThreadPerTaskExecutorTest.java @@ -228,7 +228,7 @@ class ThreadPerTaskExecutorTest { } /** - * Invoke close with interrupt status set. + * Invoke close with interrupted status set. */ @ParameterizedTest @MethodSource("executors") @@ -593,7 +593,7 @@ class ThreadPerTaskExecutorTest { } /** - * Test invokeAny with interrupt status set. + * Test invokeAny with interrupted status set. */ @ParameterizedTest @MethodSource("executors") @@ -822,7 +822,7 @@ class ThreadPerTaskExecutorTest { } /** - * Test untimed-invokeAll with interrupt status set. + * Test untimed-invokeAll with interrupted status set. */ @ParameterizedTest @MethodSource("executors") @@ -846,7 +846,7 @@ class ThreadPerTaskExecutorTest { } /** - * Test timed-invokeAll with interrupt status set. + * Test timed-invokeAll with interrupted status set. */ @ParameterizedTest @MethodSource("executors") diff --git a/test/jdk/java/util/concurrent/forkjoin/ContextClassLoaderTest.java b/test/jdk/java/util/concurrent/forkjoin/ContextClassLoaderTest.java new file mode 100644 index 00000000000..eb6465e9619 --- /dev/null +++ b/test/jdk/java/util/concurrent/forkjoin/ContextClassLoaderTest.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. + */ + +/* + * @test + * @bug 8368500 + * @run junit/othervm ContextClassLoaderTest + * @summary Check the context classloader is reset + */ +import java.net.URL; +import java.net.URLClassLoader; +import java.util.concurrent.Future; +import java.util.concurrent.ForkJoinPool; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class ContextClassLoaderTest { + + @Test + void testContextClassLoaderIsSetAndRestored() throws Exception { + Future future = ForkJoinPool.commonPool().submit(() -> { + Thread thread = Thread.currentThread(); + ClassLoader originalCCL = thread.getContextClassLoader(); + ClassLoader customCCL = new URLClassLoader(new URL[0], originalCCL); + // Set custom context classloader and verify it + thread.setContextClassLoader(customCCL); + assertSame(customCCL, thread.getContextClassLoader(), "Custom context class loader not set"); + + // Reset to original and verify restoration + thread.setContextClassLoader(originalCCL); + assertSame(originalCCL, thread.getContextClassLoader(), "Original context class loader not restored"); + }); + future.get(); + } +} + 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 @@ import java.util.stream.Stream; * @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/java/util/concurrent/tck/JSR166TestCase.java b/test/jdk/java/util/concurrent/tck/JSR166TestCase.java index 18007f72ca8..80e6c98bfa7 100644 --- a/test/jdk/java/util/concurrent/tck/JSR166TestCase.java +++ b/test/jdk/java/util/concurrent/tck/JSR166TestCase.java @@ -832,7 +832,7 @@ public class JSR166TestCase extends TestCase { * by rethrowing, in the test harness thread, any exception recorded * earlier by threadRecordFailure. * - * Triggers test case failure if interrupt status is set in the main thread. + * Triggers test case failure if interrupted status is set in the main thread. */ public void tearDown() throws Exception { Throwable t = threadFailure.getAndSet(null); @@ -848,7 +848,7 @@ public class JSR166TestCase extends TestCase { } if (Thread.interrupted()) - tearDownFail("interrupt status set in main thread"); + tearDownFail("interrupted status set in main thread"); checkForkJoinPoolThreadLeaks(); } @@ -1460,7 +1460,7 @@ public class JSR166TestCase extends TestCase { /** * Spin-waits up to LONG_DELAY_MS milliseconds for the current thread to - * be interrupted. Clears the interrupt status before returning. + * be interrupted. Clears the interrupted status before returning. */ void awaitInterrupted() { for (long startTime = 0L; !Thread.interrupted(); ) { diff --git a/test/jdk/java/util/concurrent/tck/StampedLockTest.java b/test/jdk/java/util/concurrent/tck/StampedLockTest.java index 673047ccf12..8fe8aaca569 100644 --- a/test/jdk/java/util/concurrent/tck/StampedLockTest.java +++ b/test/jdk/java/util/concurrent/tck/StampedLockTest.java @@ -410,7 +410,7 @@ public class StampedLockTest extends JSR166TestCase { } /** - * Non-interruptible operations ignore and preserve interrupt status + * Non-interruptible operations ignore and preserve interrupted status */ public void testNonInterruptibleOperationsIgnoreInterrupts() { final StampedLock lock = new StampedLock(); diff --git a/test/jdk/java/util/zip/InterruptibleZip.java b/test/jdk/java/util/zip/InterruptibleZip.java index f7e2be4fdfb..5a4b9904db9 100644 --- a/test/jdk/java/util/zip/InterruptibleZip.java +++ b/test/jdk/java/util/zip/InterruptibleZip.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 @@ -45,7 +45,7 @@ public class InterruptibleZip { System.out.printf("interrupted=%s n=%d name=%s%n", interrupted, n, ze.getName()); if (! interrupted) { - throw new Error("Wrong interrupt status"); + throw new Error("Wrong interrupted status"); } if (n != buf.length) { throw new Error("Read error"); diff --git a/test/jdk/javax/net/ssl/DTLS/DTLSNoNewSessionTicket.java b/test/jdk/javax/net/ssl/DTLS/DTLSNoNewSessionTicket.java new file mode 100644 index 00000000000..dba191d0193 --- /dev/null +++ b/test/jdk/javax/net/ssl/DTLS/DTLSNoNewSessionTicket.java @@ -0,0 +1,47 @@ +/* + * 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 8367059 + * @summary DTLS: loss of NewSessionTicket message results in handshake failure + * @modules java.base/sun.security.util + * @library /test/lib + * @build DTLSOverDatagram + * + * @comment Make sure client doesn't expect NewSessionTicket in the final + * flight if server doesn't send the "session_ticket" extension with + * ServerHello handshake message. + * + * @run main/othervm -Djdk.tls.client.enableSessionTicketExtension=false + * DTLSNoNewSessionTicket + * @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=false + * DTLSNoNewSessionTicket + */ + +public class DTLSNoNewSessionTicket extends DTLSOverDatagram { + public static void main(String[] args) throws Exception { + var testCase = new DTLSNoNewSessionTicket(); + testCase.runTest(testCase); + } +} diff --git a/test/jdk/javax/net/ssl/DTLS/DTLSOverDatagram.java b/test/jdk/javax/net/ssl/DTLS/DTLSOverDatagram.java index 9780586cd57..89fe39a792c 100644 --- a/test/jdk/javax/net/ssl/DTLS/DTLSOverDatagram.java +++ b/test/jdk/javax/net/ssl/DTLS/DTLSOverDatagram.java @@ -21,9 +21,6 @@ * questions. */ -// SunJSSE does not support dynamic system properties, no way to re-use -// system properties in samevm/agentvm mode. - /* * @test * @bug 8043758 @@ -132,8 +129,15 @@ public class DTLSOverDatagram { * The remainder is support stuff for DTLS operations. */ SSLEngine createSSLEngine(boolean isClient) throws Exception { - SSLContext context = getDTLSContext(); - SSLEngine engine = context.createSSLEngine(); + SSLContext context = + isClient ? getClientDTLSContext() : getServerDTLSContext(); + + // Note: client and server ports are not to be used for network + // communication, but only to be set in client and server SSL engines. + // We must use the same context, host and port for initial and resuming + // sessions when testing session resumption (abbreviated handshake). + SSLEngine engine = context.createSSLEngine("localhost", + isClient ? 51111 : 52222); SSLParameters paras = engine.getSSLParameters(); paras.setMaximumPacketSize(MAXIMUM_PACKET_SIZE); @@ -507,7 +511,7 @@ public class DTLSOverDatagram { } // get DTSL context - SSLContext getDTLSContext() throws Exception { + static SSLContext getDTLSContext() throws Exception { String passphrase = "passphrase"; return SSLContextBuilder.builder() .trustStore(KeyStoreUtils.loadKeyStore(TRUST_FILENAME, passphrase)) @@ -517,6 +521,14 @@ public class DTLSOverDatagram { .build(); } + protected SSLContext getServerDTLSContext() throws Exception { + return getDTLSContext(); + } + + protected SSLContext getClientDTLSContext() throws Exception { + return getDTLSContext(); + } + /* * ============================================================= diff --git a/test/jdk/javax/net/ssl/DTLS/PacketLossRetransmission.java b/test/jdk/javax/net/ssl/DTLS/PacketLossRetransmission.java index 2dd0de830f1..828e4c316b2 100644 --- a/test/jdk/javax/net/ssl/DTLS/PacketLossRetransmission.java +++ b/test/jdk/javax/net/ssl/DTLS/PacketLossRetransmission.java @@ -21,56 +21,95 @@ * questions. */ -// SunJSSE does not support dynamic system properties, no way to re-use -// system properties in samevm/agentvm mode. - /* * @test - * @bug 8161086 + * @bug 8161086 8367059 * @summary DTLS handshaking fails if some messages were lost * @modules java.base/sun.security.util * @library /test/lib * @build DTLSOverDatagram * - * @run main/othervm PacketLossRetransmission client 1 client_hello - * @run main/othervm PacketLossRetransmission client 16 client_key_exchange - * @run main/othervm PacketLossRetransmission client 20 finished - * @run main/othervm PacketLossRetransmission client -1 change_cipher_spec - * @run main/othervm PacketLossRetransmission server 2 server_hello - * @run main/othervm PacketLossRetransmission server 3 hello_verify_request - * @run main/othervm PacketLossRetransmission server 11 certificate - * @run main/othervm PacketLossRetransmission server 12 server_key_exchange - * @run main/othervm PacketLossRetransmission server 14 server_hello_done - * @run main/othervm PacketLossRetransmission server 20 finished - * @run main/othervm PacketLossRetransmission server -1 change_cipher_spec + * @run main/othervm PacketLossRetransmission client full 1 client_hello + * @run main/othervm PacketLossRetransmission client full 16 client_key_exchange + * @run main/othervm PacketLossRetransmission client full 20 finished + * @run main/othervm PacketLossRetransmission client full -1 change_cipher_spec + * @run main/othervm PacketLossRetransmission server full 2 server_hello + * @run main/othervm PacketLossRetransmission server full 3 hello_verify_request + * @run main/othervm PacketLossRetransmission server full 11 certificate + * @run main/othervm PacketLossRetransmission server full 12 server_key_exchange + * @run main/othervm PacketLossRetransmission server full 14 server_hello_done + * @run main/othervm PacketLossRetransmission server full 20 finished + * @run main/othervm PacketLossRetransmission server full -1 change_cipher_spec + * @run main/othervm PacketLossRetransmission server full 4 new_session_ticket + * @run main/othervm PacketLossRetransmission client resume 1 client_hello + * @run main/othervm PacketLossRetransmission client resume 20 finished + * @run main/othervm PacketLossRetransmission client resume -1 change_cipher_spec + * @run main/othervm PacketLossRetransmission server resume 2 server_hello + * @run main/othervm PacketLossRetransmission server resume 3 hello_verify_request + * @run main/othervm PacketLossRetransmission server resume 20 finished + * @run main/othervm PacketLossRetransmission server resume -1 change_cipher_spec + * @run main/othervm PacketLossRetransmission server resume 4 new_session_ticket */ + +import java.nio.ByteBuffer; import java.util.List; -import java.util.ArrayList; import java.net.DatagramPacket; import java.net.SocketAddress; +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.SSLException; +import javax.net.ssl.SSLSession; /** * Test that DTLS implementation is able to do retransmission internally * automatically if packet get lost. */ public class PacketLossRetransmission extends DTLSOverDatagram { + private static boolean isClient; private static byte handshakeType; private static final int TIMEOUT = 500; private boolean needPacketLoss = true; + private final SSLContext clientContext; + private final SSLContext serverContext; + + protected PacketLossRetransmission() throws Exception { + this.clientContext = getDTLSContext(); + this.serverContext = getDTLSContext(); + } public static void main(String[] args) throws Exception { isClient = args[0].equals("client"); - handshakeType = Byte.parseByte(args[1]); + boolean isResume = args[1].equals("resume"); + handshakeType = Byte.parseByte(args[2]); PacketLossRetransmission testCase = new PacketLossRetransmission(); testCase.setSocketTimeout(TIMEOUT); + + if (isResume) { + System.out.println("Starting initial handshake"); + // The initial session will populate the TLS session cache. + initialSession(testCase.createSSLEngine(true), + testCase.createSSLEngine(false)); + } + testCase.runTest(testCase); } + @Override + protected SSLContext getClientDTLSContext() throws Exception { + return clientContext; + } + + @Override + protected SSLContext getServerDTLSContext() throws Exception { + return serverContext; + } + @Override boolean produceHandshakePackets(SSLEngine engine, SocketAddress socketAddr, String side, List packets) throws Exception { @@ -90,4 +129,170 @@ public class PacketLossRetransmission extends DTLSOverDatagram { return finished; } + + private static void initialSession(SSLEngine clientEngine, + SSLEngine serverEngine) throws SSLException { + boolean clientDone = false; + boolean serverDone = false; + boolean cliDataReady = false; + boolean servDataReady = false; + SSLEngineResult clientResult; + SSLEngineResult serverResult; + SSLSession session = clientEngine.getSession(); + int appBufferMax = session.getApplicationBufferSize(); + int netBufferMax = session.getPacketBufferSize(); + ByteBuffer clientIn = ByteBuffer.allocate(appBufferMax + 50); + ByteBuffer serverIn = ByteBuffer.allocate(appBufferMax + 50); + ByteBuffer cTOs = ByteBuffer.allocateDirect(netBufferMax); + ByteBuffer sTOc = ByteBuffer.allocateDirect(netBufferMax); + HandshakeStatus hsStat; + final ByteBuffer clientOut = ByteBuffer.wrap( + "Hi Server, I'm Client".getBytes()); + final ByteBuffer serverOut = ByteBuffer.wrap( + "Hello Client, I'm Server".getBytes()); + + clientEngine.beginHandshake(); + serverEngine.beginHandshake(); + while (!clientDone && !serverDone) { + // Client processing + hsStat = clientEngine.getHandshakeStatus(); + log("Client HS Stat: " + hsStat); + switch (hsStat) { + case NOT_HANDSHAKING: + log("Closing client engine"); + clientEngine.closeOutbound(); + clientDone = true; + break; + case NEED_WRAP: + log(String.format("CTOS: p:%d, l:%d, c:%d", cTOs.position(), + cTOs.limit(), cTOs.capacity())); + clientResult = clientEngine.wrap(clientOut, cTOs); + log("client wrap: ", clientResult); + if (clientResult.getStatus() + == SSLEngineResult.Status.BUFFER_OVERFLOW) { + // Get a larger buffer and try again + int updateSize = 2 * netBufferMax; + log("Resizing buffer to " + updateSize + " bytes"); + cTOs = ByteBuffer.allocate(updateSize); + clientResult = clientEngine.wrap(clientOut, cTOs); + log("client wrap (resized): ", clientResult); + } + runDelegatedTasks(clientResult, clientEngine); + cTOs.flip(); + cliDataReady = true; + break; + case NEED_UNWRAP: + if (servDataReady) { + log(String.format("STOC: p:%d, l:%d, c:%d", + sTOc.position(), + sTOc.limit(), sTOc.capacity())); + clientResult = clientEngine.unwrap(sTOc, clientIn); + log("client unwrap: ", clientResult); + runDelegatedTasks(clientResult, clientEngine); + servDataReady = sTOc.hasRemaining(); + sTOc.compact(); + } else { + log("Server-to-client data not ready, skipping client" + + " unwrap"); + } + break; + case NEED_UNWRAP_AGAIN: + clientResult = clientEngine.unwrap(ByteBuffer.allocate(0), + clientIn); + log("client unwrap (again): ", clientResult); + runDelegatedTasks(clientResult, clientEngine); + break; + } + + // Server processing + hsStat = serverEngine.getHandshakeStatus(); + log("Server HS Stat: " + hsStat); + switch (hsStat) { + case NEED_WRAP: + log(String.format("STOC: p:%d, l:%d, c:%d", sTOc.position(), + sTOc.limit(), sTOc.capacity())); + serverResult = serverEngine.wrap(serverOut, sTOc); + log("server wrap: ", serverResult); + if (serverResult.getStatus() + == SSLEngineResult.Status.BUFFER_OVERFLOW) { + // Get a new buffer and try again + int updateSize = 2 * netBufferMax; + log("Resizing buffer to " + updateSize + " bytes"); + sTOc = ByteBuffer.allocate(updateSize); + serverResult = serverEngine.wrap(clientOut, sTOc); + log("server wrap (resized): ", serverResult); + } + runDelegatedTasks(serverResult, serverEngine); + sTOc.flip(); + servDataReady = true; + break; + case NOT_HANDSHAKING: + log("Closing server engine"); + serverEngine.closeOutbound(); + serverDone = true; + break; + case NEED_UNWRAP: + if (cliDataReady) { + log(String.format("CTOS: p:%d, l:%d, c:%d", + cTOs.position(), + cTOs.limit(), cTOs.capacity())); + serverResult = serverEngine.unwrap(cTOs, serverIn); + log("server unwrap: ", serverResult); + runDelegatedTasks(serverResult, serverEngine); + cliDataReady = cTOs.hasRemaining(); + cTOs.compact(); + } else { + log("Client-to-server data not ready, skipping server" + + " unwrap"); + } + break; + case NEED_UNWRAP_AGAIN: + serverResult = serverEngine.unwrap(ByteBuffer.allocate(0), + serverIn); + log("server unwrap (again): ", serverResult); + runDelegatedTasks(serverResult, serverEngine); + break; + } + } + } + + private static void log(String str) { + System.out.println(str); + } + + private static void log(String str, SSLEngineResult result) { + System.out.println("The format of the SSLEngineResult is: \n" + + "\t\"getStatus() / getHandshakeStatus()\" +\n" + + "\t\"bytesConsumed() / bytesProduced()\"\n"); + + HandshakeStatus hsStatus = result.getHandshakeStatus(); + + log(str + + result.getStatus() + "/" + hsStatus + ", " + + result.bytesConsumed() + "/" + result.bytesProduced() + + " bytes"); + + if (hsStatus == HandshakeStatus.FINISHED) { + log("\t...ready for application data"); + } + } + + private static void runDelegatedTasks(SSLEngineResult result, + SSLEngine engine) { + HandshakeStatus hsStatus = result.getHandshakeStatus(); + + if (hsStatus == HandshakeStatus.NEED_TASK) { + Runnable runnable; + while ((runnable = engine.getDelegatedTask()) != null) { + log("\trunning delegated task..."); + runnable.run(); + } + hsStatus = engine.getHandshakeStatus(); + if (hsStatus == HandshakeStatus.NEED_TASK) { + throw new RuntimeException( + "handshake shouldn't need additional tasks"); + } + log("\tnew HandshakeStatus: " + hsStatus); + } + } } diff --git a/test/jdk/javax/net/ssl/SSLSocket/Tls13PacketSize.java b/test/jdk/javax/net/ssl/SSLSocket/Tls13PacketSize.java index 0e34f4865ab..16e009ed589 100644 --- a/test/jdk/javax/net/ssl/SSLSocket/Tls13PacketSize.java +++ b/test/jdk/javax/net/ssl/SSLSocket/Tls13PacketSize.java @@ -107,5 +107,10 @@ public class Tls13PacketSize extends SSLSocketTemplate { throw new Exception( "Server record plaintext exceeds 2^14 octets: " + extra); } + + int drained = 1; + while (drained < appData.length) { + drained += sslIS.read(appData, drained, appData.length - drained); + } } } 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 @@ public class SSLSocketSNISensitive { 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 @@ public class SSLSocketSNISensitive { 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 class SSLSocketSNISensitive { 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 class SSLSocketSNISensitive { 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 @@ public abstract class ClientHelloInterOp { 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 @@ public abstract class ClientHelloInterOp { 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 @@ public abstract class ClientHelloInterOp { 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/ButtonGroup/TestButtonGroupFocusTraversal.java b/test/jdk/javax/swing/ButtonGroup/TestButtonGroupFocusTraversal.java index c115d44edbf..8dba2ae3e08 100644 --- a/test/jdk/javax/swing/ButtonGroup/TestButtonGroupFocusTraversal.java +++ b/test/jdk/javax/swing/ButtonGroup/TestButtonGroupFocusTraversal.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 @@ -30,7 +30,6 @@ * @run main TestButtonGroupFocusTraversal */ -import javax.swing.AbstractAction; import javax.swing.ButtonGroup; import javax.swing.JCheckBox; import javax.swing.JFrame; @@ -43,9 +42,7 @@ import java.awt.Component; import java.awt.Container; import java.awt.FlowLayout; import java.awt.KeyboardFocusManager; -import java.awt.Point; import java.awt.Robot; -import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; public class TestButtonGroupFocusTraversal { @@ -53,25 +50,11 @@ public class TestButtonGroupFocusTraversal { private static JTextField textFieldFirst, textFieldLast; private static JToggleButton toggleButton1, toggleButton2; private static JCheckBox checkBox1, checkBox2; - private static boolean toggleButtonActionPerformed; - private static boolean checkboxActionPerformed; + private static volatile boolean toggleButtonActionPerformed; + private static volatile boolean checkboxActionPerformed; private static JRadioButton radioButton1, radioButton2; private static Robot robot; - private static void blockTillDisplayed(Component comp) { - Point p = null; - while (p == null) { - try { - p = comp.getLocationOnScreen(); - } catch (IllegalStateException e) { - try { - Thread.sleep(500); - } catch (InterruptedException ie) { - } - } - } - } - private static void createUI() throws Exception { SwingUtilities.invokeAndWait(new Runnable() { public void run() { @@ -84,33 +67,11 @@ public class TestButtonGroupFocusTraversal { checkBox1 = new JCheckBox("1"); checkBox2 = new JCheckBox("2"); - toggleButton1.setAction(new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - toggleButtonActionPerformed = true; - } - }); + toggleButton1.addActionListener((_) -> toggleButtonActionPerformed = true); + toggleButton2.addActionListener((_) -> toggleButtonActionPerformed = true); - toggleButton2.setAction(new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - toggleButtonActionPerformed = true; - } - }); - - checkBox1.setAction(new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - checkboxActionPerformed = true; - } - }); - - checkBox2.setAction(new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - checkboxActionPerformed = true; - } - }); + checkBox1.addActionListener((_) -> checkboxActionPerformed = true); + checkBox2.addActionListener((_) -> checkboxActionPerformed = true); ButtonGroup toggleGroup = new ButtonGroup(); toggleGroup.add(toggleButton1); @@ -128,7 +89,7 @@ public class TestButtonGroupFocusTraversal { radioButton2.setSelected(true); checkBox2.setSelected(true); - frame = new JFrame("Test"); + frame = new JFrame("TestButtonGroupFocusTraversal"); frame.setLayout(new FlowLayout()); Container pane = frame.getContentPane(); @@ -178,7 +139,7 @@ public class TestButtonGroupFocusTraversal { } private static void checkCheckboxActionPerformed() { - if (toggleButtonActionPerformed) { + if (checkboxActionPerformed) { throw new RuntimeException("Checkbox Action should not be" + "performed"); } @@ -196,19 +157,13 @@ public class TestButtonGroupFocusTraversal { createUI(); robot.waitForIdle(); - robot.delay(200); - - blockTillDisplayed(frame); + robot.delay(500); SwingUtilities.invokeAndWait(textFieldFirst::requestFocus); if (!textFieldFirst.equals(KeyboardFocusManager.getCurrentKeyboardFocusManager() .getFocusOwner())) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } + robot.delay(300); SwingUtilities.invokeAndWait(textFieldFirst::requestFocus); } diff --git a/test/jdk/javax/swing/JSlider/bug4382876.java b/test/jdk/javax/swing/JSlider/bug4382876.java index b9ec64aab21..b0988de3cab 100644 --- a/test/jdk/javax/swing/JSlider/bug4382876.java +++ b/test/jdk/javax/swing/JSlider/bug4382876.java @@ -48,8 +48,8 @@ public class bug4382876 { private static Robot r; private static JFrame f; private static JSlider slider; - private static boolean upFail; - private static boolean downFail; + private static volatile boolean upFail; + private static volatile boolean downFail; public static void main(String[] args) throws Exception { try { @@ -70,23 +70,30 @@ public class bug4382876 { r.delay(1000); r.keyPress(KeyEvent.VK_PAGE_UP); + r.keyRelease(KeyEvent.VK_PAGE_UP); + SwingUtilities.invokeAndWait(() -> { if (slider.getValue() < -1000) { System.out.println("PAGE_UP VAL: " + slider.getValue()); upFail = true; } }); + if (upFail) { writeFailImage(); throw new RuntimeException("Slider value did NOT change with PAGE_UP"); } + r.keyPress(KeyEvent.VK_PAGE_DOWN); + r.keyRelease(KeyEvent.VK_PAGE_DOWN); + SwingUtilities.invokeAndWait(() -> { if (slider.getValue() > -1000) { System.out.println("PAGE_DOWN VAL: " + slider.getValue()); downFail = true; } }); + if (downFail) { writeFailImage(); throw new RuntimeException("Slider value did NOT change with PAGE_DOWN"); 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 class bug4624207 implements ChangeListener, FocusListener { 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/javax/swing/plaf/motif/bug4150591.java b/test/jdk/javax/swing/plaf/motif/bug4150591.java index 66c668a441c..f3614908cff 100644 --- a/test/jdk/javax/swing/plaf/motif/bug4150591.java +++ b/test/jdk/javax/swing/plaf/motif/bug4150591.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, 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 @@ -23,6 +23,7 @@ import com.sun.java.swing.plaf.motif.MotifInternalFrameTitlePane; import javax.swing.JInternalFrame; +import javax.swing.UIManager; /* * @test @@ -36,7 +37,8 @@ import javax.swing.JInternalFrame; */ public class bug4150591 { - public static void main(String[] args) { + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); MotifInternalFrameTitlePane mtp = new MotifInternalFrameTitlePane(new JInternalFrame()); } } diff --git a/test/jdk/javax/swing/text/GlyphView/TestGlyphBGHeight.java b/test/jdk/javax/swing/text/GlyphView/TestGlyphBGHeight.java new file mode 100644 index 00000000000..000ba16339e --- /dev/null +++ b/test/jdk/javax/swing/text/GlyphView/TestGlyphBGHeight.java @@ -0,0 +1,108 @@ +/* + * 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 8017266 + * @key headful + * @summary Verifies if Background is painted taller than needed for styled text. + * @run main TestGlyphBGHeight + */ + +import java.io.File; +import java.awt.Graphics2D; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.awt.Robot; +import javax.imageio.ImageIO; +import javax.swing.JFrame; +import javax.swing.JTextPane; +import javax.swing.text.Style; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyledDocument; +import javax.swing.SwingUtilities; + +public class TestGlyphBGHeight { + + static JFrame frame; + + public static void main(String[] args) throws Exception { + int width = 100; + int height = 100; + + try { + Robot robot = new Robot(); + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("TestGlyphBGHeight"); + frame.setSize(width, height); + frame.getContentPane().setLayout(new BorderLayout()); + + final JTextPane comp = new JTextPane(); + final StyledDocument doc = comp.getStyledDocument(); + + Style style = comp.addStyle("superscript", null); + StyleConstants.setSuperscript(style, true); + StyleConstants.setFontSize(style, 32); + StyleConstants.setBackground(style, Color.YELLOW); + try { + doc.insertString(doc.getLength(), "hello", style); + } catch (Exception e) {} + + comp.setDocument(doc); + comp.setBackground(Color.RED); + + frame.getContentPane().add(comp, BorderLayout.CENTER); + + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + + BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = (Graphics2D) img.getGraphics(); + frame.paint(g2d); + ImageIO.write(img, "png", new File("AppTest.png")); + g2d.dispose(); + + BufferedImage bimg = img.getSubimage(0, 80, width, 1); + ImageIO.write(bimg, "png", new File("AppTest1.png")); + robot.waitForIdle(); + robot.delay(1000); + for (int x = 10; x < width / 2; x++) { + Color col = new Color(bimg.getRGB(x, 0)); + System.out.println(Integer.toHexString(bimg.getRGB(x, 0))); + if (col.equals(Color.YELLOW)) { + throw new RuntimeException(" Background is painted taller than needed for styled text"); + } + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} 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.Consumer; 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 @@ import static org.testng.Assert.*; * @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 @@ public class TestTerminatingThreadLocal { // 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 @@ public class TestTerminatingThreadLocal { 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/misc/ThreadFlock/ThreadFlockTest.java b/test/jdk/jdk/internal/misc/ThreadFlock/ThreadFlockTest.java index ebc718d2203..3fc143f38c8 100644 --- a/test/jdk/jdk/internal/misc/ThreadFlock/ThreadFlockTest.java +++ b/test/jdk/jdk/internal/misc/ThreadFlock/ThreadFlockTest.java @@ -530,7 +530,7 @@ class ThreadFlockTest { } /** - * Test awaitAll with interrupt status set, should interrupt thread. + * Test awaitAll with interrupted status set, should interrupt thread. */ @ParameterizedTest @MethodSource("factories") @@ -550,17 +550,17 @@ class ThreadFlockTest { Thread thread = factory.newThread(awaitLatch); flock.start(thread); - // invoke awaitAll with interrupt status set. + // invoke awaitAll with interrupted status set. Thread.currentThread().interrupt(); try { flock.awaitAll(); fail("awaitAll did not throw"); } catch (InterruptedException e) { - // interrupt status should be clear + // interrupted status should be clear assertFalse(Thread.currentThread().isInterrupted()); } - // invoke awaitAll(Duration) with interrupt status set. + // invoke awaitAll(Duration) with interrupted status set. Thread.currentThread().interrupt(); try { flock.awaitAll(Duration.ofSeconds(30)); @@ -568,7 +568,7 @@ class ThreadFlockTest { } catch (TimeoutException e) { fail("TimeoutException not expected"); } catch (InterruptedException e) { - // interrupt status should be clear + // interrupted status should be clear assertFalse(Thread.currentThread().isInterrupted()); } @@ -609,7 +609,7 @@ class ThreadFlockTest { flock.awaitAll(); fail("awaitAll did not throw"); } catch (InterruptedException e) { - // interrupt status should be clear + // interrupted status should be clear assertFalse(Thread.currentThread().isInterrupted()); } @@ -620,7 +620,7 @@ class ThreadFlockTest { } catch (TimeoutException e) { fail("TimeoutException not expected"); } catch (InterruptedException e) { - // interrupt status should be clear + // interrupted status should be clear assertFalse(Thread.currentThread().isInterrupted()); } @@ -841,7 +841,7 @@ class ThreadFlockTest { } /** - * Test close with interrupt status set, should not interrupt threads. + * Test close with interrupted status set, should not interrupt threads. */ @ParameterizedTest @MethodSource("factories") 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 @@ public class MetricsMemoryTester { // 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.Common; 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 @@ public class TestDockerMemoryMetrics { // 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 @@ public class TestDockerMemoryMetrics { 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/net/www/protocol/file/FileURLConnStreamLeakTest.java b/test/jdk/sun/net/www/protocol/file/FileURLConnStreamLeakTest.java new file mode 100644 index 00000000000..3cf7755107c --- /dev/null +++ b/test/jdk/sun/net/www/protocol/file/FileURLConnStreamLeakTest.java @@ -0,0 +1,181 @@ +/* + * 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.io.IOException; +import java.io.InputStream; +import java.net.URLConnection; +import java.net.UnknownServiceException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import 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; + +/* + * @test + * @bug 8367561 + * @summary verify that the implementation of URLConnection APIs for "file:" + * protocol does not leak InputStream(s) + * @run junit/othervm ${test.main.class} + */ +class FileURLConnStreamLeakTest { + + private static final String FILE_URLCONNECTION_CLASSNAME = "sun.net.www.protocol.file.FileURLConnection"; + + private Path testFile; + // FileInputStream has a Cleaner which closes its underlying file descriptor. + // Here we keep reference to the URLConnection for the duration of each test method. + // This ensures that any FileInputStream this URLConnection may be retaining, won't be GCed + // until after the test method has checked for file descriptor leaks. + private URLConnection conn; + + @BeforeEach + void beforeEach() throws Exception { + final Path file = Files.createTempFile(Path.of("."), "8367561-", ".txt"); + Files.writeString(file, String.valueOf(System.currentTimeMillis())); + this.testFile = file; + this.conn = this.testFile.toUri().toURL().openConnection(); + assertNotNull(this.conn, "URLConnection for " + this.testFile + " is null"); + assertEquals(FILE_URLCONNECTION_CLASSNAME, conn.getClass().getName(), + "unexpected URLConnection type"); + } + + @AfterEach + void afterEach() throws Exception { + this.conn = null; + // the file should already have been deleted by the test method + Files.deleteIfExists(this.testFile); + } + + static List> urlConnOperations() { + return List.of( + URLConnection::getContentEncoding, + URLConnection::getContentLength, + URLConnection::getContentLengthLong, + URLConnection::getContentType, + URLConnection::getDate, + URLConnection::getExpiration, + URLConnection::getLastModified + ); + } + + @MethodSource("urlConnOperations") + @ParameterizedTest + void testURLConnOps(final Consumer connConsumer) throws IOException { + connConsumer.accept(this.conn); + // verify that the URLConnection isn't holding on to any file descriptors + // of this test file. + Files.delete(this.testFile); // must not fail + } + + @Test + void testGetHeaderField() throws Exception { + final var _ = this.conn.getHeaderField(0); + // verify that the URLConnection isn't holding on to any file descriptors + // of this test file. + Files.delete(this.testFile); // must not fail + } + + @Test + void testGetHeaderFieldString() throws Exception { + final String val = this.conn.getHeaderField("foo"); + assertNull(val, "unexpected header field value: " + val); + // verify that the URLConnection isn't holding on to any file descriptors + // of this test file. + Files.delete(this.testFile); // must not fail + } + + @Test + void testGetHeaderFieldDate() throws Exception { + final var _ = this.conn.getHeaderFieldDate("bar", 42); + // verify that the URLConnection isn't holding on to any file descriptors + // of this test file. + Files.delete(this.testFile); // must not fail + } + + @Test + void testGetHeaderFieldInt() throws Exception { + final int val = this.conn.getHeaderFieldInt("hello", 42); + assertEquals(42, val, "unexpected header value"); + // verify that the URLConnection isn't holding on to any file descriptors + // of this test file. + Files.delete(this.testFile); // must not fail + } + + @Test + void testGetHeaderFieldKey() throws Exception { + final String val = this.conn.getHeaderFieldKey(42); + assertNull(val, "unexpected header value: " + val); + // verify that the URLConnection isn't holding on to any file descriptors + // of this test file. + Files.delete(this.testFile); // must not fail + } + + @Test + void testGetHeaderFieldLong() throws Exception { + final long val = this.conn.getHeaderFieldLong("foo", 42); + assertEquals(42, val, "unexpected header value"); + // verify that the URLConnection isn't holding on to any file descriptors + // of this test file. + Files.delete(this.testFile); // must not fail + } + + @Test + void testGetHeaderFields() throws Exception { + final Map> headers = this.conn.getHeaderFields(); + assertNotNull(headers, "null headers"); + // verify that the URLConnection isn't holding on to any file descriptors + // of this test file. + Files.delete(this.testFile); // must not fail + } + + @Test + void testGetInputStream() throws Exception { + try (final InputStream is = this.conn.getInputStream()) { + assertNotNull(is, "input stream is null"); + } + // verify that the URLConnection isn't holding on to any file descriptors + // of this test file. + Files.delete(this.testFile); // must not fail + } + + @Test + void testGetOutputStream() throws Exception { + // FileURLConnection only supports reading + assertThrows(UnknownServiceException.class, this.conn::getOutputStream); + // verify that the URLConnection isn't holding on to any file descriptors + // of this test file. + Files.delete(this.testFile); // must not fail + } +} + diff --git a/test/jdk/sun/net/www/protocol/file/GetInputStreamTest.java b/test/jdk/sun/net/www/protocol/file/GetInputStreamTest.java new file mode 100644 index 00000000000..25c1ee2e965 --- /dev/null +++ b/test/jdk/sun/net/www/protocol/file/GetInputStreamTest.java @@ -0,0 +1,150 @@ +/* + * 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.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URLConnection; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.Collator; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * @test + * @summary verify the behaviour of URLConnection.getInputStream() + * for "file:" protocol + * @run junit ${test.main.class} + */ +class GetInputStreamTest { + + /** + * Calls URLConnection.getInputStream() on the URLConnection for a directory and verifies + * the contents returned by the InputStream. + */ + @Test + void testDirInputStream() throws Exception { + final Path dir = Files.createTempDirectory(Path.of("."), "fileurlconn-"); + final int numEntries = 3; + // write some files into that directory + for (int i = 1; i <= numEntries; i++) { + Files.writeString(dir.resolve(i + ".txt"), "" + i); + } + final String expectedDirListing = getDirListing(dir.toFile(), numEntries); + final URLConnection conn = dir.toUri().toURL().openConnection(); + assertNotNull(conn, "URLConnection is null for " + dir); + // call getInputStream() and verify that the streamed directory + // listing is the expected one + try (final InputStream is = conn.getInputStream()) { + assertNotNull(is, "InputStream is null for " + conn); + final String actual = new BufferedReader(new InputStreamReader(is)) + .readAllAsString(); + assertEquals(expectedDirListing, actual, + "unexpected content from input stream for dir " + dir); + } + // now that we successfully obtained the InputStream, read its content + // and closed it, call getInputStream() again and verify that it can no longer + // be used to read any more content. + try (final InputStream is = conn.getInputStream()) { + assertNotNull(is, "input stream is null for " + conn); + final int readByte = is.read(); + assertEquals(-1, readByte, "expected to have read EOF from the stream"); + } + } + + /** + * Calls URLConnection.getInputStream() on the URLConnection for a regular file and verifies + * the contents returned by the InputStream. + */ + @Test + void testRegularFileInputStream() throws Exception { + final Path dir = Files.createTempDirectory(Path.of("."), "fileurlconn-"); + final Path regularFile = dir.resolve("foo.txt"); + final String expectedContent = "bar"; + Files.writeString(regularFile, expectedContent); + + final URLConnection conn = regularFile.toUri().toURL().openConnection(); + assertNotNull(conn, "URLConnection is null for " + regularFile); + // get the input stream and verify the streamed content + try (final InputStream is = conn.getInputStream()) { + assertNotNull(is, "input stream is null for " + conn); + final String actual = new BufferedReader(new InputStreamReader(is)) + .readAllAsString(); + assertEquals(expectedContent, actual, + "unexpected content from input stream for file " + regularFile); + } + // now that we successfully obtained the InputStream, read its content + // and closed it, call getInputStream() again and verify that it can no longer + // be used to read any more content. + try (final InputStream is = conn.getInputStream()) { + assertNotNull(is, "input stream is null for " + conn); + // for regular files the FileURLConnection's InputStream throws a IOException + // when attempting to read after EOF + final IOException thrown = assertThrows(IOException.class, is::read); + final String exMessage = thrown.getMessage(); + assertEquals("Stream closed", exMessage, "unexpected exception message"); + } + } + + /** + * Verifies that URLConnection.getInputStream() for a non-existent file path + * throws FileNotFoundException. + */ + @Test + void testNonExistentFile() throws Exception { + final Path existentDir = Files.createTempDirectory(Path.of("."), "fileurlconn-"); + final Path nonExistent = existentDir.resolve("non-existent"); + final URLConnection conn = nonExistent.toUri().toURL().openConnection(); + assertNotNull(conn, "URLConnection is null for " + nonExistent); + final FileNotFoundException thrown = assertThrows(FileNotFoundException.class, + conn::getInputStream); + final String exMessage = thrown.getMessage(); + assertTrue(exMessage != null && exMessage.contains(nonExistent.getFileName().toString()), + "unexpected exception message: " + exMessage); + } + + private static String getDirListing(final File dir, final int numExpectedEntries) { + final List dirListing = Arrays.asList(dir.list()); + dirListing.sort(Collator.getInstance()); // same as what FileURLConnection does + + assertEquals(numExpectedEntries, dirListing.size(), + dir + " - expected " + numExpectedEntries + " entries but found: " + dirListing); + + final StringBuilder sb = new StringBuilder(); + for (String fileName : dirListing) { + sb.append(fileName); + sb.append("\n"); + } + return sb.toString(); + } +} diff --git a/test/jdk/sun/nio/cs/TestEncoderReplaceLatin1.java b/test/jdk/sun/nio/cs/TestEncoderReplaceLatin1.java index 401f3650734..972bc521bb6 100644 --- a/test/jdk/sun/nio/cs/TestEncoderReplaceLatin1.java +++ b/test/jdk/sun/nio/cs/TestEncoderReplaceLatin1.java @@ -192,7 +192,7 @@ class TestEncoderReplaceLatin1 { /** * Verifies {@linkplain CoderResult#isUnmappable() unmappable} character * {@linkplain CodingErrorAction#REPLACE replacement} using {@link - * ArrayEncoder#encodeFromLatin1(byte[], int, int, byte[]) + * ArrayEncoder#encodeFromLatin1(byte[], int, int, byte[], int) * ArrayEncoder::encodeFromLatin1}. */ private static void testArrayEncoderLatin1Replace(CharsetEncoder encoder, char unmappable, byte[] replacement) { @@ -202,7 +202,7 @@ class TestEncoderReplaceLatin1 { } byte[] sa = {(byte) unmappable}; byte[] da = new byte[replacement.length]; - int dp = arrayEncoder.encodeFromLatin1(sa, 0, 1, da); + int dp = arrayEncoder.encodeFromLatin1(sa, 0, 1, da, 0); assertTrue(dp == replacement.length && Arrays.equals(da, replacement), () -> { Object context = Map.of( "dp", dp, diff --git a/test/jdk/sun/nio/cs/TestEncoderReplaceUTF16.java b/test/jdk/sun/nio/cs/TestEncoderReplaceUTF16.java index a93dac16ab6..6ab49d91c6a 100644 --- a/test/jdk/sun/nio/cs/TestEncoderReplaceUTF16.java +++ b/test/jdk/sun/nio/cs/TestEncoderReplaceUTF16.java @@ -182,7 +182,7 @@ class TestEncoderReplaceUTF16 { /** * Verifies {@linkplain CoderResult#isUnmappable() unmappable} character * {@linkplain CodingErrorAction#REPLACE replacement} using {@link - * ArrayEncoder#encodeFromUTF16(byte[], int, int, byte[]) + * ArrayEncoder#encodeFromUTF16(byte[], int, int, byte[], int) * ArrayEncoder::encodeFromUTF16}. */ private static void testArrayEncoderUTF16Replace(CharsetEncoder encoder, byte[] unmappableUTF16Bytes, byte[] replacement) { @@ -191,7 +191,7 @@ class TestEncoderReplaceUTF16 { return; } byte[] da = new byte[replacement.length]; - int dp = arrayEncoder.encodeFromUTF16(unmappableUTF16Bytes, 0, unmappableUTF16Bytes.length >>> 1, da); + int dp = arrayEncoder.encodeFromUTF16(unmappableUTF16Bytes, 0, unmappableUTF16Bytes.length >>> 1, da, 0); assertTrue(dp == replacement.length && Arrays.equals(da, replacement), () -> { Object context = Map.of( "dp", dp, diff --git a/test/jdk/sun/nio/cs/TestStringCoding.java b/test/jdk/sun/nio/cs/TestStringCoding.java index d27efc35ada..d708ef180a2 100644 --- a/test/jdk/sun/nio/cs/TestStringCoding.java +++ b/test/jdk/sun/nio/cs/TestStringCoding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, 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 @@ -24,6 +24,7 @@ /* @test * @bug 6636323 6636319 7040220 7096080 7183053 8080248 8054307 * @summary Test if StringCoding and NIO result have the same de/encoding result + * @library /test/lib * @modules java.base/sun.nio.cs * @run main/othervm/timeout=2000 TestStringCoding * @key randomness @@ -32,6 +33,10 @@ import java.util.*; import java.nio.*; import java.nio.charset.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static jdk.test.lib.Asserts.assertEquals; public class TestStringCoding { public static void main(String[] args) throws Throwable { @@ -195,29 +200,70 @@ public class TestStringCoding { if (cs.name().equals("UTF-8") || // utf8 handles surrogates cs.name().equals("CESU-8")) // utf8 handles surrogates return; - enc.replaceWith(new byte[] { (byte)'A'}); - sun.nio.cs.ArrayEncoder cae = (sun.nio.cs.ArrayEncoder)enc; - String str = "ab\uD800\uDC00\uD800\uDC00cd"; - byte[] ba = new byte[str.length() - 2]; - int n = cae.encode(str.toCharArray(), 0, str.length(), ba); - if (n != 6 || !"abAAcd".equals(new String(ba, cs.name()))) - throw new RuntimeException("encode1(surrogates) failed -> " - + cs.name()); + // Configure the replacement sequence + enc.replaceWith(new byte[]{(byte) 'A'}); + + // Test `String::new(byte[], Charset)` with surrogate-pair + { + var srcStr = "ab\uD800\uDC00\uD800\uDC00cd"; + assertEquals(8, srcStr.length()); + var srcBuf = CharBuffer.wrap(srcStr.toCharArray(), 0, 8); + var dstBuf = ByteBuffer.allocate(6); + var cr = enc.encode(srcBuf, dstBuf, true); + if (cr.isError()) { + cr.throwException(); + } + var dstArr = dstBuf.array(); + assertEquals( + 6, dstBuf.position(), + "Was expecting 6 items, found: " + Map.of( + "position", dstBuf.position(), + "array", prettyPrintBytes(dstArr))); + var dstStr = new String(dstArr, cs); + assertEquals("abAAcd", dstStr); + } + + // Test `String::new(byte[], int, int, Charset)` with surrogate-pair + { + var srcStr = "ab\uD800\uDC00\uD800\uDC00cd"; + assertEquals(8, srcStr.length()); + var srcBuf = CharBuffer.wrap(srcStr.toCharArray(), 0, 8); + var dstBuf = ByteBuffer.allocate(8); + var cr = enc.encode(srcBuf, dstBuf, true); + if (cr.isError()) { + cr.throwException(); + } + var dstArr = dstBuf.array(); + assertEquals( + 6, dstBuf.position(), + "Was expecting 6 items, found: " + Map.of( + "position", dstBuf.position(), + "array", prettyPrintBytes(dstArr))); + var dstStr = new String(dstArr, 0, 6, cs); + assertEquals("abAAcd", dstStr); + } + + // Test `String::new(byte[], int, int, Charset)` with a dangling + // high- and low-surrogate + { + var srcStr = "ab\uD800B\uDC00Bcd"; + var srcBuf = CharBuffer.wrap(srcStr.toCharArray(), 0, 8); + var dstBuf = ByteBuffer.allocate(8); + var cr = enc.encode(srcBuf, dstBuf, true); + if (cr.isError()) { + cr.throwException(); + } + var dstArr = dstBuf.array(); + assertEquals( + 8, dstBuf.position(), + "Was expecting 8 items, found: " + Map.of( + "position", dstBuf.position(), + "array", prettyPrintBytes(dstArr))); + var dstStr = new String(dstArr, 0, 8, cs); + assertEquals("abABABcd", dstStr); + } - ba = new byte[str.length()]; - n = cae.encode(str.toCharArray(), 0, str.length(), ba); - if (n != 6 || !"abAAcd".equals(new String(ba, 0, n, - cs.name()))) - throw new RuntimeException("encode2(surrogates) failed -> " - + cs.name()); - str = "ab\uD800B\uDC00Bcd"; - ba = new byte[str.length()]; - n = cae.encode(str.toCharArray(), 0, str.length(), ba); - if (n != 8 || !"abABABcd".equals(new String(ba, 0, n, - cs.name()))) - throw new RuntimeException("encode3(surrogates) failed -> " - + cs.name()); /* sun.nio.cs.ArrayDeEncoder works on the assumption that the invoker (StringCoder) allocates enough output buf, utf8 and double-byte coder does not check the output buffer limit. @@ -242,4 +288,9 @@ public class TestStringCoding { } } } + + private static String prettyPrintBytes(byte[] bs) { + return "[" + HexFormat.ofDelimiter(", ").withPrefix("0x").formatHex(bs) + "]"; + } + } diff --git a/test/jdk/sun/security/krb5/config/native/TestDynamicStore.java b/test/jdk/sun/security/krb5/config/native/TestDynamicStore.java index 7e396013a71..0ee559f33e4 100644 --- a/test/jdk/sun/security/krb5/config/native/TestDynamicStore.java +++ b/test/jdk/sun/security/krb5/config/native/TestDynamicStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 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 @@ -27,13 +27,15 @@ * @summary SCDynamicStoreConfig works * @modules java.security.jgss/sun.security.krb5 * @library /test/lib - * @run main/manual/native TestDynamicStore + * @run main/manual/native/timeout=180 TestDynamicStore * @requires (os.family == "mac") */ import jdk.test.lib.Asserts; import sun.security.krb5.Config; +import javax.swing.JOptionPane; + // =================== Attention =================== // This test calls a native method implemented in libTestDynamicStore.m // to modify system-level Kerberos 5 settings stored in the dynamic store. @@ -56,6 +58,17 @@ public class TestDynamicStore { public static void main(String[] args) throws Exception { + // Show a popup to remind to run this test as sudo user + // this will only trigger if sudo (root) user is not detected + if (!"root".equals(System.getProperty("user.name"))) { + + JOptionPane.showMessageDialog(null, """ + This test MUST be run as ROOT.\s + Please close and RESTART the test."""); + + Asserts.assertFalse(true, "This test must be run as ROOT"); + } + System.loadLibrary("TestDynamicStore"); Config cfg = Config.getInstance(); diff --git a/test/jdk/sun/security/pkcs11/Cipher/TestKATForGCM.java b/test/jdk/sun/security/pkcs11/Cipher/TestKATForGCM.java index 9844e8ecfd2..e5e8284e6f4 100644 --- a/test/jdk/sun/security/pkcs11/Cipher/TestKATForGCM.java +++ b/test/jdk/sun/security/pkcs11/Cipher/TestKATForGCM.java @@ -321,14 +321,19 @@ public class TestKATForGCM extends PKCS11Test { System.out.println("Test Passed!"); } } catch (Exception e) { - System.out.println("Exception occured using " + p.getName() + " version " + p.getVersionStr()); + System.out.println("Exception occured using " + p.getName() + + " version " + p.getVersionStr()); if (isNSS(p)) { - double ver = getNSSInfo("nss"); + Version ver = getNSSInfo("nss"); String osName = System.getProperty("os.name"); - if (ver > 3.139 && ver < 3.15 && osName.equals("Linux")) { + + if (osName.equals("Linux") && + ver.major() == 3 && ver.minor() < 15 + && (ver.minor() > 13 && ver.patch() >= 9)) { // warn about buggy behaviour on Linux with nss 3.14 - System.out.println("Warning: old NSS " + ver + " might be problematic, consider upgrading it"); + System.out.println("Warning: old NSS " + ver + + " might be problematic, consider upgrading it"); } } throw e; diff --git a/test/jdk/sun/security/pkcs11/KeyStore/SecretKeysBasic.java b/test/jdk/sun/security/pkcs11/KeyStore/SecretKeysBasic.java index 4d876604c01..1ff80fcaf07 100644 --- a/test/jdk/sun/security/pkcs11/KeyStore/SecretKeysBasic.java +++ b/test/jdk/sun/security/pkcs11/KeyStore/SecretKeysBasic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2023, 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 @@ -116,11 +116,14 @@ public class SecretKeysBasic extends PKCS11Test { // A bug in NSS 3.12 (Mozilla bug 471665) causes AES key lengths // to be read incorrectly. Checking for improper 16 byte length // in key string. - if (isNSS(provider) && expected.getAlgorithm().equals("AES") && - (getNSSVersion() >= 3.12 && getNSSVersion() <= 3.122)) { - System.out.println("NSS 3.12 bug returns incorrect AES key "+ - "length breaking key storage. Aborting..."); - return true; + if (isNSS(provider) && expected.getAlgorithm().equals("AES")) { + Version version = getNSSVersion(); + if (version.major() == 3 && version.minor() == 12 + && version.patch() <= 2) { + System.out.println("NSS 3.12 bug returns incorrect AES key " + + "length breaking key storage. Aborting..."); + return true; + } } if (saveBeforeCheck) { @@ -168,7 +171,7 @@ public class SecretKeysBasic extends PKCS11Test { private static void doTest() throws Exception { // Make sure both NSS libraries are the same version. if (isNSS(provider) && - (getLibsoftokn3Version() != getLibnss3Version())) { + (!getLibsoftokn3Version().equals(getLibnss3Version()))) { System.out.println("libsoftokn3 and libnss3 versions do not match. Aborting test..."); return; } diff --git a/test/jdk/sun/security/pkcs11/PKCS11Test.java b/test/jdk/sun/security/pkcs11/PKCS11Test.java index 8454f3ac463..98343d9b7e6 100644 --- a/test/jdk/sun/security/pkcs11/PKCS11Test.java +++ b/test/jdk/sun/security/pkcs11/PKCS11Test.java @@ -83,7 +83,7 @@ public abstract class PKCS11Test { private static final String NSS_BUNDLE_VERSION = "3.111"; private static final String NSSLIB = "jpg.tests.jdk.nsslib"; - static double nss_version = -1; + static Version nss_version = null; static ECCState nss_ecc_status = ECCState.Basic; // The NSS library we need to search for in getNSSLibDir() @@ -93,8 +93,8 @@ public abstract class PKCS11Test { // NSS versions of each library. It is simpler to keep nss_version // for quick checking for generic testing than many if-else statements. - static double softoken3_version = -1; - static double nss3_version = -1; + static Version softoken3_version = null; + static Version nss3_version = null; static Provider pkcs11 = newPKCS11Provider(); private static String PKCS11_BASE; private static Map osMap; @@ -269,13 +269,29 @@ public abstract class PKCS11Test { } static boolean isBadNSSVersion(Provider p) { - double nssVersion = getNSSVersion(); - if (isNSS(p) && nssVersion >= 3.11 && nssVersion < 3.12) { - System.out.println("NSS 3.11 has a DER issue that recent " + - "version do not, skipping"); - return true; + Version nssVersion = getNSSVersion(); + if (isNSS(p)) { + // bad version is just between [3.11,3.12) + return nssVersion.major == 3 && 11 == nssVersion.minor; + } else { + return false; } - return false; + } + + public record Version(int major, int minor, int patch) {} + + protected static Version parseVersionString(String version) { + String [] parts = version.split("\\."); + int major = Integer.parseInt(parts[0]); + int minor = 0; + int patch = 0; + if (parts.length >= 2) { + minor = Integer.parseInt(parts[1]); + } + if (parts.length >= 3) { + patch = Integer.parseInt(parts[2]); + } + return new Version(major, minor, patch); } protected static void safeReload(String lib) { @@ -304,26 +320,26 @@ public abstract class PKCS11Test { return p.getName().equalsIgnoreCase("SUNPKCS11-NSS"); } - static double getNSSVersion() { - if (nss_version == -1) + static Version getNSSVersion() { + if (nss_version == null) getNSSInfo(); return nss_version; } static ECCState getNSSECC() { - if (nss_version == -1) + if (nss_version == null) getNSSInfo(); return nss_ecc_status; } - public static double getLibsoftokn3Version() { - if (softoken3_version == -1) + public static Version getLibsoftokn3Version() { + if (softoken3_version == null) return getNSSInfo("softokn3"); return softoken3_version; } - public static double getLibnss3Version() { - if (nss3_version == -1) + public static Version getLibnss3Version() { + if (nss3_version == null) return getNSSInfo("nss3"); return nss3_version; } @@ -338,7 +354,7 @@ public abstract class PKCS11Test { // $Header: NSS // Version: NSS // Here, stands for NSS version. - static double getNSSInfo(String library) { + static Version getNSSInfo(String library) { // look for two types of headers in NSS libraries String nssHeader1 = "$Header: NSS"; String nssHeader2 = "Version: NSS"; @@ -347,15 +363,15 @@ public abstract class PKCS11Test { int i = 0; Path libfile = null; - if (library.compareTo("softokn3") == 0 && softoken3_version > -1) + if (library.compareTo("softokn3") == 0 && softoken3_version != null) return softoken3_version; - if (library.compareTo("nss3") == 0 && nss3_version > -1) + if (library.compareTo("nss3") == 0 && nss3_version != null) return nss3_version; try { libfile = getNSSLibPath(); if (libfile == null) { - return 0.0; + return parseVersionString("0.0"); } try (InputStream is = Files.newInputStream(libfile)) { byte[] data = new byte[1000]; @@ -391,7 +407,7 @@ public abstract class PKCS11Test { if (!found) { System.out.println("lib" + library + " version not found, set to 0.0: " + libfile); - nss_version = 0.0; + nss_version = parseVersionString("0.0"); return nss_version; } @@ -404,26 +420,7 @@ public abstract class PKCS11Test { version.append(c); } - // If a "dot dot" release, strip the extra dots for double parsing - String[] dot = version.toString().split("\\."); - if (dot.length > 2) { - version = new StringBuilder(dot[0] + "." + dot[1]); - for (int j = 2; dot.length > j; j++) { - version.append(dot[j]); - } - } - - // Convert to double for easier version value checking - try { - nss_version = Double.parseDouble(version.toString()); - } catch (NumberFormatException e) { - System.out.println("===== Content start ====="); - System.out.println(s); - System.out.println("===== Content end ====="); - System.out.println("Failed to parse lib" + library + - " version. Set to 0.0"); - e.printStackTrace(); - } + nss_version = parseVersionString(version.toString()); System.out.print("library: " + library + ", version: " + version + ". "); diff --git a/test/jdk/sun/security/pkcs11/Secmod/AddTrustedCert.java b/test/jdk/sun/security/pkcs11/Secmod/AddTrustedCert.java index 880adc954ea..7b4a5075da8 100644 --- a/test/jdk/sun/security/pkcs11/Secmod/AddTrustedCert.java +++ b/test/jdk/sun/security/pkcs11/Secmod/AddTrustedCert.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, 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 @@ -121,10 +121,10 @@ public class AddTrustedCert extends SecmodTest { } private static boolean improperNSSVersion(Provider p) { - double nssVersion = getNSSVersion(); - if (p.getName().equalsIgnoreCase("SunPKCS11-NSSKeyStore") - && nssVersion >= 3.28 && nssVersion < 3.35) { - return true; + Version nssVersion = getNSSVersion(); + if (p.getName().equalsIgnoreCase("SunPKCS11-NSSKeyStore")) { + return nssVersion.major() == 3 && + (nssVersion.minor() >= 28 && nssVersion.minor() < 35); } return false; diff --git a/test/jdk/sun/security/pkcs11/Signature/TestDSAKeyLength.java b/test/jdk/sun/security/pkcs11/Signature/TestDSAKeyLength.java index a9b43a647a9..ffd7b9e3ee0 100644 --- a/test/jdk/sun/security/pkcs11/Signature/TestDSAKeyLength.java +++ b/test/jdk/sun/security/pkcs11/Signature/TestDSAKeyLength.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 @@ -47,13 +47,15 @@ public class TestDSAKeyLength extends PKCS11Test { @Override protected boolean skipTest(Provider provider) { - double version = getNSSVersion(); - String[] versionStrs = Double.toString(version).split("\\."); - int major = Integer.parseInt(versionStrs[0]); - int minor = Integer.parseInt(versionStrs[1]); - if (isNSS(provider) && (version == 0.0 || (major >= 3 && minor >= 14))) { - System.out.println("Skip testing NSS " + version); - return true; + if (isNSS(provider)) { + Version version = getNSSVersion(); + if (version == null) { + return true; + } + if (version.major() >= 3 && version.minor() >= 14){ + System.out.println("Skip testing NSS " + version); + return true; + } } return false; diff --git a/test/jdk/sun/security/pkcs11/ec/TestECDH.java b/test/jdk/sun/security/pkcs11/ec/TestECDH.java index b6821b88372..2900656f626 100644 --- a/test/jdk/sun/security/pkcs11/ec/TestECDH.java +++ b/test/jdk/sun/security/pkcs11/ec/TestECDH.java @@ -111,6 +111,7 @@ public class TestECDH extends PKCS11Test { * PKCS11Test.main will remove this provider if needed */ Providers.setAt(p, 1); + System.out.println("Testing provider " + p.getName()); if (false) { KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", p); 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; - - 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 class CPValidatorTrustAnchor { } private static void validate(String trustAnchor) - throws CertPathValidatorException, Exception { + throws Exception { selfSignedCertStr = trustAnchor; CertPath path = generateCertificatePath(); @@ -176,7 +185,11 @@ public class CPValidatorTrustAnchor { 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 class InvalidBitString { } } - 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 @@ public class RSAExport { // 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 class RSAExport { 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 class RSAExport { 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 class RSAExport { // 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 @@ public class RSAExport { (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/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.java b/test/jdk/sun/security/ssl/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.java index bdc1b538608..bcfc1290cf0 100644 --- a/test/jdk/sun/security/ssl/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.java +++ b/test/jdk/sun/security/ssl/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.java @@ -272,7 +272,7 @@ public class StatusResponseManagerTests { CertStatusRequest oReq = OCSPStatusRequest.EMPTY_OCSP; try { - // Force the interrupt flag to be set on the thread that + // Force the interrupted flag to be set on the thread that // performs the invokeAll in the SRM. Thread.currentThread().interrupt(); diff --git a/test/jdk/sun/security/ssl/X509KeyManager/NullCases.java b/test/jdk/sun/security/ssl/X509KeyManager/NullCases.java index fe577cf93f6..3b50fbd149f 100644 --- a/test/jdk/sun/security/ssl/X509KeyManager/NullCases.java +++ b/test/jdk/sun/security/ssl/X509KeyManager/NullCases.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 @@ -23,7 +23,7 @@ /* * @test - * @bug 6302126 6302321 6302271 6302304 + * @bug 6302126 6302321 6302271 6302304 8369995 * @summary KeyManagerFactory.init method throws unspecified exception * for NewSunX509 algorithm * X509KeyManager implementation for NewSunX509 throws unspecified @@ -32,50 +32,192 @@ * arrays instead of null * X509KeyManager implementation for NewSunX509 throws unspecified * NullPointerException + * Extra logging and/or propagate errors in X509KeyManagerImpl + * + * @library /test/lib /test/jdk/java/net/httpclient/lib + * @modules java.base/sun.security.x509 + * + * @run junit NullCases */ -import java.io.*; -import java.net.*; -import java.security.*; -import javax.net.ssl.*; + +import jdk.test.lib.Asserts; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.X509KeyManager; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; import java.security.cert.X509Certificate; -import java.util.*; +import static jdk.httpclient.test.lib.common.DynamicKeyStoreUtil.generateCert; +import static jdk.httpclient.test.lib.common.DynamicKeyStoreUtil.generateKeyStore; +import static jdk.httpclient.test.lib.common.DynamicKeyStoreUtil.generateRSAKeyPair; public class NullCases { - public static void main(String[] args) throws Exception { - KeyManagerFactory kmf; - X509KeyManager km; - char [] password = {' '}; - // check for bug 6302126 + private static KeyManagerFactory kmf; + private static X509KeyManager km; + + @BeforeAll + public static void beforeAll() throws Exception { kmf = KeyManagerFactory.getInstance("NewSunX509"); - kmf.init((KeyStore)null, password); - // check for 6302321 + // creating a new keystore + final SecureRandom secureRandom = new SecureRandom(); + final KeyPair keyPair = generateRSAKeyPair(secureRandom); + final X509Certificate originServerCert = + generateCert(keyPair, secureRandom, "subject"); + final KeyStore ks = generateKeyStore(keyPair.getPrivate(), + new Certificate[]{originServerCert}); + + kmf.init(ks, null); km = (X509KeyManager) kmf.getKeyManagers()[0]; - X509Certificate[] certs = km.getCertificateChain("doesnotexist"); - PrivateKey priv = km.getPrivateKey("doesnotexist"); - if (certs != null || priv != null) { - throw new Exception("Should return null if the alias can't be found"); - } + } + private X509KeyManager generateNullKm() throws Exception { + char[] password = {' '}; + kmf.init((KeyStore) null, password); + return (X509KeyManager) kmf.getKeyManagers()[0]; + } + + @Test + public void JDK6302126Test() throws Exception { + // check for bug 6302126 + + generateNullKm(); + } + + @Test + public void JDK6302304Test() throws Exception { + // check for bug 6302304 + + final X509KeyManager km = generateNullKm(); + + final String[] serverAliases = + km.getServerAliases(null, null); + Asserts.assertNull(serverAliases, + "Should return null if server alias not found"); + final String[] clientAliases = + km.getClientAliases(null, null); + Asserts.assertNull(clientAliases, + "Should return null if client alias not found"); + + final X509Certificate[] certs = + km.getCertificateChain(null); + final PrivateKey priv = + km.getPrivateKey(null); + Asserts.assertNull(certs, + "Should return null if the alias can't be found"); + Asserts.assertNull(priv, + "Should return null if the alias can't be found"); + + final String serverAlias = + km.chooseServerAlias(null, null, null); + Asserts.assertNull(serverAlias, + "Should return null if the alias can't be chosen"); + final String clientAlias = + km.chooseClientAlias(null, null, null); + Asserts.assertNull(clientAlias, + "Should return null if the alias can't be chosen"); + } + + @Test + public void JDK6302321Test() { + // check for bug 6302321 + + final X509Certificate[] certs = + km.getCertificateChain("doesnotexist"); + final PrivateKey priv = km.getPrivateKey("doesnotexist"); + Asserts.assertNull(certs, + "Should return null if the alias can't be found"); + Asserts.assertNull(priv, + "Should return null if the alias can't be found"); + } + + @Test + public void JDK6302271Test() { // check for 6302271 - String[] clis = km.getClientAliases("doesnotexist", null); - if (clis != null && clis.length == 0) { - throw new Exception("Should return null instead of empty array"); - } - String[] srvs = km.getServerAliases("doesnotexist", null); - if (srvs != null && srvs.length == 0) { - throw new Exception("Should return null instead of empty array"); - } - // check for 6302304 - km.getServerAliases(null, null); - km.getClientAliases(null, null); - km.getCertificateChain(null); - km.getPrivateKey(null); - km.chooseServerAlias(null, null, null); - km.chooseClientAlias(null, null, null); + final String[] clis = + km.getClientAliases("doesnotexist", null); + Asserts.assertFalse((clis != null && clis.length == 0), + "Should return null instead of empty array"); + + final String[] srvs = + km.getServerAliases("doesnotexist", null); + Asserts.assertFalse((srvs != null && srvs.length == 0), + "Should return null instead of empty array"); + } + + /** + * The following tests are testing JDK-8369995 + */ + + @Test + public void incompleteChainAndKeyTest() { + final X509Certificate[] certs = km.getCertificateChain("1.1"); + final PrivateKey priv = km.getPrivateKey("1.1"); + + Asserts.assertNull(certs, + "Should return null if the alias is incomplete"); + Asserts.assertNull(priv, + "Should return null if the alias is incomplete"); + } + + @Test + public void nonexistentBuilderTest() { + final X509Certificate[] certs = km.getCertificateChain("RSA.1.1"); + final PrivateKey priv = km.getPrivateKey("RSA.1.1"); + + Asserts.assertNull(certs, + "Should return null if builder doesn't exist"); + Asserts.assertNull(priv, + "Should return null if builder doesn't exist"); + } + + @Test + public void nonexistentKSTest() { + final X509Certificate[] certs = km.getCertificateChain("RSA.0.1"); + final PrivateKey priv = km.getPrivateKey("RSA.0.1"); + + Asserts.assertNull(certs, + "Should return null if KS doesn't exist"); + Asserts.assertNull(priv, + "Should return null if KS doesn't exist"); + } + + @Test + public void wrongNumberFormatTest() { + final X509Certificate[] certs = + km.getCertificateChain("RSA.not.exist"); + final PrivateKey priv = km.getPrivateKey("RSA.not.exist"); + + Asserts.assertNull(certs, + "Should return null if number format is wrong in alias"); + Asserts.assertNull(priv, + "Should return null if number format is wrong in alias"); + } + + @ParameterizedTest + @ValueSource(strings = {"1..1", "1..",".1.", "..1", ".9.123456789"}) + public void invalidAliasTest(final String alias) { + final X509Certificate[] certs = km.getCertificateChain(alias); + final PrivateKey priv = km.getPrivateKey(alias); + + Asserts.assertNull(certs, + String.format( + "Should return null if the alias is invalid <%s>", + alias)); + Asserts.assertNull(priv, + String.format( + "Should return null if the alias is invalid <%s>", + alias)); } } diff --git a/test/jdk/sun/security/ssl/X509KeyManager/X509KeyManagerNegativeTests.java b/test/jdk/sun/security/ssl/X509KeyManager/X509KeyManagerNegativeTests.java new file mode 100644 index 00000000000..d50e1ee36d9 --- /dev/null +++ b/test/jdk/sun/security/ssl/X509KeyManager/X509KeyManagerNegativeTests.java @@ -0,0 +1,187 @@ +/* + * 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. + */ + +import jdk.test.lib.Asserts; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.X509KeyManager; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreSpi; +import java.security.Provider; +import java.security.Security; +import java.security.cert.Certificate; +import java.util.ConcurrentModificationException; +import java.util.Date; +import java.util.Enumeration; + +/* + * @test + * @bug 8369995 + * @summary X509KeyManagerImpl negative tests causing exceptions + * @library /test/lib + * @run junit/othervm X509KeyManagerNegativeTests + */ +public class X509KeyManagerNegativeTests { + private static X509KeyManager exceptionThrowingKM; + + @BeforeAll + public static void beforeAll() throws Exception { + + // initialising exception throwing ks + // cleaned up after the tests are complete + final KeyManagerFactory exceptionThrowingKMF = + KeyManagerFactory.getInstance("NewSunX509"); + + // adding dummy provider + Security.addProvider(new MyCustomKSProvider()); + final KeyStore exceptionThrowingKS = + KeyStore.getInstance("MyExceptionKS"); + exceptionThrowingKS.load(null, null); + + exceptionThrowingKMF + .init(exceptionThrowingKS, null); + exceptionThrowingKM = + (X509KeyManager) exceptionThrowingKMF.getKeyManagers()[0]; + } + + @AfterAll + public static void cleanup() { + // remove custom provider + Security.removeProvider("MyCustomKSProvider"); + } + + @Test + public void ksExceptionTest() { + Asserts.assertThrows(ConcurrentModificationException.class, + () -> exceptionThrowingKM.getCertificateChain("RSA.0.0")); + Asserts.assertThrows(ConcurrentModificationException.class, + () -> exceptionThrowingKM.getPrivateKey("RSA.0.0")); + } + + public static class MyCustomKSProvider extends Provider { + public MyCustomKSProvider() { + super("MyCustomKSProvider", + "1.0", + "My Custom KS Provider"); + put("KeyStore.MyExceptionKS", MyExceptionKS.class.getName()); + } + } + + public static class MyExceptionKS extends KeyStoreSpi { + + @Override + public KeyStore.Entry engineGetEntry(String alias, + KeyStore.ProtectionParameter param) + { + throw new ConcurrentModificationException("getEntry exception"); + } + + @Override + public Key engineGetKey(String alias, char[] password) { + return null; + } + + @Override + public Certificate[] engineGetCertificateChain(String alias) { + return null; + } + + @Override + public Certificate engineGetCertificate(String alias) { + return null; + } + + @Override + public Date engineGetCreationDate(String alias) { + return null; + } + + @Override + public Enumeration engineAliases() { + return null; + } + + @Override + public boolean engineContainsAlias(String alias) { + return false; + } + + @Override + public int engineSize() { + return 0; + } + + @Override + public boolean engineIsKeyEntry(String alias) { + return false; + } + + @Override + public boolean engineIsCertificateEntry(String alias) { + return false; + } + + @Override + public String engineGetCertificateAlias(Certificate cert) { + return null; + } + + @Override + public void engineStore(OutputStream stream, char[] password) { + } + + @Override + public void engineLoad(InputStream stream, char[] password) { + } + + @Override + public void engineSetKeyEntry(String alias, + Key key, + char[] password, + Certificate[] chain) { + } + + @Override + public void engineSetKeyEntry(String alias, + byte[] key, + Certificate[] chain) { + } + + @Override + public void engineSetCertificateEntry(String alias, + Certificate cert) { + } + + @Override + public void engineDeleteEntry(String alias) { + } + } +} 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 @@ public class BasicConstraints { // 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 @@ public class BasicConstraints { // 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 @@ public class BasicConstraints { 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 class BasicConstraints { 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 class BasicConstraints { 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 @@ public class ComodoHacker { } 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 @@ public class ComodoHacker { 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/tools/jarsigner/EC.java b/test/jdk/sun/security/tools/jarsigner/EC.java index 848dc8bf843..1b41c48e234 100644 --- a/test/jdk/sun/security/tools/jarsigner/EC.java +++ b/test/jdk/sun/security/tools/jarsigner/EC.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 @@ -26,6 +26,7 @@ * @bug 6870812 * @summary enhance security tools to use ECC algorithm * @library /test/lib + * @run main/timeout=300 EC */ import jdk.test.lib.SecurityTools; diff --git a/test/jdk/sun/security/tools/keytool/i18n.java b/test/jdk/sun/security/tools/keytool/i18n.java index ab9da8b1d3e..030735e966c 100644 --- a/test/jdk/sun/security/tools/keytool/i18n.java +++ b/test/jdk/sun/security/tools/keytool/i18n.java @@ -28,7 +28,7 @@ * @author charlie lai * @modules java.base/sun.security.tools.keytool * @library /test/lib - * @run main/manual/othervm -Duser.language=en i18n + * @run main/manual/othervm -Duser.language=en -Duser.country=USA i18n */ /* @@ -38,7 +38,7 @@ * @author charlie lai * @modules java.base/sun.security.tools.keytool * @library /test/lib - * @run main/manual/othervm -Duser.language=de i18n + * @run main/manual/othervm -Duser.language=de -Duser.country=DE i18n */ /* @@ -48,7 +48,7 @@ * @author charlie lai * @modules java.base/sun.security.tools.keytool * @library /test/lib - * @run main/manual/othervm -Duser.language=ja i18n + * @run main/manual/othervm -Duser.language=ja -Duser.country=JP i18n */ /* 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 class AlgorithmIdEqualsHashCode { } 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 class NullParams { 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 class NullParams { // 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 class Verify { } } - private static void setup() throws CertificateException, CRLException { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); + private static void setup() { + final PEMDecoder pemDecoder = PEMDecoder.of(); /* Create CRL */ - ByteArrayInputStream inputStream = - new ByteArrayInputStream(crlStr.getBytes()); - crl = (X509CRL)cf.generateCRL(inputStream); + crl = pemDecoder.decode(crlStr, X509CRL.class); /* Get public key of the CRL issuer cert */ - inputStream = new ByteArrayInputStream(crlIssuerCertStr.getBytes()); - X509Certificate cert - = (X509Certificate)cf.generateCertificate(inputStream); - crlIssuerCertPubKey = cert.getPublicKey(); + crlIssuerCertPubKey = pemDecoder.decode(crlIssuerCertStr, X509Certificate.class) + .getPublicKey(); + /* Get public key of the self-signed Cert */ - inputStream = new ByteArrayInputStream(selfSignedCertStr.getBytes()); - selfSignedCertPubKey = cf.generateCertificate(inputStream).getPublicKey(); + selfSignedCertPubKey = pemDecoder.decode(selfSignedCertStr, X509Certificate.class) + .getPublicKey(); + } private static void verifyCRL(PublicKey key, String providerName) diff --git a/test/jdk/tools/jpackage/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 @@ public class PrintEnv { 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 class AnnotationsTest extends JUnitAdapter { 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 class AnnotationsTest extends JUnitAdapter { "().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 class PackageTestTest extends JUnitAdapter { } @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 @@ public final class AdditionalLauncher { } } - 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 record ApplicationLayout(Path launchersDirectory, Path appDirectory, 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 @@ final class ConfigFilesStasher { 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..0d68d055b92 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigurationTarget.java @@ -0,0 +1,89 @@ +/* + * 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 addInstallVerifier(Consumer verifier) { + cmd.ifPresent(Objects.requireNonNull(verifier)); + test.ifPresent(v -> { + v.addInstallVerifier(verifier::accept); + }); + return this; + } + + public ConfigurationTarget addRunOnceInitializer(Consumer initializer) { + Objects.requireNonNull(initializer); + cmd.ifPresent(_ -> { + initializer.accept(this); + }); + test.ifPresent(v -> { + v.addRunOnceInitializer(() -> { + initializer.accept(this); + }); + }); + 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..eb9cb4b0cb6 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/FileAssociations.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/FileAssociations.java @@ -27,13 +27,11 @@ import java.io.IOException; 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 jdk.jpackage.internal.util.PathUtils; +import java.util.TreeMap; public final class FileAssociations { @@ -44,7 +42,7 @@ public final class FileAssociations { } private void createFile() { - Map entries = new HashMap<>(Map.of( + Map entries = new TreeMap<>(Map.of( "extension", suffixName, "mime-type", getMime() )); @@ -76,13 +74,6 @@ public final class FileAssociations { return this; } - Path getLinuxIconFileName() { - if (icon == null) { - return null; - } - return Path.of(getMime().replace('/', '-') + PathUtils.getSuffix(icon)); - } - Path getPropertiesFile() { return file; } @@ -95,6 +86,10 @@ public final class FileAssociations { return "application/x-jpackage-" + suffixName; } + boolean hasIcon() { + return icon != null; + } + public void applyTo(PackageTest test) { test.notForTypes(PackageType.MAC_DMG, () -> { test.addInitializer(cmd -> { 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 @@ public final class HelloApp { 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.nio.file.Path; 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 @@ import jdk.jpackage.internal.util.function.ThrowingSupplier; */ 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 @@ public class JPackageCommand extends CommandArguments { 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 class JPackageCommand extends CommandArguments { 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 class JPackageCommand extends CommandArguments { if (layout != null) { } else if (isRuntime()) { - layout = ApplicationLayout.javaRuntime(); + layout = ApplicationLayout.platformJavaRuntime(); } else { layout = ApplicationLayout.platformAppImage(); } @@ -691,7 +703,7 @@ public class JPackageCommand extends CommandArguments { } public static void useToolProviderByDefault(ToolProvider jpackageToolProvider) { - defaultToolProvider = Optional.of(jpackageToolProvider); + defaultToolProvider.set(Optional.of(jpackageToolProvider)); } public static void useToolProviderByDefault() { @@ -699,7 +711,7 @@ public class JPackageCommand extends CommandArguments { } public static void useExecutableByDefault() { - defaultToolProvider = Optional.empty(); + defaultToolProvider.set(Optional.empty()); } public JPackageCommand useToolProvider(boolean v) { @@ -808,7 +820,9 @@ public class JPackageCommand extends CommandArguments { } 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 @@ public class JPackageCommand extends CommandArguments { .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 class JPackageCommand extends CommandArguments { public JPackageCommand assertImageCreated() { verifyIsOfType(PackageType.IMAGE); - assertAppLayout(); + runStandardAsserts(); return this; } @@ -975,10 +989,10 @@ public class JPackageCommand extends CommandArguments { 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 class JPackageCommand extends CommandArguments { 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 class JPackageCommand extends CommandArguments { 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 class JPackageCommand extends CommandArguments { 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 @@ public class JPackageCommand extends CommandArguments { 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 class JPackageCommand extends CommandArguments { 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 @@ public class JPackageCommand extends CommandArguments { 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 class JPackageCommand extends CommandArguments { 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 class JPackageCommand extends CommandArguments { 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.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Optional; /** @@ -48,6 +49,11 @@ public final class JarBuilder { 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 final class JarBuilder { 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 final class JarBuilder { 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..9d0c97160f7 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,32 +38,39 @@ public final class LauncherIconVerifier { 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 void applyTo(JPackageCommand cmd) throws IOException { - final String curLauncherName; - final String label; - if (launcherName == null) { - curLauncherName = cmd.name(); - label = "main"; - } else { - curLauncherName = launcherName; - label = String.format("[%s]", launcherName); - } + public boolean expectDefaultIcon() { + return expectedDefault; + } - Path iconPath = cmd.appLayout().desktopIntegrationDirectory().resolve( - curLauncherName + TKit.ICON_SUFFIX); + public Optional expectIcon() { + return Optional.ofNullable(expectedIcon); + } + + public void applyTo(JPackageCommand cmd) throws IOException { + final String label = Optional.ofNullable(launcherName).map(v -> { + return String.format("[%s]", v); + }).orElse("main"); + + Path iconPath = cmd.appLayout().desktopIntegrationDirectory().resolve(iconFileName(cmd)); if (TKit.isWindows()) { TKit.assertPathExists(iconPath, false); @@ -71,13 +78,13 @@ public final class LauncherIconVerifier { 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)); @@ -85,6 +92,14 @@ public final class LauncherIconVerifier { } } + private Path iconFileName(JPackageCommand cmd) { + if (TKit.isLinux()) { + return LinuxHelper.getLauncherIconFileName(cmd, launcherName); + } else { + return Path.of(Optional.ofNullable(launcherName).orElseGet(cmd::name) + TKit.ICON_SUFFIX); + } + } + private String launcherName; private Path expectedIcon; private boolean expectedDefault; 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..489788b565a 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java @@ -204,7 +204,7 @@ public final class LauncherVerifier { verifier.setExpectedIcon(icon); } }, () -> { - // No "icon" property in the property file + // No "icon" property in the property file. iconInResourceDir(cmd, name).ifPresentOrElse(verifier::setExpectedIcon, () -> { // No icon for this additional launcher in the resource directory. mainLauncherIcon.ifPresentOrElse(verifier::setExpectedIcon, verifier::setExpectedDefaultIcon); @@ -212,6 +212,29 @@ public final class LauncherVerifier { }); } + if (TKit.isLinux()) { + // On Linux, a launcher may have an icon only if it has a corresponding .desktop file. + // In case of "app-image" packaging there are no .desktop files, but jpackage will add icon files + // in the app image anyways so that in two-step packaging jpackage can pick the icons for .desktop files. + // jpackage should not add the default icon to the app image in case of "app-image" packaging. + if (cmd.isImagePackageType()) { + // This is "app-image" packaging. Let's see if, in two-step packaging, + // jpackage creates a .desktop file for this launcher. + if (!withLinuxDesktopFile(cmd.createMutableCopy().setPackageType(PackageType.LINUX_RPM))) { + // No .desktop file in the "future" package for this launcher, + // then don't expect an icon in the app image produced by the `cmd`. + verifier.setExpectedNoIcon(); + } else if (verifier.expectDefaultIcon()) { + // A .desktop file in the "future" package for this launcher, + // but it will use the default icon. + // Don't expect an icon in the app image produced by the `cmd`. + verifier.setExpectedNoIcon(); + } + } else if (!withLinuxDesktopFile(cmd)) { + verifier.setExpectedNoIcon(); + } + } + return verifier; } @@ -339,9 +362,20 @@ public final class LauncherVerifier { 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..4bc8295eaf8 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 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 static jdk.jpackage.test.AdditionalLauncher.getAdditionalLauncherProperties; + import java.io.IOException; import java.io.UncheckedIOException; import java.lang.reflect.InvocationTargetException; @@ -32,17 +38,18 @@ import java.util.ArrayList; 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.Consumer; 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; @@ -84,9 +91,7 @@ public final class LinuxHelper { public static Path getDesktopFile(JPackageCommand cmd, String launcherName) { cmd.verifyIsOfType(PackageType.LINUX); - String desktopFileName = String.format("%s-%s.desktop", getPackageName( - cmd), Optional.ofNullable(launcherName).orElseGet( - () -> cmd.name()).replaceAll("\\s+", "_")); + var desktopFileName = getLauncherDesktopFileName(cmd, launcherName); return cmd.appLayout().desktopIntegrationDirectory().resolve( desktopFileName); } @@ -161,8 +166,7 @@ public final class LinuxHelper { 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") @@ -199,6 +203,20 @@ public final class LinuxHelper { } } + private static Path getFaIconFileName(JPackageCommand cmd, String mimeType) { + return Path.of(mimeType.replace('/', '-') + ".png"); + } + + static Path getLauncherDesktopFileName(JPackageCommand cmd, String launcherName) { + return Path.of(String.format("%s-%s.desktop", getPackageName(cmd), + Optional.ofNullable(launcherName).orElseGet(cmd::name).replaceAll("\\s+", "_"))); + } + + static Path getLauncherIconFileName(JPackageCommand cmd, String launcherName) { + return Path.of(String.format("%s.png", + Optional.ofNullable(launcherName).orElseGet(cmd::name).replaceAll("\\s+", "_"))); + } + static PackageHandlers createDebPackageHandlers() { return new PackageHandlers(LinuxHelper::installDeb, LinuxHelper::uninstallDeb, LinuxHelper::unpackDeb); } @@ -323,10 +341,9 @@ public final class LinuxHelper { 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 +389,7 @@ public final class LinuxHelper { 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 +405,7 @@ public final class LinuxHelper { 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", @@ -421,7 +436,12 @@ public final class LinuxHelper { }); } - static void verifyDesktopFiles(JPackageCommand cmd, boolean installed) { + static void verifyDesktopIntegrationFiles(JPackageCommand cmd, boolean installed) { + verifyDesktopFiles(cmd, installed); + verifyAllIconsReferenced(cmd); + } + + private static void verifyDesktopFiles(JPackageCommand cmd, boolean installed) { final var desktopFiles = getDesktopFiles(cmd); try { if (installed) { @@ -452,11 +472,62 @@ public final class LinuxHelper { } 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 void verifyAllIconsReferenced(JPackageCommand cmd) { + + var installCmd = Optional.ofNullable(cmd.unpackedPackageDirectory()).map(_ -> { + return cmd.createMutableCopy().setUnpackedPackageLocation(null); + }).orElse(cmd); + + var installedIconFiles = relativePackageFilesInSubdirectory( + installCmd, + ApplicationLayout::desktopIntegrationDirectory + ).filter(path -> { + return ".png".equals(PathUtils.getSuffix(path)); + }).map(installCmd.appLayout().desktopIntegrationDirectory()::resolve).collect(toSet()); + + var referencedIcons = getDesktopFiles(cmd).stream().map(path -> { + return new DesktopFile(path, false); + }).mapMulti((desktopFile, sink) -> { + desktopFile.findQuotedValue("Icon").map(Path::of).ifPresent(sink); + desktopFile.find("MimeType").ifPresent(str -> { + Stream.of(str.split(";")) + .map(mimeType -> { + return getFaIconFileName(cmd, mimeType); + }) + .map(installCmd.appLayout().desktopIntegrationDirectory()::resolve) + .forEach(sink); + }); + }).collect(toSet()); + + var unreferencedIconFiles = Comm.compare(installedIconFiles, referencedIcons).unique1().stream().sorted().toList(); + + // Verify that all package icon (.png) files are referenced from package .desktop files. + TKit.assertEquals(List.of(), unreferencedIconFiles, "Check there are no unreferenced icon files in the package"); } private static String launcherNameFromDesktopFile(JPackageCommand cmd, Optional predefinedAppImage, Path desktopFile) { @@ -488,13 +559,26 @@ public final class LinuxHelper { var data = new DesktopFile(desktopFile, true); - final Set 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)); @@ -628,16 +712,19 @@ public final class LinuxHelper { }); test.addBundleVerifier(cmd -> { - final Path mimeTypeIconFileName = fa.getLinuxIconFileName(); - if (mimeTypeIconFileName != null) { - // Verify there are xdg registration commands for mime icon file. - Path mimeTypeIcon = cmd.appLayout().desktopIntegrationDirectory().resolve( - mimeTypeIconFileName); + Optional.of(fa).filter(FileAssociations::hasIcon) + .map(FileAssociations::getMime) + .map(mimeType -> { + return getFaIconFileName(cmd, mimeType); + }).ifPresent(mimeTypeIconFileName -> { + // Verify there are xdg registration commands for mime icon file. + Path mimeTypeIcon = cmd.appLayout().desktopIntegrationDirectory().resolve( + mimeTypeIconFileName); - Map> scriptlets = getScriptlets(cmd); - scriptlets.entrySet().stream().forEach(e -> verifyIconInScriptlet( - e.getKey(), e.getValue(), mimeTypeIcon)); - } + Map> scriptlets = getScriptlets(cmd); + scriptlets.entrySet().stream().forEach(e -> verifyIconInScriptlet( + e.getKey(), e.getValue(), mimeTypeIcon)); + }); }); } @@ -710,7 +797,7 @@ public final class LinuxHelper { 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 +819,7 @@ public final class LinuxHelper { 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 +853,10 @@ public final class LinuxHelper { 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 +933,7 @@ public final class LinuxHelper { } 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 +974,8 @@ public final class LinuxHelper { 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.Objects; 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.PathUtils; 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 @@ public final class MacHelper { } } + /** + * 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 final class MacHelper { }).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.Optional; 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 @@ public final class MacSign { 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 @@ public final class MacSign { 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 final class MacSign { 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 final class MacSign { 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 @@ import jdk.jpackage.test.MacSign.CertificateRequest; */ 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 final class MacSignVerify { } 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 final class MacSignVerify { } 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 @@ import java.util.stream.Stream; 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..2e73f43b58d 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.nio.file.Path; 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 @@ public final class PackageTest extends RunnablePackageTest { 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 final class PackageTest extends RunnablePackageTest { } 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 final class PackageTest extends RunnablePackageTest { } 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() { @@ -772,7 +784,7 @@ public final class PackageTest extends RunnablePackageTest { } if (isOfType(cmd, LINUX)) { - LinuxHelper.verifyDesktopFiles(cmd, true); + LinuxHelper.verifyDesktopIntegrationFiles(cmd, true); } } @@ -780,7 +792,7 @@ public final class PackageTest extends RunnablePackageTest { LauncherAsServiceVerifier.verify(cmd); } - cmd.assertAppLayout(); + cmd.runStandardAsserts(); installVerifiers.forEach(v -> v.accept(cmd)); } @@ -853,7 +865,7 @@ public final class PackageTest extends RunnablePackageTest { } if (isOfType(cmd, LINUX)) { - LinuxHelper.verifyDesktopFiles(cmd, false); + LinuxHelper.verifyDesktopIntegrationFiles(cmd, false); } } 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..baeeda4e569 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,11 @@ package jdk.jpackage.test; 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.ThrowingFunction.toFunction; 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 +39,7 @@ import java.nio.file.Files; 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 +111,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 +121,44 @@ public final class TKit { } 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 +175,25 @@ public final class TKit { 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(); + withNewState(() -> { + try { + if (modes.contains(RunTestMode.FAIL_FAST)) { + test.run(); + } else { + ignoreExceptions(test).run(); + } + } finally { + Optional.ofNullable(state().extraLogStream).ifPresent(PrintStream::flush); } - } finally { - currentTest = null; - if (extraLogStream != null) { - extraLogStream.flush(); - } - } + }, stateBuilder -> { + stateBuilder.currentTest(test); + }); }); }); } @@ -217,18 +250,18 @@ public final class TKit { } 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 +289,10 @@ public final class TKit { 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 +342,13 @@ public final class TKit { } 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 +609,14 @@ public final class TKit { 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 +691,9 @@ public final class TKit { } 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 +704,7 @@ public final class TKit { } 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 +715,7 @@ public final class TKit { } 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 +726,7 @@ public final class TKit { } 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 +737,7 @@ public final class TKit { } 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 +749,7 @@ public final class TKit { 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 +761,7 @@ public final class TKit { } 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 +774,7 @@ public final class TKit { } 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 +784,7 @@ public final class TKit { } public static void assertNotNull(Object value, String msg) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); if (value == null) { error(concatMessages("Unexpected null value", msg)); } @@ -764,7 +801,7 @@ public final class TKit { } public static void assertTrue(boolean actual, String msg, Runnable onFail) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); if (!actual) { if (onFail != null) { onFail.run(); @@ -776,7 +813,7 @@ public final class TKit { } public static void assertFalse(boolean actual, String msg, Runnable onFail) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); if (actual) { if (onFail != null) { onFail.run(); @@ -797,6 +834,35 @@ public final class TKit { } } + 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)); } @@ -831,7 +897,14 @@ public final class TKit { public static void assertSymbolicLinkExists(Path path) { assertPathExists(path, true); assertTrue(Files.isSymbolicLink(path), String.format - ("Check [%s] is a symbolic link", path)); + ("Check [%s] is a symbolic link", Objects.requireNonNull(path))); + } + + public static void assertSymbolicLinkTarget(Path symlinkPath, Path expectedTargetPath) { + assertSymbolicLinkExists(symlinkPath); + var targetPath = toFunction(Files::readSymbolicLink).apply(symlinkPath); + assertEquals(expectedTargetPath, targetPath, + String.format("Check the target of the symbolic link [%s]", symlinkPath)); } public static void assertFileExists(Path path) { @@ -853,7 +926,7 @@ public final class TKit { } public static void assertUnexpected(String msg) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); error(concatMessages("Unexpected", msg)); } @@ -879,7 +952,7 @@ public final class TKit { } public void match(Set expected) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); var comm = Comm.compare(content, expected); if (!comm.unique1().isEmpty() && !comm.unique2().isEmpty()) { @@ -906,7 +979,7 @@ public final class TKit { } public void contains(Set expected) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); var comm = Comm.compare(content, expected); if (!comm.unique2().isEmpty()) { @@ -951,7 +1024,7 @@ public final class TKit { public static void assertStringListEquals(List expected, List actual, String msg) { - currentTest.notifyAssert(); + currentTest().notifyAssert(); traceAssert(concatMessages("assertStringListEquals()", msg)); @@ -1177,12 +1250,13 @@ public final class TKit { } 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 +1268,8 @@ public final class TKit { 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 +1300,6 @@ public final class TKit { } } - 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 +1327,19 @@ public final class TKit { 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(); - - 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); - }; - - 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")); - } + private static TestInstance currentTest() { + return state().currentTest; } + static boolean verboseJPackage() { + return state().verboseJPackage; + } + + static boolean verboseTestSetup() { + return state().verboseTestSetup; + } + + private static final class JtregSkippedExceptionClass extends ClassLoader { @SuppressWarnings("unchecked") JtregSkippedExceptionClass() { @@ -1319,4 +1365,159 @@ public final class TKit { 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 @@ final class TestBuilder implements AutoCloseable { } 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 @@ final class TestMethodSupplier { } 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 class ShortcutHintTest { "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.List; 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 class CustomInfoPListTest { 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 class CustomInfoPListTest { 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 static jdk.jpackage.test.MacHelper.writeFaPListFragment; 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 class MacFileAssociationsTest { 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 class MacSignTest { 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 class MacSignTest { .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 class MacSignTest { 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 class MacSignTest { @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 class MacSignTest { @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 jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.TKit; -import jdk.jpackage.test.PackageType; -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; +import jdk.jpackage.test.PackageType; +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 class SigningAppImageTwoStepsTest { 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 class SigningAppImageTwoStepsTest { 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 @@ public class SigningPackageFromTwoStepAppImageTest { // 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 class SigningPackageFromTwoStepAppImageTest { .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 class SigningPackageFromTwoStepAppImageTest { 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 class SigningPackageFromTwoStepAppImageTest { 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 @@ public class SigningPackageTest { // 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 class SigningPackageTest { .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 @@ import jdk.jpackage.test.Annotations.Parameter; */ public class SigningPackageTwoStepTest { - private static void verifyPKG(JPackageCommand cmd) { - if (!cmd.hasArgument("--mac-sign")) { - return; // Nothing to check if not signed - } - - Path outputBundle = cmd.outputBundle(); - SigningBase.verifyPkgutil(outputBundle, true, SigningBase.DEFAULT_INDEX); - SigningBase.verifySpctl(outputBundle, "install", SigningBase.DEFAULT_INDEX); - } - - private static void verifyDMG(JPackageCommand cmd) { - // DMG always unsigned, so we will check it - Path outputBundle = cmd.outputBundle(); - SigningBase.verifyDMG(outputBundle); - } - - 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); - } - } - }); - } - @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"); + @ParameterSupplier + public static void test(TestSpec spec) { + MacSign.withKeychain(toConsumer(keychain -> { + spec.test(keychain); + }), SigningBase.StandardKeychain.MAIN.keychain()); + } - 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)); + 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); } } - 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(); + @Override + public String toString() { + var sb = new StringBuilder(); + + signAppImage.ifPresent(signOption -> { + sb.append(String.format("app-image=%s", signOption)); + }); + + if (!sb.isEmpty() && !signPackage.isEmpty()) { + sb.append("; "); + } + + if (!signPackage.isEmpty()) { + sb.append(signPackage); + } + + return sb.toString(); + } + + boolean signNativeBundle() { + return signPackage.isEmpty(); + } + + 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 @@ import jdk.jpackage.test.TKit; */ 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 @@ public class SigningRuntimeImagePackageTest { .addArguments("--runtime-image", runtimeImage) .addArguments("--dest", runtimeBundleWorkDir); - addSignOptions(cmd, certIndex); + addSignOptions(cmd, keychain, certIndex); cmd.execute(); @@ -147,13 +149,21 @@ public class SigningRuntimeImagePackageTest { 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 class SigningRuntimeImagePackageTest { // 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 @@ public class SigningBase { 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 class SigningBase { 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 @@ public class SigningBase { "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 class SigningBase { 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 class SigningBase { 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 class AddLShortcutTest { 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 class AddLauncherTest { "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/AppContentTest.java b/test/jdk/tools/jpackage/share/AppContentTest.java index 2c6498a631b..5b5734df61d 100644 --- a/test/jdk/tools/jpackage/share/AppContentTest.java +++ b/test/jdk/tools/jpackage/share/AppContentTest.java @@ -21,24 +21,43 @@ * questions. */ -import static jdk.internal.util.OperatingSystem.LINUX; -import static jdk.internal.util.OperatingSystem.MACOS; +import static java.util.Map.entry; import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toMap; +import static jdk.internal.util.OperatingSystem.MACOS; +import static jdk.internal.util.OperatingSystem.WINDOWS; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; -import jdk.jpackage.test.PackageTest; -import jdk.jpackage.test.TKit; -import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.Annotations.Parameter; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collection; +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.function.Predicate; +import java.util.function.Supplier; import java.util.stream.Stream; +import java.util.stream.StreamSupport; import jdk.jpackage.internal.util.FileUtils; -import jdk.jpackage.internal.util.function.ThrowingFunction; +import jdk.jpackage.internal.util.IdentityWrapper; +import jdk.jpackage.internal.util.function.ThrowingSupplier; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.ParameterSupplier; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.ConfigurationTarget; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JPackageStringBundle; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.TKit; /** @@ -57,68 +76,23 @@ import jdk.jpackage.test.JPackageStringBundle; */ public class AppContentTest { - private static final String TEST_JAVA = "apps/PrintEnv.java"; - private static final String TEST_DUKE = "apps/dukeplug.png"; - private static final String TEST_DUKE_LINK = "dukeplugLink.txt"; - private static final String TEST_DIR = "apps"; - private static final String TEST_BAD = "non-existant"; - - // On OSX `--app-content` paths will be copied into the "Contents" folder - // of the output app image. - // "codesign" imposes restrictions on the directory structure of "Contents" folder. - // In particular, random files should be placed in "Contents/Resources" folder - // otherwise "codesign" will fail to sign. - // Need to prepare arguments for `--app-content` accordingly. - private static final boolean copyInResources = TKit.isOSX(); - - private static final String RESOURCES_DIR = "Resources"; - private static final String LINKS_DIR = "Links"; + @Test + @ParameterSupplier + @ParameterSupplier(value="testSymlink", ifNotOS = WINDOWS) + public void test(TestSpec testSpec) throws Exception { + testSpec.test(new ConfigurationTarget(new PackageTest().configureHelloApp())); + } @Test - // include two files in two options - @Parameter({TEST_JAVA, TEST_DUKE}) - // try to include non-existant content - @Parameter({TEST_JAVA, TEST_BAD}) - // two files in one option and a dir tree in another option. - @Parameter({TEST_JAVA + "," + TEST_DUKE, TEST_DIR}) - // include one file and one link to the file - @Parameter(value = {TEST_JAVA, TEST_DUKE_LINK}, ifOS = {MACOS,LINUX}) - public void test(String... args) throws Exception { - final List testPathArgs = List.of(args); - final int expectedJPackageExitCode; - if (testPathArgs.contains(TEST_BAD)) { - expectedJPackageExitCode = 1; - } else { - expectedJPackageExitCode = 0; - } - - var appContentInitializer = new AppContentInitializer(testPathArgs); - - new PackageTest().configureHelloApp() - .addRunOnceInitializer(appContentInitializer::initAppContent) - .addInitializer(appContentInitializer::applyTo) - .addInstallVerifier(cmd -> { - for (String arg : testPathArgs) { - List paths = Arrays.asList(arg.split(",")); - for (String p : paths) { - Path name = Path.of(p).getFileName(); - if (isSymlinkPath(name)) { - TKit.assertSymbolicLinkExists(getAppContentRoot(cmd) - .resolve(LINKS_DIR).resolve(name)); - } else { - TKit.assertPathExists(getAppContentRoot(cmd) - .resolve(name), true); - } - } - } - }) - .setExpectedExitCode(expectedJPackageExitCode) - .run(); + @ParameterSupplier("test") + @ParameterSupplier(value="testSymlink", ifNotOS = WINDOWS) + public void testAppImage(TestSpec testSpec) throws Exception { + testSpec.test(new ConfigurationTarget(JPackageCommand.helloAppImage())); } @Test(ifOS = MACOS) - @Parameter({TEST_DIR, "warning.non.standard.contents.sub.dir"}) - @Parameter({TEST_DUKE, "warning.app.content.is.not.dir"}) + @Parameter({"apps", "warning.non.standard.contents.sub.dir"}) + @Parameter({"apps/dukeplug.png", "warning.app.content.is.not.dir"}) public void testWarnings(String testPath, String warningId) throws Exception { final var appContentValue = TKit.TEST_SRC_ROOT.resolve(testPath); final var expectedWarning = JPackageStringBundle.MAIN.cannedFormattedString( @@ -126,96 +100,588 @@ public class AppContentTest { JPackageCommand.helloAppImage() .addArguments("--app-content", appContentValue) + .setFakeRuntime() .validateOutput(expectedWarning) .executeIgnoreExitCode(); } + public static Collection test() { + return Stream.of( + build().add(TEST_JAVA).add(TEST_DUKE), + build().add(TEST_JAVA).add(TEST_BAD), + build().startGroup().add(TEST_JAVA).add(TEST_DUKE).endGroup().add(TEST_DIR), + // Same directory specified multiple times. + build().add(TEST_DIR).add(TEST_DIR), + // Same file specified multiple times. + build().add(TEST_JAVA).add(TEST_JAVA), + // Two files with the same name but different content. + build() + .add(createTextFileContent("welcome.txt", "Welcome")) + .add(createTextFileContent("welcome.txt", "Benvenuti")), + // Same name: one is a directory, another is a file. + build().add(createTextFileContent("a/b/c/d", "Foo")).add(createTextFileContent("a", "Bar")), + // Same name: one is a file, another is a directory. + build().add(createTextFileContent("a", "Bar")).add(createTextFileContent("a/b/c/d", "Foo")) + ).map(TestSpec.Builder::create).map(v -> { + return new Object[] {v}; + }).toList(); + } + + public static Collection testSymlink() { + return Stream.of( + build().add(TEST_JAVA) + .add(new SymlinkContentFactory("Links", "duke-link", "duke-target")) + .add(new SymlinkContentFactory("", "a/b/foo-link", "c/bar-target")) + ).map(TestSpec.Builder::create).map(v -> { + return new Object[] {v}; + }).toList(); + } + + public record TestSpec(List> contentFactories) { + public TestSpec { + contentFactories.stream().flatMap(List::stream).forEach(Objects::requireNonNull); + } + + @Override + public String toString() { + return contentFactories.stream().map(group -> { + return group.stream().map(ContentFactory::toString).collect(joining(",")); + }).collect(joining("; ")); + } + + void test(ConfigurationTarget target) { + final int expectedJPackageExitCode; + if (contentFactories.stream().flatMap(List::stream).anyMatch(TEST_BAD::equals)) { + expectedJPackageExitCode = 1; + } else { + expectedJPackageExitCode = 0; + } + + final List> allContent = new ArrayList<>(); + + target.addInitializer(JPackageCommand::setFakeRuntime) + .addRunOnceInitializer(_ -> { + contentFactories.stream().map(group -> { + return group.stream().map(ContentFactory::create).toList(); + }).forEach(allContent::add); + }).addInitializer(cmd -> { + allContent.stream().map(group -> { + return Stream.of("--app-content", group.stream() + .map(Content::paths) + .flatMap(List::stream) + .map(appContentArg -> { + if (COPY_IN_RESOURCES && Optional.ofNullable(appContentArg.getParent()) + .map(Path::getFileName) + .map(RESOURCES_DIR::equals) + .orElse(false)) { + return appContentArg.getParent(); + } else { + return appContentArg; + } + }) + .map(Path::toString) + .collect(joining(","))); + }).flatMap(x -> x).forEachOrdered(cmd::addArgument); + }); + + target.cmd().ifPresent(cmd -> { + if (expectedJPackageExitCode == 0) { + cmd.executeAndAssertImageCreated(); + } else { + cmd.execute(expectedJPackageExitCode); + } + }); + + target.addInstallVerifier(cmd -> { + var appContentRoot = getAppContentRoot(cmd); + + Set disabledVerifiers = new HashSet<>(); + + var verifiers = allContent.stream().flatMap(List::stream).flatMap(content -> { + return StreamSupport.stream(content.verifiers(appContentRoot).spliterator(), false).map(verifier -> { + return new PathVerifierWithOrigin(verifier, content); + }); + }).collect(toMap(PathVerifierWithOrigin::path, x -> x, (first, second) -> { + // The same file in the content directory is sourced from multiple origins. + // jpackage will handle this case such that the following origins overwrite preceding origins. + // Scratch all path verifiers affected by overrides. + first.getNestedVerifiers(appContentRoot, first.path()).forEach(disabledVerifiers::add); + return second; + }, TreeMap::new)).values().stream() + .map(PathVerifierWithOrigin::verifier) + .filter(Predicate.not(disabledVerifiers::contains)) + .filter(verifier -> { + if (!(verifier instanceof DirectoryVerifier dirVerifier)) { + return true; + } else { + try { + // Run the directory verifier if the directory is empty. + // Otherwise, it just pollutes the test log. + return isDirectoryEmpty(verifier.path()); + } catch (NoSuchFileException ex) { + // If an MSI contains an empty directory, it will be installed but not created when the MSI is unpacked. + // In the latter the control flow will reach this point. + if (dirVerifier.isEmpty() + && PackageType.WINDOWS.contains(cmd.packageType()) + && cmd.isPackageUnpacked(String.format( + "Expected empty directory [%s] is missing", verifier.path()))) { + return false; + } + throw new UncheckedIOException(ex); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + }) + .toList(); + + verifiers.forEach(PathVerifier::verify); + }); + + target.test().ifPresent(test -> { + test.setExpectedExitCode(expectedJPackageExitCode).run(); + }); + } + + static final class Builder { + TestSpec create() { + return new TestSpec(groups); + } + + final class GroupBuilder { + GroupBuilder add(ContentFactory cf) { + group.add(Objects.requireNonNull(cf)); + return this; + } + + Builder endGroup() { + if (!group.isEmpty()) { + groups.add(group); + } + return Builder.this; + } + + private final List group = new ArrayList<>(); + } + + Builder add(ContentFactory cf) { + return startGroup().add(cf).endGroup(); + } + + GroupBuilder startGroup() { + return new GroupBuilder(); + } + + private final List> groups = new ArrayList<>(); + } + + private record PathVerifierWithOrigin(PathVerifier verifier, Content origin) { + PathVerifierWithOrigin { + Objects.requireNonNull(verifier); + Objects.requireNonNull(origin); + } + + Path path() { + return verifier.path(); + } + + Stream getNestedVerifiers(Path appContentRoot, Path path) { + if (!path.startsWith(appContentRoot)) { + throw new IllegalArgumentException(); + } + + return StreamSupport.stream(origin.verifiers(appContentRoot).spliterator(), false).filter(v -> { + return v.path().getNameCount() > path.getNameCount() && v.path().startsWith(path); + }); + } + } + } + + private static TestSpec.Builder build() { + return new TestSpec.Builder(); + } + private static Path getAppContentRoot(JPackageCommand cmd) { - Path contentDir = cmd.appLayout().contentDirectory(); - if (copyInResources) { + final Path contentDir = cmd.appLayout().contentDirectory(); + if (COPY_IN_RESOURCES) { return contentDir.resolve(RESOURCES_DIR); } else { return contentDir; } } - private static boolean isSymlinkPath(Path v) { - return v.getFileName().toString().contains("Link"); + private static Path createAppContentRoot() { + if (COPY_IN_RESOURCES) { + return TKit.createTempDirectory("app-content").resolve(RESOURCES_DIR); + } else { + return TKit.createTempDirectory("app-content"); + } } - private static final class AppContentInitializer { - AppContentInitializer(List appContentArgs) { - appContentPathGroups = appContentArgs.stream().map(arg -> { - return Stream.of(arg.split(",")).map(Path::of).toList(); - }).toList(); + private static boolean isDirectoryEmpty(Path path) throws IOException { + if (Files.exists(path) && !Files.isDirectory(path)) { + throw new IllegalArgumentException(); } - void initAppContent() { - jpackageArgs = appContentPathGroups.stream() - .map(AppContentInitializer::initAppContentPaths) - .mapMulti((appContentPaths, consumer) -> { - consumer.accept("--app-content"); - consumer.accept( - appContentPaths.stream().map(Path::toString).collect( - joining(","))); - }).toList(); + try (var files = Files.list(path)) { + return files.findAny().isEmpty(); + } + } + + @FunctionalInterface + private interface ContentFactory { + Content create(); + } + + private interface Content { + List paths(); + Iterable verifiers(Path appContentRoot); + } + + private sealed interface PathVerifier permits + RegularFileVerifier, + DirectoryVerifier, + SymlinkTargetVerifier, + NoPathVerifier { + + Path path(); + void verify(); + } + + private record RegularFileVerifier(Path path, Path srcFile) implements PathVerifier { + RegularFileVerifier { + Objects.requireNonNull(path); + Objects.requireNonNull(srcFile); } - void applyTo(JPackageCommand cmd) { - cmd.addArguments(jpackageArgs); + @Override + public void verify() { + TKit.assertSameFileContent(srcFile, path); + } + } + + private record DirectoryVerifier(Path path, boolean isEmpty, IdentityWrapper origin) implements PathVerifier { + DirectoryVerifier { + Objects.requireNonNull(path); } - private static Path copyAppContentPath(Path appContentPath) throws IOException { - var appContentArg = TKit.createTempDirectory("app-content").resolve(RESOURCES_DIR); - var srcPath = TKit.TEST_SRC_ROOT.resolve(appContentPath); - var dstPath = appContentArg.resolve(srcPath.getFileName()); - FileUtils.copyRecursive(srcPath, dstPath); - return appContentArg; - } - - private static Path createAppContentLink(Path appContentPath) throws IOException { - var appContentArg = TKit.createTempDirectory("app-content"); - Path dstPath; - if (copyInResources) { - appContentArg = appContentArg.resolve(RESOURCES_DIR); - dstPath = appContentArg.resolve(LINKS_DIR) - .resolve(appContentPath.getFileName()); + @Override + public void verify() { + if (isEmpty) { + TKit.assertDirectoryEmpty(path); } else { - appContentArg = appContentArg.resolve(LINKS_DIR); - dstPath = appContentArg.resolve(appContentPath.getFileName()); + TKit.assertDirectoryExists(path); + } + } + } + + private record SymlinkTargetVerifier(Path path, Path targetPath) implements PathVerifier { + SymlinkTargetVerifier { + Objects.requireNonNull(path); + Objects.requireNonNull(targetPath); + } + + @Override + public void verify() { + TKit.assertSymbolicLinkTarget(path, targetPath); + } + } + + private record NoPathVerifier(Path path) implements PathVerifier { + NoPathVerifier { + Objects.requireNonNull(path); + } + + @Override + public void verify() { + TKit.assertPathExists(path, false); + } + } + + private record FileContent(Path path, int level) implements Content { + + FileContent { + Objects.requireNonNull(path); + if ((level < 0) || (path.getNameCount() <= level)) { + throw new IllegalArgumentException(); + } + } + + @Override + public List paths() { + return List.of(appContentOptionValue()); + } + + @Override + public Iterable verifiers(Path appContentRoot) { + List verifiers = new ArrayList<>(); + + var appContentPath = appContentRoot.resolve(pathInAppContentRoot()); + + if (Files.isDirectory(path)) { + try (var walk = Files.walk(path)) { + verifiers.addAll(walk.map(srcFile -> { + var dstFile = appContentPath.resolve(path.relativize(srcFile)); + if (Files.isRegularFile(srcFile)) { + return new RegularFileVerifier(dstFile, srcFile); + } else { + return new DirectoryVerifier(dstFile, + toFunction(AppContentTest::isDirectoryEmpty).apply(srcFile), + new IdentityWrapper<>(this)); + } + }).toList()); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } else if (Files.isRegularFile(path)) { + verifiers.add(new RegularFileVerifier(appContentPath, path)); + } else { + verifiers.add(new NoPathVerifier(appContentPath)); } - Files.createDirectories(dstPath.getParent()); - - // Create target file for a link - String tagetName = dstPath.getFileName().toString().replace("Link", ""); - Path targetPath = dstPath.getParent().resolve(tagetName); - Files.write(targetPath, "foo".getBytes()); - // Create link - Files.createSymbolicLink(dstPath, targetPath.getFileName()); - - return appContentArg; - } - - private static List initAppContentPaths(List appContentPaths) { - return appContentPaths.stream().map(appContentPath -> { - if (appContentPath.endsWith(TEST_BAD)) { - return appContentPath; - } else if (isSymlinkPath(appContentPath)) { - return ThrowingFunction.toFunction( - AppContentInitializer::createAppContentLink).apply( - appContentPath); - } else if (copyInResources) { - return ThrowingFunction.toFunction( - AppContentInitializer::copyAppContentPath).apply( - appContentPath); - } else { - return TKit.TEST_SRC_ROOT.resolve(appContentPath); + if (level > 0) { + var cur = appContentPath; + for (int i = 0; i != level; i++) { + cur = cur.getParent(); + verifiers.add(new DirectoryVerifier(cur, false, new IdentityWrapper<>(this))); } - }).toList(); + } + + return verifiers; } - private List jpackageArgs; - private final List> appContentPathGroups; + private Path appContentOptionValue() { + var cur = path; + for (int i = 0; i != level; i++) { + cur = cur.getParent(); + } + return cur; + } + + private Path pathInAppContentRoot() { + return StreamSupport.stream(path.spliterator(), false) + .skip(path.getNameCount() - level - 1) + .reduce(Path::resolve).orElseThrow(); + } } + + /** + * Non-existing content. + */ + private static final class NonExistantPath implements ContentFactory { + @Override + public Content create() { + var nonExistant = TKit.createTempFile("non-existant"); + try { + TKit.deleteIfExists(nonExistant); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + return new FileContent(nonExistant, 0); + } + + @Override + public String toString() { + return "*non-existant*"; + } + } + + /** + * Creates a content from a directory tree. + * + * @param path name of directory where to create a directory tree + */ + private static ContentFactory createDirTreeContent(Path path) { + if (path.isAbsolute()) { + throw new IllegalArgumentException(); + } + + return new FileContentFactory(() -> { + var basedir = TKit.createTempDirectory("content").resolve(path); + + for (var textFile : Map.ofEntries( + entry("woods/moose", "The moose"), + entry("woods/bear", "The bear"), + entry("woods/trees/jay", "The gray jay") + ).entrySet()) { + var src = basedir.resolve(textFile.getKey()); + Files.createDirectories(src.getParent()); + TKit.createTextFile(src, Stream.of(textFile.getValue())); + } + + for (var emptyDir : List.of("sky")) { + Files.createDirectories(basedir.resolve(emptyDir)); + } + + return basedir; + }, path); + } + + private static ContentFactory createDirTreeContent(String path) { + return createDirTreeContent(Path.of(path)); + } + + /** + * Creates a content from a text file. + * + * @param path the path where to copy the text file in app image's content directory + * @param lines the content of the source text file + */ + private static ContentFactory createTextFileContent(Path path, String ... lines) { + if (path.isAbsolute()) { + throw new IllegalArgumentException(); + } + + return new FileContentFactory(() -> { + var srcPath = TKit.createTempDirectory("content").resolve(path); + Files.createDirectories(srcPath.getParent()); + TKit.createTextFile(srcPath, Stream.of(lines)); + return srcPath; + }, path); + } + + private static ContentFactory createTextFileContent(String path, String ... lines) { + return createTextFileContent(Path.of(path), lines); + } + + /** + * Symlink content factory. + * + * @path basedir the directory where to write the content in app image's content + * directory + * @param symlink the path to the symlink relative to {@code basedir} path + * @param symlinked the path to the source file for the symlink + */ + private record SymlinkContentFactory(Path basedir, Path symlink, Path symlinked) implements ContentFactory { + SymlinkContentFactory { + for (final var path : List.of(basedir, symlink, symlinked)) { + if (path.isAbsolute()) { + throw new IllegalArgumentException(); + } + } + } + + SymlinkContentFactory(String basedir, String symlink, String symlinked) { + this(Path.of(basedir), Path.of(symlink), Path.of(symlinked)); + } + + @Override + public Content create() { + final var appContentRoot = createAppContentRoot(); + + final var symlinkPath = appContentRoot.resolve(symlinkPath()); + final var symlinkedPath = appContentRoot.resolve(symlinkedPath()); + try { + Files.createDirectories(symlinkPath.getParent()); + Files.createDirectories(symlinkedPath.getParent()); + // Create the target file for the link. + Files.writeString(symlinkedPath, symlinkedPath().toString()); + // Create the link. + Files.createSymbolicLink(symlinkPath, symlinkTarget()); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + + List contentPaths; + if (COPY_IN_RESOURCES) { + contentPaths = List.of(appContentRoot); + } else if (basedir.equals(Path.of(""))) { + contentPaths = Stream.of(symlinkPath(), symlinkedPath()).map(path -> { + return path.getName(0); + }).map(appContentRoot::resolve).toList(); + } else { + contentPaths = List.of(appContentRoot.resolve(basedir)); + } + + return new Content() { + @Override + public List paths() { + return contentPaths; + } + + @Override + public Iterable verifiers(Path appContentRoot) { + return List.of( + new RegularFileVerifier(appContentRoot.resolve(symlinkedPath()), symlinkedPath), + new SymlinkTargetVerifier(appContentRoot.resolve(symlinkPath()), symlinkTarget()) + ); + } + }; + } + + @Override + public String toString() { + return String.format("symlink:[%s]->[%s][%s]", symlinkPath(), symlinkedPath(), symlinkTarget()); + } + + private Path symlinkPath() { + return basedir.resolve(symlink); + } + + private Path symlinkedPath() { + return basedir.resolve(symlinked); + } + + private Path symlinkTarget() { + return Optional.ofNullable(symlinkPath().getParent()).map(dir -> { + return dir.relativize(symlinkedPath()); + }).orElseGet(this::symlinkedPath); + } + } + + private static final class FileContentFactory implements ContentFactory { + + FileContentFactory(ThrowingSupplier factory, Path pathInAppContentRoot) { + this.factory = ThrowingSupplier.toSupplier(factory); + this.pathInAppContentRoot = pathInAppContentRoot; + if (pathInAppContentRoot.isAbsolute()) { + throw new IllegalArgumentException(); + } + } + + @Override + public Content create() { + Path srcPath = factory.get(); + if (!srcPath.endsWith(pathInAppContentRoot)) { + throw new IllegalArgumentException(); + } + + Path dstPath; + if (!COPY_IN_RESOURCES) { + dstPath = srcPath; + } else { + var contentDir = createAppContentRoot(); + dstPath = contentDir.resolve(pathInAppContentRoot); + try { + FileUtils.copyRecursive(srcPath, dstPath); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + return new FileContent(dstPath, pathInAppContentRoot.getNameCount() - 1); + } + + @Override + public String toString() { + return pathInAppContentRoot.toString(); + } + + private final Supplier factory; + private final Path pathInAppContentRoot; + } + + private static final ContentFactory TEST_JAVA = createTextFileContent("apps/PrintEnv.java", "Not what someone would expect"); + private static final ContentFactory TEST_DUKE = createTextFileContent("duke.txt", "Hi Duke!"); + private static final ContentFactory TEST_DIR = createDirTreeContent("apps"); + private static final ContentFactory TEST_BAD = new NonExistantPath(); + + // On OSX `--app-content` paths will be copied into the "Contents" folder + // of the output app image. + // "codesign" imposes restrictions on the directory structure of "Contents" folder. + // In particular, random files should be placed in "Contents/Resources" folder + // otherwise "codesign" will fail to sign. + // Need to prepare arguments for `--app-content` accordingly. + private static final boolean COPY_IN_RESOURCES = TKit.isOSX(); + + private static final Path RESOURCES_DIR = Path.of("Resources"); } 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 @@ import jdk.jpackage.test.Annotations.Test; */ 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 class AppImagePackageTest { }).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 class AppImagePackageTest { 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 class AppImagePackageTest { }).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 class FileAssociationsTest { 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 class FileAssociationsTest { 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..12458eda34b 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.ArrayList; 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 class IconTest { 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 class IconTest { @Test public void test() throws IOException { - if (appImage) { - JPackageCommand cmd = initAppImageTest(); - var result = cmd.executeAndAssertImageCreated(); - ThrowingConsumer.toConsumer(createInstallVerifier()).accept(cmd); - ThrowingBiConsumer.toBiConsumer(createBundleVerifier()).accept(cmd, result); - } else { - PackageTest test = initPackageTest(); - test.addInstallVerifier(createInstallVerifier()); - test.addBundleVerifier(createBundleVerifier()); + final ConfigurationTarget target; + if (appImage) { + target = new ConfigurationTarget(JPackageCommand.helloAppImage()); + } else { + 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 @@ public class IconTest { 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; - - case DefaultIcon: - verifier.setExpectedDefaultIcon(); - 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 -> { + var verifier = new LauncherIconVerifier(); + + var bundleIconType = getBundleIconType(cmd, Launcher.Main); + + switch (bundleIconType) { + case NoIcon: + verifier.setExpectedNoIcon(); + break; + + case DefaultIcon: + verifier.setExpectedDefaultIcon(); + 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; + } + 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 @@ public class IconTest { }; } - 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,54 @@ public class IconTest { } } - 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; + } + + if (TKit.isLinux()) { + var noDefaultIcon = cmd.isImagePackageType() || !cmd.hasArgument("--linux-shortcut"); + + if (noDefaultIcon && iconType == IconType.DefaultIcon) { + iconType = IconType.NoIcon; + } + } + + return iconType; } private static void addResourceDirIcon(JPackageCommand cmd, String launcherName, Path iconPath) throws IOException { - Path resourceDir = cmd.getArgumentValue("--resource-dir", () -> null, - Path::of); - if (resourceDir == null) { - resourceDir = TKit.createTempDirectory("resources"); - cmd.addArguments("--resource-dir", resourceDir); - } + var resourceDir = Optional.ofNullable(cmd.getArgumentValue("--resource-dir")).map(Path::of).orElseGet(() -> { + return TKit.createTempDirectory("resources"); + }); - String dstIconFileName = Optional.ofNullable(launcherName).orElseGet( - () -> cmd.name()) + TKit.ICON_SUFFIX; + cmd.addArguments("--resource-dir", resourceDir); + + String dstIconFileName = Optional.ofNullable(launcherName).orElseGet(cmd::name) + TKit.ICON_SUFFIX; TKit.trace(String.format("Resource file: [%s] <- [%s]", resourceDir.resolve(dstIconFileName), iconPath)); @@ -392,23 +436,16 @@ public class IconTest { 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.Parameters; 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 @@ public final class InOutPathTest { 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 @@ public class LicenseTest { 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 @@ public class RuntimePackageTest { }) .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 @@ public class WinNoRestartTest { // 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/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 @@ class APITest { "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 @@ class APITest { && !s.equals("system-properties.html")) .collect(Collectors.toSet()); } - diff --git a/test/langtools/tools/javac/OverrideChecks/Private.out b/test/langtools/tools/javac/OverrideChecks/Private.out index f49ef46a255..2ccbecd6d3a 100644 --- a/test/langtools/tools/javac/OverrideChecks/Private.out +++ b/test/langtools/tools/javac/OverrideChecks/Private.out @@ -1,2 +1,2 @@ -Private.java:14:5: compiler.err.method.does.not.override.superclass +Private.java:14:5: compiler.err.method.does.not.override.superclass: m(), Bar 1 error diff --git a/test/langtools/tools/javac/annotations/6359949/T6359949a.out b/test/langtools/tools/javac/annotations/6359949/T6359949a.out index f179ca1bc1a..2c2aed1da27 100644 --- a/test/langtools/tools/javac/annotations/6359949/T6359949a.out +++ b/test/langtools/tools/javac/annotations/6359949/T6359949a.out @@ -1,2 +1,2 @@ -T6359949a.java:15:5: compiler.err.static.methods.cannot.be.annotated.with.override +T6359949a.java:15:5: compiler.err.static.methods.cannot.be.annotated.with.override: example(), Test 1 error diff --git a/test/langtools/tools/javac/annotations/crash_empty_enum_const/CrashEmptyEnumConstructorTest.java b/test/langtools/tools/javac/annotations/crash_empty_enum_const/CrashEmptyEnumConstructorTest.java index 29340dd720e..865c53a19c3 100644 --- a/test/langtools/tools/javac/annotations/crash_empty_enum_const/CrashEmptyEnumConstructorTest.java +++ b/test/langtools/tools/javac/annotations/crash_empty_enum_const/CrashEmptyEnumConstructorTest.java @@ -92,7 +92,7 @@ public class CrashEmptyEnumConstructorTest extends TestRunner { """); List expected = List.of( - "E.java:3: error: missing method body, or declare abstract", + "E.java:3: error: method E(String) in E is missing a method body, or should be declared abstract", " E(String one);", " ^", "1 error"); diff --git a/test/langtools/tools/javac/annotations/neg/OverrideNo.out b/test/langtools/tools/javac/annotations/neg/OverrideNo.out index 2af05a13fc3..34f4fe08e2a 100644 --- a/test/langtools/tools/javac/annotations/neg/OverrideNo.out +++ b/test/langtools/tools/javac/annotations/neg/OverrideNo.out @@ -1,2 +1,2 @@ -OverrideNo.java:16:5: compiler.err.method.does.not.override.superclass +OverrideNo.java:16:5: compiler.err.method.does.not.override.superclass: f(), overrideNo.B 1 error diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/NewClassTypeAnnotation.java b/test/langtools/tools/javac/annotations/typeAnnotations/NewClassTypeAnnotation.java index f99cb1c1a34..1f8f700fd82 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/NewClassTypeAnnotation.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/NewClassTypeAnnotation.java @@ -31,7 +31,6 @@ * @run main NewClassTypeAnnotation */ import com.sun.source.tree.NewClassTree; -import com.sun.source.tree.Tree; import com.sun.source.util.TaskEvent; import com.sun.source.util.TaskListener; import com.sun.source.util.TreePathScanner; @@ -91,6 +90,7 @@ public class NewClassTypeAnnotation extends TestRunner { public void testMethod() { new Test<@TypeAnnotation String>(); + new Test<@TypeAnnotation String>() {}; } } """); @@ -112,11 +112,7 @@ public class NewClassTypeAnnotation extends TestRunner { @Override public Void visitNewClass(final NewClassTree node, final Void unused) { TypeMirror type = trees.getTypeMirror(getCurrentPath()); - System.err.println(">>> " + type); - for (Tree t : getCurrentPath()) { - System.err.println(t); - } - actual.add(String.format("Expression: %s, Type: %s", node, type)); + actual.add(String.format("Type: %s", type)); return null; } } @@ -144,8 +140,8 @@ public class NewClassTypeAnnotation extends TestRunner { List expected = List.of( - "Expression: new Test<@TypeAnnotation String>(), Type:" - + " test.Test"); + "Type: test.Test", + "Type: >"); if (!expected.equals(actual)) { throw new AssertionError("expected: " + expected + ", actual: " + actual); } diff --git a/test/langtools/tools/javac/defaultMethods/private/Private02.out b/test/langtools/tools/javac/defaultMethods/private/Private02.out index cc61d11113f..ba6fb69fdbc 100644 --- a/test/langtools/tools/javac/defaultMethods/private/Private02.out +++ b/test/langtools/tools/javac/defaultMethods/private/Private02.out @@ -1,4 +1,4 @@ Private02.java:13:31: compiler.err.illegal.combination.of.modifiers: abstract, private Private02.java:16:22: compiler.err.already.defined: kindname.method, foo(int), kindname.interface, Private02.I -Private02.java:12:22: compiler.err.missing.meth.body.or.decl.abstract +Private02.java:12:22: compiler.err.missing.meth.body.or.decl.abstract: foo(java.lang.String), Private02.I 3 errors diff --git a/test/langtools/tools/javac/defaultMethods/private/Private08.out b/test/langtools/tools/javac/defaultMethods/private/Private08.out index f259aae76ba..bf1e68c5e73 100644 --- a/test/langtools/tools/javac/defaultMethods/private/Private08.out +++ b/test/langtools/tools/javac/defaultMethods/private/Private08.out @@ -2,7 +2,7 @@ Private08.java:13:28: compiler.err.illegal.combination.of.modifiers: public, pri Private08.java:14:30: compiler.err.illegal.combination.of.modifiers: abstract, private Private08.java:15:29: compiler.err.illegal.combination.of.modifiers: private, default Private08.java:16:24: compiler.err.mod.not.allowed.here: protected -Private08.java:17:22: compiler.err.missing.meth.body.or.decl.abstract +Private08.java:17:22: compiler.err.missing.meth.body.or.decl.abstract: missingBody(), Private08.I Private08.java:22:33: compiler.err.report.access: foo(), private, Private08.I Private08.java:27:21: compiler.err.override.weaker.access: (compiler.misc.clashes.with: doo(), Private08_01.J, doo(), Private08.I), public Private08.java:25:13: compiler.err.non-static.cant.be.ref: kindname.variable, super diff --git a/test/langtools/tools/javac/lvti/BadLocalVarInferenceTest.out b/test/langtools/tools/javac/lvti/BadLocalVarInferenceTest.out index 9e7cab9633d..d9e7ce5a552 100644 --- a/test/langtools/tools/javac/lvti/BadLocalVarInferenceTest.out +++ b/test/langtools/tools/javac/lvti/BadLocalVarInferenceTest.out @@ -5,7 +5,7 @@ BadLocalVarInferenceTest.java:22:13: compiler.err.cant.infer.local.var.type: g, BadLocalVarInferenceTest.java:23:13: compiler.err.cant.infer.local.var.type: d, (compiler.misc.local.self.ref) BadLocalVarInferenceTest.java:24:13: compiler.err.cant.infer.local.var.type: k, (compiler.misc.local.array.missing.target) BadLocalVarInferenceTest.java:25:29: compiler.err.does.not.override.abstract: compiler.misc.anonymous.class: BadLocalVarInferenceTest$1, m(java.lang.Object), BadLocalVarInferenceTest.Foo -BadLocalVarInferenceTest.java:26:13: compiler.err.method.does.not.override.superclass +BadLocalVarInferenceTest.java:26:13: compiler.err.method.does.not.override.superclass: m(java.lang.String), compiler.misc.anonymous.class: BadLocalVarInferenceTest$1 BadLocalVarInferenceTest.java:29:27: compiler.err.cant.resolve.location.args: kindname.method, charAt, , int, (compiler.misc.location.1: kindname.variable, x, java.lang.Object) BadLocalVarInferenceTest.java:30:13: compiler.err.cant.infer.local.var.type: t, (compiler.misc.local.cant.infer.void) 10 errors diff --git a/test/langtools/tools/javac/patterns/Exhaustiveness.java b/test/langtools/tools/javac/patterns/Exhaustiveness.java index 6b52c36fd63..16f86488ecf 100644 --- a/test/langtools/tools/javac/patterns/Exhaustiveness.java +++ b/test/langtools/tools/javac/patterns/Exhaustiveness.java @@ -23,7 +23,7 @@ /** * @test - * @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 8325215 8333169 8327368 8364991 + * @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 8325215 8333169 8327368 8366968 8364991 * @summary Check exhaustiveness of switches over sealed types. * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -2183,6 +2183,135 @@ public class Exhaustiveness extends TestRunner { """); } + @Test //JDK-8366968 + public void testNonSealedDiamond(Path base) throws Exception { + doTest(base, + new String[0], + """ + class Demo { + + sealed interface Base permits Special, Value {} + + non-sealed interface Value extends Base {} + + sealed interface Special extends Base permits SpecialValue {} + + non-sealed interface SpecialValue extends Value, Special {} + + static int demo(final Base base) { + return switch (base) { + case Value value -> 0; + }; + + } + + } + """); + } + + @Test //JDK-8366968 + public void testNonSealedDiamond2(Path base) throws Exception { + doTest(base, + new String[0], + """ + class Demo { + + sealed interface Base permits Special, Value {} + + non-sealed interface Value extends Base {} + + non-sealed interface Special extends Base {} + + interface SpecialValue extends Value, Special {} + + static int demo(final Base base) { + return switch (base) { + case Value value -> 0; + }; + + } + + } + """, + "Demo.java:12:16: compiler.err.not.exhaustive", + "1 error"); + } + + @Test //JDK-8366968 + public void testNonAbstract(Path base) throws Exception { + doTest(base, + new String[0], + """ + class Demo { + sealed interface I permits Base, C3 { } + sealed class Base implements I permits C1, C2 { } + final class C1 extends Base { } + final class C2 extends Base { } + final class C3 implements I { } + + void method1(I i) { + switch (i) { + case C1 _ -> {} + case C2 _ -> {} + case C3 _ -> {} + } + } + } + """, + "Demo.java:9:9: compiler.err.not.exhaustive.statement", + "1 error"); + } + + @Test //JDK-8366968 + public void testNonSealedDiamondGeneric(Path base) throws Exception { + doTest(base, + new String[0], + """ + class Demo { + class SomeType {} + sealed interface Base permits Special, Value {} + non-sealed interface Value extends Base {} + sealed interface Special extends Base permits SpecialValue {} + non-sealed interface SpecialValue extends Value, Special {} + + static int demo(final Base base) { + return switch (base) { + case Value value -> 0; + }; + } + } + """); + } + + @Test //JDK-8366968 + public void testNonSealedDiamondMultiple(Path base) throws Exception { + doTest(base, + new String[0], + """ + class Demo { + + sealed interface Base permits Special, Value {} + + non-sealed interface Value extends Base {} + + sealed interface Special extends Base permits SpecialValue, Special2 {} + + non-sealed interface SpecialValue extends Value, Special {} + non-sealed interface Special2 extends Special {} + + static int demo(final Base base) { + return switch (base) { + case Value value -> 0; + }; + + } + + } + """, + "Demo.java:13:16: compiler.err.not.exhaustive", + "1 error"); + } + @Test //JDK-8364991 public void testDifferentReductionPaths(Path base) throws Exception { doTest(base, diff --git a/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV2.java b/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV2.java index a3723e2eda2..2a756102ded 100644 --- a/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV2.java +++ b/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV2.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Red Hat Inc. + * 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 @@ -447,13 +448,13 @@ public class MetricsTesterCgroupV2 implements CgroupMetricsTester { Metrics metrics = Metrics.systemMetrics(); long oldVal = metrics.getBlkIOServiceCount(); long newVal = getIoStatAccumulate(new String[] { "rios", "wios" }); - if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) { + if (newVal < oldVal) { fail("io.stat->rios/wios: ", oldVal, newVal); } oldVal = metrics.getBlkIOServiced(); newVal = getIoStatAccumulate(new String[] { "rbytes", "wbytes" }); - if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) { + if (newVal < oldVal) { fail("io.stat->rbytes/wbytes: ", oldVal, newVal); } } diff --git a/test/lib/jdk/test/lib/process/OutputAnalyzer.java b/test/lib/jdk/test/lib/process/OutputAnalyzer.java index 3c5be74558b..fa5e30dd815 100644 --- a/test/lib/jdk/test/lib/process/OutputAnalyzer.java +++ b/test/lib/jdk/test/lib/process/OutputAnalyzer.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 @@ -354,6 +354,27 @@ public final class OutputAnalyzer { return this; } + /** + * Returns true if stdout matches the given pattern + */ + public boolean stdoutMatches(String regexp) { + return getStdout().matches(regexp); + } + + /** + * Returns true if stderr matches the given pattern + */ + public boolean stderrMatches(String regexp) { + return getStderr().matches(regexp); + } + + /** + * Returns true if either stdout or stderr matches the given pattern + */ + public boolean matches(String regexp) { + return stdoutMatches(regexp) || stderrMatches(regexp); + } + /** * Verify that the stdout and stderr contents of output buffer matches * the pattern 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.KeyUsageExtension; import sun.security.x509.SubjectAlternativeNameExtension; import sun.security.x509.URIName; import sun.security.x509.KeyIdentifier; +import sun.security.x509.X500Name; /** @@ -90,7 +91,7 @@ import sun.security.x509.KeyIdentifier; 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 class CertificateBuilder { * on this certificate. */ public CertificateBuilder setSubjectName(X500Principal name) { - subjectName = name; + subjectName = X500Name.asX500Name(name); return this; } @@ -209,7 +210,23 @@ public class CertificateBuilder { * @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/lib/jdk/test/whitebox/WhiteBox.java b/test/lib/jdk/test/whitebox/WhiteBox.java index d07dedd2a8c..e989b0aca88 100644 --- a/test/lib/jdk/test/whitebox/WhiteBox.java +++ b/test/lib/jdk/test/whitebox/WhiteBox.java @@ -78,6 +78,8 @@ public class WhiteBox { public native long getHeapSpaceAlignment(); public native long getHeapAlignment(); + public native boolean hasExternalSymbolsStripped(); + private native boolean isObjectInOldGen0(Object o); public boolean isObjectInOldGen(Object o) { Objects.requireNonNull(o); diff --git a/test/micro/org/openjdk/bench/java/lang/FloatingDecimal.java b/test/micro/org/openjdk/bench/java/lang/Doubles.java similarity index 89% rename from test/micro/org/openjdk/bench/java/lang/FloatingDecimal.java rename to test/micro/org/openjdk/bench/java/lang/Doubles.java index b8d29aabc84..50c295900af 100644 --- a/test/micro/org/openjdk/bench/java/lang/FloatingDecimal.java +++ b/test/micro/org/openjdk/bench/java/lang/Doubles.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,7 +48,7 @@ import java.util.concurrent.TimeUnit; @Warmup(iterations = 10, time = 1) @Measurement(iterations = 5, time = 2) @Fork(3) -public class FloatingDecimal { +public class Doubles { private double[] randomArray, twoDecimalsArray, integerArray; private static final int TESTSIZE = 1000; @@ -65,6 +66,14 @@ public class FloatingDecimal { } } + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void toHexString(Blackhole bh) { + for (double d : randomArray) { + bh.consume(Double.toHexString(d)); + } + } + /** Tests Double.toString on double values generated from Random.nextDouble() */ @Benchmark @OperationsPerInvocation(TESTSIZE) 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/javax/crypto/AESDecrypt.java b/test/micro/org/openjdk/bench/javax/crypto/AESDecrypt.java new file mode 100644 index 00000000000..0514a6d25f3 --- /dev/null +++ b/test/micro/org/openjdk/bench/javax/crypto/AESDecrypt.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. + */ +package org.openjdk.bench.javax.crypto; + +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Benchmark; +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; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@OutputTimeUnit(TimeUnit.SECONDS) +@State(Scope.Thread) +public class AESDecrypt { + + @Param("10000000") + private int count; + + private Cipher cipher; + private byte[] src; + private byte[] ct; + + @Setup + public void setup() throws Exception { + SecretKeySpec keySpec = new SecretKeySpec(new byte[]{-80, -103, -1, 68, -29, -94, 61, -52, 93, -59, -128, 105, 110, 88, 44, 105}, "AES"); + IvParameterSpec iv = new IvParameterSpec(new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); + + cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); + + src = new byte[count]; + new Random(1).nextBytes(src); + + ct = cipher.doFinal(src); + + cipher.init(Cipher.DECRYPT_MODE, keySpec, iv); + } + + @Benchmark + @Fork(jvmArgs = {"-XX:+UnlockDiagnosticVMOptions", "-XX:-UseAES", "-XX:-UseAESIntrinsics"}) + public byte[] testBaseline() throws Exception { + return cipher.doFinal(ct); + } + + @Benchmark + @Fork(jvmArgs = {"-XX:+UnlockDiagnosticVMOptions", "-XX:+UseAES", "-XX:-UseAESIntrinsics"}) + public byte[] testUseAes() throws Exception { + return cipher.doFinal(ct); + } + + @Benchmark + @Fork(jvmArgs = {"-XX:+UnlockDiagnosticVMOptions", "-XX:+UseAES", "-XX:+UseAESIntrinsics"}) + public byte[] testUseAesIntrinsics() throws Exception { + return cipher.doFinal(ct); + } + +} 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..1b89b510fae 100644 --- a/test/micro/org/openjdk/bench/jdk/internal/jrtfs/ImageReaderBenchmark.java +++ b/test/micro/org/openjdk/bench/jdk/internal/jrtfs/ImageReaderBenchmark.java @@ -1062,7 +1062,6 @@ public class ImageReaderBenchmark { "/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",