From 16042556f394adfa93e54173944198397ad29dea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Casta=C3=B1eda=20Lozano?= Date: Thu, 10 Oct 2024 08:34:10 +0000 Subject: [PATCH 01/95] 8341619: C2: remove unused StoreCM node Reviewed-by: chagedorn, thartmann, kvn --- src/hotspot/cpu/aarch64/aarch64.ad | 30 ------------ src/hotspot/cpu/arm/arm.ad | 12 ----- src/hotspot/cpu/ppc/ppc.ad | 17 ------- src/hotspot/cpu/riscv/riscv.ad | 35 -------------- src/hotspot/cpu/s390/s390.ad | 22 --------- src/hotspot/cpu/x86/x86_32.ad | 11 ----- src/hotspot/cpu/x86/x86_64.ad | 26 ----------- src/hotspot/share/adlc/forms.cpp | 1 - src/hotspot/share/adlc/formssel.cpp | 1 - src/hotspot/share/opto/classes.hpp | 1 - src/hotspot/share/opto/compile.cpp | 58 ------------------------ src/hotspot/share/opto/compile.hpp | 1 - src/hotspot/share/opto/escape.cpp | 9 +--- src/hotspot/share/opto/gcm.cpp | 18 +++----- src/hotspot/share/opto/idealKit.cpp | 20 -------- src/hotspot/share/opto/idealKit.hpp | 9 ---- src/hotspot/share/opto/lcm.cpp | 17 ------- src/hotspot/share/opto/loopTransform.cpp | 2 +- src/hotspot/share/opto/memnode.cpp | 48 +------------------- src/hotspot/share/opto/memnode.hpp | 33 +------------- src/hotspot/share/opto/output.cpp | 25 +--------- src/hotspot/share/opto/superword.cpp | 3 -- src/hotspot/share/runtime/vmStructs.cpp | 1 - 23 files changed, 12 insertions(+), 388 deletions(-) diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index c7cae54d14c..4049ab1fe80 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -6892,36 +6892,6 @@ instruct loadConD(vRegD dst, immD con) %{ // Store Instructions -// Store CMS card-mark Immediate -instruct storeimmCM0(immI0 zero, memory1 mem) -%{ - match(Set mem (StoreCM mem zero)); - - ins_cost(INSN_COST); - format %{ "storestore (elided)\n\t" - "strb zr, $mem\t# byte" %} - - ins_encode(aarch64_enc_strb0(mem)); - - ins_pipe(istore_mem); -%} - -// Store CMS card-mark Immediate with intervening StoreStore -// needed when using CMS with no conditional card marking -instruct storeimmCM0_ordered(immI0 zero, memory1 mem) -%{ - match(Set mem (StoreCM mem zero)); - - ins_cost(INSN_COST * 2); - format %{ "storestore\n\t" - "dmb ishst" - "\n\tstrb zr, $mem\t# byte" %} - - ins_encode(aarch64_enc_strb0_ordered(mem)); - - ins_pipe(istore_mem); -%} - // Store Byte instruct storeB(iRegIorL2I src, memory1 mem) %{ diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 716f6d87230..bb81f2af599 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -4226,18 +4226,6 @@ instruct storeB(memoryB mem, store_RegI src) %{ ins_pipe(istore_mem_reg); %} -instruct storeCM(memoryB mem, store_RegI src) %{ - match(Set mem (StoreCM mem src)); - ins_cost(MEMORY_REF_COST); - - size(4); - format %{ "STRB $src,$mem\t! CMS card-mark byte" %} - ins_encode %{ - __ strb($src$$Register, $mem$$Address); - %} - ins_pipe(istore_mem_reg); -%} - // Store Char/Short diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index d15f9929671..b5bfdf5067a 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -6482,23 +6482,6 @@ instruct storeD(memory mem, regD src) %{ ins_pipe(pipe_class_memory); %} -//----------Store Instructions With Zeros-------------------------------------- - -instruct storeCM(memory mem, immI_0 zero) %{ - match(Set mem (StoreCM mem zero)); - ins_cost(MEMORY_REF_COST); - - format %{ "STB #0, $mem \t// CMS card-mark byte store" %} - size(8); - ins_encode %{ - __ li(R0, 0); - // No release barrier: Oops are allowed to get visible after marking. - guarantee($mem$$base$$Register != R1_SP, "use frame_slots_bias"); - __ stb(R0, $mem$$disp, $mem$$base$$Register); - %} - ins_pipe(pipe_class_memory); -%} - // Convert oop pointer into compressed form. // Nodes for postalloc expand. diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index a76d1722670..30b8ff77be8 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -5039,41 +5039,6 @@ instruct loadConD0(fRegD dst, immD0 con) %{ ins_pipe(fp_load_constant_d); %} -// Store Instructions -// Store CMS card-mark Immediate -instruct storeimmCM0(immI0 zero, memory mem) -%{ - match(Set mem (StoreCM mem zero)); - - ins_cost(STORE_COST); - format %{ "storestore (elided)\n\t" - "sb zr, $mem\t# byte, #@storeimmCM0" %} - - ins_encode %{ - __ sb(zr, Address(as_Register($mem$$base), $mem$$disp)); - %} - - ins_pipe(istore_mem); -%} - -// Store CMS card-mark Immediate with intervening StoreStore -// needed when using CMS with no conditional card marking -instruct storeimmCM0_ordered(immI0 zero, memory mem) -%{ - match(Set mem (StoreCM mem zero)); - - ins_cost(ALU_COST + STORE_COST); - format %{ "membar(StoreStore)\n\t" - "sb zr, $mem\t# byte, #@storeimmCM0_ordered" %} - - ins_encode %{ - __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); - __ sb(zr, Address(as_Register($mem$$base), $mem$$disp)); - %} - - ins_pipe(istore_mem); -%} - // Store Byte instruct storeB(iRegIorL2I src, memory mem) %{ diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 8181e96ecfc..ebb2ca36a64 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -4226,28 +4226,6 @@ instruct storeB(memory mem, iRegI src) %{ ins_pipe(pipe_class_dummy); %} -instruct storeCM(memory mem, immI_0 src) %{ - match(Set mem (StoreCM mem src)); - ins_cost(MEMORY_REF_COST); - // TODO: s390 port size(VARIABLE_SIZE); - format %{ "STC(Y) $src,$mem\t # CMS card-mark byte (must be 0!)" %} - ins_encode %{ - guarantee($mem$$index$$Register != Z_R0, "content will not be used."); - if ($mem$$index$$Register != noreg) { - // Can't use clear_mem --> load const zero and store character. - __ load_const_optimized(Z_R0_scratch, (long)0); - if (Immediate::is_uimm12($mem$$disp)) { - __ z_stc(Z_R0_scratch, $mem$$Address); - } else { - __ z_stcy(Z_R0_scratch, $mem$$Address); - } - } else { - __ clear_mem(Address($mem$$Address), 1); - } - %} - ins_pipe(pipe_class_dummy); -%} - // CHAR/SHORT // Store Char/Short diff --git a/src/hotspot/cpu/x86/x86_32.ad b/src/hotspot/cpu/x86/x86_32.ad index 7aa4f6a29a1..7c9695571da 100644 --- a/src/hotspot/cpu/x86/x86_32.ad +++ b/src/hotspot/cpu/x86/x86_32.ad @@ -6322,17 +6322,6 @@ instruct storeImmB(memory mem, immI8 src) %{ ins_pipe( ialu_mem_imm ); %} -// Store CMS card-mark Immediate -instruct storeImmCM(memory mem, immI8 src) %{ - match(Set mem (StoreCM mem src)); - - ins_cost(150); - format %{ "MOV8 $mem,$src\t! CMS card-mark imm0" %} - opcode(0xC6); /* C6 /0 */ - ins_encode( SetInstMark, OpcP, RMopc_Mem(0x00,mem), Con8or32(src), ClearInstMark); - ins_pipe( ialu_mem_imm ); -%} - // Store Double instruct storeDPR( memory mem, regDPR1 src) %{ predicate(UseSSE<=1); diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index fee265473be..c3fa4c16e55 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -5298,32 +5298,6 @@ instruct storeImmB(memory mem, immI8 src) ins_pipe(ialu_mem_imm); %} -// Store CMS card-mark Immediate -instruct storeImmCM0_reg(memory mem, immI_0 zero) -%{ - predicate(UseCompressedOops && (CompressedOops::base() == nullptr)); - match(Set mem (StoreCM mem zero)); - - ins_cost(125); // XXX - format %{ "movb $mem, R12\t# CMS card-mark byte 0 (R12_heapbase==0)" %} - ins_encode %{ - __ movb($mem$$Address, r12); - %} - ins_pipe(ialu_mem_reg); -%} - -instruct storeImmCM0(memory mem, immI_0 src) -%{ - match(Set mem (StoreCM mem src)); - - ins_cost(150); // XXX - format %{ "movb $mem, $src\t# CMS card-mark byte 0" %} - ins_encode %{ - __ movb($mem$$Address, $src$$constant); - %} - ins_pipe(ialu_mem_imm); -%} - // Store Float instruct storeF(memory mem, regF src) %{ diff --git a/src/hotspot/share/adlc/forms.cpp b/src/hotspot/share/adlc/forms.cpp index 068d745254e..c34a73ea1e1 100644 --- a/src/hotspot/share/adlc/forms.cpp +++ b/src/hotspot/share/adlc/forms.cpp @@ -276,7 +276,6 @@ Form::DataType Form::is_load_from_memory(const char *opType) const { Form::DataType Form::is_store_to_memory(const char *opType) const { if( strcmp(opType,"StoreB")==0) return Form::idealB; - if( strcmp(opType,"StoreCM")==0) return Form::idealB; if( strcmp(opType,"StoreC")==0) return Form::idealC; if( strcmp(opType,"StoreD")==0) return Form::idealD; if( strcmp(opType,"StoreF")==0) return Form::idealF; diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index 15bc7ddc67d..ac2d3d94153 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -3654,7 +3654,6 @@ int MatchNode::needs_ideal_memory_edge(FormDict &globals) const { #if INCLUDE_SHENANDOAHGC "ShenandoahCompareAndSwapN", "ShenandoahCompareAndSwapP", "ShenandoahWeakCompareAndSwapP", "ShenandoahWeakCompareAndSwapN", "ShenandoahCompareAndExchangeP", "ShenandoahCompareAndExchangeN", #endif - "StoreCM", "GetAndSetB", "GetAndSetS", "GetAndAddI", "GetAndSetI", "GetAndSetP", "GetAndAddB", "GetAndAddS", "GetAndAddL", "GetAndSetL", "GetAndSetN", "ClearArray" diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 8bee6279446..215e48ef9da 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -341,7 +341,6 @@ macro(Start) macro(StartOSR) macro(StoreB) macro(StoreC) -macro(StoreCM) macro(StoreD) macro(StoreF) macro(StoreI) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index fa0d39057cb..c6b316e5277 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -3061,52 +3061,6 @@ struct Final_Reshape_Counts : public StackObj { int get_inner_loop_count() const { return _inner_loop_count; } }; -// Eliminate trivially redundant StoreCMs and accumulate their -// precedence edges. -void Compile::eliminate_redundant_card_marks(Node* n) { - assert(n->Opcode() == Op_StoreCM, "expected StoreCM"); - if (n->in(MemNode::Address)->outcnt() > 1) { - // There are multiple users of the same address so it might be - // possible to eliminate some of the StoreCMs - Node* mem = n->in(MemNode::Memory); - Node* adr = n->in(MemNode::Address); - Node* val = n->in(MemNode::ValueIn); - Node* prev = n; - bool done = false; - // Walk the chain of StoreCMs eliminating ones that match. As - // long as it's a chain of single users then the optimization is - // safe. Eliminating partially redundant StoreCMs would require - // cloning copies down the other paths. - while (mem->Opcode() == Op_StoreCM && mem->outcnt() == 1 && !done) { - if (adr == mem->in(MemNode::Address) && - val == mem->in(MemNode::ValueIn)) { - // redundant StoreCM - if (mem->req() > MemNode::OopStore) { - // Hasn't been processed by this code yet. - n->add_prec(mem->in(MemNode::OopStore)); - } else { - // Already converted to precedence edge - for (uint i = mem->req(); i < mem->len(); i++) { - // Accumulate any precedence edges - if (mem->in(i) != nullptr) { - n->add_prec(mem->in(i)); - } - } - // Everything above this point has been processed. - done = true; - } - // Eliminate the previous StoreCM - prev->set_req(MemNode::Memory, mem->in(MemNode::Memory)); - assert(mem->outcnt() == 0, "should be dead"); - mem->disconnect_inputs(this); - } else { - prev = mem; - } - mem = prev->in(MemNode::Memory); - } - } -} - //------------------------------final_graph_reshaping_impl---------------------- // Implement items 1-5 from final_graph_reshaping below. void Compile::final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes) { @@ -3276,18 +3230,6 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f } break; } - - case Op_StoreCM: - { - // Convert OopStore dependence into precedence edge - Node* prec = n->in(MemNode::OopStore); - n->del_req(MemNode::OopStore); - n->add_prec(prec); - eliminate_redundant_card_marks(n); - } - - // fall through - case Op_StoreB: case Op_StoreC: case Op_StoreI: diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 1ccfedd0d46..6568828125f 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -1244,7 +1244,6 @@ class Compile : public Phase { void final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes); void final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes); void final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes); - void eliminate_redundant_card_marks(Node* n); void handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned); // Logic cone optimization. diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 6ab28eaa6ee..b26480ba9b3 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -4009,10 +4009,6 @@ void ConnectionGraph::move_inst_mem(Node* n, GrowableArray &orig_phi --i; #ifdef ASSERT } else if (use->is_Mem()) { - if (use->Opcode() == Op_StoreCM && use->in(MemNode::OopStore) == n) { - // Don't move related cardmark. - continue; - } // Memory nodes should have new memory input. tp = igvn->type(use->in(MemNode::Address))->isa_ptr(); assert(tp != nullptr, "ptr type"); @@ -4564,7 +4560,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, // They overwrite memory edge corresponding to destination array, memnode_worklist.append_if_missing(use); } else if (!(op == Op_CmpP || op == Op_Conv2B || - op == Op_CastP2X || op == Op_StoreCM || + op == Op_CastP2X || op == Op_FastLock || op == Op_AryEq || op == Op_StrComp || op == Op_CountPositives || op == Op_StrCompressedCopy || op == Op_StrInflatedCopy || @@ -4703,9 +4699,6 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, if (use->is_Phi() || use->is_ClearArray()) { memnode_worklist.append_if_missing(use); } else if (use->is_Mem() && use->in(MemNode::Memory) == n) { - if (use->Opcode() == Op_StoreCM) { // Ignore cardmark stores - continue; - } memnode_worklist.append_if_missing(use); } else if (use->is_MemBar() || use->is_CallLeaf()) { if (use->in(TypeFunc::Memory) == n) { // Ignore precedent edge diff --git a/src/hotspot/share/opto/gcm.cpp b/src/hotspot/share/opto/gcm.cpp index 9ba571b926d..dd51bb47094 100644 --- a/src/hotspot/share/opto/gcm.cpp +++ b/src/hotspot/share/opto/gcm.cpp @@ -216,19 +216,13 @@ void PhaseCFG::schedule_pinned_nodes(VectorSet &visited) { for (uint i = node->len()-1; i >= node->req(); i--) { Node* m = node->in(i); if (m == nullptr) continue; - - // Only process precedence edges that are CFG nodes. Safepoints and control projections can be in the middle of a block - if (is_CFG(m)) { - node->rm_prec(i); - if (n == nullptr) { - n = m; - } else { - assert(is_dominator(n, m) || is_dominator(m, n), "one must dominate the other"); - n = is_dominator(n, m) ? m : n; - } + assert(is_CFG(m), "must be a CFG node"); + node->rm_prec(i); + if (n == nullptr) { + n = m; } else { - assert(node->is_Mach(), "sanity"); - assert(node->as_Mach()->ideal_Opcode() == Op_StoreCM, "must be StoreCM node"); + assert(is_dominator(n, m) || is_dominator(m, n), "one must dominate the other"); + n = is_dominator(n, m) ? m : n; } } if (n != nullptr) { diff --git a/src/hotspot/share/opto/idealKit.cpp b/src/hotspot/share/opto/idealKit.cpp index 80c4791cfd5..baa055bc60a 100644 --- a/src/hotspot/share/opto/idealKit.cpp +++ b/src/hotspot/share/opto/idealKit.cpp @@ -381,26 +381,6 @@ Node* IdealKit::store(Node* ctl, Node* adr, Node *val, BasicType bt, return st; } -// Card mark store. Must be ordered so that it will come after the store of -// the oop. -Node* IdealKit::storeCM(Node* ctl, Node* adr, Node *val, Node* oop_store, int oop_adr_idx, - BasicType bt, - int adr_idx) { - assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); - const TypePtr* adr_type = nullptr; - debug_only(adr_type = C->get_adr_type(adr_idx)); - Node *mem = memory(adr_idx); - - // Add required edge to oop_store, optimizer does not support precedence edges. - // Convert required edge to precedence edge before allocation. - Node* st = new StoreCMNode(ctl, mem, adr, adr_type, val, oop_store, oop_adr_idx); - - st = transform(st); - set_memory(st, adr_idx); - - return st; -} - //---------------------------- do_memory_merge -------------------------------- // The memory from one merging cvstate needs to be merged with the memory for another // join cvstate. If the join cvstate doesn't have a merged memory yet then we diff --git a/src/hotspot/share/opto/idealKit.hpp b/src/hotspot/share/opto/idealKit.hpp index 20acec47211..727b70129ef 100644 --- a/src/hotspot/share/opto/idealKit.hpp +++ b/src/hotspot/share/opto/idealKit.hpp @@ -234,15 +234,6 @@ class IdealKit: public StackObj { bool require_atomic_access = false, bool mismatched = false); - // Store a card mark ordered after store_oop - Node* storeCM(Node* ctl, - Node* adr, - Node* val, - Node* oop_store, - int oop_adr_idx, - BasicType bt, - int adr_idx); - // Trivial call Node* make_leaf_call(const TypeFunc *slow_call_type, address slow_call, diff --git a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp index 87be6a76eb2..2a40cf000d8 100644 --- a/src/hotspot/share/opto/lcm.cpp +++ b/src/hotspot/share/opto/lcm.cpp @@ -191,7 +191,6 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo break; case Op_StoreB: case Op_StoreC: - case Op_StoreCM: case Op_StoreD: case Op_StoreF: case Op_StoreI: @@ -723,7 +722,6 @@ void PhaseCFG::adjust_register_pressure(Node* n, Block* block, intptr_t* recalc_ switch (iop) { case Op_StoreB: case Op_StoreC: - case Op_StoreCM: case Op_StoreD: case Op_StoreF: case Op_StoreI: @@ -1004,21 +1002,6 @@ bool PhaseCFG::schedule_local(Block* block, GrowableArray& ready_cnt, Vecto local++; // One more block-local input } ready_cnt.at_put(n->_idx, local); // Count em up - -#ifdef ASSERT - if (UseG1GC) { - if( n->is_Mach() && n->as_Mach()->ideal_Opcode() == Op_StoreCM ) { - // Check the precedence edges - for (uint prec = n->req(); prec < n->len(); prec++) { - Node* oop_store = n->in(prec); - if (oop_store != nullptr) { - assert(get_block_for_node(oop_store)->_dom_depth <= block->_dom_depth, "oop_store must dominate card-mark"); - } - } - } - } -#endif - // A few node types require changing a required edge to a precedence edge // before allocation. if( n->is_Mach() && n->req() > TypeFunc::Parms && diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 0bed38e5fb0..c3cc8532e7d 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -3817,7 +3817,7 @@ bool PhaseIdealLoop::match_fill_loop(IdealLoopTree* lpt, Node*& store, Node*& st break; } int opc = n->Opcode(); - if (opc == Op_StoreP || opc == Op_StoreN || opc == Op_StoreNKlass || opc == Op_StoreCM) { + if (opc == Op_StoreP || opc == Op_StoreN || opc == Op_StoreNKlass) { msg = "oop fills not handled"; break; } diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 66139188260..27c0d16fac1 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -3462,9 +3462,7 @@ Node *StoreNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node* address = in(MemNode::Address); Node* value = in(MemNode::ValueIn); // Back-to-back stores to same address? Fold em up. Generally - // unsafe if I have intervening uses... Also disallowed for StoreCM - // since they must follow each StoreP operation. Redundant StoreCMs - // are eliminated just before matching in final_graph_reshape. + // unsafe if I have intervening uses. { Node* st = mem; // If Store 'st' has more than one use, we cannot fold 'st' away. @@ -3474,7 +3472,7 @@ Node *StoreNode::Ideal(PhaseGVN *phase, bool can_reshape) { // require exactly ONE user until such time as we clone 'mem' for // each of 'mem's uses (thus making the exactly-1-user-rule hold // true). - while (st->is_Store() && st->outcnt() == 1 && st->Opcode() != Op_StoreCM) { + while (st->is_Store() && st->outcnt() == 1) { // Looking at a dead closed cycle of memory? assert(st != st->in(MemNode::Memory), "dead loop in StoreNode::Ideal"); assert(Opcode() == st->Opcode() || @@ -3781,48 +3779,6 @@ Node *StoreCNode::Ideal(PhaseGVN *phase, bool can_reshape){ return StoreNode::Ideal(phase, can_reshape); } -//============================================================================= -//------------------------------Identity--------------------------------------- -Node* StoreCMNode::Identity(PhaseGVN* phase) { - // No need to card mark when storing a null ptr - Node* my_store = in(MemNode::OopStore); - if (my_store->is_Store()) { - const Type *t1 = phase->type( my_store->in(MemNode::ValueIn) ); - if( t1 == TypePtr::NULL_PTR ) { - return in(MemNode::Memory); - } - } - return this; -} - -//============================================================================= -//------------------------------Ideal--------------------------------------- -Node *StoreCMNode::Ideal(PhaseGVN *phase, bool can_reshape){ - Node* progress = StoreNode::Ideal(phase, can_reshape); - if (progress != nullptr) return progress; - - Node* my_store = in(MemNode::OopStore); - if (my_store->is_MergeMem()) { - Node* mem = my_store->as_MergeMem()->memory_at(oop_alias_idx()); - set_req_X(MemNode::OopStore, mem, phase); - return this; - } - - return nullptr; -} - -//------------------------------Value----------------------------------------- -const Type* StoreCMNode::Value(PhaseGVN* phase) const { - // Either input is TOP ==> the result is TOP (checked in StoreNode::Value). - // If extra input is TOP ==> the result is TOP - const Type* t = phase->type(in(MemNode::OopStore)); - if (t == Type::TOP) { - return Type::TOP; - } - return StoreNode::Value(phase); -} - - //============================================================================= //----------------------------------SCMemProjNode------------------------------ const Type* SCMemProjNode::Value(PhaseGVN* phase) const diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index 323ab3dba7d..1ca3a4b16ce 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -55,8 +55,7 @@ class MemNode : public Node { enum { Control, // When is it safe to do this load? Memory, // Chunk of memory is being loaded from Address, // Actually address, derived from base - ValueIn, // Value to store - OopStore // Preceding oop store, only in StoreCM + ValueIn // Value to store }; typedef enum { unordered = 0, acquire, // Load has to acquire or be succeeded by MemBarAcquire. @@ -777,36 +776,6 @@ class StoreNKlassNode : public StoreNNode { virtual BasicType memory_type() const { return T_NARROWKLASS; } }; -//------------------------------StoreCMNode----------------------------------- -// Store card-mark byte to memory for CM -// The last StoreCM before a SafePoint must be preserved and occur after its "oop" store -// Preceding equivalent StoreCMs may be eliminated. -class StoreCMNode : public StoreNode { - private: - virtual uint hash() const { return StoreNode::hash() + _oop_alias_idx; } - virtual bool cmp( const Node &n ) const { - return _oop_alias_idx == ((StoreCMNode&)n)._oop_alias_idx - && StoreNode::cmp(n); - } - virtual uint size_of() const { return sizeof(*this); } - int _oop_alias_idx; // The alias_idx of OopStore - -public: - StoreCMNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Node *oop_store, int oop_alias_idx ) : - StoreNode(c, mem, adr, at, val, oop_store, MemNode::release), - _oop_alias_idx(oop_alias_idx) { - assert(_oop_alias_idx >= Compile::AliasIdxRaw || - (_oop_alias_idx == Compile::AliasIdxBot && !Compile::current()->do_aliasing()), - "bad oop alias idx"); - } - virtual int Opcode() const; - virtual Node* Identity(PhaseGVN* phase); - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type* Value(PhaseGVN* phase) const; - virtual BasicType memory_type() const { return T_VOID; } // unspecific - int oop_alias_idx() const { return _oop_alias_idx; } -}; - //------------------------------SCMemProjNode--------------------------------------- // This class defines a projection of the memory state of a store conditional node. // These nodes return a value, but also update memory. diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index eda0f65d6bc..2865cf67429 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -1665,30 +1665,7 @@ void PhaseOutput::fill_buffer(C2_MacroAssembler* masm, uint* blk_starts) { } } } - } -#ifdef ASSERT - // Check that oop-store precedes the card-mark - else if (mach->ideal_Opcode() == Op_StoreCM) { - uint storeCM_idx = j; - int count = 0; - for (uint prec = mach->req(); prec < mach->len(); prec++) { - Node *oop_store = mach->in(prec); // Precedence edge - if (oop_store == nullptr) continue; - count++; - uint i4; - for (i4 = 0; i4 < last_inst; ++i4) { - if (block->get_node(i4) == oop_store) { - break; - } - } - // Note: This test can provide a false failure if other precedence - // edges have been added to the storeCMNode. - assert(i4 == last_inst || i4 < storeCM_idx, "CM card-mark executes before oop-store"); - } - assert(count > 0, "storeCM expects at least one precedence edge"); - } -#endif - else if (!n->is_Proj()) { + } else if (!n->is_Proj()) { // Remember the beginning of the previous instruction, in case // it's followed by a flag-kill and a null-check. Happens on // Intel all the time, with add-to-memory kind of opcodes. diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index bb5fed78b02..20c8dfbff17 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -657,9 +657,6 @@ void VLoopMemorySlices::get_slice_in_reverse_order(PhiNode* head, MemNode* tail, // or need to run igvn.optimize() again before SLP } else if (out->is_memory_phi() && !_vloop.in_bb(out)) { // Ditto. Not sure what else to check further. - } else if (out->Opcode() == Op_StoreCM && out->in(MemNode::OopStore) == n) { - // StoreCM has an input edge used as a precedence edge. - // Maybe an issue when oop stores are vectorized. } else { assert(out == prev || prev == nullptr, "no branches off of store slice"); } diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index e0a36330687..3060e225427 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1590,7 +1590,6 @@ declare_c2_type(StorePNode, StoreNode) \ declare_c2_type(StoreNNode, StoreNode) \ declare_c2_type(StoreNKlassNode, StoreNode) \ - declare_c2_type(StoreCMNode, StoreNode) \ declare_c2_type(SCMemProjNode, ProjNode) \ declare_c2_type(LoadStoreNode, Node) \ declare_c2_type(CompareAndSwapNode, LoadStoreConditionalNode) \ From e9327b6e3c1fcc47ec790fa4e4019f7651a8f912 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Thu, 10 Oct 2024 14:32:32 +0000 Subject: [PATCH 02/95] 8339205: Optimize StackMapGenerator Reviewed-by: liach --- .../classfile/impl/StackMapGenerator.java | 434 +++++++++++++----- 1 file changed, 309 insertions(+), 125 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java index 9bd5084061a..3007c8ca40a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java @@ -44,6 +44,7 @@ import java.util.Objects; import java.util.stream.Collectors; import jdk.internal.constant.ReferenceClassDescImpl; +import jdk.internal.constant.PrimitiveClassDescImpl; import jdk.internal.util.Preconditions; import static java.lang.classfile.ClassFile.*; @@ -349,6 +350,7 @@ private void removeRangeFromExcTable(int rangeStart, int rangeEnd) { var it = handlers.listIterator(); while (it.hasNext()) { var e = it.next(); + var labelContext = this.labelContext; int handlerStart = labelContext.labelToBci(e.tryStart()); int handlerEnd = labelContext.labelToBci(e.tryEnd()); if (rangeStart >= handlerEnd || rangeEnd <= handlerStart) { @@ -390,14 +392,15 @@ public Attribute stackMapTableAttribute() { return framesCount == 0 ? null : new UnboundAttribute.AdHocAttribute<>(Attributes.stackMapTable()) { @Override public void writeBody(BufWriterImpl b) { - b.writeU2(framesCount); - Frame prevFrame = new Frame(classHierarchy); - prevFrame.setLocalsFromArg(methodName, methodDesc, isStatic, thisType); + var gen = StackMapGenerator.this; + b.writeU2(gen.framesCount); + Frame prevFrame = gen.new Frame(gen.classHierarchy); + prevFrame.setLocalsFromArg(gen.methodName, gen.methodDesc, gen.isStatic, gen.thisType); prevFrame.trimAndCompress(); - for (int i = 0; i < framesCount; i++) { - var fr = frames[i]; + for (int i = 0; i < gen.framesCount; i++) { + var fr = gen.frames[i]; fr.trimAndCompress(); - fr.writeTo(b, prevFrame, cp); + fr.writeTo(b, prevFrame, gen.cp); prevFrame = fr; } } @@ -460,6 +463,7 @@ private boolean processBlock(RawBytecodeHelper bcs) { processExceptionHandlerTargets(bci, this_uninit); verified_exc_handlers = true; } + var currentFrame = this.currentFrame; switch (opcode) { case NOP -> {} case RETURN -> { @@ -500,27 +504,27 @@ private boolean processBlock(RawBytecodeHelper bcs) { case ALOAD_0, ALOAD_1, ALOAD_2, ALOAD_3 -> currentFrame.pushStack(currentFrame.getLocal(opcode - ALOAD_0)); case IALOAD, BALOAD, CALOAD, SALOAD -> - currentFrame.decStack(2).pushStack(Type.INTEGER_TYPE); + currentFrame.decStack2PushStack(Type.INTEGER_TYPE); case LALOAD -> - currentFrame.decStack(2).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); + currentFrame.decStack2PushStack(Type.LONG_TYPE, Type.LONG2_TYPE); case FALOAD -> - currentFrame.decStack(2).pushStack(Type.FLOAT_TYPE); + currentFrame.decStack2PushStack(Type.FLOAT_TYPE); case DALOAD -> - currentFrame.decStack(2).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); + currentFrame.decStack2PushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); case AALOAD -> - currentFrame.pushStack((type1 = currentFrame.decStack(1).popStack()) == Type.NULL_TYPE ? Type.NULL_TYPE : type1.getComponent()); + currentFrame.pushStack((type1 = currentFrame.decStack().popStack()) == Type.NULL_TYPE ? Type.NULL_TYPE : type1.getComponent()); case ISTORE -> - currentFrame.decStack(1).setLocal(bcs.getIndex(), Type.INTEGER_TYPE); + currentFrame.decStack().setLocal(bcs.getIndex(), Type.INTEGER_TYPE); case ISTORE_0, ISTORE_1, ISTORE_2, ISTORE_3 -> - currentFrame.decStack(1).setLocal(opcode - ISTORE_0, Type.INTEGER_TYPE); + currentFrame.decStack().setLocal(opcode - ISTORE_0, Type.INTEGER_TYPE); case LSTORE -> currentFrame.decStack(2).setLocal2(bcs.getIndex(), Type.LONG_TYPE, Type.LONG2_TYPE); case LSTORE_0, LSTORE_1, LSTORE_2, LSTORE_3 -> currentFrame.decStack(2).setLocal2(opcode - LSTORE_0, Type.LONG_TYPE, Type.LONG2_TYPE); case FSTORE -> - currentFrame.decStack(1).setLocal(bcs.getIndex(), Type.FLOAT_TYPE); + currentFrame.decStack().setLocal(bcs.getIndex(), Type.FLOAT_TYPE); case FSTORE_0, FSTORE_1, FSTORE_2, FSTORE_3 -> - currentFrame.decStack(1).setLocal(opcode - FSTORE_0, Type.FLOAT_TYPE); + currentFrame.decStack().setLocal(opcode - FSTORE_0, Type.FLOAT_TYPE); case DSTORE -> currentFrame.decStack(2).setLocal2(bcs.getIndex(), Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); case DSTORE_0, DSTORE_1, DSTORE_2, DSTORE_3 -> @@ -534,98 +538,75 @@ private boolean processBlock(RawBytecodeHelper bcs) { case IASTORE, BASTORE, CASTORE, SASTORE, FASTORE, AASTORE -> currentFrame.decStack(3); case POP, MONITORENTER, MONITOREXIT -> - currentFrame.decStack(1); + currentFrame.decStack(); case POP2 -> currentFrame.decStack(2); case DUP -> - currentFrame.pushStack(type1 = currentFrame.popStack()).pushStack(type1); - case DUP_X1 -> { - type1 = currentFrame.popStack(); - type2 = currentFrame.popStack(); - currentFrame.pushStack(type1).pushStack(type2).pushStack(type1); - } - case DUP_X2 -> { - type1 = currentFrame.popStack(); - type2 = currentFrame.popStack(); - type3 = currentFrame.popStack(); - currentFrame.pushStack(type1).pushStack(type3).pushStack(type2).pushStack(type1); - } - case DUP2 -> { - type1 = currentFrame.popStack(); - type2 = currentFrame.popStack(); - currentFrame.pushStack(type2).pushStack(type1).pushStack(type2).pushStack(type1); - } - case DUP2_X1 -> { - type1 = currentFrame.popStack(); - type2 = currentFrame.popStack(); - type3 = currentFrame.popStack(); - currentFrame.pushStack(type2).pushStack(type1).pushStack(type3).pushStack(type2).pushStack(type1); - } - case DUP2_X2 -> { - type1 = currentFrame.popStack(); - type2 = currentFrame.popStack(); - type3 = currentFrame.popStack(); - type4 = currentFrame.popStack(); - currentFrame.pushStack(type2).pushStack(type1).pushStack(type4).pushStack(type3).pushStack(type2).pushStack(type1); - } - case SWAP -> { - type1 = currentFrame.popStack(); - type2 = currentFrame.popStack(); - currentFrame.pushStack(type1); - currentFrame.pushStack(type2); - } + currentFrame.dup(); + case DUP_X1 -> + currentFrame.dup_x1(); + case DUP_X2 -> + currentFrame.dup_x2(); + case DUP2 -> + currentFrame.dup2(); + case DUP2_X1 -> + currentFrame.dup2_x1(); + case DUP2_X2 -> + currentFrame.dup2_x2(); + case SWAP -> + currentFrame.swap(); case IADD, ISUB, IMUL, IDIV, IREM, ISHL, ISHR, IUSHR, IOR, IXOR, IAND -> - currentFrame.decStack(2).pushStack(Type.INTEGER_TYPE); + currentFrame.decStack2PushStack(Type.INTEGER_TYPE); case INEG, ARRAYLENGTH, INSTANCEOF -> - currentFrame.decStack(1).pushStack(Type.INTEGER_TYPE); + currentFrame.decStack1PushStack(Type.INTEGER_TYPE); case LADD, LSUB, LMUL, LDIV, LREM, LAND, LOR, LXOR -> - currentFrame.decStack(4).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); + currentFrame.decStack4PushStack(Type.LONG_TYPE, Type.LONG2_TYPE); case LNEG -> - currentFrame.decStack(2).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); + currentFrame.decStack2PushStack(Type.LONG_TYPE, Type.LONG2_TYPE); case LSHL, LSHR, LUSHR -> - currentFrame.decStack(3).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); + currentFrame.decStack3PushStack(Type.LONG_TYPE, Type.LONG2_TYPE); case FADD, FSUB, FMUL, FDIV, FREM -> - currentFrame.decStack(2).pushStack(Type.FLOAT_TYPE); + currentFrame.decStack2PushStack(Type.FLOAT_TYPE); case FNEG -> - currentFrame.decStack(1).pushStack(Type.FLOAT_TYPE); + currentFrame.decStack1PushStack(Type.FLOAT_TYPE); case DADD, DSUB, DMUL, DDIV, DREM -> - currentFrame.decStack(4).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); + currentFrame.decStack4PushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); case DNEG -> - currentFrame.decStack(2).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); + currentFrame.decStack2PushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); case IINC -> currentFrame.checkLocal(bcs.getIndex()); case I2L -> - currentFrame.decStack(1).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); + currentFrame.decStack1PushStack(Type.LONG_TYPE, Type.LONG2_TYPE); case L2I -> - currentFrame.decStack(2).pushStack(Type.INTEGER_TYPE); + currentFrame.decStack2PushStack(Type.INTEGER_TYPE); case I2F -> - currentFrame.decStack(1).pushStack(Type.FLOAT_TYPE); + currentFrame.decStack1PushStack(Type.FLOAT_TYPE); case I2D -> - currentFrame.decStack(1).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); + currentFrame.decStack1PushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); case L2F -> - currentFrame.decStack(2).pushStack(Type.FLOAT_TYPE); + currentFrame.decStack2PushStack(Type.FLOAT_TYPE); case L2D -> - currentFrame.decStack(2).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); + currentFrame.decStack2PushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); case F2I -> - currentFrame.decStack(1).pushStack(Type.INTEGER_TYPE); + currentFrame.decStack1PushStack(Type.INTEGER_TYPE); case F2L -> - currentFrame.decStack(1).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); + currentFrame.decStack1PushStack(Type.LONG_TYPE, Type.LONG2_TYPE); case F2D -> - currentFrame.decStack(1).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); + currentFrame.decStack1PushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); case D2L -> - currentFrame.decStack(2).pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); + currentFrame.decStack2PushStack(Type.LONG_TYPE, Type.LONG2_TYPE); case D2F -> - currentFrame.decStack(2).pushStack(Type.FLOAT_TYPE); + currentFrame.decStack2PushStack(Type.FLOAT_TYPE); case I2B, I2C, I2S -> - currentFrame.decStack(1).pushStack(Type.INTEGER_TYPE); + currentFrame.decStack1PushStack(Type.INTEGER_TYPE); case LCMP, DCMPL, DCMPG -> - currentFrame.decStack(4).pushStack(Type.INTEGER_TYPE); + currentFrame.decStack4PushStack(Type.INTEGER_TYPE); case FCMPL, FCMPG, D2I -> - currentFrame.decStack(2).pushStack(Type.INTEGER_TYPE); + currentFrame.decStack2PushStack(Type.INTEGER_TYPE); case IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE -> checkJumpTarget(currentFrame.decStack(2), bcs.dest()); case IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IFNULL, IFNONNULL -> - checkJumpTarget(currentFrame.decStack(1), bcs.dest()); + checkJumpTarget(currentFrame.decStack(), bcs.dest()); case GOTO -> { checkJumpTarget(currentFrame, bcs.dest()); ncf = true; @@ -643,7 +624,7 @@ private boolean processBlock(RawBytecodeHelper bcs) { ncf = true; } case IRETURN, FRETURN, ARETURN, ATHROW -> { - currentFrame.decStack(1); + currentFrame.decStack(); ncf = true; } case GETSTATIC, PUTSTATIC, GETFIELD, PUTFIELD -> @@ -653,17 +634,16 @@ private boolean processBlock(RawBytecodeHelper bcs) { case NEW -> currentFrame.pushStack(Type.uninitializedType(bci)); case NEWARRAY -> - currentFrame.decStack(1).pushStack(getNewarrayType(bcs.getIndex())); + currentFrame.decStack1PushStack(getNewarrayType(bcs.getIndex())); case ANEWARRAY -> processAnewarray(bcs.getIndexU2()); case CHECKCAST -> - currentFrame.decStack(1).pushStack(cpIndexToType(bcs.getIndexU2(), cp)); + currentFrame.decStack1PushStack(cpIndexToType(bcs.getIndexU2(), cp)); case MULTIANEWARRAY -> { type1 = cpIndexToType(bcs.getIndexU2(), cp); - int dim = bcs.getU1Unchecked(bcs.bci() + 3); - for (int i = 0; i < dim; i++) { - currentFrame.popStack(); - } + currentFrame.decStack( + bcs.getU1Unchecked(bcs.bci() + 3) /* dim */ + ); currentFrame.pushStack(type1); } case JSR, JSR_W, RET -> @@ -678,6 +658,7 @@ private boolean processBlock(RawBytecodeHelper bcs) { } private void processExceptionHandlerTargets(int bci, boolean this_uninit) { + var currentFrame = this.currentFrame; for (var ex : rawHandlers) { if (bci == ex.start || (currentFrame.localsChanged && bci > ex.start && bci < ex.end)) { int flags = currentFrame.flags; @@ -690,7 +671,10 @@ private void processExceptionHandlerTargets(int bci, boolean this_uninit) { } private void processLdc(int index) { - switch (cp.entryByIndex(index).tag()) { + var e = cp.entryByIndex(index); + byte tag = e.tag(); + var currentFrame = this.currentFrame; + switch (tag) { case TAG_UTF8 -> currentFrame.pushStack(Type.OBJECT_TYPE); case TAG_STRING -> @@ -710,9 +694,9 @@ private void processLdc(int index) { case TAG_METHOD_TYPE -> currentFrame.pushStack(Type.METHOD_TYPE); case TAG_DYNAMIC -> - currentFrame.pushStack(cp.entryByIndex(index, ConstantDynamicEntry.class).asSymbol().constantType()); + currentFrame.pushStack(ClassReaderImpl.checkType(e, index, ConstantDynamicEntry.class).asSymbol().constantType()); default -> - throw generatorError("CP entry #%d %s is not loadable constant".formatted(index, cp.entryByIndex(index).tag())); + throw generatorError("CP entry #%d %s is not loadable constant".formatted(index, tag)); } } @@ -721,7 +705,7 @@ private void processSwitch(RawBytecodeHelper bcs) { int alignedBci = RawBytecodeHelper.align(bci + 1); int defaultOffset = bcs.getIntUnchecked(alignedBci); int keys, delta; - currentFrame.popStack(); + currentFrame.decStack(); if (bcs.opcode() == TABLESWITCH) { int low = bcs.getIntUnchecked(alignedBci + 4); int high = bcs.getIntUnchecked(alignedBci + 2 * 4); @@ -765,8 +749,7 @@ private void processFieldInstructions(RawBytecodeHelper bcs) { currentFrame.decStack(Util.isDoubleSlot(desc) ? 2 : 1); } case GETFIELD -> { - currentFrame.decStack(1); - currentFrame.pushStack(desc); + currentFrame.dec1PushStack(desc); } case PUTFIELD -> { currentFrame.decStack(Util.isDoubleSlot(desc) ? 3 : 2); @@ -804,7 +787,7 @@ private boolean processInvokeInstructions(RawBytecodeHelper bcs, boolean inTryBl throw generatorError("Bad operand type when invoking "); } } else { - currentFrame.decStack(1); + currentFrame.decStack(); } } currentFrame.pushStack(mDesc.returnType()); @@ -817,10 +800,17 @@ private Type getNewarrayType(int index) { } private void processAnewarray(int index) { - currentFrame.popStack(); + currentFrame.decStack(); currentFrame.pushStack(cpIndexToType(index, cp).toArray()); } + /** + * {@return the generator error with stack underflow} + */ + private IllegalArgumentException stackUnderflow() { + return generatorError("Operand stack underflow"); + } + /** * {@return the generator error with attached details} * @param msg error message @@ -900,6 +890,7 @@ private void detectFrames() { } catch (IllegalArgumentException iae) { throw generatorError("Detected branch target out of bytecode range", bci); } + for (int i = 0; i < rawHandlers.size(); i++) try { addFrame(rawHandlers.get(i).handler()); } catch (IllegalArgumentException iae) { @@ -968,44 +959,214 @@ public String toString() { return (dirty ? "frame* @" : "frame @") + offset + " with locals " + (locals == null ? "[]" : Arrays.asList(locals).subList(0, localsSize)) + " and stack " + (stack == null ? "[]" : Arrays.asList(stack).subList(0, stackSize)); } + Frame dup() { + int stackSize = this.stackSize; + if (stackSize < 1) throw stackUnderflow(); + checkStack(stackSize + 1); + stack[stackSize] = stack[stackSize - 1]; + this.stackSize = stackSize + 1; + return this; + } + + Frame dup_x1() { + int stackSize = this.stackSize; + if (stackSize < 2) throw stackUnderflow(); + checkStack(stackSize + 1); + var stack = this.stack; + Type type0 = stack[stackSize - 2]; + Type type1 = stack[stackSize - 1]; + stack[stackSize - 2] = type1; + stack[stackSize - 1] = type0; + stack[stackSize ] = type1; + this.stackSize = stackSize + 1; + return this; + } + + Frame dup_x2() { + int stackSize = this.stackSize; + if (stackSize < 3) throw stackUnderflow(); + checkStack(stackSize + 1); + var stack = this.stack; + Type type0 = stack[stackSize - 3]; + Type type1 = stack[stackSize - 2]; + Type type2 = stack[stackSize - 1]; + stack[stackSize - 3] = type2; + stack[stackSize - 2] = type0; + stack[stackSize - 1] = type1; + stack[stackSize ] = type2; + this.stackSize = stackSize + 1; + return this; + } + + Frame dup2() { + int stackSize = this.stackSize; + if (stackSize < 2) throw stackUnderflow(); + checkStack(stackSize + 2); + stack[stackSize ] = stack[stackSize - 2]; + stack[stackSize + 1] = stack[stackSize - 1]; + this.stackSize = stackSize + 2; + return this; + } + + Frame dup2_x1() { + int stackSize = this.stackSize; + if (stackSize < 3) throw stackUnderflow(); + checkStack(stackSize + 2); + var stack = this.stack; + Type type0 = stack[stackSize - 3]; + Type type1 = stack[stackSize - 2]; + Type type2 = stack[stackSize - 1]; + stack[stackSize - 3] = type1; + stack[stackSize - 2] = type2; + stack[stackSize - 1] = type0; + stack[stackSize ] = type1; + stack[stackSize + 1] = type2; + this.stackSize = stackSize + 2; + return this; + } + + Frame dup2_x2() { + int stackSize = this.stackSize; + if (stackSize < 4) throw stackUnderflow(); + checkStack(stackSize + 2); + var stack = this.stack; + Type type0 = stack[stackSize - 4]; + Type type1 = stack[stackSize - 3]; + Type type2 = stack[stackSize - 2]; + Type type3 = stack[stackSize - 1]; + stack[stackSize - 4] = type2; + stack[stackSize - 3] = type3; + stack[stackSize - 2] = type0; + stack[stackSize - 1] = type1; + stack[stackSize ] = type2; + stack[stackSize + 1] = type3; + this.stackSize = stackSize + 4; + return this; + } + + Frame swap() { + int stackSize = this.stackSize - 2; + if (stackSize < 0) throw stackUnderflow(); + var stack = this.stack; + Type type = stack[stackSize]; + stack[stackSize] = stack[stackSize + 1]; + stack[stackSize + 1] = type; + return this; + } + Frame pushStack(ClassDesc desc) { - return switch (desc.descriptorString().charAt(0)) { - case 'J' -> - pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); - case 'D' -> - pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); - case 'I', 'Z', 'B', 'C', 'S' -> - pushStack(Type.INTEGER_TYPE); - case 'F' -> - pushStack(Type.FLOAT_TYPE); - case 'V' -> - this; - default -> - pushStack(Type.referenceType(desc)); - }; + if (desc == CD_long) return pushStack(Type.LONG_TYPE, Type.LONG2_TYPE); + if (desc == CD_double) return pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); + return desc == CD_void ? this + : pushStack( + desc instanceof PrimitiveClassDescImpl + ? (desc == CD_float ? Type.FLOAT_TYPE : Type.INTEGER_TYPE) + : Type.referenceType(desc)); + } + + Frame dec1PushStack(ClassDesc desc) { + if (desc == CD_long) return decStack1PushStack(Type.LONG_TYPE, Type.LONG2_TYPE); + if (desc == CD_double) return decStack1PushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); + return desc == CD_void ? this + : decStack1PushStack( + desc instanceof PrimitiveClassDescImpl + ? (desc == CD_float ? Type.FLOAT_TYPE : Type.INTEGER_TYPE) + : Type.referenceType(desc)); } Frame pushStack(Type type) { + int stackSize = this.stackSize; checkStack(stackSize); - stack[stackSize++] = type; + stack[stackSize] = type; + this.stackSize = stackSize + 1; return this; } Frame pushStack(Type type1, Type type2) { + int stackSize = this.stackSize; checkStack(stackSize + 1); - stack[stackSize++] = type1; - stack[stackSize++] = type2; + stack[stackSize] = type1; + stack[stackSize + 1] = type2; + this.stackSize = stackSize + 2; return this; } Type popStack() { - if (stackSize < 1) throw generatorError("Operand stack underflow"); - return stack[--stackSize]; + int stackSize = this.stackSize - 1; + if (stackSize < 0) throw stackUnderflow(); + this.stackSize = stackSize; + return stack[stackSize]; + } + + Frame decStack() { + if (--stackSize < 0) throw stackUnderflow(); + return this; } Frame decStack(int size) { stackSize -= size; - if (stackSize < 0) throw generatorError("Operand stack underflow"); + if (stackSize < 0) throw stackUnderflow(); + return this; + } + + Frame decStack1PushStack(Type type1, Type type2) { + int stackSize = this.stackSize - 1; + if (stackSize < 0) throw stackUnderflow(); + checkStack(stackSize + 2); + stack[stackSize ] = type1; + stack[stackSize + 1] = type2; + this.stackSize = stackSize + 2; + return this; + } + + Frame decStack1PushStack(Type type) { + int stackSize = this.stackSize - 1; + if (stackSize < 0) throw stackUnderflow(); + stack[stackSize] = type; + return this; + } + + Frame decStack2PushStack(Type type) { + int stackSize = this.stackSize - 2; + if (stackSize < 0) throw stackUnderflow(); + stack[stackSize] = type; + this.stackSize = stackSize + 1; + return this; + } + + Frame decStack2PushStack(Type type1, Type type2) { + int stackSize = this.stackSize - 2; + if (stackSize < 0) throw stackUnderflow(); + var stack = this.stack; + stack[stackSize ] = type1; + stack[stackSize + 1] = type2; + return this; + } + + Frame decStack3PushStack(Type type1, Type type2) { + int stackSize = this.stackSize - 3; + if (stackSize < 0) throw stackUnderflow(); + stack[stackSize ] = type1; + stack[stackSize + 1] = type2; + this.stackSize = stackSize + 2; + return this; + } + + Frame decStack4PushStack(Type type) { + int stackSize = this.stackSize - 4; + if (stackSize < 0) throw stackUnderflow(); + stack[stackSize] = type; + this.stackSize = stackSize + 1; + return this; + } + + Frame decStack4PushStack(Type type1, Type type2) { + int stackSize = this.stackSize - 4; + if (stackSize < 0) throw stackUnderflow(); + var stack = this.stack; + stack[stackSize ] = type1; + stack[stackSize + 1] = type2; + this.stackSize = stackSize + 2; return this; } @@ -1034,34 +1195,59 @@ void initializeObject(Type old_object, Type new_object) { Frame checkLocal(int index) { if (index >= frameMaxLocals) frameMaxLocals = index + 1; if (locals == null) { - locals = new Type[index + FRAME_DEFAULT_CAPACITY]; - Arrays.fill(locals, Type.TOP_TYPE); + initLocals(index); } else if (index >= locals.length) { - int current = locals.length; - locals = Arrays.copyOf(locals, index + FRAME_DEFAULT_CAPACITY); - Arrays.fill(locals, current, locals.length, Type.TOP_TYPE); + growLocals(index); } return this; } + private void initLocals(int index) { + locals = new Type[index + FRAME_DEFAULT_CAPACITY]; + Arrays.fill(locals, Type.TOP_TYPE); + } + + private void growLocals(int index) { + int current = locals.length; + locals = Arrays.copyOf(locals, index + FRAME_DEFAULT_CAPACITY); + Arrays.fill(locals, current, locals.length, Type.TOP_TYPE); + } + private void checkStack(int index) { if (index >= frameMaxStack) frameMaxStack = index + 1; if (stack == null) { - stack = new Type[index + FRAME_DEFAULT_CAPACITY]; - Arrays.fill(stack, Type.TOP_TYPE); + initStack(index); } else if (index >= stack.length) { - int current = stack.length; - stack = Arrays.copyOf(stack, index + FRAME_DEFAULT_CAPACITY); - Arrays.fill(stack, current, stack.length, Type.TOP_TYPE); + growStack(index); } } + private void initStack(int index) { + stack = new Type[index + FRAME_DEFAULT_CAPACITY]; + Arrays.fill(stack, Type.TOP_TYPE); + } + + private void growStack(int index) { + int current = stack.length; + stack = Arrays.copyOf(stack, index + FRAME_DEFAULT_CAPACITY); + Arrays.fill(stack, current, stack.length, Type.TOP_TYPE); + } + private void setLocalRawInternal(int index, Type type) { checkLocal(index); + var locals = this.locals; localsChanged |= !type.equals(locals[index]); locals[index] = type; } + private void setLocalRawInternal(int index, Type type1, Type type2) { + checkLocal(index + 1); + var locals = this.locals; + localsChanged |= !type1.equals(locals[index]) || !type2.equals(locals[index + 1]); + locals[index ] = type1; + locals[index + 1] = type2; + } + void setLocalsFromArg(String name, MethodTypeDesc methodDesc, boolean isStatic, Type thisKlass) { int localsSize = 0; // Pre-emptively create a locals array that encompass all parameter slots @@ -1162,8 +1348,7 @@ void setLocal(int index, Type type) { Type old = getLocalRawInternal(index); if (old == Type.DOUBLE_TYPE || old == Type.LONG_TYPE) { setLocalRawInternal(index + 1, Type.TOP_TYPE); - } - if (old == Type.DOUBLE2_TYPE || old == Type.LONG2_TYPE) { + } else if (old == Type.DOUBLE2_TYPE || old == Type.LONG2_TYPE) { setLocalRawInternal(index - 1, Type.TOP_TYPE); } setLocalRawInternal(index, type); @@ -1181,8 +1366,7 @@ void setLocal2(int index, Type type1, Type type2) { if (old == Type.DOUBLE2_TYPE || old == Type.LONG2_TYPE) { setLocalRawInternal(index - 1, Type.TOP_TYPE); } - setLocalRawInternal(index, type1); - setLocalRawInternal(index + 1, type2); + setLocalRawInternal(index, type1, type2); if (index >= localsSize - 1) { localsSize = index + 2; } From 6e0138450ab4b0af917cbf61701b0d1d17eeaa44 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Thu, 10 Oct 2024 15:13:39 +0000 Subject: [PATCH 03/95] 8341136: Optimize StackMapGenerator::trimAndCompress Reviewed-by: liach --- .../jdk/internal/classfile/impl/StackMapGenerator.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java index 3007c8ca40a..4bc4e259f5a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java @@ -1387,7 +1387,10 @@ private static int trimAndCompress(Type[] types, int count) { int compressed = 0; for (int i = 0; i < count; i++) { if (!types[i].isCategory2_2nd()) { - types[compressed++] = types[i]; + if (compressed != i) { + types[compressed] = types[i]; + } + compressed++; } } return compressed; From 2d8fcc4271802b211c4718c6abae3ce9c99eafbd Mon Sep 17 00:00:00 2001 From: Tejesh R Date: Thu, 10 Oct 2024 15:34:39 +0000 Subject: [PATCH 04/95] 8340193: Open source several AWT Dialog tests - Batch 1 Reviewed-by: psadhukhan --- .../Dialog/DialogIconTest/DialogIconTest.java | 91 ++++++ .../awt/Dialog/DialogIconTest/swing.small.gif | Bin 0 -> 1136 bytes .../jdk/java/awt/Dialog/DialogResizeTest.java | 118 ++++++++ .../FileDialogIconTest.java | 258 ++++++++++++++++++ .../java/awt/Dialog/FileDialogIconTest/T1.gif | Bin 0 -> 2553 bytes .../java/awt/Dialog/FileDialogIconTest/T2.gif | Bin 0 -> 2704 bytes .../java/awt/Dialog/FileDialogIconTest/T3.gif | Bin 0 -> 2523 bytes .../java/awt/Dialog/FileDialogIconTest/T4.gif | Bin 0 -> 2721 bytes .../Dialog/FileDialogIconTest/loading-msg.gif | Bin 0 -> 1518 bytes .../awt/Dialog/FileDialogWrongNameCrash.java | 72 +++++ .../java/awt/Dialog/GetLocationTest_1.java | 129 +++++++++ 11 files changed, 668 insertions(+) create mode 100644 test/jdk/java/awt/Dialog/DialogIconTest/DialogIconTest.java create mode 100644 test/jdk/java/awt/Dialog/DialogIconTest/swing.small.gif create mode 100644 test/jdk/java/awt/Dialog/DialogResizeTest.java create mode 100644 test/jdk/java/awt/Dialog/FileDialogIconTest/FileDialogIconTest.java create mode 100644 test/jdk/java/awt/Dialog/FileDialogIconTest/T1.gif create mode 100644 test/jdk/java/awt/Dialog/FileDialogIconTest/T2.gif create mode 100644 test/jdk/java/awt/Dialog/FileDialogIconTest/T3.gif create mode 100644 test/jdk/java/awt/Dialog/FileDialogIconTest/T4.gif create mode 100644 test/jdk/java/awt/Dialog/FileDialogIconTest/loading-msg.gif create mode 100644 test/jdk/java/awt/Dialog/FileDialogWrongNameCrash.java create mode 100644 test/jdk/java/awt/Dialog/GetLocationTest_1.java diff --git a/test/jdk/java/awt/Dialog/DialogIconTest/DialogIconTest.java b/test/jdk/java/awt/Dialog/DialogIconTest/DialogIconTest.java new file mode 100644 index 00000000000..06debe28fc5 --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogIconTest/DialogIconTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Image; +import java.awt.Label; +import java.awt.MediaTracker; +import java.awt.Toolkit; +import java.awt.Window; +import java.util.List; + +/* + * @test + * @bug 4779641 + * @summary Test to verify that Non-resizable dialogs should not show icons + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogIconTest + */ + +public class DialogIconTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. This is a Windows-only test of Dialog icons + 2. You can see a frame with a swing icon and two dialogs that it + owns. The resizable dialog should have the same icon as the + frame. The non-resizable dialog should have no icon at all + 3. Press PASS if this is true, press FAIL otherwise + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + public static List initialize() { + Frame f = new Frame("Parent frame"); + f.setBounds(50, 50, 200, 200); + + Dialog dr = new Dialog(f, "Resizable Dialog"); + dr.setLocation(100, 100); + dr.add(new Label("Should inherit icon from parent")); + dr.pack(); + + Dialog dn = new Dialog(f, "NON Resizable Dialog"); + dn.setLocation(150, 150); + dn.add(new Label("Should have no icon")); + dn.pack(); + dn.setResizable(false); + + String fileName = System.getProperty("test.src") + + System.getProperty("file.separator") + "swing.small.gif"; + + Image icon = Toolkit.getDefaultToolkit().createImage(fileName); + MediaTracker tracker = new MediaTracker(f); + tracker.addImage(icon, 0); + try { + tracker.waitForAll(); + } catch (InterruptedException ie) { + throw new RuntimeException("MediaTracker addImage Interrupted!"); + } + f.setIconImage(icon); + return List.of(f, dn, dr); + } +} diff --git a/test/jdk/java/awt/Dialog/DialogIconTest/swing.small.gif b/test/jdk/java/awt/Dialog/DialogIconTest/swing.small.gif new file mode 100644 index 0000000000000000000000000000000000000000..14a489ff4e7df0b4a268582701a1e7ef655047fa GIT binary patch literal 1136 zcmV-$1dsbiNk%w1VL$*t0QCR>_xJa}z`*bC@8{>|*4EagrKOCFjD&=QeSLjeT3Tac zW0aJXwY9ZrX=zeYQZX?xDJdx!7#Ii$2mk;85D*X{AtA=b#@^oEb8~Y_N=iaPLanW> zoSdAhs;ZWjmSb94)6>&TOiV%u7-I+_F&GGx5HWKYDV!lftvOoo@bFR?5PKL1jD&=* zudk()gq(X?j9M{!N-29vA%ts6b5c2DLLr=lbG@aU|Ns9|LNQt~5K=J^LLmrBDF{+2 z5IG?TA^8LW00031EC2ui06+jh000L5z<_W_EE5BEk63?u2XB#Ki+2bU6B-(+VV7|Ys1pivl9>%6B$wrjDnqEyr{Dp!$}Mc z5q+OT$x<1{$I(T95vY$GhC{0riB}G>s0vLRtEdn~23A8DvlIaa4GtL%!A%USvl8w~ z2qch8P-K)KW8_eUW0I!9g9!wcFqoEb;ie)R5+;1`Pv3(G8?13CmawCiA__Dnl=oy` zL?i|TYGgU&3y}laRFZ?jL4p1+m^x_{DK%uAN}0N#EWq&72!^1&3O<^qVUYm?9MEuB zB>~C-bVnLcpddkni4h`@HYjpS2!{#glyzj1NkP6%5dt(AQKD~&6EYW`^j1sFEezcP z9FV{O2L!);Q+Pm?j{zGY4Io&f(w4&k&k{HQ!0`ie2^1(qOxVWAK!wdyo(b7Ep#hHD z8CI+?o%Ye?1r+Xv;Z_8~g8{d3bReM{@`MTn11zW@oK!vx5(p&60RRE>z9vW{m4g6T zAXfo6gx;RKfQAYxG;GMQZU}=1l?)SkpkXxo+W`27Lk5M-lYvF1z_Azz|1~(^I^2*^ zoqlIjfPjDwCKtj4bN-}(-X4FXVL$}hWyoF#_3@!e05$~x0S6}jmqSw*X)?)l7sSZk zjP@ZIM>_#zfB|wkCYM79LrkK{8w|WwLkzIM!~WoH0h(A==?pdK;DgH_f84{v zJVYF@nJd~?K)E!u{IbVA$jrmc5r2>~6D_z images; + static String fileBase; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Select the Image for a Dialog and Frame using either + Load/Save/Just Dialog. + 2. Set the Icon Image/s to Frame and Dialog. Verify that the + Icon is set for the respective Frame and Dialog. + If selected Icon is set to Frame and Dialog press PASS + else FAIL. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .logArea(8) + .build() + .awaitAndCheck(); + } + + public static void setImagesToFD(java.util.List listIcon) { + FileDialogIconTest.images = listIcon; + } + + public static void setImagesToFrame(java.util.List listIcon) { + frame.setIconImages(listIcon); + } + + public static void setImageToFD(Image img) { + FileDialogIconTest.image = img; + } + + public static void setImageToFrame(Image img) { + frame.setIconImage(img); + } + + public static Frame initialize() { + frame = new Frame("FileDialogIconTest"); + Button setImageButton1 = new Button("setIconImageToFrame"); + Button setImageButton2 = new Button("setIconImageToDialog"); + Button setImageButton3 = new Button("setIconImagesToFrame"); + Button setImageButton4 = new Button("setIconImagesToDialog"); + Button setImageButton5 = new Button("setIconBufferedImagesToDialog"); + Button setImageButton6 = new Button("setIconBufferedImagesToFrame"); + + if (System.getProperty("test.src") == null) { + fileBase = ""; + } else { + fileBase = System.getProperty("test.src") + System.getProperty("file.separator"); + } + + final String fileName = fileBase + "loading-msg.gif"; + + setImageButton1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + try { + Image image = Toolkit.getDefaultToolkit().getImage(fileName); + setImageToFrame(image); + PassFailJFrame.log("Loaded image . setting to frame"); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + setImageButton2.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + try { + Image image = Toolkit.getDefaultToolkit().getImage(fileName); + setImageToFD(image); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + setImageButton3.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + try { + Image image; + java.util.List list = new java.util.ArrayList(); + for (int i = 1; i <= 4; i++) { + String fileName = fileBase + "T" + i + ".gif"; + image = Toolkit.getDefaultToolkit().getImage(fileName); + PassFailJFrame.log("Loaded image " + fileName + ". setting to the list for frame"); + list.add(image); + } + setImagesToFrame(list); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + + setImageButton4.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + try { + Image image; + List list = new ArrayList<>(); + for (int i = 1; i <= 4; i++) { + String fileName = fileBase + "T" + i + ".gif"; + image = Toolkit.getDefaultToolkit().getImage(fileName); + PassFailJFrame.log("Loaded image " + fileName + ". setting to the list for dialog"); + list.add(image); + } + setImagesToFD(list); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + + setImageButton5.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + List list = new ArrayList<>(); + try { + Robot robot = new Robot(); + Rectangle rectangle; + for (int i = 1; i <= 4; i++) { + rectangle = new Rectangle(i * 10, i * 10, i * 10 + 40, i * 10 + 40); + java.awt.image.BufferedImage image = robot.createScreenCapture(rectangle); + robot.delay(100); + list.add(image); + } + } catch (Throwable t) { + t.printStackTrace(); + } + PassFailJFrame.log("Captured images and set to the list for dialog"); + } + }); + + setImageButton6.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + List list = new ArrayList<>(); + try { + Robot robot = new Robot(); + Rectangle rectangle; + for (int i = 1; i <= 4; i++) { + rectangle = new Rectangle(i * 10, i * 10, i * 10 + 40, i * 10 + 40); + java.awt.image.BufferedImage image = robot.createScreenCapture(rectangle); + robot.delay(100); + list.add(image); + } + } catch (Throwable t) { + t.printStackTrace(); + } + PassFailJFrame.log("Captured images and set to the list for frame"); + } + }); + + Button buttonLoad = new Button("Load Dialog"); + Button buttonSave = new Button("Save Dialog"); + Button buttonSimple = new Button("Just Dialog"); + buttonLoad.addActionListener(new MyActionListener(FileDialog.LOAD, "LOAD")); + buttonSave.addActionListener(new MyActionListener(FileDialog.SAVE, "SAVE")); + buttonSimple.addActionListener(new MyActionListener(-1, "")); + + frame.setSize(400, 400); + frame.setLayout(new FlowLayout()); + frame.add(buttonLoad); + frame.add(buttonSave); + frame.add(buttonSimple); + frame.add(setImageButton1); + frame.add(setImageButton2); + frame.add(setImageButton3); + frame.add(setImageButton4); + frame.pack(); + return frame; + } +} + +class MyActionListener implements ActionListener { + int id; + String name; + + public MyActionListener(int id, String name) { + this.id = id; + this.name = name; + } + + public void actionPerformed(ActionEvent ae) { + try { + FileDialog filedialog; + if (id == -1 && Objects.equals(name, "")) { + filedialog = new FileDialog(FileDialogIconTest.frame); + } else { + filedialog = new FileDialog(FileDialogIconTest.frame, name, id); + } + if (FileDialogIconTest.image != null) { + filedialog.setIconImage(FileDialogIconTest.image); + } + + if (FileDialogIconTest.images != null) { + filedialog.setIconImages(FileDialogIconTest.images); + } + filedialog.setVisible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/T1.gif b/test/jdk/java/awt/Dialog/FileDialogIconTest/T1.gif new file mode 100644 index 0000000000000000000000000000000000000000..24a7dea299fd1c877792ce09a5b64d1f57c1b323 GIT binary patch literal 2553 zcmVUj1S&E~Ljg3aD)&M9tGcT}~(d!To zlU7%}Yinx^3Su-gH3WCA1!a_IXvTzu&NP_DCyBIcy4y`oPqntT1%$p&YjviUU=Ge8LEq* zb)s9PMTG?}I-MCfA~Ole$_X>g%@sGbxw?`uzCx!AoW;w@&fm}96$a9YyOWh#hqw+I zBHhmw@&@$uE%*2>755Xj7CL?cS#y&G2OV7Qmd%)V!@z_I6evJ^NKuBpd@cI@i_{Cu zsbRSYC>(bqB83ttA*fVY;=uz95oJuUSdX5G40j}vqLL+tF#kpj-g$!tG3AD%NRw_@ z=@Nqg8Bk@kusFd4ADvaQ1Ow^7#WJENAs_`nmh1)qAI>IK`Je;_3>Krxw8{W0A%k3T zD4TJksMxb*(SG>Afy3azC)Pq}`c$qA3axliio)jSBE89=MHEZ+0DvAJB7hhTB7$hs z3pgwojFj!$#!(^vVIxJ3w#!2eoCHiZGzbY2NSj8zVEBU6BhtEd+W0AjKck??aluF= zvkDM7fBx`0cxmFteHN03RJ;JOUpGAV5Qa z0s`oRcnFSQ!%~brr5*+**tG-~hb*!JG7H(q*#Q}JSO3}q2LdP{4J6XgL5T$Z_uqjd zFqoct0=z}Qdjn0l&LZz*NP%|x1=rt!0(KzekVF!RVu~Z6$6SmUJg~-OSjYjANHd6_ z8;`)D24Im~c6lO+1k#{jQ_Q{fz$+bLaYzpiSa8ff6p#Q|k3W)Fqyj=Jfajih771p6 z11|PfnN5xYr4bZP`AkG}`Uql>TwFehFR;73fO?F znGl$8=r3|?VnGEuLO>P=sfH=ual@5sZgJ!R3IAxVdY%BmYcW2ck_e!Rn!!T4m2m25 z2evAnY2J+%oCM~c8y=w0;%UQ@Z>dCpnzsQv&afP4=`X;*k--BG9(aI44Fe2NgU2ZF zV8#ypJv!^8LPFqRlk$>q?GV0NaKkwEor_-s_;l3!)!h{0S65)$my& zNI(5LE7X(o2P1%}Dwj1F+0b;*f#Zct1BobI$ZFsK8VF5*pGzClNZ>pSa0Ul3$RN#v z!3ui#Wdr>K(6cJlffppg0xQr{3Kr;q(BUZr3oKrn7y&v7HlPC~sNe-NXuls&F9X(t z$O9@TqGzS8eI4+@09}YD2ZoFV#RDDtQs=(nQBZz6)F2$T0z~=U#|9(lml4x;A|LcE zWK6^p0$@0R5a=n5%X^~&C};o~$p4Eh1>oTZ8L)y3Kt+fp03(6=q&gD*X91dXBNCBS zfT0ENh=zM(14u9v8CZ}kI0%3ifABau__2-@kQW5PI3OZj>X17i8q+XWoCxq>XewaZ z4Gp)v2E2d-Q<(q;j>kXtIY~@Z=w#HpCIMie*DOq$`QGf)4yA0^T8C4Qfcjghg?V zb(Dd=0C0d9z(A8jxPVU>;Kx5UNsuElrH~G}0P+!TK*jlu0f%-rT9WXMFL(h3>NtTR za#IAq4CizhP|0{bzy|3gBjrPybQ;0PQU4tpoJno15h1I89K&CP5&f)l4agR?1SMg}|kI zk|pRy;K3bnGXt6gC(Wi~g7p~G26qZ-rBqPVq8-(qN_|1yY`1{}93Ybcy^p!q)opm zRU%Nx6@xuzAunZE#VVGJl59W^CQv*3aPt5zh^k+)A-7<_Yfuj`k^AmxSUI9Cu4r4D z>g4*^j8Nb#X8l25O+khNm|##$!hj75yS#hGv5={2ZX9bnMVaDEUuK2sa2OsxtN-Z%Ji1-(lEDOIg3$vCOGf9CSDm#D-2r;58xovWyOkv_8Crte z#nIQWDCLuaIeS;oU2Pu_0j>jt+ukU}fB=hmk%5aB-k`qpqBfK(5Fz{9)26nss}-;b zGRz#Pkl+MJ%`Jl$d|SDGfLjyuuUUKW(F#w93-@WzQ|8NIi%PJzN1W@`W^g?@?U=m* z7K7ZF;*_XNzy`LAtC4G4OY?%R2a;PzNw8~HwEEZy7a+h=1XX1hpJ0kL#BFH3tY3FD zgC21zt65KcW+L>~IEn?u6VnJ4c$ z#aGZE0RnRrRF{C;6R1f&8Uhh#%gNEYK6fdkm%$5UpaRjqm P7x~0Bcm#DS1OWg$YOidd literal 0 HcmV?d00001 diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/T2.gif b/test/jdk/java/awt/Dialog/FileDialogIconTest/T2.gif new file mode 100644 index 0000000000000000000000000000000000000000..1f3fd66328ba678811608133a189ae76837cb7db GIT binary patch literal 2704 zcmV;B3UBpCNk%w1VN3u`0M-8hu_Q{*Ol?k0x$Nxh|K0$RjE`e1PgeyW%`jQtbd9MB zG7t@wAP5mAB_^qhn%91U%H|a zhI4xj2yP7sPJ3%H&ax>&Btmg+v>_Ofb#07rKU{BBV^&U-H8pNE5pPaObi!kMhICkd ze$TyzmLeELG%%EufF2-rlvfwZjEA=Y*Wz#t{Gi|Cm8t?amE_F;YS~ zg#Z8lA^!_OL{CCRI!RJ7E-(V#?EoS91OWg5KL9KM0000?08Ib@2LHgHa7Zi~kI1BQ z$!t2G(5Q4uty+&Zn`7%Jbh(b~b*$8O3k42j&1Q4Z-Y}VLMz19jl%&D$>+vRQaAI>( zT6QLTd<=|@j*NYHf?kA$ONV!hkB)ttkrEJZa+XMkYI~jy5`i2Rtr#pOC5nxFl4zr( zLuU}EoF*JD6~hz76FA7o2?;9~1`Uv^foPS5S!uqI5*!o5#Ky(RI0@q`%gv|_dJwoa zmYCTMCc@qo$Km4%8O!|q0n7oMMG8e}jtw*>P&krMpq{;A8TLs$x1!>T02ea`*oZ(8 z9#QVD9OMv>L%nbjmszl|0>gz17-DXmN%JDd9wAh1D_G@V0sj-w7)i+B%~(noD=(Nx zRHA7K6FV*hq=^7Q0IO9IJX^&sSCo<2Ae@{K5osU;FJ5?1>!GcO6fZ)cD6s=UkE>X- zRxJ^ND^Dngd^Irnt*nKFY~6b3u$XbcKZx(gCv${;(F{Y*Fg#t zOnBdmC3NM$B@XlgO#?0%GnxekFo2qQ2`0$kf(;U>po8u$ha3g=y?6r!Cg8F}7oRck z-!UH!_#=BI8hK=qCZhMCk`y>;n*s)4mxLDFb(aA|2^_{2kY9RfCy8E?s2q!C-k?B~ z84__&mE4e(fD0JRY2FB7-U;TTUZS`_3NGHKfC4>GlSCJ&gpf>}D}>k>mtvxqB!o)J zrzCQWp0~h($&GM8rDswAfo@oA=N~frh^JP84$hDwbHpCED1s!i$LEU*P>^YMMR0m! z2KiJ}+@h|=$J%M9F~%IL$T~us}1;C+Pja)(^nI zqKjZtV1wXoTFdrLTbU_a(6v!y!BbdN~1}tzuLgx=mOm*i1kU)Y48aSXq1eP1GHwrB#@9zNx zH2*`5C^QfPMjP!=LiGy^SGIV-gWwT>128Zk2)w5`it*qC5fC2%2~dFc&A@nkiw*$5 zMnC&KZ+|oC!T*58AO+=(1m3IN3|Qbm2vUw~!OOt|$_IxEwl8mb6M(E@n4=RRi72%z8v2I#?Tz{vm`3;>lsI9VR- zQyrc;azXlvPbTYTvmE3uj{l&OVp@Lrq%nrTj6fv9c6!l+`kd(i>>*W&oVHSyy7Prp z%$ps35SJ3n1_1m#+5kR^$omECIm{~PNlWOe6&m$sAh;tObr%7PdcXjb0Vf%USykR7 z!ZuB!0M8EkrB^YrtSx9kS@(+9yyms6f1F`iPw)Z;OlCIxJU~aM3R33DvX@0F6R{|Q5QSg|SAnWxLom$7VnP;Xk}I+(YQoLh5sR>uB5XC=#|X55uty6( zUHrv>13+M^1E{JHd;$Rz(5i=w!rLQ__rlycS9j2@r2DX_*9LeX1Q)1_XnX5XstOkq z5HNt5#E4RCeo||nOaE%#&WlIT`2eq-^?+eH(yZP}fU%7of+d?43J&Kj*$TlV61S_WrrRdw^_<`pK|lmDA3t4uL`~{Xaitj#WJ7-z|F6q zc)_o%rT~Y!L}{I33CC?lKm;HzYy)5t+Qc59yTP5XpnTGU7ST$KBiN+-E{Dxs_K~`I zRR{xKz+-(zl>xt%FlT(?FVcOD0xuYWAKSO&CBGKIEg*q)r_AH4MD+kSm9UUawgnOh z&C5`Q3n$0?T<2CUi+&Y>3e>^?>|$UeMSk$|Qaoo3*?_-BhAUKY;sF@wF1D|=<6iyR z0zt3&Xl>r7V*kmg137=i1|_(&i}u;0wV>bxl+$Zjaco;0iy#6g$e(-*T>=cWgv9mj zZI-o6g(7!Z0UC@APT=AJ6gc|?(8e{iPoRSQ6&4Z(h{>U!I%+Ecm#SAV!2wha9c6dZ zM{G$;TI9>_DTkm0Fu4c@9Nb$g)qv4t5I4D@1Dn?vASY7}_>}c$0C>kl-c{l4s7>(g zAU3)Qf};Th5^H2+%SJYJ*})E8QvwmHEh^eR^i^Q<=H7B(0HyZs7$RPP5R7r$M*d}1 zHWCvA$VB8;8M$b?9RdfWe5zz4!`RL!?w8+rptRBLRn%w~sFqyVHTb#13lVW3I=}#< z9wR^f#hdSPlU!(uu6GG+@N<=0qUt~_0M>i^a-(k@>^80Us3j2LZ=0PIX#c1KOb`OO z*PHE)==P{XV1ggm{qDL5oZyBD^cp?G#3ly3VjGYqf9jy@h)=u_7SH&52XQ2xS;{oR zaPLxMe(IV>0_Qz{_^Fm1^Y{&V>Xm?YAh!PYA9eV{XJ3Sf43n3VVR-D8sHgXUyp~kIBhkv1JVwC^? z|NsC0A^!_OL{CCRI!RJ7E-(V#?EoS91OWg5KL9KM0000?08Ib@2LHgHa7Zi~kI1BQ z$!t2G(5Q4uty+_qYz(vAX0PC;HfAWB&*hl<8*cy9v<{*v1zC z?(gmK0PO%3-kEA{S!KF~=+wpl;HX!@fx&_ZE68{^@0~phHNepd;|Z39X~2rPGw5K% zhzB4;HW<-hgFzW68E(`NF%6q4YCtKwknvbSj}JKL+{u%J$o~co3MgC%U||IiEhkpN zkjoH>U^YAEa-3;?@OFsA?naC@jO0lBq;=g_0eeoosq z-vWKHkKhrmEEA@{&k4g(;iFp&Nb(_xIn0s4znU zqd8C-0xG1&Tx;ze;GlyaeDL6e@45G!1SrsO*>(Cg5dT0pVw6*eF9nt+00_&;HerM; zMo8g{?O}-DZP)=7z#2<@R>%tkw&Q?tIiR?p0`Rri;)@WnNMVM1?Y99?N?{?Q1=s-4 zky|OO_F9rgx=3GiTq^nC2OM~%-;*A0a)e-okbq5jSgN+=0bP12A8fL*xWEEO()oc7 zdU04lR}SPOC2$sGQiYIfvt6BN`t2$1(3w3 z4v1?UYCV{15&@tEIJ5&NTzJkm@Ps`? zfF}>p643C&eA0QY#CzVGTLHYD;H?o{$fQ9)S8~wk1nrIdU6kns=TO#%Y3Kk5X6ib5X{LI}^bwy??sL_q7j3pf}fkz;;fE5$YD1j3pz z0M{{FIhg>Pz{+M20)4Gza5Mo*GtfZ?IWS$HxqfDfLUx1Zu0{?@?nQvZ!#T=k;;maJeHyo^X z${j=?f!vuwz=8uPRjyMEx8p8}3h!Jg*Y6{%P&V;LAAP*7dlId}`lHvnz|#vr@WBaS zZD3pgFyuePZ=`JAv+sZtQ@~*3fx{s#UMxeO1*XNP7Dx+y@}l4DN`pWB34wna@SnxL z7dNIk$OQy6;0hQ}I<}$j1`)88${@G^3Cf^r0;pj2-jIt7ilqS>dJdPCWdJ;l4r%c! z9sJO@rxa$Y0|c;@>0HRIs>pB%;8B4FP*MiC_-wCU;H3& zgA)`*1{OO`0N^-7rhI~q6&RGo@JON18NdTYdshvP$2N3bNCFX3K;Gn{jPY*(1urVQSPscOCKwkKO#!cahtda%5 z4C*me%VL(En!QCF%(NdG(Dqu^vMq9<7d_L(kRArIECZ+sTr8-;0Pu`$hjeh%YSq@e z$-NbLn+pL1qyV}jQ2#AXtE&WDkc38q(t!zY06p-c@mjSFuM$nCf`Al+0}OC0Yg>!A z7zCgz@O^-MyL(?fHJG{Nl~;eEYtzeSHn^Qo!$B?p+!vgW=Fe4Cmx|*1^n!ec5vg>9b<>7{5JvMkwK>8OkDMUX=U%o32HfaoNIQQqFgKX1T^XKSCBVzRDZSa-3?bx?uoxhL2>>4O zhBw>-B$%h2SImifW0amTAb|=j;P8OI*=iFfc)^Px?VM(o3#u@M%46WIkgIs)ODMU^ lXC87W!`uk>ra8^ycylM@T<4V3?A3bCgxgtM+O`M+06SJcV>kc+ literal 0 HcmV?d00001 diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/T4.gif b/test/jdk/java/awt/Dialog/FileDialogIconTest/T4.gif new file mode 100644 index 0000000000000000000000000000000000000000..67876264d8f7f4d350815a4e08f6eb1ec6f1bb0e GIT binary patch literal 2721 zcmV;S3SRX`Nk%w1VN3u`0M-8h+uPd)OKmeRut%%b1&+)A-T-Zurw|d6O-8tt((P<) zYz_@)Gcz*;cdh~~KPiZ@1!t2pn8s_m+m@J_dV0=4NK3e)FSWL|o{U1!v?7X#hXsVa z23d`eeNRtob!~@{7#O8&Z@wCJo}8_&OK_A%PmxkbYh(#A^!_OL{CCRI!RJ7E-(V#?EoS91OWg5KL9KM0000?08Ib@2LHgHa7Zi~kI1BQ z$!t2G(5Q4uty+_pY<6_vX0PDpwl*ni&S*_}QuBu2!qLbKgDH55*NgYMD-?BVY+rCv zTXcLC6%34xjf#4BFlse57llrT8Ze2Do{Ec(kdl;ym`7a`8hN3j9xxvUva=r~9<7Rh zlM`I0MO`(kFRcs}9tJ20%FD_&&d)mrx5azAZN5UOoW#e;%+KJ@5;o&5J3GS_)oQ(k zS;2XZ9wW>N&Jy?%1N;0g{{HyeM-4X9N;J*^IC%=BG!8da)WsVUw{y$Ye`gRNb4 zKKR3qq#>d_8l_Z*&!UL7QXRAi&?7~P5-dtQPJx(i-3IHPr?~|b#0y7$%U};MT zZWTZF5F!Kx3aw+uhymI0)R_%1XWr^_mnkv9L^BvnZa~Yn4g#bg;rcaf*|TSdh=Cx+ z3d*>3?hEcfrn-iTvVO| z=s5=)dk6@Spa2Xm_?~+u;BZxa_u)4PaG^2sLNer7g#TO#=}p%jgAA&0!-*=WprC^8 zu_(X_IMkIJ0W{LM!3P?&@f|7?C=d#V5?J(QVRPduf6YC3Q%GfTEI4a*&&LS2kdPB|8pr0hgmGb(~Zz1d2e4QNCBAlPt!7K><3H zZ~zFao_NEOEjSA4l_Xq)MURVAfC5Ahlvm(%47%E22o|8KfB>Yjs6hc46kx%s2dt{- z4T|D0V~sRHV1SlHjIatLA2?&2pUTM=ER!j=Xa7MC9W;A_p{3?v!3`Z05G@EYNXulj z)Sg}gIN`fqgHt!QXnX`YpPg42PbBb!!9$5Um0= zGgz+*2k=k;4?X)Fx5hZ!cjZ$f3tW^jysFH?+i*WEt(rPuK&=2dWWeCgte%>$#*%;C zLId_49stos@OKgk5_qI^3nj1@xz|rSS^sYdn-@?50yQ&<^8$h2E3MB5kd7^CB=8u| z4r})cmF%#0>Vg}>l}w9hDnc22v}D21b{cJMGI%dd*8mG zwSdtD5GGO?fdRWEj2EmV0uA6m3J~QquSttd(J26`GEl6C;h|>48{XHV)w2fZ1cl`> zzzl|HsZ|(bA2e_YR8}w|2YSs(?x|O^P;~+fSYV1W%)kj`z`vjIumvm#;0A&?#1a0$ zTY2yRM=X-U6fm(w8e~$bBsj$raR04m3+RCfPSC6z^uT-2+9B5tz=FJp&jAxCV-bs@ zMjEj3c?)8gz97h~8|bV67|5Z<{ANecxex@vTR<3fvVo{LK!`+)LyunI0p7p_2XBnt zn%?s;R?(~gJqRT6tT?=0a*&WYp#ca3paB4+@(27;KqG1JfE03I1#gODgQydOD{8D* z>?(l+H<>d)4sHQ55Ty(BVoC!T(~O`|<`2h~LTI*-W2CAM267-nyMaJ^nUkSDad`lm zaZixIlYs~ZkdqXerv@`HrlnM&&L7g`nQ4-wq8fCn4)7ocIxvv{>eT}kG)o6O$Ynn> z=1D-_@c~^)nRxayg9yY(6C9xErpdxu)SRU^8DapO3uqEA_xHX~rT~dbb3g?WddxGb z0tQp1DhH6Y&51H7W-!nwJ#}ybdh!f-lgmLT_ouN?7LuU7a)1LoV9cjx%^lg%W*TAr~T@+SXKTLTv1;Z0Mj?f|})^1UM~# z-7p4P5?NqsgA|@o7mxzfi;T z1n+Pi@Bs2AHo53M@Gs^tfo7&yY%K_!Nee3j9#~PI7U;ld??}AQMVSOBNS}`vo8=`a zWCAbw*x2~iwJ{sj!+`BC_dM)@)**JO4j^%m?OX{1&_vG-2(y^;HRiwtI>*BWo(XN> zI^;T_z^v6W$^S-B1E)}EvJFr{C_6kzOG`P^3wS{}jKhMZp101KxRj|sCd@FinbpB1 zUX(*wXikp{(aKHlelB1DtYBs<5m>>c@yJ6>4m-*W_&_;!&=N#X0KLij2984d)R<>_X`YQFvQN8&fo^axbJq&Fa7VLbrJ}<-wJn$9{xCA3R z;YdeZ(5$+EsJtb$-gyO*z34ZT@d6`H&Rk4>k&N(UBN5;>TUzjRP)8jTWgM7dI^ozT;}AyxgPqI8EmesNq6<&z?dR6nbZ literal 0 HcmV?d00001 diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/loading-msg.gif b/test/jdk/java/awt/Dialog/FileDialogIconTest/loading-msg.gif new file mode 100644 index 0000000000000000000000000000000000000000..6c7570a6d84358d4c81bbb4177d50410bd464674 GIT binary patch literal 1518 zcmV6ciK{6%`g178e&67#J8C85tTH8XFrM z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C@3f?DJd!{Dl021EG#T7 zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}? zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~EC2ui06YLZ000R80RIUbNU)&6g9sBUT*$DY!-o(fN}NcsqQ#3CGiuz( zv7^U`zbbzF_U_rofdDH0`ST0NNr5R1h8+0r*EfRCzO5|y&E&wG7h?|m1mh2kPyc=u zg#zc_Hy~hu6m9tr2oN|-ox+G(w4zXf5Z?WJ2jL$}m}Z;)o9YtCpRr~CsCDZVCyaqW zp1u2IG$oLkE2;iPJJ{@7ieI0(6bM5xlvm&U0%qBEreuLT5f=;#_a6YU&r&{8%eJcJ zf1?FLIXn@7YuCSs8bulvD~M0;U|=m>IAw0ztToOQ_{(HZfg=}=90}&p%Zf^40}=b2 z;9Thm|F@*k8Xu@W?NLE#{eLzw~I6Q;%2$qiHe<^p{+!y~SB*_{{{zJJWS`+Fx%5j+0GxNxH@}D#=ZD{vMchpT{Wu<8H@di| zLwlX|=b(gg4%z~`~_*$hJV2UfR1hU=%r!*fFRa84hrhmhGsQsq)!IA z1Vv-t+y#Z4F_ya0AIz?_<+dQ`#g~|%Ph_X-%#bg35vaQyPhye1a3ld)qpMIn<>lI83_PZIP9yTv-Flt4ieS*k%h z$p*rPwLx*4QLv&%rkMmqW>S7*Z3PAxAhZ Date: Thu, 10 Oct 2024 15:50:19 +0000 Subject: [PATCH 05/95] 8340366: Open source several AWT Dialog tests - Batch 3 Reviewed-by: prr, dnguyen --- test/jdk/ProblemList.txt | 2 + .../java/awt/Dialog/DialogModalityTest.java | 114 +++++++++++++ .../java/awt/Dialog/DialogResizeTest2.java | 105 ++++++++++++ .../awt/Dialog/FileDialogUserFilterTest.java | 150 ++++++++++++++++++ test/jdk/java/awt/Dialog/HideDialogTest.java | 138 ++++++++++++++++ test/jdk/java/awt/Dialog/ModalDialogTest.java | 147 +++++++++++++++++ 6 files changed, 656 insertions(+) create mode 100644 test/jdk/java/awt/Dialog/DialogModalityTest.java create mode 100644 test/jdk/java/awt/Dialog/DialogResizeTest2.java create mode 100644 test/jdk/java/awt/Dialog/FileDialogUserFilterTest.java create mode 100644 test/jdk/java/awt/Dialog/HideDialogTest.java create mode 100644 test/jdk/java/awt/Dialog/ModalDialogTest.java diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 8f6b0c74664..fb20eb1f63d 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -473,6 +473,8 @@ java/awt/KeyboardFocusmanager/ConsumeNextMnemonicKeyTypedTest/ConsumeNextMnemoni java/awt/Window/GetScreenLocation/GetScreenLocationTest.java 8225787 linux-x64 java/awt/Dialog/MakeWindowAlwaysOnTop/MakeWindowAlwaysOnTop.java 8266243 macosx-aarch64 +java/awt/Dialog/FileDialogUserFilterTest.java 8001142 generic-all + java/awt/dnd/BadSerializationTest/BadSerializationTest.java 8277817 linux-x64,windows-x64 java/awt/dnd/DragSourceMotionListenerTest.java 8225131 windows-all java/awt/dnd/RejectDragTest.java 7124259 macosx-all diff --git a/test/jdk/java/awt/Dialog/DialogModalityTest.java b/test/jdk/java/awt/Dialog/DialogModalityTest.java new file mode 100644 index 00000000000..d8ac9e4620b --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogModalityTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * 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.Button; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Event; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.Window; +import java.util.List; + +/* + * @test + * @bug 4058370 + * @summary Test to verify Modality of Dialog + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogModalityTest + */ + +public class DialogModalityTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. When the test is running, there will be a Frame, a Modal Dialog + and a Window that is Modal Dialog's parent. + 2. Verify that it is impossible to bring up the menu in Frame before + closing the Modal Dialog. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + public static List initialize() { + Frame f = new Frame("Parent Frame"); + DialogTest dlg = new DialogTest(f, "Modal Dialog"); + f.add(new Button("push me")); + f.setSize(200, 200); + f.setLocation(210, 1); + dlg.setBounds(210, 203, 200, 200); + return List.of(f, dlg); + } +} + +class DialogTest extends Dialog { + Button closeButton; + Frame parent; + + public DialogTest(Frame parent, String title) { + this(parent, title, true); + } + + public DialogTest(Frame parent, String title, boolean modal) { + super(parent, title, modal); + this.parent = parent; + setLayout(new BorderLayout()); + Panel buttonPanel = new Panel(); + closeButton = new Button("Close"); + buttonPanel.add(closeButton); + add("Center", buttonPanel); + pack(); + } + + public boolean action(Event e, Object arg) { + if (e.target == closeButton) { + Dialog dialog = null; + Component c = (Component) e.target; + + while (c != null && !(c instanceof Dialog)) { + c = c.getParent(); + } + + if (c != null) { + dialog = (Dialog) c; + } + + if (dialog == null) { + return false; + } + + dialog.setVisible(false); + dialog.dispose(); + return true; + } + return false; + } +} diff --git a/test/jdk/java/awt/Dialog/DialogResizeTest2.java b/test/jdk/java/awt/Dialog/DialogResizeTest2.java new file mode 100644 index 00000000000..3124719637a --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogResizeTest2.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.GridLayout; + +/* + * @test + * @bug 4172302 + * @summary Test to make sure non-resizable Dialogs can be resized with the + * setSize() method. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogResizeTest2 + */ + +public class DialogResizeTest2 { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This tests the programmatic resizability of non-resizable Dialogs + Even when a Dialog is set to be non-resizable, it should be + programmatically resizable using the setSize() method. + + 1. Initially the Dialog will be resizable. Try using the \\"Smaller\\" + and \\"Larger\\" buttons to verify that the Dialog resizes correctly + 2. Then, click the \\"Toggle\\" button to make the Dialog non-resizable + 3. Again, verify that clicking the \\"Larger\\" and \\"Smaller\\" buttons + causes the Dialog to get larger and smaller. If the Dialog does + not change size, or does not re-layout correctly, the test FAILS + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .logArea(8) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + Frame frame = new Frame("Parent Frame"); + frame.add(new Button("Button")); + frame.setSize(100, 100); + new dlg(frame).setVisible(true); + return frame; + } + + static class dlg extends Dialog { + public dlg(Frame f_) { + super(f_, "Dialog", false); + setSize(200, 200); + Button bLarger = new Button("Larger"); + bLarger.addActionListener(e -> setSize(400, 400)); + Button bSmaller = new Button("Smaller"); + bSmaller.addActionListener(e -> setSize(200, 100)); + Button bCheck = new Button("Resizable?"); + bCheck.addActionListener(e -> { + if (isResizable()) { + PassFailJFrame.log("Dialog is resizable"); + } else { + PassFailJFrame.log("Dialog is not resizable"); + } + }); + Button bToggle = new Button("Toggle"); + bToggle.addActionListener(e -> { + if (isResizable()) { + setResizable(false); + PassFailJFrame.log("Dialog is now not resizable"); + } else { + setResizable(true); + PassFailJFrame.log("Dialog is now resizable"); + } + }); + setLayout(new GridLayout(1, 4)); + add(bSmaller); + add(bLarger); + add(bCheck); + add(bToggle); + } + } +} diff --git a/test/jdk/java/awt/Dialog/FileDialogUserFilterTest.java b/test/jdk/java/awt/Dialog/FileDialogUserFilterTest.java new file mode 100644 index 00000000000..80ff9ed0bde --- /dev/null +++ b/test/jdk/java/awt/Dialog/FileDialogUserFilterTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Checkbox; +import java.awt.Component; +import java.awt.Container; +import java.awt.Event; +import java.awt.FileDialog; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Label; +import java.awt.Panel; +import java.awt.TextField; +import java.io.File; +import java.io.FilenameFilter; + +/* + * @test + * @bug 4293697 4416433 4417139 4409600 + * @summary Test to verify that user filter always gets called on changing the + * directory in FileDialog + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FileDialogUserFilterTest + */ + +public class FileDialogUserFilterTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Enter a mask into the field, a directory into + the field (or leave the default values). + 2. Then click the button, file dialog will appear. + Output of the user filter will be shown in the output + area. Enter several different directories to the file dialog + via double-clicking on the directory list. The output + area should show some filtering output on each directory + change. If any output was only given on dialog startup, + the test is FAILED. + 3. Look at the list of files accepted by the filter. + If some files do not match the filter, + the test is FAILED. + 4. Open dialog with an empty filter. + Enter some directories with a lot of files (like /usr/bin). + If dialog crashes the test is FAILED. + Enter the directory that contain files and other directories. + If the directories are shown in the files box along with files + then the test is FAILED. + 5. Click in checkbox 'do not use filter', make it checked. + Open dialog, enter the directory with some files. + If no files is shown in the File list box (while you are sure + there are some files there) the test is FAILED + Otherwise it is PASSED." + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(new DialogFilterTest()) + .build() + .awaitAndCheck(); + } +} + +class DialogFilterTest extends Frame implements FilenameFilter { + FileDialog fd; + static TextField tfDirectory = new TextField(); + static TextField tfFile = new TextField(); + static TextField tfFilter = new TextField(); + static Checkbox useFilterCheck = new Checkbox("do not use filter"); + + public DialogFilterTest() { + setTitle("File Dialog User Filter test"); + add("North", new Button("Load")); + Panel p = new Panel(); + p.setLayout(new GridBagLayout()); + addRow(p, new Label("directory:", Label.RIGHT), tfDirectory); + addRow(p, new Label("file:", Label.RIGHT), tfFile); + addRow(p, new Label("filter:", Label.RIGHT), tfFilter); + addRow(p, new Label(""), useFilterCheck); + tfFilter.setText(".java"); + tfDirectory.setText("."); + add("Center", p); + setSize(300, 200); + } + + static void addRow(Container cont, Component c1, Component c2) { + GridBagLayout gbl = (GridBagLayout) cont.getLayout(); + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + cont.add(c1); + gbl.setConstraints(c1, c); + + c.gridwidth = GridBagConstraints.REMAINDER; + c.weightx = 1.0; + cont.add(c2); + gbl.setConstraints(c2, c); + } + + public boolean accept(File dir, String name) { + System.out.println("File " + dir + " String " + name); + if (fd.getMode() == FileDialog.LOAD) { + return name.lastIndexOf(tfFilter.getText()) > 0; + } + return true; + } + + public boolean action(Event evt, Object what) { + boolean load = "Load".equals(what); + + if (load || "Save".equals(what)) { + fd = new FileDialog(new Frame(), null, + load ? FileDialog.LOAD : FileDialog.SAVE); + fd.setDirectory(tfDirectory.getText()); + fd.setFile(tfFile.getText()); + if (!useFilterCheck.getState()) { + fd.setFilenameFilter(this); + } + fd.setVisible(true); + tfDirectory.setText(fd.getDirectory()); + tfFile.setText(fd.getFile()); + + return true; + } + return false; + } +} diff --git a/test/jdk/java/awt/Dialog/HideDialogTest.java b/test/jdk/java/awt/Dialog/HideDialogTest.java new file mode 100644 index 00000000000..78d0344a6cf --- /dev/null +++ b/test/jdk/java/awt/Dialog/HideDialogTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * 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.Button; +import java.awt.Color; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 4048664 4065506 4122094 4171979 + * @summary Test if Dialog can be successfully hidden, see that no other app + * comes to front, see if hide + dispose causes assertion failure + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual HideDialogTest + */ + +public class HideDialogTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. A Frame should appear with a "test" button in it + 2. Click on the "test" button. A Dialog will appear with a "dismiss" button + and a "dismiss-with-dispose" button + 3. First, click on the "dismiss-with-dispose" button. Verify that + no assertion failure appears. + 4. Now, click on the "dismiss" button. The Dialog should go away. + 5. Repeat from (2) 10-20 times. + 6. When the dialog goes away check that the frame window does not briefly + get obscured by another app or repaint it's entire area. There should be + no flicker at all in areas obscured by the dialog. (4065506 4122094) + If there is the test fails. + 7. If the Dialog is successfully hidden each time, the test passed. If the + Dialog did not hide, the test failed (4048664). + + NOTE: When the dialog does not go away (meaning the bug has manifested itself), + the "dismiss-with-dispose" button can be used to get rid of it. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(new MyFrame()) + .build() + .awaitAndCheck(); + } +} + +class MyDialog extends Dialog { + public MyDialog(Frame f) { + super(f, "foobar", true); + setSize(200, 200); + setLayout(new BorderLayout()); + Panel p = new Panel(); + p.setLayout(new FlowLayout(FlowLayout.CENTER)); + Button okButton; + okButton = new Button("dismiss"); + p.add(okButton); + okButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.out.println("Calling setVisible(false)"); + setVisible(false); + } + }); + Button newButton; + p.add(newButton = new Button("dismiss-with-dispose")); + newButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.out.println("Calling setVisible(false) + dispose()"); + setVisible(false); + dispose(); + } + }); + add("South", p); + pack(); + } +} + +class MyFrame extends Frame implements ActionListener { + public MyFrame() { + super(); + setSize(600, 400); + setTitle("HideDialogTest"); + setLayout(new BorderLayout()); + Panel toolbar = new Panel(); + toolbar.setLayout(new FlowLayout(FlowLayout.LEFT)); + Button testButton = new Button("test"); + testButton.addActionListener(this); + toolbar.add(testButton); + add("North", toolbar); + } + + public void actionPerformed(ActionEvent e) { + String s = e.getActionCommand(); + if (s.equals("test")) { + System.out.println("Begin test"); + MyDialog d = new MyDialog(this); + d.setVisible(true); + System.out.println("End test"); + } + } + + public void paint(Graphics g) { + for (int i = 0; i < 10; i++) { + g.setColor(Color.red); + g.fillRect(0, 0, 2000, 2000); + g.setColor(Color.blue); + g.fillRect(0, 0, 2000, 2000); + } + } +} diff --git a/test/jdk/java/awt/Dialog/ModalDialogTest.java b/test/jdk/java/awt/Dialog/ModalDialogTest.java new file mode 100644 index 00000000000..aceacc9209a --- /dev/null +++ b/test/jdk/java/awt/Dialog/ModalDialogTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * 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.Button; +import java.awt.Checkbox; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +/* + * @test + * @bug 4078176 + * @summary Test to verify Modal dialogs don't act modal if addNotify() + * is called before setModal(true). + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ModalDialogTest + */ + +public class ModalDialogTest implements ActionListener { + public boolean modal = true; + Button closeBtn = new Button("Close me"); + Button createBtn = new Button("Create Dialog"); + Button createNewBtn = new Button("Create Modal Dialog"); + Button lastBtn = new Button("Show Last Dialog"); + Dialog dialog; + Dialog newDialog; + Frame testFrame; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Use 'Modal' checkbox to select which dialog you're + going to create - modal or non-modal. + (this checkbox affects only new created dialog but + not existing one) + 2. Use 'Create Dialog' button to create a dialog. + If you have selected 'Modal' checkbox then dialog has to + be created modal - you can make sure of that clicking + on any other control (i.e. 'Modal' checkbox) - they + should not work. + 3. Use 'Show Last Dialog' button to bring up last + created dialog - to make sure that if you show/hide + modal dialog several times it stays modal. + 4. On the appearing dialog there are two buttons: + 'Close Me' which closes the dialog, + and 'Create Modal Dialog' which creates one more + MODAL dialog just to make sure that + in situation with two modal dialogs all is fine. + 5. If created modal dialogs are really modal + (which means that they blocks the calling app) + then test is PASSED, otherwise it's FAILED." + """; + ModalDialogTest test = new ModalDialogTest(); + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(test.initialize()) + .build() + .awaitAndCheck(); + } + + public Frame initialize() { + testFrame = new Frame("Parent Frame"); + Frame frame = new Frame("Modal Dialog test"); + Panel panel = new Panel(); + panel.setLayout(new BorderLayout()); + + createBtn.addActionListener(this); + createNewBtn.addActionListener(this); + closeBtn.addActionListener(this); + lastBtn.addActionListener(this); + panel.add("Center", createBtn); + panel.add("South", lastBtn); + Checkbox cb = new Checkbox("Modal", modal); + cb.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + modal = ((Checkbox) e.getSource()).getState(); + } + }); + panel.add("North", cb); + panel.setSize(200, 100); + + frame.add(panel); + frame.pack(); + return frame; + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == createBtn) { + if (dialog != null) { + dialog.dispose(); + } + dialog = new Dialog(testFrame, "Modal Dialog"); + dialog.add("North", closeBtn); + dialog.add("South", createNewBtn); + createBtn.setEnabled(false); + dialog.pack(); + dialog.setModal(modal); + dialog.setVisible(true); + } else if (e.getSource() == closeBtn && dialog != null) { + createBtn.setEnabled(true); + dialog.setVisible(false); + } else if (e.getSource() == lastBtn && dialog != null) { + dialog.setVisible(true); + } else if (e.getSource() == createNewBtn && newDialog == null) { + newDialog = new Dialog(testFrame, "New Modal Dialog"); + Button clsBtn = new Button("Close Me"); + clsBtn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + newDialog.dispose(); + newDialog = null; + } + }); + newDialog.add("North", clsBtn); + newDialog.pack(); + newDialog.setModal(true); + newDialog.setVisible(true); + } + } +} From 4beb77192f54f27183285400d7cae7528df64e43 Mon Sep 17 00:00:00 2001 From: Aleksei Efimov Date: Thu, 10 Oct 2024 15:56:04 +0000 Subject: [PATCH 06/95] 8339538: Wrong timeout computations in DnsClient 8220213: com/sun/jndi/dns/ConfigTests/Timeout.java failed intermittent Reviewed-by: dfuchs, msheppar, djelinski --- .../classes/com/sun/jndi/dns/DnsClient.java | 132 +++++++++--- .../classes/com/sun/jndi/dns/DnsContext.java | 8 +- .../com/sun/jndi/dns/ConfigTests/Timeout.java | 40 ++-- .../TimeoutWithEmptyDatagrams.java | 196 ++++++++++++++++++ 4 files changed, 324 insertions(+), 52 deletions(-) create mode 100644 test/jdk/com/sun/jndi/dns/ConfigTests/TimeoutWithEmptyDatagrams.java diff --git a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java index 0d5ca39c6f7..3d55186bac8 100644 --- a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java +++ b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * 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,11 +46,15 @@ import javax.naming.OperationNotSupportedException; import javax.naming.ServiceUnavailableException; +import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.Map; import java.util.HashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.IntStream; import jdk.internal.ref.CleanerFactory; import sun.security.jca.JCAUtil; @@ -95,7 +99,7 @@ public class DnsClient { private static final int DEFAULT_PORT = 53; private static final int TRANSACTION_ID_BOUND = 0x10000; - private static final int MIN_TIMEOUT = 50; // msec after which there are no retries. + private static final int MIN_TIMEOUT = 0; // msec after which there are no retries. private static final SecureRandom random = JCAUtil.getSecureRandom(); private InetAddress[] servers; private int[] serverPorts; @@ -223,20 +227,28 @@ ResourceRecords query(DnsName fqdn, int qclass, int qtype, Exception caughtException = null; boolean[] doNotRetry = new boolean[servers.length]; - + // Holder for unfulfilled timeouts left for each server + AtomicLong[] unfulfilledUdpTimeouts = IntStream.range(0, servers.length) + .mapToObj(_ -> new AtomicLong()) + .toArray(AtomicLong[]::new); try { // // The UDP retry strategy is to try the 1st server, and then // each server in order. If no answer, double the timeout // and try each server again. // - for (int retry = 0; retry < retries; retry++) { - + for (int retry = 0; retry <= retries; retry++) { + boolean isLastRetry = retry == retries; // Try each name server. for (int i = 0; i < servers.length; i++) { if (doNotRetry[i]) { continue; } + // unfulfilledServerTimeout is always >= 0 + AtomicLong unfulfilledServerTimeout = unfulfilledUdpTimeouts[i]; + if (isLastRetry && unfulfilledServerTimeout.get() == 0) { + continue; + } // send the request packet and wait for a response. try { @@ -245,7 +257,7 @@ ResourceRecords query(DnsName fqdn, int qclass, int qtype, } byte[] msg = doUdpQuery(pkt, servers[i], serverPorts[i], - retry, xid); + retry, xid, unfulfilledServerTimeout, isLastRetry); assert msg != null; Header hdr = new Header(msg, msg.length); @@ -259,7 +271,12 @@ ResourceRecords query(DnsName fqdn, int qclass, int qtype, // Try each server, starting with the one that just // provided the truncated message. - int retryTimeout = (timeout * (1 << retry)); + long retryTimeout = Math.clamp( + timeout * (1L << (isLastRetry + ? retry - 1 + : retry)), + 0L, Integer.MAX_VALUE); + ; for (int j = 0; j < servers.length; j++) { int ij = (i + j) % servers.length; if (doNotRetry[ij]) { @@ -301,15 +318,23 @@ ResourceRecords query(DnsName fqdn, int qclass, int qtype, if (debug) { dprint("Caught Exception:" + ex); } - if (caughtException == null) { + if (caughtException == null || servers.length == 1) { + // If there are several servers we continue trying with other + // servers, otherwise this exception will be reported caughtException = ex; + } else { + // Best reporting effort + caughtException.addSuppressed(ex); } doNotRetry[i] = true; } catch (IOException e) { if (debug) { dprint("Caught IOException:" + e); } - if (caughtException == null) { + if (caughtException instanceof CommunicationException ce) { + e.addSuppressed(ce); + caughtException = e; + } else if (caughtException == null) { caughtException = e; } } catch (ClosedSelectorException e) { @@ -327,8 +352,13 @@ ResourceRecords query(DnsName fqdn, int qclass, int qtype, caughtException = e; } } catch (NamingException e) { - if (caughtException == null) { + if (caughtException == null || servers.length == 1) { + // If there are several servers we continue trying with other + // servers, otherwise this exception will be reported caughtException = e; + } else { + // Best reporting effort + caughtException.addSuppressed(e); } doNotRetry[i] = true; } @@ -339,8 +369,8 @@ ResourceRecords query(DnsName fqdn, int qclass, int qtype, reqs.remove(xid); // cleanup } - if (caughtException instanceof NamingException) { - throw (NamingException) caughtException; + if (caughtException instanceof NamingException ne) { + throw ne; } // A network timeout or other error occurred. NamingException ne = new CommunicationException("DNS error"); @@ -424,10 +454,32 @@ ResourceRecords queryZone(DnsName zone, int qclass, boolean recursion) * is enqueued with the corresponding xid in 'resps'. */ private byte[] doUdpQuery(Packet pkt, InetAddress server, - int port, int retry, int xid) + int port, int retry, int xid, + AtomicLong unfulfilledTimeout, + boolean unfulfilledOnly) throws IOException, NamingException { udpChannelLock.lock(); + + + // use 1L below to ensure conversion to long and avoid potential + // integer overflow (timeout is an int). + // no point in supporting timeout > Integer.MAX_VALUE, clamp if needed + // timeout remaining after successive 'blockingReceive()'. + long thisIterationTimeout = unfulfilledOnly + ? 0L + : Math.clamp(timeout * (1L << retry), 0L, Integer.MAX_VALUE); + + // Compensate with server's positive unfulfilled timeout. + // Calling method never supplies zero 'unfulfilledTimeout' when + // 'unfulfilledOnly' is 'true', therefore 'thisIterationTimeout' + // will always be a positive number, ie infinite timeout + // is not possible. + thisIterationTimeout += unfulfilledTimeout.get(); + + // Track left timeout for the current retry + long timeoutLeft = thisIterationTimeout; + long start = 0; try { try (DatagramChannel udpChannel = getDatagramChannel()) { ByteBuffer opkt = ByteBuffer.wrap(pkt.getData(), 0, pkt.length()); @@ -436,13 +488,11 @@ private byte[] doUdpQuery(Packet pkt, InetAddress server, // Packets may only be sent to or received from this server address InetSocketAddress target = new InetSocketAddress(server, port); udpChannel.connect(target); - int pktTimeout = (timeout * (1 << retry)); udpChannel.write(opkt); - // timeout remaining after successive 'blockingReceive()' - int timeoutLeft = pktTimeout; int cnt = 0; boolean gotData = false; + start = System.nanoTime(); do { // prepare for retry if (gotData) { @@ -456,9 +506,7 @@ private byte[] doUdpQuery(Packet pkt, InetAddress server, ") for:" + xid + " sock-timeout:" + timeoutLeft + " ms."); } - long start = System.currentTimeMillis(); - gotData = blockingReceive(udpChannel, ipkt, timeoutLeft); - long end = System.currentTimeMillis(); + gotData = blockingReceive(udpChannel, target, ipkt, timeoutLeft); assert gotData || ipkt.position() == 0; if (gotData && isMatchResponse(data, xid)) { return data; @@ -471,17 +519,23 @@ private byte[] doUdpQuery(Packet pkt, InetAddress server, return cachedMsg; } } - timeoutLeft = pktTimeout - ((int) (end - start)); + long elapsedMillis = TimeUnit.NANOSECONDS + .toMillis(System.nanoTime() - start); + timeoutLeft = thisIterationTimeout - elapsedMillis; } while (timeoutLeft > MIN_TIMEOUT); // no matching packets received within the timeout throw new SocketTimeoutException(); } } finally { + long carryoverTimeout = thisIterationTimeout - + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + unfulfilledTimeout.set(Math.max(0, carryoverTimeout)); udpChannelLock.unlock(); } } - boolean blockingReceive(DatagramChannel dc, ByteBuffer buffer, long timeout) throws IOException { + boolean blockingReceive(DatagramChannel dc, InetSocketAddress target, + ByteBuffer buffer, long timeout) throws IOException { boolean dataReceived = false; // The provided datagram channel will be used by the caller only to receive data after // it is put to non-blocking mode @@ -491,10 +545,15 @@ boolean blockingReceive(DatagramChannel dc, ByteBuffer buffer, long timeout) thr udpChannelSelector.select(timeout); var keys = udpChannelSelector.selectedKeys(); if (keys.contains(selectionKey) && selectionKey.isReadable()) { - dc.receive(buffer); - dataReceived = true; + int before = buffer.position(); + var senderAddress = dc.receive(buffer); + // Empty packets are ignored + dataReceived = target.equals(senderAddress) && buffer.position() > before; + } + // Avoid contention with Selector.close() if called by a clean-up thread + synchronized (keys) { + keys.clear(); } - keys.clear(); } finally { selectionKey.cancel(); // Flush the canceled key out of the selected key set @@ -750,14 +809,19 @@ class Tcp { private final Socket sock; private final java.io.InputStream in; final java.io.OutputStream out; - private int timeoutLeft; + private long timeoutLeft; - Tcp(InetAddress server, int port, int timeout) throws IOException { + Tcp(InetAddress server, int port, long timeout) throws IOException { sock = new Socket(); try { - long start = System.currentTimeMillis(); - sock.connect(new InetSocketAddress(server, port), timeout); - timeoutLeft = (int) (timeout - (System.currentTimeMillis() - start)); + long start = System.nanoTime(); + // It is safe to cast to int since the value is + // clamped by the caller + int intTimeout = (int) timeout; + sock.connect(new InetSocketAddress(server, port), intTimeout); + timeoutLeft = Duration.ofMillis(timeout) + .minus(Duration.ofNanos((System.nanoTime() - start))) + .toMillis(); if (timeoutLeft <= 0) throw new SocketTimeoutException(); @@ -785,14 +849,16 @@ private interface SocketReadOp { private int readWithTimeout(SocketReadOp reader) throws IOException { if (timeoutLeft <= 0) throw new SocketTimeoutException(); - - sock.setSoTimeout(timeoutLeft); - long start = System.currentTimeMillis(); + // It is safe to cast to int since the value is clamped + int intTimeout = (int) timeoutLeft; + sock.setSoTimeout(intTimeout); + long start = System.nanoTime(); try { return reader.read(); } finally { - timeoutLeft -= (int) (System.currentTimeMillis() - start); + timeoutLeft -= TimeUnit.NANOSECONDS.toMillis( + System.nanoTime() - start); } } diff --git a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsContext.java b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsContext.java index 028108494af..fe35a4979cc 100644 --- a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsContext.java +++ b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsContext.java @@ -176,13 +176,13 @@ public Object addToEnvironment(String propName, Object propVal) } else if (propName.equals(INIT_TIMEOUT)) { int val = Integer.parseInt((String) propVal); if (timeout != val) { - timeout = val; + timeout = Math.max(val, 0); resolver = null; } } else if (propName.equals(RETRIES)) { int val = Integer.parseInt((String) propVal); if (retries != val) { - retries = val; + retries = Math.clamp(val, 1, 30); resolver = null; } } @@ -257,11 +257,11 @@ private void initFromEnvironment() val = (String) environment.get(INIT_TIMEOUT); timeout = (val == null) ? DEFAULT_INIT_TIMEOUT - : Integer.parseInt(val); + : Math.max(Integer.parseInt(val), 0); val = (String) environment.get(RETRIES); retries = (val == null) ? DEFAULT_RETRIES - : Integer.parseInt(val); + : Math.clamp(Integer.parseInt(val), 1, 30); } private CT getLookupCT(String attrId) diff --git a/test/jdk/com/sun/jndi/dns/ConfigTests/Timeout.java b/test/jdk/com/sun/jndi/dns/ConfigTests/Timeout.java index 90a2a7b9815..e03923b6c34 100644 --- a/test/jdk/com/sun/jndi/dns/ConfigTests/Timeout.java +++ b/test/jdk/com/sun/jndi/dns/ConfigTests/Timeout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ import java.net.InetSocketAddress; import java.net.SocketTimeoutException; import java.time.Duration; -import java.time.Instant; import jdk.test.lib.net.URIBuilder; @@ -40,7 +39,7 @@ * number of retries. * @library ../lib/ /test/lib * @modules java.base/sun.security.util - * @run main Timeout + * @run main/othervm Timeout */ public class Timeout extends DNSTestBase { @@ -48,8 +47,12 @@ public class Timeout extends DNSTestBase { private static final int TIMEOUT = 250; // try 5 times per server private static final int RETRIES = 5; + // DnsClient retries again with increased timeout if left + // timeout is less than this value, and max retry attempts + // is not reached + private static final int DNS_CLIENT_MIN_TIMEOUT = 0; - private Instant startTime; + private long startTime; public Timeout() { setLocalServer(false); @@ -81,7 +84,7 @@ public void runTest() throws Exception { setContext(new InitialDirContext(env())); // Any request should fail after timeouts have expired. - startTime = Instant.now(); + startTime = System.nanoTime(); context().getAttributes(""); throw new RuntimeException( @@ -92,28 +95,35 @@ public void runTest() throws Exception { @Override public boolean handleException(Exception e) { if (e instanceof CommunicationException) { - Duration elapsedTime = Duration.between(startTime, Instant.now()); + Duration elapsedTime = Duration.ofNanos(System.nanoTime() - startTime); if (!(((CommunicationException) e) .getRootCause() instanceof SocketTimeoutException)) { return false; } - Duration expectedTime = Duration.ofMillis(TIMEOUT) - .multipliedBy((1 << RETRIES) - 1); + Duration minAllowedTime = Duration.ofMillis(TIMEOUT) + .multipliedBy((1 << RETRIES) - 1) + .minus(Duration.ofMillis(DNS_CLIENT_MIN_TIMEOUT * RETRIES)); + Duration maxAllowedTime = Duration.ofMillis(TIMEOUT) + .multipliedBy((1 << RETRIES) - 1) + // max allowed timeout value is set to 2 * expected timeout + .multipliedBy(2); + DNSTestUtils.debug("Elapsed (ms): " + elapsedTime.toMillis()); - DNSTestUtils.debug("Expected (ms): " + expectedTime.toMillis()); + String expectedRangeMsg = "%s - %s" + .formatted(minAllowedTime.toMillis(), maxAllowedTime.toMillis()); + DNSTestUtils.debug("Expected range (ms): " + expectedRangeMsg); // Check that elapsed time is as long as expected, and - // not more than 50% greater. - if (elapsedTime.compareTo(expectedTime) >= 0 && - elapsedTime.multipliedBy(2) - .compareTo(expectedTime.multipliedBy(3)) <= 0) { + // not more than 2 times greater. + if (elapsedTime.compareTo(minAllowedTime) >= 0 && + elapsedTime.compareTo(maxAllowedTime) <= 0) { System.out.println("elapsed time is as long as expected."); return true; } throw new RuntimeException( - "Failed: timeout in " + elapsedTime.toMillis() - + " ms, expected" + expectedTime.toMillis() + "ms"); + "Failed: timeout in " + elapsedTime.toMillis() + + " ms, expected to be in a range (ms): " + expectedRangeMsg); } return super.handleException(e); diff --git a/test/jdk/com/sun/jndi/dns/ConfigTests/TimeoutWithEmptyDatagrams.java b/test/jdk/com/sun/jndi/dns/ConfigTests/TimeoutWithEmptyDatagrams.java new file mode 100644 index 00000000000..9fb8954c99c --- /dev/null +++ b/test/jdk/com/sun/jndi/dns/ConfigTests/TimeoutWithEmptyDatagrams.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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.test.lib.net.URIBuilder; + +import javax.naming.CommunicationException; +import javax.naming.Context; +import javax.naming.directory.InitialDirContext; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketTimeoutException; +import java.time.Duration; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +/* + * @test + * @bug 8339538 + * @summary Tests that DnsClient correctly calculates left timeout in + * presence of empty datagram packets. + * @library ../lib /test/lib + * @modules java.base/sun.security.util + * @run main/othervm TimeoutWithEmptyDatagrams + */ + +public class TimeoutWithEmptyDatagrams extends DNSTestBase { + // initial timeout = 1/4 sec + private static final int TIMEOUT = 250; + // try 5 times per server + private static final int RETRIES = 5; + // DnsClient retries again with increased timeout if left + // timeout is less than this value, and max retry attempts + // is not reached + private static final int DNS_CLIENT_MIN_TIMEOUT = 0; + + public TimeoutWithEmptyDatagrams() { + setLocalServer(false); + } + + public static void main(String[] args) throws Exception { + new TimeoutWithEmptyDatagrams().run(args); + } + + /* + * Tests that we can set the initial UDP timeout interval and the + * number of retries. + */ + @Override + public void runTest() throws Exception { + // Create a DatagramSocket and bind it to the loopback address to simulate + // UDP DNS server that doesn't respond + try (DatagramSocket ds = new DatagramSocket(new InetSocketAddress( + InetAddress.getLoopbackAddress(), 0))) { + CountDownLatch gotClientAddress = new CountDownLatch(1); + AtomicReference clientAddress = new AtomicReference<>(); + AtomicBoolean stopTestThreads = new AtomicBoolean(); + + String allQuietUrl = URIBuilder.newBuilder() + .scheme("dns") + .loopback() + .port(ds.getLocalPort()) + .build() + .toString(); + + // Run a virtual thread that receives client request packets and extracts + // sender address from them. + Thread receiverThread = Thread.ofVirtual().start(() -> { + while (!stopTestThreads.get()) { + try { + DatagramPacket packet = new DatagramPacket(new byte[1024], 1024); + ds.receive(packet); + System.err.println("Got packet from " + packet.getSocketAddress()); + boolean hasClientAddress = clientAddress.get() != null; + clientAddress.set(packet.getSocketAddress()); + if (!hasClientAddress) { + gotClientAddress.countDown(); + } + } catch (IOException e) { + if (!stopTestThreads.get()) { + throw new RuntimeException(e); + } else { + return; + } + } + } + }); + + // Run a virtual thread that will send an empty packets via server socket + // that should wake up the selector on a client side. + Thread wakeupThread = Thread.ofVirtual().start(() -> { + try { + long timeout = Math.max(1, TIMEOUT / 4); + // wait for a first packet on a server socket + gotClientAddress.await(); + + // Now start sending empty packets until we get a notification + // from client part to stop sending + while (!stopTestThreads.get()) { + System.err.println("Server timeout = " + timeout); + TimeUnit.MILLISECONDS.sleep(timeout); + System.err.println("Sending wakeup packet to " + clientAddress.get()); + var wakeupPacket = new DatagramPacket(new byte[0], 0); + wakeupPacket.setSocketAddress(clientAddress.get()); + ds.send(wakeupPacket); + timeout += Math.max(1, timeout / 2); + } + } catch (IOException ioe) { + throw new RuntimeException("Test machinery failure", ioe); + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted during wakeup packets sending"); + } finally { + System.err.println("Server thread exiting"); + } + }); + + long startTime = 0; + try { + env().put(Context.PROVIDER_URL, allQuietUrl); + env().put("com.sun.jndi.dns.timeout.initial", String.valueOf(TIMEOUT)); + env().put("com.sun.jndi.dns.timeout.retries", String.valueOf(RETRIES)); + setContext(new InitialDirContext(env())); + + startTime = System.nanoTime(); + context().getAttributes(""); + + // Any request should fail after timeouts have expired. + throw new RuntimeException("Failed: getAttributes succeeded unexpectedly"); + } catch (CommunicationException ce) { + // We need to catch CommunicationException outside the test framework + // flow because wakeupThread.join() can take some time that could + // increase measured timeout + long endTime = System.nanoTime(); + Duration elapsedTime = Duration.ofNanos(endTime - startTime); + if (ce.getRootCause() instanceof SocketTimeoutException) { + + Duration minAllowedTime = Duration.ofMillis(TIMEOUT) + .multipliedBy((1 << RETRIES) - 1) + .minus(Duration.ofMillis(DNS_CLIENT_MIN_TIMEOUT * RETRIES)); + Duration maxAllowedTime = Duration.ofMillis(TIMEOUT) + .multipliedBy((1 << RETRIES) - 1) + // max allowed timeout value is set to 2 * expected timeout + .multipliedBy(2); + + DNSTestUtils.debug("Elapsed (ms): " + elapsedTime.toMillis()); + String expectedRangeMsg = "%s - %s" + .formatted(minAllowedTime.toMillis(), maxAllowedTime.toMillis()); + DNSTestUtils.debug("Expected range (ms): " + expectedRangeMsg); + + // Check that elapsed time is as long as expected, and + // not more than 2 times greater. + if (elapsedTime.compareTo(minAllowedTime) >= 0 && + elapsedTime.compareTo(maxAllowedTime) <= 0) { + System.out.println("elapsed time is as long as expected."); + } else { + throw new RuntimeException( + "Failed: timeout in " + elapsedTime.toMillis() + + " ms, expected to be in a range (ms): " + expectedRangeMsg); + } + } else { + throw ce; + } + } finally { + stopTestThreads.set(true); + wakeupThread.join(); + ds.close(); + receiverThread.join(); + } + } + } +} From 6fad6af0de5e749aa60038d70ae196b5f666286f Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Thu, 10 Oct 2024 17:02:54 +0000 Subject: [PATCH 07/95] 8341819: LightweightSynchronizer::enter_for races with deflation Reviewed-by: pchilanomate, rkennke --- .../share/runtime/lightweightSynchronizer.cpp | 7 +++++-- test/jdk/com/sun/jdi/EATests.java | 13 +++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/runtime/lightweightSynchronizer.cpp b/src/hotspot/share/runtime/lightweightSynchronizer.cpp index 5037bb9607b..7cf7c201faf 100644 --- a/src/hotspot/share/runtime/lightweightSynchronizer.cpp +++ b/src/hotspot/share/runtime/lightweightSynchronizer.cpp @@ -635,8 +635,11 @@ void LightweightSynchronizer::enter_for(Handle obj, BasicLock* lock, JavaThread* bool entered = monitor->enter_for(locking_thread); assert(entered, "recursive ObjectMonitor::enter_for must succeed"); } else { - // It is assumed that enter_for must enter on an object without contention. - monitor = inflate_and_enter(obj(), ObjectSynchronizer::inflate_cause_monitor_enter, locking_thread, current); + do { + // It is assumed that enter_for must enter on an object without contention. + monitor = inflate_and_enter(obj(), ObjectSynchronizer::inflate_cause_monitor_enter, locking_thread, current); + // But there may still be a race with deflation. + } while (monitor == nullptr); } assert(monitor != nullptr, "LightweightSynchronizer::enter_for must succeed"); diff --git a/test/jdk/com/sun/jdi/EATests.java b/test/jdk/com/sun/jdi/EATests.java index adeb2174143..b2a2cad49db 100644 --- a/test/jdk/com/sun/jdi/EATests.java +++ b/test/jdk/com/sun/jdi/EATests.java @@ -158,6 +158,19 @@ * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks * -XX:LockingMode=0 * -XX:GuaranteedAsyncDeflationInterval=1000 + * + * @bug 8341819 + * @comment Regression test for re-locking racing with deflation with LM_LIGHTWEIGHT. + * @run driver EATests + * -XX:+UnlockDiagnosticVMOptions + * -Xms256m -Xmx256m + * -Xbootclasspath/a:. + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+WhiteBoxAPI + * -Xbatch + * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks + * -XX:LockingMode=2 + * -XX:GuaranteedAsyncDeflationInterval=1 */ /** From 76541b0646d27e79948d73759f21383c099e2436 Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Thu, 10 Oct 2024 17:22:45 +0000 Subject: [PATCH 08/95] 8341792: Fix ExceptionOccurred in java.security.jgss Reviewed-by: jlu, mullan --- .../macosx/native/libosxkrb5/SCDynamicStoreConfig.m | 2 +- .../windows/native/libw2k_lsa_auth/NativeCreds.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/java.security.jgss/macosx/native/libosxkrb5/SCDynamicStoreConfig.m b/src/java.security.jgss/macosx/native/libosxkrb5/SCDynamicStoreConfig.m index 11645d152cd..43cab418e17 100644 --- a/src/java.security.jgss/macosx/native/libosxkrb5/SCDynamicStoreConfig.m +++ b/src/java.security.jgss/macosx/native/libosxkrb5/SCDynamicStoreConfig.m @@ -51,7 +51,7 @@ void _SCDynamicStoreCallBack(SCDynamicStoreRef store, CFArrayRef changedKeys, vo jmethodID jm_Config_refresh = (*env)->GetStaticMethodID(env, jc_Config, "refresh", "()V"); CHECK_NULL(jm_Config_refresh); (*env)->CallStaticVoidMethod(env, jc_Config, jm_Config_refresh); - if ((*env)->ExceptionOccurred(env) != NULL) { + if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionClear(env); } if (createdFromAttach) { diff --git a/src/java.security.jgss/windows/native/libw2k_lsa_auth/NativeCreds.c b/src/java.security.jgss/windows/native/libw2k_lsa_auth/NativeCreds.c index 221aaccbf2b..fe71f334617 100644 --- a/src/java.security.jgss/windows/native/libw2k_lsa_auth/NativeCreds.c +++ b/src/java.security.jgss/windows/native/libw2k_lsa_auth/NativeCreds.c @@ -877,13 +877,13 @@ jobject BuildTicket(JNIEnv *env, PUCHAR encodedTicket, ULONG encodedTicketSize) (*env)->SetByteArrayRegion(env, ary, (jsize) 0, encodedTicketSize, (jbyte *)encodedTicket); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { (*env)->DeleteLocalRef(env, ary); return (jobject) NULL; } ticket = (*env)->NewObject(env, ticketClass, ticketConstructor, ary); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { (*env)->DeleteLocalRef(env, ary); return (jobject) NULL; } @@ -993,7 +993,7 @@ jobject BuildEncryptionKey(JNIEnv *env, PKERB_CRYPTO_KEY cryptoKey) { } (*env)->SetByteArrayRegion(env, ary, (jsize) 0, cryptoKey->Length, (jbyte *)cryptoKey->Value); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { (*env)->DeleteLocalRef(env, ary); } else { encryptionKey = (*env)->NewObject(env, encryptionKeyClass, @@ -1018,7 +1018,7 @@ jobject BuildTicketFlags(JNIEnv *env, PULONG flags) { } (*env)->SetByteArrayRegion(env, ary, (jsize) 0, sizeof(*flags), (jbyte *)&nlflags); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { (*env)->DeleteLocalRef(env, ary); } else { ticketFlags = (*env)->NewObject(env, ticketFlagsClass, From 7eb55357ab169c21dd5d0ed1738155e794e5faaf Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Thu, 10 Oct 2024 17:33:22 +0000 Subject: [PATCH 09/95] 8341789: Fix ExceptionOccurred in java.base Reviewed-by: bpb, jpai, dfuchs, lancea, rriggs, naoto --- .../share/native/libjava/ClassLoader.c | 6 ++--- src/java.base/share/native/libjava/System.c | 6 ++--- src/java.base/share/native/libjava/io_util.c | 4 +-- src/java.base/share/native/libjava/jni_util.c | 6 ++--- src/java.base/share/native/libjli/java.c | 6 ++--- src/java.base/share/native/libjli/java.h | 4 +-- .../unix/native/libjava/io_util_md.c | 4 +-- .../unix/native/libnet/NetworkInterface.c | 26 +++++++++---------- src/java.base/unix/native/libnet/SdpSupport.c | 2 +- .../unix/native/libnio/ch/UnixDomainSockets.c | 4 +-- 10 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/java.base/share/native/libjava/ClassLoader.c b/src/java.base/share/native/libjava/ClassLoader.c index 927432d1509..4519a4777f8 100644 --- a/src/java.base/share/native/libjava/ClassLoader.c +++ b/src/java.base/share/native/libjava/ClassLoader.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * 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 @@ Java_java_lang_ClassLoader_defineClass1(JNIEnv *env, (*env)->GetByteArrayRegion(env, data, offset, length, body); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { goto free_body; } @@ -259,7 +259,7 @@ Java_java_lang_ClassLoader_defineClass0(JNIEnv *env, (*env)->GetByteArrayRegion(env, data, offset, length, body); - if ((*env)->ExceptionOccurred(env)) + if ((*env)->ExceptionCheck(env)) goto free_body; if (name != NULL) { diff --git a/src/java.base/share/native/libjava/System.c b/src/java.base/share/native/libjava/System.c index 098b943cc40..7b038a6a9d5 100644 --- a/src/java.base/share/native/libjava/System.c +++ b/src/java.base/share/native/libjava/System.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. * 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 @@ Java_java_lang_System_identityHashCode(JNIEnv *env, jobject this, jobject x) if (jval == NULL) \ return NULL; \ (*env)->SetObjectArrayElement(env, array, jdk_internal_util_SystemProps_Raw_##prop_index, jval); \ - if ((*env)->ExceptionOccurred(env)) \ + if ((*env)->ExceptionCheck(env)) \ return NULL; \ (*env)->DeleteLocalRef(env, jval); \ } @@ -86,7 +86,7 @@ Java_java_lang_System_identityHashCode(JNIEnv *env, jobject this, jobject x) if (jval == NULL) \ return NULL; \ (*env)->SetObjectArrayElement(env, array, jdk_internal_util_SystemProps_Raw_##prop_index, jval); \ - if ((*env)->ExceptionOccurred(env)) \ + if ((*env)->ExceptionCheck(env)) \ return NULL; \ (*env)->DeleteLocalRef(env, jval); \ } diff --git a/src/java.base/share/native/libjava/io_util.c b/src/java.base/share/native/libjava/io_util.c index 3fb7675d277..76ff58ec632 100644 --- a/src/java.base/share/native/libjava/io_util.c +++ b/src/java.base/share/native/libjava/io_util.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -175,7 +175,7 @@ writeBytes(JNIEnv *env, jobject this, jbyteArray bytes, (*env)->GetByteArrayRegion(env, bytes, off, len, (jbyte *)buf); - if (!(*env)->ExceptionOccurred(env)) { + if (!(*env)->ExceptionCheck(env)) { off = 0; while (len > 0) { fd = getFD(env, this, fid); diff --git a/src/java.base/share/native/libjava/jni_util.c b/src/java.base/share/native/libjava/jni_util.c index 3d9004d969c..ce23d160430 100644 --- a/src/java.base/share/native/libjava/jni_util.c +++ b/src/java.base/share/native/libjava/jni_util.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * 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,7 +117,7 @@ JNU_ThrowByNameWithLastError(JNIEnv *env, const char *name, (*env)->Throw(env, x); } } - if (!(*env)->ExceptionOccurred(env)) { + if (!(*env)->ExceptionCheck(env)) { JNU_ThrowByName(env, name, defaultDetail); } } @@ -166,7 +166,7 @@ JNU_ThrowByNameWithMessageAndLastError } } - if (!(*env)->ExceptionOccurred(env)) { + if (!(*env)->ExceptionCheck(env)) { if (messagelen > 0) { JNU_ThrowByName(env, name, message); } else { diff --git a/src/java.base/share/native/libjli/java.c b/src/java.base/share/native/libjli/java.c index d4e20fb21fe..355ac4b9e28 100644 --- a/src/java.base/share/native/libjli/java.c +++ b/src/java.base/share/native/libjli/java.c @@ -351,7 +351,7 @@ JLI_Launch(int argc, char ** argv, /* main argc, argv */ #define CHECK_EXCEPTION_NULL_LEAVE(CENL_exception) \ do { \ - if ((*env)->ExceptionOccurred(env)) { \ + if ((*env)->ExceptionCheck(env)) { \ JLI_ReportExceptionDescription(env); \ LEAVE(); \ } \ @@ -363,7 +363,7 @@ JLI_Launch(int argc, char ** argv, /* main argc, argv */ #define CHECK_EXCEPTION_LEAVE(CEL_return_value) \ do { \ - if ((*env)->ExceptionOccurred(env)) { \ + if ((*env)->ExceptionCheck(env)) { \ JLI_ReportExceptionDescription(env); \ ret = (CEL_return_value); \ LEAVE(); \ @@ -1522,7 +1522,7 @@ NewPlatformString(JNIEnv *env, char *s) if (ary != 0) { jstring str = 0; (*env)->SetByteArrayRegion(env, ary, 0, len, (jbyte *)s); - if (!(*env)->ExceptionOccurred(env)) { + if (!(*env)->ExceptionCheck(env)) { if (makePlatformStringMID == NULL) { NULL_CHECK0(makePlatformStringMID = (*env)->GetStaticMethodID(env, cls, "makePlatformString", "(Z[B)Ljava/lang/String;")); diff --git a/src/java.base/share/native/libjli/java.h b/src/java.base/share/native/libjli/java.h index ce5224a7da3..19493fedaae 100644 --- a/src/java.base/share/native/libjli/java.h +++ b/src/java.base/share/native/libjli/java.h @@ -246,14 +246,14 @@ typedef struct { #define CHECK_EXCEPTION_RETURN_VALUE(CER_value) \ do { \ - if ((*env)->ExceptionOccurred(env)) { \ + if ((*env)->ExceptionCheck(env)) { \ return CER_value; \ } \ } while (JNI_FALSE) #define CHECK_EXCEPTION_RETURN() \ do { \ - if ((*env)->ExceptionOccurred(env)) { \ + if ((*env)->ExceptionCheck(env)) { \ return; \ } \ } while (JNI_FALSE) diff --git a/src/java.base/unix/native/libjava/io_util_md.c b/src/java.base/unix/native/libjava/io_util_md.c index 28659b3a1c2..9895ac3b73f 100644 --- a/src/java.base/unix/native/libjava/io_util_md.c +++ b/src/java.base/unix/native/libjava/io_util_md.c @@ -135,7 +135,7 @@ void fileDescriptorClose(JNIEnv *env, jobject this) { FD fd = (*env)->GetIntField(env, this, IO_fd_fdID); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return; } @@ -150,7 +150,7 @@ fileDescriptorClose(JNIEnv *env, jobject this) * taking extra precaution over here. */ (*env)->SetIntField(env, this, IO_fd_fdID, -1); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return; } /* diff --git a/src/java.base/unix/native/libnet/NetworkInterface.c b/src/java.base/unix/native/libnet/NetworkInterface.c index 0cd69399d91..61267ec13d5 100644 --- a/src/java.base/unix/native/libnet/NetworkInterface.c +++ b/src/java.base/unix/native/libnet/NetworkInterface.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -374,7 +374,7 @@ JNIEXPORT jboolean JNICALL Java_java_net_NetworkInterface_boundInetAddress0 if (family == AF_INET) { sock = openSocket(env, AF_INET); - if (sock < 0 && (*env)->ExceptionOccurred(env)) { + if (sock < 0 && (*env)->ExceptionCheck(env)) { return JNI_FALSE; } @@ -383,7 +383,7 @@ JNIEXPORT jboolean JNICALL Java_java_net_NetworkInterface_boundInetAddress0 ifs = enumIPv4Interfaces(env, sock, ifs); close(sock); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { goto cleanup; } } @@ -401,7 +401,7 @@ JNIEXPORT jboolean JNICALL Java_java_net_NetworkInterface_boundInetAddress0 ifs = enumIPv6Interfaces(env, sock, ifs); close(sock); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { goto cleanup; } @@ -856,7 +856,7 @@ static netif *enumInterfaces(JNIEnv *env) { int sock; sock = openSocket(env, AF_INET); - if (sock < 0 && (*env)->ExceptionOccurred(env)) { + if (sock < 0 && (*env)->ExceptionCheck(env)) { return NULL; } @@ -865,7 +865,7 @@ static netif *enumInterfaces(JNIEnv *env) { ifs = enumIPv4Interfaces(env, sock, ifs); close(sock); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { freeif(ifs); return NULL; } @@ -884,7 +884,7 @@ static netif *enumInterfaces(JNIEnv *env) { ifs = enumIPv6Interfaces(env, sock, ifs); close(sock); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { freeif(ifs); return NULL; } @@ -1237,7 +1237,7 @@ static netif *enumIPv4Interfaces(JNIEnv *env, int sock, netif *ifs) { &addr, broadaddrP, AF_INET, prefix); // in case of exception, free interface list and buffer and return NULL - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { free(buf); freeif(ifs); return NULL; @@ -1281,7 +1281,7 @@ static netif *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs) { NULL, AF_INET6, (short)prefix); // if an exception occurred then return the list as is - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { break; } } @@ -1478,7 +1478,7 @@ static netif *enumIPv4Interfaces(JNIEnv *env, int sock, netif *ifs) { &addr, broadaddrP, AF_INET, prefix); // in case of exception, free interface list and buffer and return NULL - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { free(buf); freeif(ifs); return NULL; @@ -1552,7 +1552,7 @@ static netif *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs) { NULL, AF_INET6, prefix); // if an exception occurred then free the list - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { free(buf); freeif(ifs); return NULL; @@ -1717,7 +1717,7 @@ static netif *enumIPv4Interfaces(JNIEnv *env, int sock, netif *ifs) { ifa->ifa_netmask)); // if an exception occurred then free the list - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { freeifaddrs(origifa); freeif(ifs); return NULL; @@ -1757,7 +1757,7 @@ static netif *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs) { ifa->ifa_netmask)); // if an exception occurred then free the list - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { freeifaddrs(origifa); freeif(ifs); return NULL; diff --git a/src/java.base/unix/native/libnet/SdpSupport.c b/src/java.base/unix/native/libnet/SdpSupport.c index 6729170efb4..b56f16905fa 100644 --- a/src/java.base/unix/native/libnet/SdpSupport.c +++ b/src/java.base/unix/native/libnet/SdpSupport.c @@ -106,7 +106,7 @@ Java_sun_net_sdp_SdpSupport_convert0(JNIEnv *env, jclass cls, int fd) if (res < 0) JNU_ThrowIOExceptionWithLastError(env, "dup2"); res = close(s); - if (res < 0 && !(*env)->ExceptionOccurred(env)) + if (res < 0 && !(*env)->ExceptionCheck(env)) JNU_ThrowIOExceptionWithLastError(env, "close"); } } diff --git a/src/java.base/unix/native/libnio/ch/UnixDomainSockets.c b/src/java.base/unix/native/libnio/ch/UnixDomainSockets.c index 73db22a9176..c43c3b90695 100644 --- a/src/java.base/unix/native/libnio/ch/UnixDomainSockets.c +++ b/src/java.base/unix/native/libnio/ch/UnixDomainSockets.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -62,7 +62,7 @@ jbyteArray sockaddrToUnixAddressBytes(JNIEnv *env, struct sockaddr_un *sa, sockl jbyteArray name = (*env)->NewByteArray(env, namelen); if (namelen != 0) { (*env)->SetByteArrayRegion(env, name, 0, namelen, (jbyte*)sa->sun_path); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return NULL; } } From a5cad0ee1e86285b7d2561dfce37f2b22067c9e6 Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Thu, 10 Oct 2024 17:33:38 +0000 Subject: [PATCH 10/95] 8341791: Fix ExceptionOccurred in java.prefs Reviewed-by: bpb, jpai, naoto --- .../native/libprefs/MacOSXPreferencesFile.m | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/java.prefs/macosx/native/libprefs/MacOSXPreferencesFile.m b/src/java.prefs/macosx/native/libprefs/MacOSXPreferencesFile.m index eb2338ca944..bc3d9aaf682 100644 --- a/src/java.prefs/macosx/native/libprefs/MacOSXPreferencesFile.m +++ b/src/java.prefs/macosx/native/libprefs/MacOSXPreferencesFile.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -83,7 +83,7 @@ static void throwOutOfMemoryError(JNIEnv *env, const char *msg) c = exceptionClass; } else { c = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); - if ((*env)->ExceptionOccurred(env)) return; + if ((*env)->ExceptionCheck(env)) return; exceptionClass = (*env)->NewGlobalRef(env, c); } @@ -211,7 +211,7 @@ static jarray createJavaStringArray(JNIEnv *env, CFIndex count) c = stringClass; } else { c = (*env)->FindClass(env, "java/lang/String"); - if ((*env)->ExceptionOccurred(env)) return NULL; + if ((*env)->ExceptionCheck(env)) return NULL; stringClass = (*env)->NewGlobalRef(env, c); } @@ -892,7 +892,7 @@ static void createTreeForPath(CFStringRef path, CFStringRef name, result = NULL; } else { CFStringRef cfString = copyToCFString(env, value); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { // memory error in copyToCFString result = NULL; } else if (cfString == NULL) { @@ -940,10 +940,10 @@ static void BuildJavaArrayFn(const void *key, const void *value, void *context) CFStringRef cfString = NULL; JNIEnv *env = args->env; - if ((*env)->ExceptionOccurred(env)) return; // already failed + if ((*env)->ExceptionCheck(env)) return; // already failed cfString = copyToCFString(env, propkey); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { // memory error in copyToCFString } else if (!cfString) { // bogus value type in prefs file - no Java errors available @@ -960,9 +960,9 @@ static void BuildJavaArrayFn(const void *key, const void *value, void *context) } if (CFStringGetLength(cfString) <= 0) goto bad; // ignore empty javaString = toJavaString(env, cfString); - if ((*env)->ExceptionOccurred(env)) goto bad; + if ((*env)->ExceptionCheck(env)) goto bad; (*env)->SetObjectArrayElement(env, args->result,args->used,javaString); - if ((*env)->ExceptionOccurred(env)) goto bad; + if ((*env)->ExceptionCheck(env)) goto bad; args->used++; } @@ -1003,7 +1003,7 @@ static jarray getStringsForNode(JNIEnv *env, jobject klass, jobject jpath, args.used = 0; args.allowSlash = allowSlash; CFDictionaryApplyFunction(node, BuildJavaArrayFn, &args); - if (!(*env)->ExceptionOccurred(env)) { + if (!(*env)->ExceptionCheck(env)) { // array construction succeeded if (args.used < count) { // finished array is smaller than expected. From 32f817a46068b61d599b714a4480e3ea5d6e9050 Mon Sep 17 00:00:00 2001 From: Harshitha Onkar Date: Thu, 10 Oct 2024 17:55:26 +0000 Subject: [PATCH 11/95] 8340978: Open source few DnD tests - Set6 Reviewed-by: prr --- test/jdk/ProblemList.txt | 1 + .../java/awt/dnd/CustomDragCursorTest.java | 289 ++++++++++++++++++ .../DnDAcceptanceTest/DnDAcceptanceTest.java | 87 ++++++ .../awt/dnd/DnDAcceptanceTest/DnDSource.java | 155 ++++++++++ .../awt/dnd/DnDAcceptanceTest/DnDTarget.java | 115 +++++++ 5 files changed, 647 insertions(+) create mode 100644 test/jdk/java/awt/dnd/CustomDragCursorTest.java create mode 100644 test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDAcceptanceTest.java create mode 100644 test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDSource.java create mode 100644 test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDTarget.java diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index fb20eb1f63d..66ba244a6e2 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -131,6 +131,7 @@ java/awt/dnd/URIListBetweenJVMsTest/URIListBetweenJVMsTest.java 8171510 macosx-a java/awt/dnd/MissingDragExitEventTest/MissingDragExitEventTest.java 8288839 windows-x64 java/awt/dnd/DragExitBeforeDropTest.java 8242805 macosx-all java/awt/dnd/DragThresholdTest.java 8076299 macosx-all +java/awt/dnd/CustomDragCursorTest.java 8242805 macosx-all java/awt/Focus/ChoiceFocus/ChoiceFocus.java 8169103 windows-all,macosx-all java/awt/Focus/ClearLwQueueBreakTest/ClearLwQueueBreakTest.java 8198618 macosx-all java/awt/Focus/ConsumeNextKeyTypedOnModalShowTest/ConsumeNextKeyTypedOnModalShowTest.java 6986252 macosx-all diff --git a/test/jdk/java/awt/dnd/CustomDragCursorTest.java b/test/jdk/java/awt/dnd/CustomDragCursorTest.java new file mode 100644 index 00000000000..9e5e006b31c --- /dev/null +++ b/test/jdk/java/awt/dnd/CustomDragCursorTest.java @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/* + * @test + * @bug 4451328 + * @summary tests that a custom drag cursor is not changed + to the default drag cursor + * @key headful + * @run main CustomDragCursorTest + */ + +public class CustomDragCursorTest { + private static Frame frame; + private static final DragSourcePanel dragSourcePanel = new DragSourcePanel(); + private static final DropTargetPanel dropTargetPanel = new DropTargetPanel(); + + private static volatile Point srcPoint; + private static volatile Point dstPoint; + private static volatile boolean passed = true; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + EventQueue.invokeAndWait(CustomDragCursorTest::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + Point p = dragSourcePanel.getLocationOnScreen(); + Dimension d = dragSourcePanel.getSize(); + p.translate(d.width / 2, d.height / 2); + srcPoint = p; + + p = dropTargetPanel.getLocationOnScreen(); + d = dropTargetPanel.getSize(); + p.translate(d.width / 2, d.height / 2); + dstPoint = p; + }); + + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (; !srcPoint.equals(dstPoint); + srcPoint.translate(sign(dstPoint.x - srcPoint.x), + sign(dstPoint.y - srcPoint.y))) { + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.delay(10); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + robot.waitForIdle(); + robot.delay(1000); + + if (!passed) { + throw new RuntimeException("Custom drag cursor changed to default."); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + frame = new Frame("CustomDragCursorTest"); + frame.setLayout(new GridLayout(2, 1)); + frame.add(dragSourcePanel); + frame.add(dropTargetPanel); + frame.setLocationRelativeTo(null); + frame.setSize(300, 400); + frame.setVisible(true); + } + + public static void failed() { + passed = false; + } + + private static int sign(int n) { + return Integer.compare(n, 0); + } + + private static class DragSourceButton extends Button implements Serializable, + Transferable, + DragGestureListener, + DragSourceListener { + private final DataFlavor dataflavor = + new DataFlavor(Button.class, "DragSourceButton"); + private final Cursor dragCursor = new Cursor(Cursor.HAND_CURSOR); + + public DragSourceButton() { + this("DragSourceButton"); + } + + public DragSourceButton(String str) { + super(str); + + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(dragCursor, this, this); + } + + public void dragEnter(DragSourceDragEvent dsde) { + if (!dragCursor.equals(dsde.getDragSourceContext().getCursor())) { + CustomDragCursorTest.failed(); + } + } + + public void dragExit(DragSourceEvent dse) { + if (!dragCursor.equals(dse.getDragSourceContext().getCursor())) { + CustomDragCursorTest.failed(); + } + } + + public void dragOver(DragSourceDragEvent dsde) { + if (!dragCursor.equals(dsde.getDragSourceContext().getCursor())) { + CustomDragCursorTest.failed(); + } + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + if (!dragCursor.equals(dsde.getDragSourceContext().getCursor())) { + CustomDragCursorTest.failed(); + } + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + if (!dragCursor.equals(dsde.getDragSourceContext().getCursor())) { + CustomDragCursorTest.failed(); + } + } + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException { + + if (!isDataFlavorSupported(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + + Object retObj; + + ByteArrayOutputStream baoStream = new ByteArrayOutputStream(); + ObjectOutputStream ooStream = new ObjectOutputStream(baoStream); + ooStream.writeObject(this); + + ByteArrayInputStream baiStream = new ByteArrayInputStream(baoStream.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(baiStream); + try { + retObj = ois.readObject(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + throw new RuntimeException(e.toString()); + } + + return retObj; + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { dataflavor }; + } + + public boolean isDataFlavorSupported(DataFlavor dflavor) { + return dataflavor.equals(dflavor); + } + } + + private static class DragSourcePanel extends Panel { + + final Dimension preferredDimension = new Dimension(200, 100); + + public DragSourcePanel() { + setLayout(new GridLayout(1, 1)); + add(new DragSourceButton()); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } + } + + private static class DropTargetPanel extends Panel implements DropTargetListener { + + final Dimension preferredDimension = new Dimension(200, 100); + + public DropTargetPanel() { + setDropTarget(new DropTarget(this, this)); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } + + public void dragEnter(DropTargetDragEvent dtde) {} + + public void dragExit(DropTargetEvent dte) {} + + public void dragOver(DropTargetDragEvent dtde) {} + + public void dropActionChanged(DropTargetDragEvent dtde) {} + + public void drop(DropTargetDropEvent dtde) { + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + Component comp = null; + + if(dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + + try { + comp = (Component)transfer.getTransferData(dfs[0]); + } catch (Throwable e) { + e.printStackTrace(); + dtc.dropComplete(false); + } + } + dtc.dropComplete(true); + add(comp); + } + } +} diff --git a/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDAcceptanceTest.java b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDAcceptanceTest.java new file mode 100644 index 00000000000..700187f9dce --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDAcceptanceTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * 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.Component; +import java.awt.Frame; +import java.awt.Panel; + +/* + * @test + * @bug 4166541 4225247 4297663 + * @summary Tests Basic DnD functionality + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DnDAcceptanceTest + */ + +public class DnDAcceptanceTest { + private static final String INSTRUCTIONS = """ + When test runs a Frame which contains a yellow button labeled + "Drag ME!" and a RED Panel will appear. + + Click on the button and drag to the red panel. + When the mouse enters the red panel + during the drag the panel should turn yellow. + + Release the mouse button, panel should turn red again and + a yellow button labeled Drag ME! should appear inside the panel. + You should be able to repeat this operation multiple times. + + If above is true press PASS, else press FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(38) + .testUI(DnDAcceptanceTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame frame = new Frame("DnDAcceptanceTest"); + Panel mainPanel; + Component dragSource, dropTarget; + + frame.setSize(400, 400); + frame.setLayout(new BorderLayout()); + + mainPanel = new Panel(); + mainPanel.setLayout(new BorderLayout()); + + mainPanel.setBackground(Color.BLACK); + + dropTarget = new DnDTarget(Color.RED, Color.YELLOW); + dragSource = new DnDSource("Drag ME!"); + + mainPanel.add(dragSource, "North"); + mainPanel.add(dropTarget, "Center"); + frame.add(mainPanel, BorderLayout.CENTER); + frame.setAlwaysOnTop(true); + return frame; + } +} diff --git a/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDSource.java b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDSource.java new file mode 100644 index 00000000000..a35ccd9ee12 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDSource.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Container; +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.MouseDragGestureRecognizer; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.InvalidDnDOperationException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +class DnDSource extends Button implements Transferable, + DragGestureListener, + DragSourceListener { + private DataFlavor df; + private transient int dropAction; + + DnDSource(String label) { + super(label); + Toolkit.getDefaultToolkit().createDragGestureRecognizer(MouseDragGestureRecognizer.class, + DragSource.getDefaultDragSource(), + this, DnDConstants.ACTION_COPY, this); + setBackground(Color.yellow); + setForeground(Color.blue); + df = new DataFlavor(DnDSource.class, "DnDSource"); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + System.err.println("starting Drag"); + try { + dge.startDrag(null, this, this); + } catch (InvalidDnDOperationException e) { + e.printStackTrace(); + } + } + + public void dragEnter(DragSourceDragEvent dsde) { + System.err.println("[Source] dragEnter"); + dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop); + } + + public void dragOver(DragSourceDragEvent dsde) { + System.err.println("[Source] dragOver"); + dropAction = dsde.getDropAction(); + System.out.println("dropAction = " + dropAction); + } + + public void dragGestureChanged(DragSourceDragEvent dsde) { + System.err.println("[Source] dragGestureChanged"); + dropAction = dsde.getDropAction(); + System.out.println("dropAction = " + dropAction); + } + + public void dragExit(DragSourceEvent dsde) { + System.err.println("[Source] dragExit"); + dsde.getDragSourceContext().setCursor(null); + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + System.err.println("[Source] dragDropEnd"); + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + System.err.println("[Source] dropActionChanged"); + dropAction = dsde.getDropAction(); + System.out.println("dropAction = " + dropAction); + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] {df}; + } + + public boolean isDataFlavorSupported(DataFlavor sdf) { + return df.equals(sdf); + } + + public Object getTransferData(DataFlavor tdf) throws UnsupportedFlavorException, IOException { + + Object copy = null; + + if (!df.equals(tdf)) { + throw new UnsupportedFlavorException(tdf); + } + Container parent = getParent(); + switch (dropAction) { + case DnDConstants.ACTION_COPY: + try { + copy = this.clone(); + } catch (CloneNotSupportedException e) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + + oos.writeObject(this); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bais); + try { + copy = ois.readObject(); + } catch (ClassNotFoundException cnfe) { + // do nothing + } + } + + parent.add(this); + return copy; + + case DnDConstants.ACTION_MOVE: + synchronized(this) { + if (parent != null) parent.remove(this); + } + return this; + + case DnDConstants.ACTION_LINK: + return this; + + default: + //throw new IOException("bad operation"); + return this; // workaround for: 4135456 getDropAction() always return 0 + } + } +} diff --git a/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDTarget.java b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDTarget.java new file mode 100644 index 00000000000..d147741f0d2 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDTarget.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Panel; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.io.IOException; + +class DnDTarget extends Panel implements DropTargetListener { + Color bgColor; + Color htColor; + + DnDTarget(Color bgColor, Color htColor) { + super(); + this.bgColor = bgColor; + this.htColor = htColor; + setBackground(bgColor); + setDropTarget(new DropTarget(this, this)); + } + + public void dragEnter(DropTargetDragEvent e) { + System.err.println("[Target] dragEnter"); + e.acceptDrag(DnDConstants.ACTION_COPY); + setBackground(htColor); + repaint(); + } + + public void dragOver(DropTargetDragEvent e) { + System.err.println("[Target] dragOver"); + e.acceptDrag(DnDConstants.ACTION_COPY); + } + + public void dragExit(DropTargetEvent e) { + System.err.println("[Target] dragExit"); + setBackground(bgColor); + repaint(); + } + + public void drop(DropTargetDropEvent dtde) { + System.err.println("[Target] drop"); + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + return; + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + if (dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + Object obj; + try { + obj = transfer.getTransferData(dfs[0]); + } catch (IOException | UnsupportedFlavorException ex) { + System.err.println(ex.getMessage()); + dtc.dropComplete(false); + return; + } + + if (obj != null) { + Button button; + try { + button = (Button) obj; + } catch (Exception e) { + System.err.println(e.getMessage()); + dtc.dropComplete(false); + return; + } + add(button); + repaint(); + } + } + setBackground(bgColor); + invalidate(); + validate(); + repaint(); + dtc.dropComplete(true); + } + + public void dropActionChanged(DropTargetDragEvent e) { + System.err.println("[Target] dropActionChanged"); + } +} From 2a6f0307e8d287fc9177e9454386e63faf8e61a0 Mon Sep 17 00:00:00 2001 From: Daniel Gredler Date: Thu, 10 Oct 2024 18:00:33 +0000 Subject: [PATCH 12/95] 8339974: Graphics2D.drawString doesn't always work with Font derived from AffineTransform Reviewed-by: prr, azvegint --- .../classes/java/awt/font/TextLayout.java | 11 +- .../share/classes/java/awt/font/TextLine.java | 6 +- .../FontScaling/RotatedScaledFontTest.java | 149 +++++++++++++ .../PostScriptRotatedScaledFontTest.java | 211 ++++++++++++++++++ 4 files changed, 371 insertions(+), 6 deletions(-) create mode 100644 test/jdk/java/awt/font/FontScaling/RotatedScaledFontTest.java create mode 100644 test/jdk/javax/print/PostScriptRotatedScaledFontTest.java diff --git a/src/java.desktop/share/classes/java/awt/font/TextLayout.java b/src/java.desktop/share/classes/java/awt/font/TextLayout.java index 352d3c81df2..855097b6a26 100644 --- a/src/java.desktop/share/classes/java/awt/font/TextLayout.java +++ b/src/java.desktop/share/classes/java/awt/font/TextLayout.java @@ -2663,11 +2663,20 @@ static byte getBaselineFromGraphic(GraphicAttribute graphic) { */ public Shape getOutline(AffineTransform tx) { ensureCache(); - Shape result = textLine.getOutline(tx); + Shape result = textLine.getOutline(); LayoutPathImpl lp = textLine.getLayoutPath(); if (lp != null) { result = lp.mapShape(result); } + if (tx != null) { + if (result instanceof GeneralPath gp) { + // transform in place + gp.transform(tx); + } else { + // create a transformed copy + result = tx.createTransformedShape(result); + } + } return result; } diff --git a/src/java.desktop/share/classes/java/awt/font/TextLine.java b/src/java.desktop/share/classes/java/awt/font/TextLine.java index 9d5da23bfe7..1e4b9c784a6 100644 --- a/src/java.desktop/share/classes/java/awt/font/TextLine.java +++ b/src/java.desktop/share/classes/java/awt/font/TextLine.java @@ -864,19 +864,15 @@ public Rectangle2D getItalicBounds() { return new Rectangle2D.Float(left, top, right-left, bottom-top); } - public Shape getOutline(AffineTransform tx) { + public Shape getOutline() { GeneralPath dstShape = new GeneralPath(GeneralPath.WIND_NON_ZERO); for (int i=0, n = 0; i < fComponents.length; i++, n += 2) { TextLineComponent tlc = fComponents[getComponentLogicalIndex(i)]; - dstShape.append(tlc.getOutline(locs[n], locs[n+1]), false); } - if (tx != null) { - dstShape.transform(tx); - } return dstShape; } diff --git a/test/jdk/java/awt/font/FontScaling/RotatedScaledFontTest.java b/test/jdk/java/awt/font/FontScaling/RotatedScaledFontTest.java new file mode 100644 index 00000000000..3e17bbd92c1 --- /dev/null +++ b/test/jdk/java/awt/font/FontScaling/RotatedScaledFontTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.File; + +import javax.imageio.ImageIO; + +/* + * @test + * @bug 8339974 + * @summary Verifies that text draws correctly using scaled and rotated fonts. + */ +public class RotatedScaledFontTest { + + public static void main(String[] args) throws Exception { + test(0); + test(1); + test(2); + test(3); + test(4); + } + + private static void test(int quadrants) throws Exception { + + int size = 2000; + int center = size / 2; + Font base = new Font("SansSerif", Font.PLAIN, 10); + BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_BYTE_BINARY); + Graphics2D g2d = image.createGraphics(); + + try { + for (int scale = 1; scale <= 100; scale++) { + AffineTransform at = AffineTransform.getQuadrantRotateInstance(quadrants); + at.scale(scale, scale); + Font font = base.deriveFont(at); + g2d.setColor(Color.WHITE); + g2d.fillRect(0, 0, image.getWidth(), image.getHeight()); + g2d.setColor(Color.BLACK); + g2d.setFont(font); + g2d.drawString("TEST", center, center); + Rectangle bounds = findTextBoundingBox(image); + if (bounds == null) { + saveImage("bounds", image); + throw new RuntimeException("Text missing: scale=" + scale + + ", quadrants=" + quadrants + ", center=" + center); + } + boolean horizontal = (bounds.width > bounds.height); + boolean expectedHorizontal = (quadrants % 2 == 0); + if (horizontal != expectedHorizontal) { + saveImage("orientation", image); + throw new RuntimeException("Wrong orientation: scale=" + scale + + ", quadrants=" + quadrants + ", center=" + center + + ", bounds=" + bounds + ", horizontal=" + horizontal + + ", expectedHorizontal=" + expectedHorizontal); + } + if (!roughlyEqual(center, bounds.x, scale) && !roughlyEqual(center, bounds.x + bounds.width, scale)) { + saveImage("xedge", image); + throw new RuntimeException("No x-edge at center: scale=" + scale + + ", quadrants=" + quadrants + ", center=" + center + + ", bounds=" + bounds); + } + if (!roughlyEqual(center, bounds.y, scale) && !roughlyEqual(center, bounds.y + bounds.height, scale)) { + saveImage("yedge", image); + throw new RuntimeException("No y-edge at center: scale=" + scale + + ", quadrants=" + quadrants + ", center=" + center + + ", bounds=" + bounds); + } + } + } finally { + g2d.dispose(); + } + } + + private static Rectangle findTextBoundingBox(BufferedImage image) { + int minX = Integer.MAX_VALUE; + int minY = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + int maxY = Integer.MIN_VALUE; + int width = image.getWidth(); + int height = image.getHeight(); + int[] rowPixels = new int[width]; + for (int y = 0; y < height; y++) { + image.getRGB(0, y, width, 1, rowPixels, 0, width); + for (int x = 0; x < width; x++) { + boolean white = (rowPixels[x] == -1); + if (!white) { + if (x < minX) { + minX = x; + } + if (y < minY) { + minY = y; + } + if (x > maxX) { + maxX = x; + } + if (y > maxY) { + maxY = y; + } + } + } + } + if (minX != Integer.MAX_VALUE) { + return new Rectangle(minX, minY, maxX - minX, maxY - minY); + } else { + return null; + } + } + + private static boolean roughlyEqual(int x1, int x2, int scale) { + return Math.abs(x1 - x2) <= Math.ceil(scale / 2d) + 1; // higher scale = higher allowed variance + } + + private static void saveImage(String name, BufferedImage image) { + try { + String dir = System.getProperty("test.classes", "."); + String path = dir + File.separator + name + ".png"; + File file = new File(path); + ImageIO.write(image, "png", file); + } catch (Exception e) { + // we tried, and that's enough + } + } +} diff --git a/test/jdk/javax/print/PostScriptRotatedScaledFontTest.java b/test/jdk/javax/print/PostScriptRotatedScaledFontTest.java new file mode 100644 index 00000000000..e6e274f7cac --- /dev/null +++ b/test/jdk/javax/print/PostScriptRotatedScaledFontTest.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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.Font; +import java.awt.Graphics; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import javax.print.Doc; +import javax.print.DocFlavor; +import javax.print.DocPrintJob; +import javax.print.SimpleDoc; +import javax.print.StreamPrintService; +import javax.print.StreamPrintServiceFactory; +import javax.print.event.PrintJobAdapter; +import javax.print.event.PrintJobEvent; + +/* + * @test + * @bug 8339974 + * @summary Verifies that text prints correctly using scaled and rotated fonts. + */ +public class PostScriptRotatedScaledFontTest { + + public static void main(String[] args) throws Exception { + test(0); + test(1); + test(2); + test(3); + test(4); + } + + private static void test(int quadrants) throws Exception { + + DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE; + String mime = "application/postscript"; + StreamPrintServiceFactory[] factories = StreamPrintServiceFactory.lookupStreamPrintServiceFactories(flavor, mime); + if (factories.length == 0) { + throw new RuntimeException("Unable to find PostScript print service factory"); + } + + StreamPrintServiceFactory factory = factories[0]; + + // required to trigger "text-as-shapes" code path in + // PSPathGraphics.drawString(String, float, float, Font, FontRenderContext, float) + // for *all* text, not just text that uses a transformed font + String shapeText = "sun.java2d.print.shapetext"; + System.setProperty(shapeText, "true"); + + try { + for (int scale = 1; scale <= 100; scale++) { + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + StreamPrintService service = factory.getPrintService(output); + DocPrintJob job = service.createPrintJob(); + + PrintJobMonitor monitor = new PrintJobMonitor(); + job.addPrintJobListener(monitor); + + Printable printable = new TestPrintable(scale, quadrants); + Doc doc = new SimpleDoc(printable, flavor, null); + job.print(doc, null); + monitor.waitForJobToFinish(); + + byte[] ps = output.toByteArray(); + Rectangle2D.Double bounds = findTextBoundingBox(ps); + if (bounds == null) { + throw new RuntimeException("Text missing: scale=" + scale + + ", quadrants=" + quadrants); + } + + boolean horizontal = (bounds.width > bounds.height); + boolean expectedHorizontal = (quadrants % 2 == 0); + if (horizontal != expectedHorizontal) { + throw new RuntimeException("Wrong orientation: scale=" + scale + + ", quadrants=" + quadrants + ", bounds=" + bounds + + ", expectedHorizontal=" + expectedHorizontal + + ", horizontal=" + horizontal); + } + } + } finally { + System.clearProperty(shapeText); + } + } + + // very basic, uses moveto ("x y M"), lineto ("x y L"), and curveto ("x1 y1 x2 y2 x3 y3 C") + private static Rectangle2D.Double findTextBoundingBox(byte[] ps) { + double minX = Double.MAX_VALUE; + double minY = Double.MAX_VALUE; + double maxX = Double.MIN_VALUE; + double maxY = Double.MIN_VALUE; + boolean pastPageClip = false; + List< String > lines = new String(ps, StandardCharsets.ISO_8859_1).lines().toList(); + for (String line : lines) { + if (!pastPageClip) { + pastPageClip = "WC".equals(line); + continue; + } + String[] values = line.split(" "); + if (values.length == 3 || values.length == 7) { + String cmd = values[values.length - 1]; + if ("M".equals(cmd) || "L".equals(cmd) || "C".equals(cmd)) { + String sx = values[values.length - 3]; + String sy = values[values.length - 2]; + double x = Double.parseDouble(sx); + double y = Double.parseDouble(sy); + if (x < minX) { + minX = x; + } + if (y < minY) { + minY = y; + } + if (x > maxX) { + maxX = x; + } + if (y > maxY) { + maxY = y; + } + } + } + } + if (minX != Double.MAX_VALUE) { + return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY); + } else { + return null; + } + } + + private static final class TestPrintable implements Printable { + private final int scale; + private final int quadrants; + public TestPrintable(int scale, int quadrants) { + this.scale = scale; + this.quadrants = quadrants; + } + @Override + public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { + if (pageIndex > 0) { + return NO_SUCH_PAGE; + } + AffineTransform at = AffineTransform.getQuadrantRotateInstance(quadrants); + at.scale(scale, scale); + Font base = new Font("SansSerif", Font.PLAIN, 10); + Font font = base.deriveFont(at); + graphics.setFont(font); + graphics.drawString("TEST", 300, 300); + return PAGE_EXISTS; + } + } + + private static class PrintJobMonitor extends PrintJobAdapter { + private boolean finished; + @Override + public void printJobCanceled(PrintJobEvent pje) { + finished(); + } + @Override + public void printJobCompleted(PrintJobEvent pje) { + finished(); + } + @Override + public void printJobFailed(PrintJobEvent pje) { + finished(); + } + @Override + public void printJobNoMoreEvents(PrintJobEvent pje) { + finished(); + } + private synchronized void finished() { + finished = true; + notify(); + } + public synchronized void waitForJobToFinish() { + try { + while (!finished) { + wait(); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } +} From 97ee8bbda2c7d7f76866690a34a5021fade2f438 Mon Sep 17 00:00:00 2001 From: Damon Nguyen Date: Thu, 10 Oct 2024 18:17:55 +0000 Subject: [PATCH 13/95] 8340173: Open source some Component/Panel/EventQueue tests - Set2 Reviewed-by: honkar --- test/jdk/ProblemList.txt | 1 + .../PushPopDeadlock/PushPopDeadlock.java | 97 ++++ .../MultipleAddNotifyTest.java | 118 +++++ .../PopupTest/PopupTest.java | 93 ++++ .../awt/Panel/PanelRepaint/PanelRepaint.java | 455 ++++++++++++++++++ 5 files changed, 764 insertions(+) create mode 100644 test/jdk/java/awt/EventQueue/PushPopDeadlock/PushPopDeadlock.java create mode 100644 test/jdk/java/awt/LightweightComponent/MultipleAddNotifyTest/MultipleAddNotifyTest.java create mode 100644 test/jdk/java/awt/LightweightComponent/PopupTest/PopupTest.java create mode 100644 test/jdk/java/awt/Panel/PanelRepaint/PanelRepaint.java diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 66ba244a6e2..db48c24cd7e 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -144,6 +144,7 @@ java/awt/Focus/TestDisabledAutoTransferSwing.java 6962362 windows-all java/awt/Focus/ActivateOnProperAppContextTest.java 8136516 macosx-all java/awt/Focus/FocusPolicyTest.java 7160904 linux-all java/awt/EventQueue/6980209/bug6980209.java 8198615 macosx-all +java/awt/EventQueue/PushPopDeadlock/PushPopDeadlock.java 8024034 generic-all java/awt/grab/EmbeddedFrameTest1/EmbeddedFrameTest1.java 7080150 macosx-all java/awt/event/InputEvent/EventWhenTest/EventWhenTest.java 8168646 generic-all java/awt/Mixing/AWT_Mixing/HierarchyBoundsListenerMixingTest.java 8049405 macosx-all diff --git a/test/jdk/java/awt/EventQueue/PushPopDeadlock/PushPopDeadlock.java b/test/jdk/java/awt/EventQueue/PushPopDeadlock/PushPopDeadlock.java new file mode 100644 index 00000000000..82ca5487184 --- /dev/null +++ b/test/jdk/java/awt/EventQueue/PushPopDeadlock/PushPopDeadlock.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 4212687 + * @summary Verifies that calling EventQueue.push() and EventQueue.pop() + * does not deadlock. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PushPopDeadlock + */ + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Robot; +import java.awt.Toolkit; + +public class PushPopDeadlock { + static int counter = 0; + static Robot robot; + static Frame f; + static Label l; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + String INSTRUCTIONS = """ + Click rapidly in the Frame labeled 'Click Here!'. + The number in the Frame should continue to increase. If the number + stops increasing (remains at a constant value), the test fails. + """; + + PassFailJFrame pfJFrame = PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(PushPopDeadlock::createUI) + .build(); + PushPopDeadlock.test(); + pfJFrame.awaitAndCheck(); + } + + public static Frame createUI() { + f = new Frame("Click Here!"); + l = new Label("Counter: " + counter); + f.add(l); + f.setSize(200, 200); + return f; + } + + public static void test() { + EventQueue q = new EventQueue() { + public void push(EventQueue queue) { + super.push(queue); + pop(); + } + }; + EventQueue q2 = new EventQueue(); + + Toolkit.getDefaultToolkit().getSystemEventQueue().push(q); + + new Thread(() -> { + while (true) { + robot.delay(500); + l.setText("Counter: " + ++counter); + q.push(q2); + try { + Thread.currentThread().sleep(500); + } catch (InterruptedException e) { + return; + } + } + }).start(); + } +} diff --git a/test/jdk/java/awt/LightweightComponent/MultipleAddNotifyTest/MultipleAddNotifyTest.java b/test/jdk/java/awt/LightweightComponent/MultipleAddNotifyTest/MultipleAddNotifyTest.java new file mode 100644 index 00000000000..fbbc2ae6185 --- /dev/null +++ b/test/jdk/java/awt/LightweightComponent/MultipleAddNotifyTest/MultipleAddNotifyTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 4058400 + * @summary Tests that calling addNotify on a lightweight component more than + * once does not break event dispatching for that component. + * @key headful + * @run main MultipleAddNotifyTest + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class MultipleAddNotifyTest { + static volatile boolean passFlag; + static volatile int posX; + static volatile int posY; + static Frame f; + static LightComponent l; + + public static void main(String[] args) throws Exception { + Robot r; + try { + r = new Robot(); + r.setAutoWaitForIdle(true); + passFlag = false; + + EventQueue.invokeAndWait(() -> { + f = new Frame("Multiple addNotify Test"); + l = new LightComponent(); + f.setLayout(new FlowLayout()); + l.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + System.out.println("Mouse Clicked"); + passFlag = true; + } + }); + f.add(l); + f.addNotify(); + f.addNotify(); + + if (!l.isVisible()) { + throw new RuntimeException("Test failed. LW Component " + + "not visible."); + } + f.setSize(200, 200); + f.setLocationRelativeTo(null); + f.setVisible(true); + }); + r.waitForIdle(); + r.delay(1000); + + EventQueue.invokeAndWait(() -> { + posX = f.getX() + l.getWidth() + (l.getWidth() / 2); + posY = f.getY() + l.getHeight(); + }); + + r.mouseMove(posX, posY); + r.delay(500); + + r.mousePress(InputEvent.BUTTON1_DOWN_MASK); + r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + r.delay(500); + + if (!passFlag) { + throw new RuntimeException("Test failed. MouseClicked event " + + "not working properly."); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } +} + +class LightComponent extends Component { + public void paint(Graphics g) { + setSize(100, 100); + Dimension d = getSize(); + g.setColor(Color.red); + g.fillRect(0, 0, d.width, d.height); + } +} diff --git a/test/jdk/java/awt/LightweightComponent/PopupTest/PopupTest.java b/test/jdk/java/awt/LightweightComponent/PopupTest/PopupTest.java new file mode 100644 index 00000000000..beae4b4d904 --- /dev/null +++ b/test/jdk/java/awt/LightweightComponent/PopupTest/PopupTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 4476083 + * @summary Disabled components do not receive MouseEvent in Popups + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PopupTest + */ + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Frame; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; + +public class PopupTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + PopupMenus should disappear when a disabled component is + clicked. + + Step 1. Pop down the popup menu by clicking on it. + Step 2. Click on the disabled component to make the menu + disappear. + + If the menu disappears when the disabled component is clicked, + the test passes, otherwise, the test fails. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(PopupTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("Disabled Component in Popup Test"); + f.setLayout(new BorderLayout()); + + JButton b = new JButton("step 1: press me to display menu"); + b.addActionListener(e -> { + JPopupMenu m = new JPopupMenu(); + m.add(new JMenuItem("item 1")); + m.add(new JMenuItem("item 2")); + m.add(new JMenuItem("item 3")); + m.add(new JMenuItem("item 4")); + m.add(new JMenuItem("item 5")); + m.add(new JMenuItem("item 6")); + m.show((Component) e.getSource(), 0, 10); + }); + + JLabel disabled = new JLabel("step 2: click me. the menu should be " + + "dismissed"); + disabled.setEnabled(false); + + JLabel enabled = new JLabel("step 3: there is no step 3"); + + f.add(BorderLayout.NORTH, b); + f.add(BorderLayout.CENTER, disabled); + f.add(BorderLayout.SOUTH, enabled); + f.setSize(300, 200); + return f; + } +} diff --git a/test/jdk/java/awt/Panel/PanelRepaint/PanelRepaint.java b/test/jdk/java/awt/Panel/PanelRepaint/PanelRepaint.java new file mode 100644 index 00000000000..da8de3947be --- /dev/null +++ b/test/jdk/java/awt/Panel/PanelRepaint/PanelRepaint.java @@ -0,0 +1,455 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 4148078 + * @summary Repainting problems in scrolled panel + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PanelRepaint + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Scrollbar; +import java.awt.TextField; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +public class PanelRepaint extends Panel implements FocusListener { + static ScrollPanel sPanel; + static Panel panel; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Using scrollbars or tab keys to scroll the panel and + the panel is messy sometimes, e.g. one row bumps into + another. If all components painted correctly, the test passes. + Otherwise, the test fails. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(PanelRepaint::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("Panel Repaint Test"); + f.setLayout(new FlowLayout()); + f.setSize(620, 288); + PanelRepaint pr = new PanelRepaint(); + + panel = new Panel(); + panel.setLayout(null); + panel.setSize(500, 500); + sPanel = new ScrollPanel(panel); + + Button btn = new Button("Open"); + pr.addComp(btn); + btn.setBounds(400, 10, 60, 20); + btn.setActionCommand("OPEN"); + + Button btn1 = new Button("Close"); + pr.addComp(btn1); + btn1.setBounds(400, 50, 60, 20); + btn1.setActionCommand("CLOSE"); + + TextField t1 = new TextField("1"); + pr.addComp(t1); + t1.setBounds(10, 10, 100, 20); + TextField t2 = new TextField("2"); + pr.addComp(t2); + t2.setBounds(10, 50, 100, 20); + TextField t3 = new TextField("3"); + pr.addComp(t3); + t3.setBounds(10, 90, 100, 20); + TextField t4 = new TextField("4"); + pr.addComp(t4); + t4.setBounds(10, 130, 100, 20); + TextField t5 = new TextField("5"); + pr.addComp(t5); + t5.setBounds(10, 170, 100, 20); + TextField t6 = new TextField("6"); + pr.addComp(t6); + t6.setBounds(10, 210, 100, 20); + TextField t7 = new TextField("7"); + pr.addComp(t7); + t7.setBounds(10, 250, 100, 20); + TextField t8 = new TextField("8"); + pr.addComp(t8); + t8.setBounds(10, 290, 100, 20); + TextField t9 = new TextField("9"); + pr.addComp(t9); + t9.setBounds(10, 330, 100, 20); + + TextField t11 = new TextField("1"); + pr.addComp(t11); + t11.setBounds(120, 10, 100, 20); + TextField t12 = new TextField("2"); + pr.addComp(t12); + t12.setBounds(120, 50, 100, 20); + TextField t13 = new TextField("3"); + pr.addComp(t13); + t13.setBounds(120, 90, 100, 20); + TextField t14 = new TextField("4"); + pr.addComp(t14); + t14.setBounds(120, 130, 100, 20); + TextField t15 = new TextField("5"); + pr.addComp(t15); + t15.setBounds(120, 170, 100, 20); + TextField t16 = new TextField("6"); + pr.addComp(t16); + t16.setBounds(120, 210, 100, 20); + TextField t17 = new TextField("7"); + pr.addComp(t17); + t17.setBounds(120, 250, 100, 20); + TextField t18 = new TextField("8"); + pr.addComp(t18); + t18.setBounds(120, 290, 100, 20); + TextField t19 = new TextField("9"); + pr.addComp(t19); + t19.setBounds(120, 330, 100, 20); + + + TextField t21 = new TextField("1"); + pr.addComp(t21); + t21.setBounds(240, 10, 100, 20); + TextField t22 = new TextField("2"); + pr.addComp(t22); + t22.setBounds(240, 50, 100, 20); + TextField t23 = new TextField("3"); + pr.addComp(t23); + t23.setBounds(240, 90, 100, 20); + TextField t24 = new TextField("4"); + pr.addComp(t24); + t24.setBounds(240, 130, 100, 20); + TextField t25 = new TextField("5"); + pr.addComp(t25); + t25.setBounds(240, 170, 100, 20); + TextField t26 = new TextField("6"); + pr.addComp(t26); + t26.setBounds(240, 210, 100, 20); + TextField t27 = new TextField("7"); + pr.addComp(t27); + t27.setBounds(240, 250, 100, 20); + TextField t28 = new TextField("8"); + pr.addComp(t28); + t28.setBounds(240, 290, 100, 20); + TextField t29 = new TextField("9"); + pr.addComp(t29); + t29.setBounds(240, 330, 100, 20); + + pr.add(sPanel); + f.add(pr); + sPanel.setBounds(100, 100, 500, 250); + sPanel.doLayout(); + return f; + } + + public void addComp(Component c) { + panel.add(c); + c.addFocusListener(this); + } + + public void focusGained(FocusEvent e) { + sPanel.showComponent(e.getComponent()); + } + + public void focusLost(FocusEvent e) { + } +} + +class ScrollPanel extends Panel implements AdjustmentListener { + /** + * Constructor + */ + public ScrollPanel(Component c) { + setLayout(null); + setBackground(Color.lightGray); + add(hScroll = new Scrollbar(Scrollbar.HORIZONTAL)); + add(vScroll = new Scrollbar(Scrollbar.VERTICAL)); + add(square = new Panel()); + square.setBackground(Color.lightGray); + add(c); + } + + /** + * Scroll up/down/left/right to show the component specified + * + * @param comp is the component to be shown + */ + public void showComponent(Component comp) { + Component view = getComponent(3); + Rectangle viewRect = view.getBounds(); + Rectangle scrollRect = getBounds(); + Rectangle rect = comp.getBounds(); + while (comp != null) { + Component parent = comp.getParent(); + if (parent == null || parent == view) { + break; + } + Point p = parent.getLocation(); + rect.x += p.x; + rect.y += p.y; + comp = parent; + } + + int i = viewRect.y + rect.y; + int j = (viewRect.y + rect.y + rect.height + ScrollPanel.H_HEIGHT) + - (scrollRect.height); + + if (i < 0) { + vertUpdate(i); + } else if (j > 0) { + vertUpdate(j); + } + + i = viewRect.x + rect.x; + j = (viewRect.x + rect.x + rect.width + (V_WIDTH * 2)) - (scrollRect.width); + + if (i < 0) { + horzUpdate(i); + } else if (j > 0) { + horzUpdate(j); + } + } + + /** + * Returns the panel component of ScrollPanel + * + * @return the panel component of ScrollPanel + */ + public Component getScrolled() { + return getComponent(3); + } + + /** + * updates the scroll panel vertically with value i passed + * + * @param i the value to be updated with + */ + public void vertUpdate(int i) { + update(true, vScroll.getValue() + i); + } + + /** + * updates the scroll panel horizontally with value i passed + * + * @param i the value to be updated with + */ + public void horzUpdate(int i) { + update(false, hScroll.getValue() + i); + } + + /** + * updates the scroll panel vertically if bVert is true else horizontally + * + * @param n is the value + */ + public void update(boolean bVert, int n) { + if (n < 0) n = 0; + if (bVert) { + if (n > max.height) { + n = max.height; + } + if (offset.y != n) { + offset.y = n; + vScroll.setValue(n); + } + } else { + if (n > max.width) { + n = max.width; + } + if (offset.x != n) { + offset.x = n; + hScroll.setValue(n); + } + } + getScrolled().setLocation(-offset.x, -offset.y); + } + + /** + * Implementation of AdjustmentListener + */ + public void adjustmentValueChanged(AdjustmentEvent e) { + boolean bVert = e.getSource() == vScroll; + update(bVert, e.getValue()); + } + + /** + * Reimplementation of Component Methods + */ + public void addNotify() { + super.addNotify(); + vScroll.addAdjustmentListener(this); + hScroll.addAdjustmentListener(this); + } + + public void removeNotify() { + super.removeNotify(); + vScroll.removeAdjustmentListener(this); + hScroll.removeAdjustmentListener(this); + } + + public void setBounds(int x, int y, int w, int h) { + super.setBounds(x, y, w, h); + doLayout(); + } + + public void doLayout() { + Component c = getScrolled(); + Dimension d = c.getSize(); + if (d.width == 0 || d.height == 0) { + d = c.getPreferredSize(); + } + vert = 0; + horz = 0; + Dimension m = getSize(); + if (d.height > m.height || isScroll(true, m.height - horz, 0, d.height)) { + vert = V_WIDTH; + } + if (d.width + vert > m.width || isScroll(false, m.width - vert, 0, d.width)) { + horz = H_HEIGHT; + } + if (d.height + horz > m.height || isScroll(true, m.height - horz, 0, d.height)) { + vert = V_WIDTH; + } + if (d.width + vert > m.width || isScroll(false, m.width - vert, 0, d.width)) { + horz = H_HEIGHT; + } + if (horz != 0) { + if (m.width <= 0) { + m.width = 1; + } + hScroll.setBounds(0, m.height - H_HEIGHT, m.width - vert, H_HEIGHT); + hScroll.setValues(offset.x, m.width - vert, 0, d.width); + int i = d.width / 10; + if (i < 2) { + i = 2; + } + hScroll.setBlockIncrement(i); + i = d.width / 50; + if (i < 1) { + i = 1; + } + hScroll.setUnitIncrement(i); + max.width = d.width; + hScroll.setVisible(true); + } else { + offset.x = 0; + } + if (vert != 0) { + if (m.height <= 0) { + m.height = 1; + } + vScroll.setBounds(m.width - V_WIDTH, 0, V_WIDTH, m.height - horz); + vScroll.setValues(offset.y, m.height - horz, 0, d.height); + int i = d.height / 10; + if (i < 2) i = 2; + vScroll.setBlockIncrement(i); + i = d.height / 50; + if (i < 1) i = 1; + vScroll.setUnitIncrement(i); + max.height = d.height; + vScroll.setVisible(true); + } else { + offset.y = 0; + } + if (horz != 0 && vert != 0) { + square.setBounds(m.width - V_WIDTH, m.height - H_HEIGHT, V_WIDTH, H_HEIGHT); + square.setVisible(true); + } else { + square.setVisible(false); + } + c.setBounds(-offset.x, -offset.y, d.width, d.height); + c.repaint(); + updateScroll(true, offset.y); + updateScroll(false, offset.x); + } + + public Dimension getPreferredSize() { + return getScrolled().getPreferredSize(); + } + + public Dimension getMinimumSize() { + return getScrolled().getMinimumSize(); + } + + boolean isScroll(boolean bVert, int visible, int min, int max) { + int tot = max - min; + int net = tot - visible; + if (net <= 0) { + return false; + } + return true; + } + + void updateScroll(boolean bVert, int n) { + Component c = getScrolled(); + Dimension d = c.getSize(); + Dimension m = getSize(); + m.width -= vert; + m.height -= horz; + if (bVert) { + if (n >= 0 && d.height > m.height) { + if (n + m.height > d.height) + n = d.height - m.height; + } else + n = 0; + update(true, n); + } else { + if (n >= 0 && d.width > m.width) { + if (n + m.width > d.width) + n = d.width - m.width; + } else + n = 0; + update(false, n); + } + } + + static Scrollbar hScroll; + static Scrollbar vScroll; + static int vert = 0; + static int horz = 0; + + static Point offset = new Point(); + static Dimension max = new Dimension(); + // ScrollTimer timer; + static Component square; + final static int V_WIDTH = 17; + final static int H_HEIGHT = 17; +} From 06f34d7ed2ac77e30b2a891b7a0549055ed40da3 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Thu, 10 Oct 2024 18:45:10 +0000 Subject: [PATCH 14/95] 8339651: ShenandoahPacer::setup_for_mark, ShenandoahPacer::setup_for_updaterefs and ShenandoahPacer::setup_for_evac runtime error: division by zero Reviewed-by: shade, mli --- src/hotspot/share/gc/shenandoah/shenandoahPacer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPacer.cpp b/src/hotspot/share/gc/shenandoah/shenandoahPacer.cpp index e67d3d197d4..8d10b7cbfcf 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPacer.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPacer.cpp @@ -65,6 +65,7 @@ void ShenandoahPacer::setup_for_mark() { size_t non_taxable = free * ShenandoahPacingCycleSlack / 100; size_t taxable = free - non_taxable; + taxable = MAX2(1, taxable); double tax = 1.0 * live / taxable; // base tax for available free space tax *= 1; // mark can succeed with immediate garbage, claim all available space @@ -88,6 +89,7 @@ void ShenandoahPacer::setup_for_evac() { size_t non_taxable = free * ShenandoahPacingCycleSlack / 100; size_t taxable = free - non_taxable; + taxable = MAX2(1, taxable); double tax = 1.0 * used / taxable; // base tax for available free space tax *= 2; // evac is followed by update-refs, claim 1/2 of remaining free @@ -112,6 +114,7 @@ void ShenandoahPacer::setup_for_updaterefs() { size_t non_taxable = free * ShenandoahPacingCycleSlack / 100; size_t taxable = free - non_taxable; + taxable = MAX2(1, taxable); double tax = 1.0 * used / taxable; // base tax for available free space tax *= 1; // update-refs is the last phase, claim the remaining free From cd4981c29245b4ddd37b49aef1a051e29a1001f9 Mon Sep 17 00:00:00 2001 From: Damon Nguyen Date: Thu, 10 Oct 2024 21:42:23 +0000 Subject: [PATCH 15/95] 8341257: Open source few DND tests - Set1 Reviewed-by: honkar, prr --- test/jdk/ProblemList.txt | 2 + .../awt/dnd/DnDClipboardDeadlockTest.java | 441 ++++++++++++++++++ test/jdk/java/awt/dnd/DnDCursorCrashTest.java | 231 +++++++++ .../awt/dnd/DnDRemoveFocusOwnerCrashTest.java | 234 ++++++++++ test/jdk/java/awt/dnd/DnDToWordpadTest.java | 237 ++++++++++ test/jdk/java/awt/dnd/NonAsciiFilenames.java | 158 +++++++ 6 files changed, 1303 insertions(+) create mode 100644 test/jdk/java/awt/dnd/DnDClipboardDeadlockTest.java create mode 100644 test/jdk/java/awt/dnd/DnDCursorCrashTest.java create mode 100644 test/jdk/java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java create mode 100644 test/jdk/java/awt/dnd/DnDToWordpadTest.java create mode 100644 test/jdk/java/awt/dnd/NonAsciiFilenames.java diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index db48c24cd7e..db0794f8f2d 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -201,6 +201,8 @@ java/awt/event/KeyEvent/ExtendedKeyCode/ExtendedKeyCodeTest.java 8169476 windows java/awt/event/KeyEvent/KeyChar/KeyCharTest.java 8169474,8224055 macosx-all,windows-all java/awt/event/KeyEvent/KeyTyped/CtrlASCII.java 8298910 linux-all +java/awt/dnd/DnDCursorCrashTest.java 8242805 macosx-all +java/awt/dnd/DnDClipboardDeadlockTest.java 8079553 linux-all java/awt/dnd/URIListToFileListBetweenJVMsTest/URIListToFileListBetweenJVMsTest.java 8194947 generic-all java/awt/Frame/FramesGC/FramesGC.java 8079069 macosx-all java/awt/TrayIcon/ActionCommand/ActionCommand.java 8150540 windows-all diff --git a/test/jdk/java/awt/dnd/DnDClipboardDeadlockTest.java b/test/jdk/java/awt/dnd/DnDClipboardDeadlockTest.java new file mode 100644 index 00000000000..ab2bff8ecc5 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDClipboardDeadlockTest.java @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 4388802 + * @summary tests that clipboard operations during drag-and-drop don't deadlock + * @key headful + * @run main DnDClipboardDeadlockTest + */ + +import java.awt.AWTEvent; +import java.awt.AWTException; +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.GridLayout; +import java.awt.List; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.AWTEventListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; + +public class DnDClipboardDeadlockTest { + + public static final int CODE_NOT_RETURNED = -1; + public static final int CODE_OK = 0; + public static final int CODE_FAILURE = 1; + + private int returnCode = CODE_NOT_RETURNED; + + final Frame frame = new Frame(); + Robot robot = null; + Panel panel = null; + + public static void main(String[] args) throws Exception { + DnDClipboardDeadlockTest test = new DnDClipboardDeadlockTest(); + if (args.length == 4) { + test.run(args); + } else { + test.start(); + } + } + + public void run(String[] args) throws InterruptedException, AWTException { + try { + if (args.length != 4) { + throw new RuntimeException("Incorrect command line arguments."); + } + + int x = Integer.parseInt(args[0]); + int y = Integer.parseInt(args[1]); + int w = Integer.parseInt(args[2]); + int h = Integer.parseInt(args[3]); + + Transferable t = new StringSelection("TEXT"); + panel = new DragSourcePanel(t); + + frame.setTitle("DragSource frame"); + frame.setLocation(300, 200); + frame.add(panel); + frame.pack(); + frame.setVisible(true); + + Util.waitForInit(); + + Point sourcePoint = panel.getLocationOnScreen(); + Dimension d = panel.getSize(); + sourcePoint.translate(d.width / 2, d.height / 2); + + Point targetPoint = new Point(x + w / 2, y + h / 2); + + robot = new Robot(); + + if (!Util.pointInComponent(robot, sourcePoint, panel)) { + throw new RuntimeException("WARNING: Cannot locate source panel"); + } + + robot.mouseMove(sourcePoint.x, sourcePoint.y); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (; !sourcePoint.equals(targetPoint); + sourcePoint.translate(sign(targetPoint.x - sourcePoint.x), + sign(targetPoint.y - sourcePoint.y))) { + robot.mouseMove(sourcePoint.x, sourcePoint.y); + Thread.sleep(10); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + if (frame != null) { + frame.dispose(); + } + + } catch (Throwable e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } // run() + + public static int sign(int n) { + return n < 0 ? -1 : n == 0 ? 0 : 1; + } + + public void start() { + panel = new DropTargetPanel(); + + frame.setTitle("DropTarget frame"); + frame.setLocation(10, 200); + frame.add(panel); + + frame.pack(); + frame.setVisible(true); + + try { + Util.waitForInit(); + + Point p = panel.getLocationOnScreen(); + Dimension d = panel.getSize(); + + try { + Robot robot = new Robot(); + Point center = new Point(p); + center.translate(d.width / 2, d.height / 2); + if (!Util.pointInComponent(robot, center, panel)) { + System.err.println("WARNING: Cannot locate target panel"); + return; + } + } catch (AWTException awte) { + awte.printStackTrace(); + return; + } + + String javaPath = System.getProperty("java.home", ""); + String command = javaPath + File.separator + "bin" + + File.separator + "java -cp " + + System.getProperty("java.class.path", ".") + + " DnDClipboardDeadlockTest " + + p.x + " " + p.y + " " + d.width + " " + d.height; + + Process process = Runtime.getRuntime().exec(command); + returnCode = process.waitFor(); + + InputStream errorStream = process.getErrorStream(); + int count = errorStream.available(); + if (count > 0) { + byte[] b = new byte[count]; + errorStream.read(b); + System.err.println("========= Child VM System.err ========"); + System.err.print(new String(b)); + System.err.println("======================================"); + } + + } catch (Throwable e) { + e.printStackTrace(); + } + switch (returnCode) { + case CODE_NOT_RETURNED: + System.err.println("Child VM: failed to start"); + break; + case CODE_OK: + System.err.println("Child VM: normal termination"); + break; + case CODE_FAILURE: + System.err.println("Child VM: abnormal termination"); + break; + } + if (returnCode != CODE_OK) { + throw new RuntimeException("The test failed."); + } + if (frame != null) { + frame.dispose(); + } + } // start() +} // class DnDClipboardDeadlockTest + +class Util implements AWTEventListener { + private static final Toolkit tk = Toolkit.getDefaultToolkit(); + private static final Object SYNC_LOCK = new Object(); + private Component clickedComponent = null; + private static final int PAINT_TIMEOUT = 10000; + private static final int MOUSE_RELEASE_TIMEOUT = 10000; + private static final Util util = new Util(); + + static { + tk.addAWTEventListener(util, 0xFFFFFFFF); + } + + private void reset() { + clickedComponent = null; + } + + public void eventDispatched(AWTEvent e) { + if (e.getID() == MouseEvent.MOUSE_RELEASED) { + clickedComponent = (Component) e.getSource(); + synchronized (SYNC_LOCK) { + SYNC_LOCK.notifyAll(); + } + } + } + + public static boolean pointInComponent(Robot robot, Point p, Component comp) + throws InterruptedException { + return util.isPointInComponent(robot, p, comp); + } + + private boolean isPointInComponent(Robot robot, Point p, Component comp) + throws InterruptedException { + tk.sync(); + robot.waitForIdle(); + reset(); + robot.mouseMove(p.x, p.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + synchronized (SYNC_LOCK) { + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + SYNC_LOCK.wait(MOUSE_RELEASE_TIMEOUT); + } + + Component c = clickedComponent; + + while (c != null && c != comp) { + c = c.getParent(); + } + + return c == comp; + } + + public static void waitForInit() throws InterruptedException { + final Frame f = new Frame() { + public void paint(Graphics g) { + dispose(); + synchronized (SYNC_LOCK) { + SYNC_LOCK.notifyAll(); + } + } + }; + f.setBounds(600, 400, 200, 200); + synchronized (SYNC_LOCK) { + f.setVisible(true); + SYNC_LOCK.wait(PAINT_TIMEOUT); + } + tk.sync(); + } +} + +class DragSourceButton extends Button implements Serializable, + DragGestureListener, + DragSourceListener { + static final Clipboard systemClipboard = + Toolkit.getDefaultToolkit().getSystemClipboard(); + final Transferable transferable; + + public DragSourceButton(Transferable t) { + super("DragSourceButton"); + + this.transferable = t; + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(null, transferable, this); + } + + public void dragEnter(DragSourceDragEvent dsde) { + } + + public void dragExit(DragSourceEvent dse) { + } + + public void dragOver(DragSourceDragEvent dsde) { + try { + Transferable t = systemClipboard.getContents(null); + if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) { + String str = (String) t.getTransferData(DataFlavor.stringFlavor); + } + systemClipboard.setContents(new StringSelection("SOURCE"), null); + } catch (IOException ioe) { + ioe.printStackTrace(); + if (!ioe.getMessage().equals("Owner failed to convert data")) { + throw new RuntimeException("Owner failed to convert data"); + } + } catch (IllegalStateException e) { + // IllegalStateExceptions do not indicate a bug in this case. + // They result from concurrent modification of system clipboard + // contents by the parent and child processes. + // These exceptions are numerous, so we avoid dumping their + // backtraces to prevent blocking child process io, which + // causes test failure on timeout. + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + System.exit(DnDClipboardDeadlockTest.CODE_OK); + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + } +} + +class DragSourcePanel extends Panel { + + final Dimension preferredDimension = new Dimension(200, 200); + + public DragSourcePanel(Transferable t) { + setLayout(new GridLayout(1, 1)); + add(new DragSourceButton(t)); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } +} + +class DropTargetPanel extends Panel implements DropTargetListener { + + static final Clipboard systemClipboard = + Toolkit.getDefaultToolkit().getSystemClipboard(); + final Dimension preferredDimension = new Dimension(200, 200); + + public DropTargetPanel() { + setBackground(Color.green); + setDropTarget(new DropTarget(this, this)); + setLayout(new GridLayout(1, 1)); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } + + public void dragEnter(DropTargetDragEvent dtde) { + dtde.acceptDrag(DnDConstants.ACTION_COPY); + } + + public void dragExit(DropTargetEvent dte) { + } + + public void dragOver(DropTargetDragEvent dtde) { + dtde.acceptDrag(DnDConstants.ACTION_COPY); + try { + Transferable t = systemClipboard.getContents(null); + if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) { + String str = (String) t.getTransferData(DataFlavor.stringFlavor); + } + systemClipboard.setContents(new StringSelection("TARGET"), null); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void drop(DropTargetDropEvent dtde) { + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + return; + } + + removeAll(); + final List list = new List(); + add(list); + + Transferable t = dtde.getTransferable(); + DataFlavor[] dfs = t.getTransferDataFlavors(); + + for (int i = 0; i < dfs.length; i++) { + + DataFlavor flavor = dfs[i]; + String str = null; + + if (DataFlavor.stringFlavor.equals(flavor)) { + try { + str = (String) t.getTransferData(flavor); + } catch (Exception e) { + e.printStackTrace(); + } + } + + list.add(str + ":" + flavor.getMimeType()); + } + + dtc.dropComplete(true); + validate(); + } + + public void dropActionChanged(DropTargetDragEvent dtde) { + } +} diff --git a/test/jdk/java/awt/dnd/DnDCursorCrashTest.java b/test/jdk/java/awt/dnd/DnDCursorCrashTest.java new file mode 100644 index 00000000000..7b1f4483ef9 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDCursorCrashTest.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 4343300 + * @summary tests that drag attempt doesn't cause crash when + * custom cursor is used + * @key headful + * @run main DnDCursorCrashTest + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.Robot; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +public class DnDCursorCrashTest { + static final Frame frame = new Frame(); + static final DragSourcePanel dragSourcePanel = new DragSourcePanel(); + static final DropTargetPanel dropTargetPanel = new DropTargetPanel(); + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(() -> { + frame.setTitle("DnD Cursor Test Frame"); + frame.setLocation(200, 200); + frame.setLayout(new GridLayout(2, 1)); + frame.add(dragSourcePanel); + frame.add(dropTargetPanel); + frame.pack(); + frame.setVisible(true); + }); + + Robot robot = new Robot(); + robot.delay(1000); + robot.mouseMove(250, 250); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (int y = 250; y < 350; y += 5) { + robot.mouseMove(250, y); + robot.delay(100); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + } finally { + if (frame != null) { + EventQueue.invokeAndWait(() -> frame.dispose()); + } + } + } +} + +class DragSourceButton extends Button implements Serializable, + Transferable, + DragGestureListener, + DragSourceListener { + private final DataFlavor dataflavor = + new DataFlavor(Button.class, "DragSourceButton"); + + public DragSourceButton() { + this("DragSourceButton"); + } + + public DragSourceButton(String str) { + super(str); + + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(new Cursor(Cursor.HAND_CURSOR), this, this); + } + + public void dragEnter(DragSourceDragEvent dsde) {} + + public void dragExit(DragSourceEvent dse) {} + + public void dragOver(DragSourceDragEvent dsde) {} + + public void dragDropEnd(DragSourceDropEvent dsde) {} + + public void dropActionChanged(DragSourceDragEvent dsde) {} + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException { + + if (!isDataFlavorSupported(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + + Object retObj; + + ByteArrayOutputStream baoStream = new ByteArrayOutputStream(); + ObjectOutputStream ooStream = new ObjectOutputStream(baoStream); + ooStream.writeObject(this); + + ByteArrayInputStream baiStream = new ByteArrayInputStream(baoStream.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(baiStream); + try { + retObj = ois.readObject(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + throw new RuntimeException(e.toString()); + } + + return retObj; + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { dataflavor }; + } + + public boolean isDataFlavorSupported(DataFlavor dflavor) { + return dataflavor.equals(dflavor); + } +} + +class DragSourcePanel extends Panel { + + final Dimension preferredDimension = new Dimension(200, 100); + + public DragSourcePanel() { + setLayout(new GridLayout(1, 1)); + add(new DragSourceButton()); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } +} + +class DropTargetPanel extends Panel implements DropTargetListener { + + final Dimension preferredDimension = new Dimension(200, 100); + + public DropTargetPanel() { + setDropTarget(new DropTarget(this, this)); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } + + public void dragEnter(DropTargetDragEvent dtde) {} + + public void dragExit(DropTargetEvent dte) {} + + public void dragOver(DropTargetDragEvent dtde) {} + + public void dropActionChanged(DropTargetDragEvent dtde) {} + + public void drop(DropTargetDropEvent dtde) { + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + Component comp = null; + + if (dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + + try { + comp = (Component)transfer.getTransferData(dfs[0]); + } catch (Throwable e) { + e.printStackTrace(); + dtc.dropComplete(false); + } + } + dtc.dropComplete(true); + + add(comp); + } +} diff --git a/test/jdk/java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java b/test/jdk/java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java new file mode 100644 index 00000000000..27cf54e1845 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 4357905 + * @summary Tests that removal of the focus owner component during + * drop processing doesn't cause crash + * @key headful + * @run main DnDRemoveFocusOwnerCrashTest + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.Serializable; + +public class DnDRemoveFocusOwnerCrashTest { + public static final int FRAME_ACTIVATION_TIMEOUT = 1000; + public static Frame frame; + public static Robot robot; + public static DragSourceButton dragSourceButton; + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + EventQueue.invokeAndWait(() -> { + frame = new Frame(); + dragSourceButton = new DragSourceButton(); + DropTargetPanel dropTargetPanel = + new DropTargetPanel(dragSourceButton); + frame.add(new Button("Test")); + frame.setTitle("Remove Focus Owner Test Frame"); + frame.setLocation(200, 200); + frame.add(dropTargetPanel); + frame.pack(); + frame.setVisible(true); + + try { + robot.delay(FRAME_ACTIVATION_TIMEOUT); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("The test failed."); + } + + Point p = dragSourceButton.getLocationOnScreen(); + p.translate(10, 10); + + try { + Robot robot = new Robot(); + robot.mouseMove(p.x, p.y); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (int dy = 0; dy < 50; dy++) { + robot.mouseMove(p.x, p.y + dy); + robot.delay(10); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("The test failed."); + } + }); + } finally { + if (frame != null) { + EventQueue.invokeAndWait(() -> frame.dispose()); + } + } + } + + static class DragSourceButton extends Button implements Serializable, + Transferable, + DragGestureListener, + DragSourceListener { + + private static DataFlavor dataflavor; + + static { + try { + dataflavor = + new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType); + dataflavor.setHumanPresentableName("Local Object Flavor"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + throw new ExceptionInInitializerError(); + } + } + + public DragSourceButton() { + this("DragSourceButton"); + } + + public DragSourceButton(String str) { + super(str); + + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(null, this, this); + } + + public void dragEnter(DragSourceDragEvent dsde) { + } + + public void dragExit(DragSourceEvent dse) { + } + + public void dragOver(DragSourceDragEvent dsde) { + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + } + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException { + + if (!isDataFlavorSupported(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + + return this; + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[]{dataflavor}; + } + + public boolean isDataFlavorSupported(DataFlavor dflavor) { + return dataflavor.equals(dflavor); + } + } + + static class DropTargetPanel extends Panel implements DropTargetListener { + + public DropTargetPanel(DragSourceButton button) { + setLayout(new FlowLayout(FlowLayout.CENTER, 50, 50)); + add(button); + setDropTarget(new DropTarget(this, this)); + } + + public void dragEnter(DropTargetDragEvent dtde) { + } + + public void dragExit(DropTargetEvent dte) { + } + + public void dragOver(DropTargetDragEvent dtde) { + } + + public void dropActionChanged(DropTargetDragEvent dtde) { + } + + public void drop(DropTargetDropEvent dtde) { + removeAll(); + + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + Component comp = null; + + if (dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + + try { + comp = (Component) transfer.getTransferData(dfs[0]); + } catch (Throwable e) { + e.printStackTrace(); + dtc.dropComplete(false); + } + } + dtc.dropComplete(true); + + add(comp); + validate(); + } + } +} diff --git a/test/jdk/java/awt/dnd/DnDToWordpadTest.java b/test/jdk/java/awt/dnd/DnDToWordpadTest.java new file mode 100644 index 00000000000..181a6ee873a --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDToWordpadTest.java @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 6362095 + * @summary Tests basic DnD functionality to a wordpad + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DnDToWordpadTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Panel; +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.InvalidDnDOperationException; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; + +import javax.imageio.ImageIO; + +import static java.awt.image.BufferedImage.TYPE_INT_ARGB; + +public class DnDToWordpadTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + The test window contains a yellow button. Click on the button + to copy image into the clipboard or drag the image. + Paste or drop the image over Wordpad (when the mouse + enters the Wordpad during the drag, the application + should change the cursor to indicate that a copy operation is + about to happen; release the mouse button). + An image of a red rectangle should appear inside the document. + You should be able to repeat this operation multiple times. + Please, click "Pass" if above conditions are true, + otherwise click "Fail". + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(DnDToWordpadTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("DnD To WordPad Test"); + Panel mainPanel; + Component dragSource; + + mainPanel = new Panel(); + mainPanel.setLayout(null); + + mainPanel.setBackground(Color.black); + try { + dragSource = new DnDSource("Drag ME!"); + mainPanel.add(dragSource); + f.add(mainPanel); + } catch (IOException e) { + e.printStackTrace(); + } + + f.setSize(200, 200); + return f; + } +} + +class DnDSource extends Button implements Transferable, + DragGestureListener, + DragSourceListener { + private DataFlavor m_df; + private transient int m_dropAction; + private Image m_img; + + DnDSource(String label) throws IOException { + super(label); + + setBackground(Color.yellow); + setForeground(Color.blue); + setSize(200, 120); + + m_df = DataFlavor.imageFlavor; + + DragSource dragSource = new DragSource(); + dragSource.createDefaultDragGestureRecognizer( + this, + DnDConstants.ACTION_COPY_OR_MOVE, + this + ); + dragSource.addDragSourceListener(this); + + // Create test gif image to drag + Path p = Path.of(System.getProperty("test.classes", ".")); + BufferedImage bImg = new BufferedImage(79, 109, TYPE_INT_ARGB); + Graphics2D cg = bImg.createGraphics(); + cg.setColor(Color.RED); + cg.fillRect(0, 0, 79, 109); + ImageIO.write(bImg, "png", new File(p + java.io.File.separator + + "DnDSource_Red.gif")); + + m_img = Toolkit.getDefaultToolkit() + .getImage(System.getProperty("test.classes", ".") + + java.io.File.separator + "DnDSource_Red.gif"); + + addActionListener( + ae -> Toolkit.getDefaultToolkit().getSystemClipboard().setContents( + (Transferable) DnDSource.this, + null + ) + ); + } + + public void paint(Graphics g) { + g.drawImage(m_img, 10, 10, null); + } + + /** + * a Drag gesture has been recognized + */ + + public void dragGestureRecognized(DragGestureEvent dge) { + System.err.println("starting Drag"); + try { + dge.startDrag(null, this, this); + } catch (InvalidDnDOperationException e) { + e.printStackTrace(); + } + } + + /** + * as the hotspot enters a platform dependent drop site + */ + + public void dragEnter(DragSourceDragEvent dsde) { + System.err.println("[Source] dragEnter"); + } + + /** + * as the hotspot moves over a platform dependent drop site + */ + + public void dragOver(DragSourceDragEvent dsde) { + System.err.println("[Source] dragOver"); + m_dropAction = dsde.getDropAction(); + System.out.println("m_dropAction = " + m_dropAction); + } + + /** + * as the operation changes + */ + + public void dragGestureChanged(DragSourceDragEvent dsde) { + System.err.println("[Source] dragGestureChanged"); + m_dropAction = dsde.getDropAction(); + System.out.println("m_dropAction = " + m_dropAction); + } + + /** + * as the hotspot exits a platform dependent drop site + */ + + public void dragExit(DragSourceEvent dsde) { + System.err.println("[Source] dragExit"); + } + + /** + * as the operation completes + */ + + public void dragDropEnd(DragSourceDropEvent dsde) { + System.err.println("[Source] dragDropEnd"); + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + System.err.println("[Source] dropActionChanged"); + m_dropAction = dsde.getDropAction(); + System.out.println("m_dropAction = " + m_dropAction); + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[]{m_df}; + } + + public boolean isDataFlavorSupported(DataFlavor sdf) { + System.err.println("[Source] isDataFlavorSupported" + m_df.equals(sdf)); + return m_df.equals(sdf); + } + + public Object getTransferData(DataFlavor tdf) throws UnsupportedFlavorException { + if (!m_df.equals(tdf)) { + throw new UnsupportedFlavorException(tdf); + } + System.err.println("[Source] Ok"); + return m_img; + } +} diff --git a/test/jdk/java/awt/dnd/NonAsciiFilenames.java b/test/jdk/java/awt/dnd/NonAsciiFilenames.java new file mode 100644 index 00000000000..b508350c05e --- /dev/null +++ b/test/jdk/java/awt/dnd/NonAsciiFilenames.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 4187490 + * @summary Verify that Non-ASCII file names can be dragged and dropped + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual NonAsciiFilenames + */ + +import java.awt.Color; +import java.awt.datatransfer.DataFlavor; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.io.File; +import java.util.AbstractList; + +import javax.swing.JFrame; +import javax.swing.JLabel; + +public class NonAsciiFilenames { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test must be run on an OS which does not use ISO 8859-1 + as its default encoding. + + Open a native file browsing application, such as Windows + Explorer. Try to find a file whose name uses non-ISO 8859-1 + characters. Create a file and name it such that it contains + non-ISO 8859-1 characters (For ex. é, à, ö, €, ¥). Drag + the file from the native application and drop it on the test + Frame. If the file name appears normally, then the test passes. + If boxes or question marks appear for characters, or if you see + the word "Error", then the test fails. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(NonAsciiFilenames::createUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createUI() { + JFrame frame = new JFrame(); + frame.setTitle("DropLabel test"); + frame.getContentPane().add(new DropLabel("Drop here")); + frame.setSize(300, 100); + return frame; + } +} + +class DropLabel extends JLabel implements DropTargetListener { + public DropLabel(String s) { + setText(s); + new DropTarget(this, DnDConstants.ACTION_COPY, this, true); + showDrop(false); + } + + private void showDrop(boolean b) { + setForeground(b ? Color.white : Color.black); + } + + /** + * Configure to desired flavor of dropped data. + */ + private DataFlavor getDesiredFlavor() { + return DataFlavor.javaFileListFlavor; + } + + /** + * Check to make sure that the contains the expected object types. + */ + private void checkDroppedData(Object data) { + System.out.println("Got data: " + data.getClass().getName()); + if (data instanceof AbstractList) { + AbstractList files = (AbstractList) data; + if (((File) files.get(0)).isFile()) + setText(((File) files.get(0)).toString()); + else + setText("Error: not valid file: " + + ((File) files.get(0)).toString()); + } else { + System.out.println("Error: wrong type of data dropped"); + } + } + + private boolean isDragOk(DropTargetDragEvent e) { + boolean canDrop = false; + try { + canDrop = e.isDataFlavorSupported(getDesiredFlavor()); + } catch (Exception ex) { + } + + if (canDrop) + e.acceptDrag(DnDConstants.ACTION_COPY); + else + e.rejectDrag(); + showDrop(canDrop); + return canDrop; + } + + public void dragEnter(DropTargetDragEvent e) { + isDragOk(e); + } + + + public void dragOver(DropTargetDragEvent e) { + isDragOk(e); + } + + public void dropActionChanged(DropTargetDragEvent e) { + isDragOk(e); + } + + public void dragExit(DropTargetEvent e) { + showDrop(false); + } + + public void drop(DropTargetDropEvent e) { + try { + e.acceptDrop(DnDConstants.ACTION_COPY); + checkDroppedData(e.getTransferable(). + getTransferData(getDesiredFlavor())); + } catch (Exception err) { + } + e.dropComplete(true); + showDrop(false); + } +} From 24eb360147a5ca548abc89eb6480f46b89e11d19 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Fri, 11 Oct 2024 00:29:14 +0000 Subject: [PATCH 16/95] 8341906: Optimize ClassFile writing BufBuffer Reviewed-by: liach --- .../impl/AbstractAttributeMapper.java | 100 +++++++++--------- .../impl/AbstractBoundLocalVariable.java | 9 +- .../impl/AbstractPseudoInstruction.java | 7 +- .../classfile/impl/AnnotationReader.java | 7 +- .../classfile/impl/BufWriterImpl.java | 36 ++++++- .../classfile/impl/DirectClassBuilder.java | 8 +- .../classfile/impl/DirectCodeBuilder.java | 9 +- .../classfile/impl/DirectFieldBuilder.java | 4 +- .../internal/classfile/impl/FieldImpl.java | 6 +- .../internal/classfile/impl/MethodImpl.java | 6 +- .../classfile/impl/StackMapDecoder.java | 20 ++-- .../classfile/impl/StackMapGenerator.java | 12 +-- .../jdk/internal/classfile/impl/Util.java | 6 +- 13 files changed, 119 insertions(+), 111 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java index 50c0bdc2a9f..b29b9f6f955 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java @@ -140,14 +140,13 @@ public CharacterRangeTableAttribute readAttribute(AttributedElement e, ClassRead } @Override - protected void writeBody(BufWriter buf, CharacterRangeTableAttribute attr) { + protected void writeBody(BufWriter bufWriter, CharacterRangeTableAttribute attr) { List ranges = attr.characterRangeTable(); + BufWriterImpl buf = (BufWriterImpl) bufWriter; buf.writeU2(ranges.size()); for (CharacterRangeInfo info : ranges) { - buf.writeU2(info.startPc()); - buf.writeU2(info.endPc()); - buf.writeInt(info.characterRangeStart()); - buf.writeInt(info.characterRangeEnd()); + buf.writeU2U2(info.startPc(), info.endPc()); + buf.writeIntInt(info.characterRangeStart(), info.characterRangeEnd()); buf.writeU2(info.flags()); } } @@ -238,9 +237,10 @@ public EnclosingMethodAttribute readAttribute(AttributedElement e, ClassReader c } @Override - protected void writeBody(BufWriter buf, EnclosingMethodAttribute attr) { - buf.writeIndex(attr.enclosingClass()); - buf.writeIndexOrZero(attr.enclosingMethod().orElse(null)); + protected void writeBody(BufWriter bufWriter, EnclosingMethodAttribute attr) { + BufWriterImpl buf = (BufWriterImpl) bufWriter; + buf.writeU2U2(buf.cpIndex(attr.enclosingClass()), + buf.cpIndexOrZero(attr.enclosingMethod().orElse(null))); } } @@ -275,13 +275,14 @@ public InnerClassesAttribute readAttribute(AttributedElement e, ClassReader cf, } @Override - protected void writeBody(BufWriter buf, InnerClassesAttribute attr) { + protected void writeBody(BufWriter bufWriter, InnerClassesAttribute attr) { List classes = attr.classes(); + BufWriterImpl buf = (BufWriterImpl) bufWriter; buf.writeU2(classes.size()); for (InnerClassInfo ic : classes) { - buf.writeIndex(ic.innerClass()); - buf.writeIndexOrZero(ic.outerClass().orElse(null)); - buf.writeIndexOrZero(ic.innerName().orElse(null)); + buf.writeU2U2U2(buf.cpIndex(ic.innerClass()), + buf.cpIndexOrZero(ic.outerClass().orElse(null)), + buf.cpIndexOrZero(ic.innerName().orElse(null))); buf.writeU2(ic.flagsMask()); } } @@ -300,12 +301,12 @@ public LineNumberTableAttribute readAttribute(AttributedElement e, ClassReader c } @Override - protected void writeBody(BufWriter buf, LineNumberTableAttribute attr) { + protected void writeBody(BufWriter bufWriter, LineNumberTableAttribute attr) { List lines = attr.lineNumbers(); + BufWriterImpl buf = (BufWriterImpl) bufWriter; buf.writeU2(lines.size()); for (LineNumberInfo line : lines) { - buf.writeU2(line.startPc()); - buf.writeU2(line.lineNumber()); + buf.writeU2U2(line.startPc(), line.lineNumber()); } } } @@ -323,15 +324,13 @@ public LocalVariableTableAttribute readAttribute(AttributedElement e, ClassReade } @Override - protected void writeBody(BufWriter buf, LocalVariableTableAttribute attr) { + protected void writeBody(BufWriter bufWriter, LocalVariableTableAttribute attr) { List infos = attr.localVariables(); + BufWriterImpl buf = (BufWriterImpl) bufWriter; buf.writeU2(infos.size()); for (LocalVariableInfo info : infos) { - buf.writeU2(info.startPc()); - buf.writeU2(info.length()); - buf.writeIndex(info.name()); - buf.writeIndex(info.type()); - buf.writeU2(info.slot()); + buf.writeU2U2(info.startPc(), info.length()); + buf.writeU2U2U2(buf.cpIndex(info.name()), buf.cpIndex(info.type()), info.slot()); } } } @@ -349,15 +348,13 @@ public LocalVariableTypeTableAttribute readAttribute(AttributedElement e, ClassR } @Override - protected void writeBody(BufWriter buf, LocalVariableTypeTableAttribute attr) { + protected void writeBody(BufWriter bufWriter, LocalVariableTypeTableAttribute attr) { List infos = attr.localVariableTypes(); + BufWriterImpl buf = (BufWriterImpl) bufWriter; buf.writeU2(infos.size()); for (LocalVariableTypeInfo info : infos) { - buf.writeU2(info.startPc()); - buf.writeU2(info.length()); - buf.writeIndex(info.name()); - buf.writeIndex(info.signature()); - buf.writeU2(info.slot()); + buf.writeU2U2(info.startPc(), info.length()); + buf.writeU2U2U2(buf.cpIndex(info.name()), buf.cpIndex(info.signature()), info.slot()); } } } @@ -375,12 +372,13 @@ public MethodParametersAttribute readAttribute(AttributedElement e, ClassReader } @Override - protected void writeBody(BufWriter buf, MethodParametersAttribute attr) { + protected void writeBody(BufWriter bufWriter, MethodParametersAttribute attr) { List parameters = attr.parameters(); + BufWriterImpl buf = (BufWriterImpl) bufWriter; buf.writeU1(parameters.size()); for (MethodParameterInfo info : parameters) { - buf.writeIndexOrZero(info.name().orElse(null)); - buf.writeU2(info.flagsMask()); + buf.writeU2U2(buf.cpIndexOrZero(info.name().orElse(null)), + info.flagsMask()); } } } @@ -398,26 +396,27 @@ public ModuleAttribute readAttribute(AttributedElement e, ClassReader cf, int p) } @Override - protected void writeBody(BufWriter buf, ModuleAttribute attr) { - buf.writeIndex(attr.moduleName()); - buf.writeU2(attr.moduleFlagsMask()); - buf.writeIndexOrZero(attr.moduleVersion().orElse(null)); + protected void writeBody(BufWriter bufWriter, ModuleAttribute attr) { + BufWriterImpl buf = (BufWriterImpl) bufWriter; + buf.writeU2U2U2(buf.cpIndex(attr.moduleName()), + attr.moduleFlagsMask(), + buf.cpIndexOrZero(attr.moduleVersion().orElse(null))); buf.writeU2(attr.requires().size()); for (ModuleRequireInfo require : attr.requires()) { - buf.writeIndex(require.requires()); - buf.writeU2(require.requiresFlagsMask()); - buf.writeIndexOrZero(require.requiresVersion().orElse(null)); + buf.writeU2U2U2(buf.cpIndex(require.requires()), + require.requiresFlagsMask(), + buf.cpIndexOrZero(require.requiresVersion().orElse(null))); } buf.writeU2(attr.exports().size()); for (ModuleExportInfo export : attr.exports()) { - buf.writeIndex(export.exportedPackage()); - buf.writeU2(export.exportsFlagsMask()); + buf.writeU2U2(buf.cpIndex(export.exportedPackage()), + export.exportsFlagsMask()); Util.writeListIndices(buf, export.exportsTo()); } buf.writeU2(attr.opens().size()); for (ModuleOpenInfo open : attr.opens()) { - buf.writeIndex(open.openedPackage()); - buf.writeU2(open.opensFlagsMask()); + buf.writeU2U2(buf.cpIndex(open.openedPackage()), + open.opensFlagsMask()); Util.writeListIndices(buf, open.opensTo()); } Util.writeListIndices(buf, attr.uses()); @@ -442,13 +441,13 @@ public ModuleHashesAttribute readAttribute(AttributedElement e, ClassReader cf, } @Override - protected void writeBody(BufWriter buf, ModuleHashesAttribute attr) { - buf.writeIndex(attr.algorithm()); + protected void writeBody(BufWriter bufWriter, ModuleHashesAttribute attr) { List hashes = attr.hashes(); - buf.writeU2(hashes.size()); + BufWriterImpl buf = (BufWriterImpl) bufWriter; + buf.writeU2U2(buf.cpIndex(attr.algorithm()), hashes.size()); for (ModuleHashInfo hash : hashes) { - buf.writeIndex(hash.moduleName()); - buf.writeU2(hash.hash().length); + buf.writeU2U2(buf.cpIndex(hash.moduleName()), + hash.hash().length); buf.writeBytes(hash.hash()); } } @@ -593,13 +592,14 @@ public RecordAttribute readAttribute(AttributedElement e, ClassReader cf, int p) } @Override - protected void writeBody(BufWriter buf, RecordAttribute attr) { + protected void writeBody(BufWriter bufWriter, RecordAttribute attr) { List components = attr.components(); + BufWriterImpl buf = (BufWriterImpl) bufWriter; buf.writeU2(components.size()); for (RecordComponentInfo info : components) { - buf.writeIndex(info.name()); - buf.writeIndex(info.descriptor()); - Util.writeAttributes((BufWriterImpl) buf, info.attributes()); + buf.writeU2U2(buf.cpIndex(info.name()), + buf.cpIndex(info.descriptor())); + Util.writeAttributes(buf, info.attributes()); } } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractBoundLocalVariable.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractBoundLocalVariable.java index dcbd8c8fee8..71b08c1915f 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractBoundLocalVariable.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractBoundLocalVariable.java @@ -88,15 +88,12 @@ public boolean writeLocalTo(BufWriterImpl b) { return false; } int length = endBci - startBci; - b.writeU2(startBci); - b.writeU2(length); + b.writeU2U2(startBci, length); if (b.canWriteDirect(code.constantPool())) { - b.writeU2(nameIndex()); - b.writeU2(secondaryIndex()); + b.writeU2U2(nameIndex(), secondaryIndex()); } else { - b.writeIndex(name()); - b.writeIndex(secondaryEntry()); + b.writeU2U2(b.cpIndex(name()), b.cpIndex(secondaryEntry())); } b.writeU2(slot()); return true; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPseudoInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPseudoInstruction.java index dc170ce72b5..ffba8ec7a5d 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPseudoInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPseudoInstruction.java @@ -200,11 +200,8 @@ public boolean writeLocalTo(BufWriterImpl b) { return false; } int length = endBci - startBci; - b.writeU2(startBci); - b.writeU2(length); - b.writeIndex(name); - b.writeIndex(descriptor); - b.writeU2(slot()); + b.writeU2U2(startBci, length); + b.writeU2U2U2(b.cpIndex(name), b.cpIndex(descriptor), slot()); return true; } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java index 02f6167d436..9724cff35ad 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java @@ -282,9 +282,8 @@ private static int skipTypeAnnotation(ClassReader classReader, int p) { } public static void writeAnnotation(BufWriterImpl buf, Annotation annotation) { - buf.writeIndex(annotation.className()); var elements = annotation.elements(); - buf.writeU2(elements.size()); + buf.writeU2U2(buf.cpIndex(annotation.className()), elements.size()); for (var e : elements) { buf.writeIndex(e.name()); AnnotationReader.writeAnnotationValue(buf, e.value()); @@ -332,8 +331,8 @@ public static void writeTypeAnnotation(BufWriterImpl buf, TypeAnnotation ta) { case TypeAnnotation.CatchTarget ct -> buf.writeU2(ct.exceptionTableIndex()); case TypeAnnotation.OffsetTarget ot -> buf.writeU2(labelToBci(lr, ot.target(), ta)); case TypeAnnotation.TypeArgumentTarget tat -> { - buf.writeU2(labelToBci(lr, tat.target(), ta)); - buf.writeU1(tat.typeArgumentIndex()); + buf.writeU2U1(labelToBci(lr, tat.target(), ta), + tat.typeArgumentIndex()); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java index cf5b98dafb1..91b3b32190b 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java @@ -168,6 +168,16 @@ public void writeU1U2U2(int x1, int x2, int x3) { this.offset = offset + 5; } + public void writeU2U1(int x1, int x2) { + reserveSpace(3); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) (x1 >> 8); + elems[offset + 1] = (byte) x1; + elems[offset + 2] = (byte) x2; + this.offset = offset + 3; + } + public void writeU2U2(int x1, int x2) { reserveSpace(4); byte[] elems = this.elems; @@ -204,6 +214,21 @@ public void writeInt(int x) { this.offset = offset + 4; } + public void writeIntInt(int x1, int x2) { + reserveSpace(8); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) (x1 >> 24); + elems[offset + 1] = (byte) (x1 >> 16); + elems[offset + 2] = (byte) (x1 >> 8); + elems[offset + 3] = (byte) x1; + elems[offset + 4] = (byte) (x2 >> 24); + elems[offset + 5] = (byte) (x2 >> 16); + elems[offset + 6] = (byte) (x2 >> 8); + elems[offset + 7] = (byte) x2; + this.offset = offset + 8; + } + @Override public void writeFloat(float x) { writeInt(Float.floatToIntBits(x)); @@ -355,6 +380,12 @@ public int cpIndex(PoolEntry entry) { return idx; } + public int cpIndexOrZero(PoolEntry entry) { + if (entry == null || entry.index() == 0) + return 0; + return cpIndex(entry); + } + @ForceInline @Override public void writeIndex(PoolEntry entry) { @@ -371,10 +402,7 @@ static IllegalArgumentException invalidIndex(int idx, PoolEntry entry) { @Override public void writeIndexOrZero(PoolEntry entry) { - if (entry == null || entry.index() == 0) - writeU2(0); - else - writeIndex(entry); + writeU2(cpIndexOrZero(entry)); } /** diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java index b599f2b61aa..afa7ebac8ba 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java @@ -219,12 +219,10 @@ else if ((flags & ClassFile.ACC_MODULE) == 0 && !"java/lang/Object".equals(thisC } // Now we can make the head - head.writeLong((((long) ClassFile.MAGIC_NUMBER) << 32) - | ((minorVersion & 0xFFFFL) << 16) - | (majorVersion & 0xFFFFL)); + head.writeInt(ClassFile.MAGIC_NUMBER); + head.writeU2U2(minorVersion, majorVersion); constantPool.writeTo(head); - head.writeU2U2(flags, head.cpIndex(thisClassEntry)); - head.writeIndexOrZero(superclass); + head.writeU2U2U2(flags, head.cpIndex(thisClassEntry), head.cpIndexOrZero(superclass)); head.writeU2(interfaceEntriesSize); for (int i = 0; i < interfaceEntriesSize; i++) { head.writeIndex(ies[i]); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java index 72c37ade9ac..6f2a9cc125a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java @@ -257,8 +257,7 @@ public void writeBody(BufWriterImpl b) { } } else { b.writeU2U2(start, end - 1); - b.writeInt(cr.characterRangeStart()); - b.writeInt(cr.characterRangeEnd()); + b.writeIntInt(cr.characterRangeStart(), cr.characterRangeEnd()); b.writeU2(cr.flags()); } } @@ -640,8 +639,7 @@ public void writeTableSwitch(int low, int high, Label defaultTarget, List(cases.size()); for (var c : cases) { caseMap.put(c.caseValue(), c.target()); @@ -668,8 +666,7 @@ public void writeInvokeInterface(Opcode opcode, } public void writeInvokeDynamic(InvokeDynamicEntry ref) { - bytecodesBufWriter.writeIndex(INVOKEDYNAMIC, ref); - bytecodesBufWriter.writeU2(0); + bytecodesBufWriter.writeU1U2U2(INVOKEDYNAMIC, bytecodesBufWriter.cpIndex(ref), 0); } public void writeNewObject(ClassEntry type) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java index 15547924cf3..d1fafc626f9 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java @@ -80,9 +80,7 @@ void setFlags(int flags) { @Override public void writeTo(BufWriterImpl buf) { - buf.writeU2(flags); - buf.writeIndex(name); - buf.writeIndex(desc); + buf.writeU2U2U2(flags, buf.cpIndex(name), buf.cpIndex(desc)); attributes.writeTo(buf); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java index 30bb8136e45..205e36588b4 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java @@ -84,9 +84,9 @@ public void writeTo(BufWriterImpl buf) { reader.copyBytesTo(buf, startPos, endPos - startPos); } else { - buf.writeU2(flags().flagsMask()); - buf.writeIndex(fieldName()); - buf.writeIndex(fieldType()); + buf.writeU2U2U2(flags().flagsMask(), + buf.cpIndex(fieldName()), + buf.cpIndex(fieldType())); Util.writeAttributes(buf, attributes()); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java index 8467152504e..4f7799d1fa7 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java @@ -103,9 +103,9 @@ public void writeTo(BufWriterImpl buf) { reader.copyBytesTo(buf, startPos, endPos - startPos); } else { - buf.writeU2(flags().flagsMask()); - buf.writeIndex(methodName()); - buf.writeIndex(methodType()); + buf.writeU2U2U2(flags().flagsMask(), + buf.cpIndex(methodName()), + buf.cpIndex(methodType())); Util.writeAttributes(buf, attributes()); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java index 54b4139a9e0..2cad163b177 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java @@ -135,8 +135,7 @@ private static void writeFrame(BufWriterImpl out, int offsetDelta, List l1, List - {} + bw.writeU1(tag); case ITEM_OBJECT -> - bw.writeIndex(((ObjectVerificationTypeInfo)vti).className()); + bw.writeU1U2(tag, bw.cpIndex(((ObjectVerificationTypeInfo)vti).className())); case ITEM_UNINITIALIZED -> - bw.writeU2(bw.labelContext().labelToBci(((UninitializedVerificationTypeInfo)vti).newTarget())); + bw.writeU1U2(tag, bw.labelContext().labelToBci(((UninitializedVerificationTypeInfo)vti).newTarget())); default -> throw new IllegalArgumentException("Invalid verification type tag: " + vti.tag()); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java index 4bc4e259f5a..1af13267c80 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java @@ -1432,8 +1432,7 @@ void writeTo(BufWriterImpl out, Frame prevFrame, ConstantPoolBuilder cp) { return; } //full frame - out.writeU1U2(255, offsetDelta); - out.writeU2(localsSize); + out.writeU1U2U2(255, offsetDelta, localsSize); for (int i=0; i - bw.writeU2(cp.classEntry(sym).index()); + bw.writeU1U2(tag, cp.classEntry(sym).index()); case ITEM_UNINITIALIZED -> - bw.writeU2(bci); + bw.writeU1U2(tag, bci); + default -> + bw.writeU1(tag); } } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java index 7a097775dad..0c4410bb5ea 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java @@ -287,12 +287,10 @@ public static void dumpMethod(SplitConstantPool cp, ((DirectMethodBuilder)mb).writeAttribute(new UnboundAttribute.AdHocAttribute(Attributes.code()) { @Override public void writeBody(BufWriterImpl b) { - b.writeU2(-1);//max stack - b.writeU2(-1);//max locals + b.writeU2U2(-1, -1);//max stack & locals b.writeInt(bytecode.length()); b.writeBytes(bytecode.array(), 0, bytecode.length()); - b.writeU2(0);//exception handlers - b.writeU2(0);//attributes + b.writeU2U2(0, 0);//exception handlers & attributes } })))); ClassPrinter.toYaml(clm.methods().get(0).code().get(), ClassPrinter.Verbosity.TRACE_ALL, dump); From 472db922fabfb8942f15d39ebd58125189bc8600 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Fri, 11 Oct 2024 02:17:37 +0000 Subject: [PATCH 17/95] 8341900: Optimize DirectCodeBuilder writeBody Reviewed-by: liach --- .../classfile/impl/DirectCodeBuilder.java | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java index 6f2a9cc125a..b3c106c461d 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java @@ -373,24 +373,19 @@ public void writeBody(BufWriterImpl buf) { dcb.methodInfo.methodTypeSymbol().displayDescriptor())); } + boolean codeMatch = dcb.original != null && codeAndExceptionsMatch(codeLength); var context = dcb.context; - if (dcb.original != null && codeAndExceptionsMatch(codeLength)) { - if (context.stackMapsWhenRequired()) { + if (context.stackMapsWhenRequired()) { + if (codeMatch) { dcb.attributes.withAttribute(dcb.original.findAttribute(Attributes.stackMapTable()).orElse(null)); writeCounters(true, buf); - } else if (context.generateStackMaps()) { - generateStackMaps(buf); - } else if (context.dropStackMaps()) { - writeCounters(true, buf); - } - } else { - if (context.stackMapsWhenRequired()) { + } else { tryGenerateStackMaps(false, buf); - } else if (context.generateStackMaps()) { - generateStackMaps(buf); - } else if (context.dropStackMaps()) { - writeCounters(false, buf); } + } else if (context.generateStackMaps()) { + generateStackMaps(buf); + } else if (context.dropStackMaps()) { + writeCounters(codeMatch, buf); } buf.writeInt(codeLength); From ace44e564ffe67af5fc25a0f126cf48da0eca078 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Fri, 11 Oct 2024 07:29:11 +0000 Subject: [PATCH 18/95] 8341806: Gcc version detection failure on Alinux3 Reviewed-by: ihse, jwaters --- make/autoconf/toolchain.m4 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/make/autoconf/toolchain.m4 b/make/autoconf/toolchain.m4 index 75c8d2b61d0..5b3ef98c736 100644 --- a/make/autoconf/toolchain.m4 +++ b/make/autoconf/toolchain.m4 @@ -358,6 +358,11 @@ AC_DEFUN([TOOLCHAIN_EXTRACT_COMPILER_VERSION], # Copyright (C) 2013 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + # or look like + # gcc (GCC) 10.2.1 20200825 (Alibaba 10.2.1-3.8 2.32) + # Copyright (C) 2020 Free Software Foundation, Inc. + # This is free software; see the source for copying conditions. There is NO + # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. COMPILER_VERSION_OUTPUT=`$COMPILER --version 2>&1` # Check that this is likely to be GCC. $ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "Free Software Foundation" > /dev/null @@ -371,7 +376,8 @@ AC_DEFUN([TOOLCHAIN_EXTRACT_COMPILER_VERSION], COMPILER_VERSION_STRING=`$ECHO $COMPILER_VERSION_OUTPUT | \ $SED -e 's/ *Copyright .*//'` COMPILER_VERSION_NUMBER=`$ECHO $COMPILER_VERSION_OUTPUT | \ - $SED -e 's/^.* \(@<:@1-9@:>@<:@0-9@:>@*\.@<:@0-9.@:>@*\)@<:@^0-9.@:>@.*$/\1/'` + $AWK -F ')' '{print [$]2}' | \ + $AWK '{print [$]1}'` elif test "x$TOOLCHAIN_TYPE" = xclang; then # clang --version output typically looks like # Apple clang version 15.0.0 (clang-1500.3.9.4) From ae41daf9a780f8fd29b2af984e9aa60ae9b10937 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Fri, 11 Oct 2024 08:12:37 +0000 Subject: [PATCH 19/95] 8341880: RISC-V: riscv_vector.h native build fails with gcc13 after JDK-8320500 Co-authored-by: Fei Yang Reviewed-by: mli, fyang --- .../linux/native/libsleef/lib/vector_math_rvv.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/jdk.incubator.vector/linux/native/libsleef/lib/vector_math_rvv.c b/src/jdk.incubator.vector/linux/native/libsleef/lib/vector_math_rvv.c index 4515457fa89..438ca0c92ba 100644 --- a/src/jdk.incubator.vector/linux/native/libsleef/lib/vector_math_rvv.c +++ b/src/jdk.incubator.vector/linux/native/libsleef/lib/vector_math_rvv.c @@ -31,6 +31,8 @@ // At run-time, if the library is found and the bridge functions are available in the // library, then the java vector API will call into the bridge functions and sleef. +#if __GNUC__ >= 14 || (defined(__clang_major__) && __clang_major__ >= 17) + #ifdef __riscv_v_intrinsic #include @@ -117,4 +119,6 @@ DEFINE_VECTOR_MATH_BINARY_RVV(hypotdx_u05, vdouble_rvvm1_sleef) #undef DEFINE_VECTOR_MATH_BINARY_RVV -#endif /* __riscv_v_intrinsic */ +#endif /* __riscv_v_intrinsic */ + +#endif /* check gcc and clang version */ From 7c0dbf8e9c69d51aa8e06305e4483002116019f4 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Fri, 11 Oct 2024 08:22:27 +0000 Subject: [PATCH 20/95] 8341820: Check return value of hcreate_r Reviewed-by: cjplummer, lucy --- src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c b/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c index c36b9e5707e..4cb791111bc 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c @@ -389,9 +389,9 @@ static struct symtab* build_symtab_internal(int fd, const char *filename, bool t goto bad; } - // int rslt = - hcreate_r(htab_sz, symtab->hash_table); - // guarantee(rslt, "unexpected failure: hcreate_r"); + if (hcreate_r(htab_sz, symtab->hash_table) == 0) { + goto bad; + } // shdr->sh_link points to the section that contains the actual strings // for symbol names. the st_name field in ELF_SYM is just the From 519544c1d72bccb4528953adb054ea3ee97e35b9 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Fri, 11 Oct 2024 09:12:16 +0000 Subject: [PATCH 21/95] 8341909: G1: Add region index to region printer output Reviewed-by: kbarrett, lkorinth --- src/hotspot/share/gc/g1/g1HeapRegionPrinter.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1HeapRegionPrinter.hpp b/src/hotspot/share/gc/g1/g1HeapRegionPrinter.hpp index d7b1a6da92c..577a8552091 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionPrinter.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionPrinter.hpp @@ -35,8 +35,8 @@ class G1HeapRegionPrinter : public AllStatic { // Print an action event. static void print(const char* action, G1HeapRegion* hr) { - log_trace(gc, region)("G1HR %s(%s) [" PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT "]", - action, hr->get_type_str(), p2i(hr->bottom()), p2i(hr->top()), p2i(hr->end())); + log_trace(gc, region)("G1HR %4u %s(%s) [" PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT "]", + hr->hrm_index(), action, hr->get_type_str(), p2i(hr->bottom()), p2i(hr->top()), p2i(hr->end())); } public: From 6133866150cf6131ab578f1537f84c239703fa67 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Fri, 11 Oct 2024 10:07:34 +0000 Subject: [PATCH 22/95] 8341070: javac fails with an exception when compiling import module under source level 8 Reviewed-by: asotona --- .../com/sun/tools/javac/code/Symtab.java | 36 +++++++-------- test/langtools/tools/javac/ImportModule.java | 46 +++++++++++++++++++ 2 files changed, 63 insertions(+), 19 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java index 4e62828c426..cee6a16a10f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java @@ -420,6 +420,23 @@ protected Symtab(Context context) throws CompletionFailure { missingInfoHandler, target.runtimeUseNestAccess()); + noModule = new ModuleSymbol(names.empty, null) { + @Override public boolean isNoModule() { + return true; + } + }; + addRootPackageFor(noModule); + + Source source = Source.instance(context); + if (Feature.MODULES.allowedInSource(source)) { + java_base = enterModule(names.java_base); + //avoid completing java.base during the Symtab initialization + java_base.completer = Completer.NULL_COMPLETER; + java_base.visiblePackages = Collections.emptyMap(); + } else { + java_base = noModule; + } + // create the basic builtin symbols unnamedModule = new ModuleSymbol(names.empty, null) { { @@ -427,7 +444,6 @@ protected Symtab(Context context) throws CompletionFailure { exports = List.nil(); provides = List.nil(); uses = List.nil(); - ModuleSymbol java_base = enterModule(names.java_base); com.sun.tools.javac.code.Directive.RequiresDirective d = new com.sun.tools.javac.code.Directive.RequiresDirective(java_base, EnumSet.of(com.sun.tools.javac.code.Directive.RequiresFlag.MANDATED)); @@ -447,7 +463,6 @@ public String toString() { exports = List.nil(); provides = List.nil(); uses = List.nil(); - ModuleSymbol java_base = enterModule(names.java_base); com.sun.tools.javac.code.Directive.RequiresDirective d = new com.sun.tools.javac.code.Directive.RequiresDirective(java_base, EnumSet.of(com.sun.tools.javac.code.Directive.RequiresFlag.MANDATED)); @@ -456,13 +471,6 @@ public String toString() { }; addRootPackageFor(errModule); - noModule = new ModuleSymbol(names.empty, null) { - @Override public boolean isNoModule() { - return true; - } - }; - addRootPackageFor(noModule); - noSymbol = new TypeSymbol(NIL, 0, names.empty, Type.noType, rootPackage) { @Override @DefinedBy(Api.LANGUAGE_MODEL) public R accept(ElementVisitor v, P p) { @@ -526,16 +534,6 @@ public R accept(ElementVisitor v, P p) { // Enter symbol for the errSymbol scope.enter(errSymbol); - Source source = Source.instance(context); - if (Feature.MODULES.allowedInSource(source)) { - java_base = enterModule(names.java_base); - //avoid completing java.base during the Symtab initialization - java_base.completer = Completer.NULL_COMPLETER; - java_base.visiblePackages = Collections.emptyMap(); - } else { - java_base = noModule; - } - // Get the initial completer for ModuleSymbols from Modules moduleCompleter = Modules.instance(context).getCompleter(); diff --git a/test/langtools/tools/javac/ImportModule.java b/test/langtools/tools/javac/ImportModule.java index f38b77062e2..dc1ed9bd0c1 100644 --- a/test/langtools/tools/javac/ImportModule.java +++ b/test/langtools/tools/javac/ImportModule.java @@ -797,4 +797,50 @@ public class C {} } } + + @Test + public void testImportModuleNoModules(Path base) throws Exception { + Path current = base.resolve("."); + Path src = current.resolve("src"); + Path classes = current.resolve("classes"); + tb.writeJavaFiles(src, + """ + package test; + import module java.base; + public class Test { + List l; + } + """); + + Files.createDirectories(classes); + + List actualErrors = new JavacTask(tb) + .options("--release", "8", + "-XDshould-stop.at=FLOW", + "-XDdev", + "-XDrawDiagnostics") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expectedErrors = List.of( + "- compiler.warn.option.obsolete.source: 8", + "- compiler.warn.option.obsolete.target: 8", + "- compiler.warn.option.obsolete.suppression", + "Test.java:2:8: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.module.imports)", + "Test.java:2:1: compiler.err.import.module.not.found: java.base", + "Test.java:4:5: compiler.err.cant.resolve.location: kindname.class, List, , , (compiler.misc.location: kindname.class, test.Test, null)", + "3 errors", + "3 warnings" + ); + + if (!Objects.equals(expectedErrors, actualErrors)) { + throw new AssertionError("Incorrect Output, expected: " + expectedErrors + + ", actual: " + out); + + } + } + } From 7276a1bec0d90f63e9e433fdcdfd6564b70dc9bb Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Fri, 11 Oct 2024 15:28:15 +0000 Subject: [PATCH 23/95] 8341784: Refactor TypeVect to use a BasicType instead of a const Type* Reviewed-by: kvn, jkarthikeyan --- src/hotspot/cpu/aarch64/aarch64.ad | 4 - src/hotspot/cpu/arm/arm.ad | 4 - src/hotspot/cpu/ppc/ppc.ad | 4 - src/hotspot/cpu/riscv/riscv.ad | 4 - src/hotspot/cpu/s390/s390.ad | 4 - src/hotspot/cpu/x86/x86.ad | 4 - src/hotspot/share/opto/loopopts.cpp | 3 +- src/hotspot/share/opto/matcher.hpp | 1 - .../share/opto/superwordVTransformBuilder.cpp | 4 +- src/hotspot/share/opto/type.cpp | 137 ++++++++---------- src/hotspot/share/opto/type.hpp | 67 ++++----- src/hotspot/share/opto/vectorIntrinsics.cpp | 49 +++---- src/hotspot/share/opto/vectornode.cpp | 23 ++- src/hotspot/share/opto/vectornode.hpp | 2 +- src/hotspot/share/opto/vtransform.cpp | 3 +- src/hotspot/share/opto/vtransform.hpp | 4 +- .../compiler/lib/ir_framework/IRNode.java | 14 +- .../checkattribute/parsing/RawIRNode.java | 5 +- 18 files changed, 131 insertions(+), 205 deletions(-) diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 4049ab1fe80..d9c77a2f529 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -2307,10 +2307,6 @@ const RegMask* Matcher::predicate_reg_mask(void) { return &_PR_REG_mask; } -const TypeVectMask* Matcher::predicate_reg_type(const Type* elemTy, int length) { - return new TypeVectMask(elemTy, length); -} - // Vector calling convention not yet implemented. bool Matcher::supports_vector_calling_convention(void) { return false; diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index bb81f2af599..bfca986f350 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -1003,10 +1003,6 @@ const RegMask* Matcher::predicate_reg_mask(void) { return nullptr; } -const TypeVectMask* Matcher::predicate_reg_type(const Type* elemTy, int length) { - return nullptr; -} - // Vector calling convention not yet implemented. bool Matcher::supports_vector_calling_convention(void) { return false; diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index b5bfdf5067a..f74dde0f97e 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -2154,10 +2154,6 @@ const RegMask* Matcher::predicate_reg_mask(void) { return nullptr; } -const TypeVectMask* Matcher::predicate_reg_type(const Type* elemTy, int length) { - return nullptr; -} - // Vector calling convention not yet implemented. bool Matcher::supports_vector_calling_convention(void) { return false; diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 30b8ff77be8..54d1f1c0573 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1966,10 +1966,6 @@ const RegMask* Matcher::predicate_reg_mask(void) { return &_VMASK_REG_mask; } -const TypeVectMask* Matcher::predicate_reg_type(const Type* elemTy, int length) { - return new TypeVectMask(elemTy, length); -} - // Vector calling convention not yet implemented. bool Matcher::supports_vector_calling_convention(void) { return EnableVectorSupport && UseVectorStubs; diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index ebb2ca36a64..8b897033aa5 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -1477,10 +1477,6 @@ const RegMask* Matcher::predicate_reg_mask(void) { return nullptr; } -const TypeVectMask* Matcher::predicate_reg_type(const Type* elemTy, int length) { - return nullptr; -} - // Vector calling convention not yet implemented. bool Matcher::supports_vector_calling_convention(void) { return false; diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index b55a1208cf2..c88fa1ec5ce 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -2231,10 +2231,6 @@ const RegMask* Matcher::predicate_reg_mask(void) { return &_VECTMASK_REG_mask; } -const TypeVectMask* Matcher::predicate_reg_type(const Type* elemTy, int length) { - return new TypeVectMask(elemTy, length); -} - // Max vector size in bytes. 0 if not supported. int Matcher::vector_width_in_bytes(BasicType bt) { assert(is_java_primitive(bt), "only primitive type vectors"); diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 7f42d2d4beb..654262d21cb 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -4548,7 +4548,6 @@ void PhaseIdealLoop::move_unordered_reduction_out_of_loop(IdealLoopTree* loop) { const TypeVect* vec_t = last_ur->vect_type(); uint vector_length = vec_t->length(); BasicType bt = vec_t->element_basic_type(); - const Type* bt_t = Type::get_const_basic_type(bt); // Convert opcode from vector-reduction -> scalar -> normal-vector-op const int sopc = VectorNode::scalar_opcode(last_ur->Opcode(), bt); @@ -4628,7 +4627,7 @@ void PhaseIdealLoop::move_unordered_reduction_out_of_loop(IdealLoopTree* loop) { Node* identity_scalar = ReductionNode::make_identity_con_scalar(_igvn, sopc, bt); set_ctrl(identity_scalar, C->root()); - VectorNode* identity_vector = VectorNode::scalar2vector(identity_scalar, vector_length, bt_t); + VectorNode* identity_vector = VectorNode::scalar2vector(identity_scalar, vector_length, bt); register_new_node(identity_vector, C->root()); assert(vec_t == identity_vector->vect_type(), "matching vector type"); VectorNode::trace_new_vector(identity_vector, "Unordered Reduction"); diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index 25762835088..de3b23aa6d2 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -342,7 +342,6 @@ class Matcher : public PhaseTransform { static bool vector_needs_partial_operations(Node* node, const TypeVect* vt); static const RegMask* predicate_reg_mask(void); - static const TypeVectMask* predicate_reg_type(const Type* elemTy, int length); // Vector width in bytes static int vector_width_in_bytes(BasicType bt); diff --git a/src/hotspot/share/opto/superwordVTransformBuilder.cpp b/src/hotspot/share/opto/superwordVTransformBuilder.cpp index b0a0c97cb16..6c2d3a3be35 100644 --- a/src/hotspot/share/opto/superwordVTransformBuilder.cpp +++ b/src/hotspot/share/opto/superwordVTransformBuilder.cpp @@ -228,8 +228,8 @@ VTransformNode* SuperWordVTransformBuilder::get_or_make_vtnode_vector_input_at_i return shift_count; } else { // Replicate the scalar same_input to every vector element. - const Type* element_type = _vloop_analyzer.types().velt_type(p0); - if (index == 2 && VectorNode::is_scalar_rotate(p0) && element_type->isa_long()) { + BasicType element_type = _vloop_analyzer.types().velt_basic_type(p0); + if (index == 2 && VectorNode::is_scalar_rotate(p0) && element_type == T_LONG) { // Scalar rotate has int rotation value, but the scalar rotate expects longs. assert(same_input->bottom_type()->isa_int(), "scalar rotate expects int rotation"); VTransformNode* conv = new (_vtransform.arena()) VTransformConvI2LNode(_vtransform); diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 73f852c0f04..828cea88d83 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -679,7 +679,7 @@ void Type::Initialize_shared(Compile* current) { // get_zero_type() should not happen for T_CONFLICT _zero_type[T_CONFLICT]= nullptr; - TypeVect::VECTMASK = (TypeVect*)(new TypeVectMask(TypeInt::BOOL, MaxVectorSize))->hashcons(); + TypeVect::VECTMASK = (TypeVect*)(new TypeVectMask(T_BOOLEAN, MaxVectorSize))->hashcons(); mreg2type[Op_RegVectMask] = TypeVect::VECTMASK; if (Matcher::supports_scalable_vector()) { @@ -687,20 +687,20 @@ void Type::Initialize_shared(Compile* current) { } // Vector predefined types, it needs initialized _const_basic_type[]. - if (Matcher::vector_size_supported(T_BYTE,4)) { - TypeVect::VECTS = TypeVect::make(T_BYTE,4); + if (Matcher::vector_size_supported(T_BYTE, 4)) { + TypeVect::VECTS = TypeVect::make(T_BYTE, 4); } - if (Matcher::vector_size_supported(T_FLOAT,2)) { - TypeVect::VECTD = TypeVect::make(T_FLOAT,2); + if (Matcher::vector_size_supported(T_FLOAT, 2)) { + TypeVect::VECTD = TypeVect::make(T_FLOAT, 2); } - if (Matcher::vector_size_supported(T_FLOAT,4)) { - TypeVect::VECTX = TypeVect::make(T_FLOAT,4); + if (Matcher::vector_size_supported(T_FLOAT, 4)) { + TypeVect::VECTX = TypeVect::make(T_FLOAT, 4); } - if (Matcher::vector_size_supported(T_FLOAT,8)) { - TypeVect::VECTY = TypeVect::make(T_FLOAT,8); + if (Matcher::vector_size_supported(T_FLOAT, 8)) { + TypeVect::VECTY = TypeVect::make(T_FLOAT, 8); } - if (Matcher::vector_size_supported(T_FLOAT,16)) { - TypeVect::VECTZ = TypeVect::make(T_FLOAT,16); + if (Matcher::vector_size_supported(T_FLOAT, 16)) { + TypeVect::VECTZ = TypeVect::make(T_FLOAT, 16); } mreg2type[Op_VecA] = TypeVect::VECTA; @@ -2482,58 +2482,59 @@ bool TypeAry::ary_must_be_exact() const { //==============================TypeVect======================================= // Convenience common pre-built types. -const TypeVect *TypeVect::VECTA = nullptr; // vector length agnostic -const TypeVect *TypeVect::VECTS = nullptr; // 32-bit vectors -const TypeVect *TypeVect::VECTD = nullptr; // 64-bit vectors -const TypeVect *TypeVect::VECTX = nullptr; // 128-bit vectors -const TypeVect *TypeVect::VECTY = nullptr; // 256-bit vectors -const TypeVect *TypeVect::VECTZ = nullptr; // 512-bit vectors -const TypeVect *TypeVect::VECTMASK = nullptr; // predicate/mask vector +const TypeVect* TypeVect::VECTA = nullptr; // vector length agnostic +const TypeVect* TypeVect::VECTS = nullptr; // 32-bit vectors +const TypeVect* TypeVect::VECTD = nullptr; // 64-bit vectors +const TypeVect* TypeVect::VECTX = nullptr; // 128-bit vectors +const TypeVect* TypeVect::VECTY = nullptr; // 256-bit vectors +const TypeVect* TypeVect::VECTZ = nullptr; // 512-bit vectors +const TypeVect* TypeVect::VECTMASK = nullptr; // predicate/mask vector //------------------------------make------------------------------------------- -const TypeVect* TypeVect::make(const Type *elem, uint length, bool is_mask) { +const TypeVect* TypeVect::make(BasicType elem_bt, uint length, bool is_mask) { if (is_mask) { - return makemask(elem, length); + return makemask(elem_bt, length); } - BasicType elem_bt = elem->array_element_basic_type(); assert(is_java_primitive(elem_bt), "only primitive types in vector"); assert(Matcher::vector_size_supported(elem_bt, length), "length in range"); int size = length * type2aelembytes(elem_bt); switch (Matcher::vector_ideal_reg(size)) { case Op_VecA: - return (TypeVect*)(new TypeVectA(elem, length))->hashcons(); + return (TypeVect*)(new TypeVectA(elem_bt, length))->hashcons(); case Op_VecS: - return (TypeVect*)(new TypeVectS(elem, length))->hashcons(); + return (TypeVect*)(new TypeVectS(elem_bt, length))->hashcons(); case Op_RegL: case Op_VecD: case Op_RegD: - return (TypeVect*)(new TypeVectD(elem, length))->hashcons(); + return (TypeVect*)(new TypeVectD(elem_bt, length))->hashcons(); case Op_VecX: - return (TypeVect*)(new TypeVectX(elem, length))->hashcons(); + return (TypeVect*)(new TypeVectX(elem_bt, length))->hashcons(); case Op_VecY: - return (TypeVect*)(new TypeVectY(elem, length))->hashcons(); + return (TypeVect*)(new TypeVectY(elem_bt, length))->hashcons(); case Op_VecZ: - return (TypeVect*)(new TypeVectZ(elem, length))->hashcons(); + return (TypeVect*)(new TypeVectZ(elem_bt, length))->hashcons(); } ShouldNotReachHere(); return nullptr; } -const TypeVect *TypeVect::makemask(const Type* elem, uint length) { - BasicType elem_bt = elem->array_element_basic_type(); +const TypeVect* TypeVect::makemask(BasicType elem_bt, uint length) { if (Matcher::has_predicated_vectors() && Matcher::match_rule_supported_vector_masked(Op_VectorLoadMask, length, elem_bt)) { - return TypeVectMask::make(elem, length); + return TypeVectMask::make(elem_bt, length); } else { - return make(elem, length); + return make(elem_bt, length); } } //------------------------------meet------------------------------------------- -// Compute the MEET of two types. It returns a new Type object. -const Type *TypeVect::xmeet( const Type *t ) const { +// Compute the MEET of two types. Since each TypeVect is the only instance of +// its species, meeting often returns itself +const Type* TypeVect::xmeet(const Type* t) const { // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? + if (this == t) { + return this; + } // Current "this->_base" is Vector switch (t->base()) { // switch on original type @@ -2543,13 +2544,7 @@ const Type *TypeVect::xmeet( const Type *t ) const { default: // All else is a mistake typerr(t); - case VectorMask: { - const TypeVectMask* v = t->is_vectmask(); - assert( base() == v->base(), ""); - assert(length() == v->length(), ""); - assert(element_basic_type() == v->element_basic_type(), ""); - return TypeVect::makemask(_elem->xmeet(v->_elem), _length); - } + case VectorMask: case VectorA: case VectorS: case VectorD: @@ -2557,10 +2552,10 @@ const Type *TypeVect::xmeet( const Type *t ) const { case VectorY: case VectorZ: { // Meeting 2 vectors? const TypeVect* v = t->is_vect(); - assert( base() == v->base(), ""); + assert(base() == v->base(), ""); assert(length() == v->length(), ""); assert(element_basic_type() == v->element_basic_type(), ""); - return TypeVect::make(_elem->xmeet(v->_elem), _length); + return this; } case Top: break; @@ -2569,26 +2564,26 @@ const Type *TypeVect::xmeet( const Type *t ) const { } //------------------------------xdual------------------------------------------ -// Dual: compute field-by-field dual -const Type *TypeVect::xdual() const { - return new TypeVect(base(), _elem->dual(), _length); +// Since each TypeVect is the only instance of its species, it is self-dual +const Type* TypeVect::xdual() const { + return this; } //------------------------------eq--------------------------------------------- // Structural equality check for Type representations -bool TypeVect::eq(const Type *t) const { - const TypeVect *v = t->is_vect(); - return (_elem == v->_elem) && (_length == v->_length); +bool TypeVect::eq(const Type* t) const { + const TypeVect* v = t->is_vect(); + return (element_basic_type() == v->element_basic_type()) && (length() == v->length()); } //------------------------------hash------------------------------------------- // Type-specific hashing function. uint TypeVect::hash(void) const { - return (uint)(uintptr_t)_elem + (uint)(uintptr_t)_length; + return (uint)base() + (uint)(uintptr_t)_elem_bt + (uint)(uintptr_t)_length; } //------------------------------singleton-------------------------------------- -// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple +// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple // constants (Ldi nodes). Vector is singleton if all elements are the same // constant value (when vector is created with Replicate code). bool TypeVect::singleton(void) const { @@ -2598,52 +2593,36 @@ bool TypeVect::singleton(void) const { } bool TypeVect::empty(void) const { - return _elem->empty(); + return false; } //------------------------------dump2------------------------------------------ #ifndef PRODUCT -void TypeVect::dump2(Dict &d, uint depth, outputStream *st) const { +void TypeVect::dump2(Dict& d, uint depth, outputStream* st) const { switch (base()) { case VectorA: - st->print("vectora["); break; + st->print("vectora"); break; case VectorS: - st->print("vectors["); break; + st->print("vectors"); break; case VectorD: - st->print("vectord["); break; + st->print("vectord"); break; case VectorX: - st->print("vectorx["); break; + st->print("vectorx"); break; case VectorY: - st->print("vectory["); break; + st->print("vectory"); break; case VectorZ: - st->print("vectorz["); break; + st->print("vectorz"); break; case VectorMask: - st->print("vectormask["); break; + st->print("vectormask"); break; default: ShouldNotReachHere(); } - st->print("%d]:{", _length); - _elem->dump2(d, depth, st); - st->print("}"); + st->print("<%c,%u>", type2char(element_basic_type()), length()); } #endif -bool TypeVectMask::eq(const Type *t) const { - const TypeVectMask *v = t->is_vectmask(); - return (element_type() == v->element_type()) && (length() == v->length()); -} - -const Type *TypeVectMask::xdual() const { - return new TypeVectMask(element_type()->dual(), length()); -} - -const TypeVectMask *TypeVectMask::make(const BasicType elem_bt, uint length) { - return make(get_const_basic_type(elem_bt), length); -} - -const TypeVectMask *TypeVectMask::make(const Type* elem, uint length) { - const TypeVectMask* mtype = Matcher::predicate_reg_type(elem, length); - return (TypeVectMask*) const_cast(mtype)->hashcons(); +const TypeVectMask* TypeVectMask::make(const BasicType elem_bt, uint length) { + return (TypeVectMask*) (new TypeVectMask(elem_bt, length))->hashcons(); } //============================================================================= diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 902155e975d..f6b7efcae3b 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -784,94 +784,79 @@ class TypeAry : public Type { //------------------------------TypeVect--------------------------------------- // Class of Vector Types class TypeVect : public Type { - const Type* _elem; // Vector's element type - const uint _length; // Elements in vector (power of 2) + const BasicType _elem_bt; // Vector's element type + const uint _length; // Elements in vector (power of 2) protected: - TypeVect(TYPES t, const Type* elem, uint length) : Type(t), - _elem(elem), _length(length) {} + TypeVect(TYPES t, BasicType elem_bt, uint length) : Type(t), + _elem_bt(elem_bt), _length(length) {} public: - const Type* element_type() const { return _elem; } - BasicType element_basic_type() const { return _elem->array_element_basic_type(); } + BasicType element_basic_type() const { return _elem_bt; } uint length() const { return _length; } uint length_in_bytes() const { - return _length * type2aelembytes(element_basic_type()); + return _length * type2aelembytes(element_basic_type()); } - virtual bool eq(const Type *t) const; + virtual bool eq(const Type* t) const; virtual uint hash() const; // Type specific hashing virtual bool singleton(void) const; // TRUE if type is a singleton virtual bool empty(void) const; // TRUE if type is vacuous - static const TypeVect *make(const BasicType elem_bt, uint length, bool is_mask = false) { - // Use bottom primitive type. - return make(get_const_basic_type(elem_bt), length, is_mask); - } - // Used directly by Replicate nodes to construct singleton vector. - static const TypeVect *make(const Type* elem, uint length, bool is_mask = false); - - static const TypeVect *makemask(const BasicType elem_bt, uint length) { - // Use bottom primitive type. - return makemask(get_const_basic_type(elem_bt), length); - } - static const TypeVect *makemask(const Type* elem, uint length); - + static const TypeVect* make(const BasicType elem_bt, uint length, bool is_mask = false); + static const TypeVect* makemask(const BasicType elem_bt, uint length); - virtual const Type *xmeet( const Type *t) const; - virtual const Type *xdual() const; // Compute dual right now. + virtual const Type* xmeet( const Type *t) const; + virtual const Type* xdual() const; // Compute dual right now. - static const TypeVect *VECTA; - static const TypeVect *VECTS; - static const TypeVect *VECTD; - static const TypeVect *VECTX; - static const TypeVect *VECTY; - static const TypeVect *VECTZ; - static const TypeVect *VECTMASK; + static const TypeVect* VECTA; + static const TypeVect* VECTS; + static const TypeVect* VECTD; + static const TypeVect* VECTX; + static const TypeVect* VECTY; + static const TypeVect* VECTZ; + static const TypeVect* VECTMASK; #ifndef PRODUCT - virtual void dump2(Dict &d, uint, outputStream *st) const; // Specialized per-Type dumping + virtual void dump2(Dict& d, uint, outputStream* st) const; // Specialized per-Type dumping #endif }; class TypeVectA : public TypeVect { friend class TypeVect; - TypeVectA(const Type* elem, uint length) : TypeVect(VectorA, elem, length) {} + TypeVectA(BasicType elem_bt, uint length) : TypeVect(VectorA, elem_bt, length) {} }; class TypeVectS : public TypeVect { friend class TypeVect; - TypeVectS(const Type* elem, uint length) : TypeVect(VectorS, elem, length) {} + TypeVectS(BasicType elem_bt, uint length) : TypeVect(VectorS, elem_bt, length) {} }; class TypeVectD : public TypeVect { friend class TypeVect; - TypeVectD(const Type* elem, uint length) : TypeVect(VectorD, elem, length) {} + TypeVectD(BasicType elem_bt, uint length) : TypeVect(VectorD, elem_bt, length) {} }; class TypeVectX : public TypeVect { friend class TypeVect; - TypeVectX(const Type* elem, uint length) : TypeVect(VectorX, elem, length) {} + TypeVectX(BasicType elem_bt, uint length) : TypeVect(VectorX, elem_bt, length) {} }; class TypeVectY : public TypeVect { friend class TypeVect; - TypeVectY(const Type* elem, uint length) : TypeVect(VectorY, elem, length) {} + TypeVectY(BasicType elem_bt, uint length) : TypeVect(VectorY, elem_bt, length) {} }; class TypeVectZ : public TypeVect { friend class TypeVect; - TypeVectZ(const Type* elem, uint length) : TypeVect(VectorZ, elem, length) {} + TypeVectZ(BasicType elem_bt, uint length) : TypeVect(VectorZ, elem_bt, length) {} }; class TypeVectMask : public TypeVect { public: friend class TypeVect; - TypeVectMask(const Type* elem, uint length) : TypeVect(VectorMask, elem, length) {} - virtual bool eq(const Type *t) const; - virtual const Type *xdual() const; + TypeVectMask(BasicType elem_bt, uint length) : TypeVect(VectorMask, elem_bt, length) {} static const TypeVectMask* make(const BasicType elem_bt, uint length); - static const TypeVectMask* make(const Type* elem, uint length); }; // Set of implemented interfaces. Referenced from TypeOopPtr and TypeKlassPtr. diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index 8eb26c6c519..838f87eac01 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -524,17 +524,16 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) { Node* LibraryCallKit::partially_wrap_indexes(Node* index_vec, int num_elem, BasicType elem_bt) { assert(elem_bt == T_BYTE, "Shuffles use byte array based backing storage."); const TypeVect* vt = TypeVect::make(elem_bt, num_elem); - const Type* type_bt = Type::get_const_basic_type(elem_bt); Node* mod_mask = gvn().makecon(TypeInt::make(num_elem-1)); - Node* bcast_mod_mask = gvn().transform(VectorNode::scalar2vector(mod_mask, num_elem, type_bt)); + Node* bcast_mod_mask = gvn().transform(VectorNode::scalar2vector(mod_mask, num_elem, elem_bt)); BoolTest::mask pred = BoolTest::ugt; ConINode* pred_node = (ConINode*)gvn().makecon(TypeInt::make(pred)); Node* lane_cnt = gvn().makecon(TypeInt::make(num_elem)); - Node* bcast_lane_cnt = gvn().transform(VectorNode::scalar2vector(lane_cnt, num_elem, type_bt)); - const TypeVect* vmask_type = TypeVect::makemask(type_bt, num_elem); - Node* mask = gvn().transform(new VectorMaskCmpNode(pred, bcast_lane_cnt, index_vec, pred_node, vmask_type)); + Node* bcast_lane_cnt = gvn().transform(VectorNode::scalar2vector(lane_cnt, num_elem, elem_bt)); + const TypeVect* vmask_type = TypeVect::makemask(elem_bt, num_elem); + Node* mask = gvn().transform(new VectorMaskCmpNode(pred, bcast_lane_cnt, index_vec, pred_node, vmask_type)); // Make the indices greater than lane count as -ve values to match the java side implementation. index_vec = gvn().transform(VectorNode::make(Op_AndV, index_vec, bcast_mod_mask, vt)); @@ -600,8 +599,7 @@ bool LibraryCallKit::inline_vector_shuffle_iota() { return false; } - const Type * type_bt = Type::get_const_basic_type(elem_bt); - const TypeVect * vt = TypeVect::make(type_bt, num_elem); + const TypeVect* vt = TypeVect::make(elem_bt, num_elem); Node* res = gvn().transform(new VectorLoadConstNode(gvn().makecon(TypeInt::ZERO), vt)); @@ -609,7 +607,7 @@ bool LibraryCallKit::inline_vector_shuffle_iota() { Node* step = argument(5); if (step_multiply) { - Node* bcast_step = gvn().transform(VectorNode::scalar2vector(step, num_elem, type_bt)); + Node* bcast_step = gvn().transform(VectorNode::scalar2vector(step, num_elem, elem_bt)); res = gvn().transform(VectorNode::make(Op_MulVB, res, bcast_step, vt)); } else if (step_val->get_con() > 1) { Node* cnt = gvn().makecon(TypeInt::make(log2i_exact(step_val->get_con()))); @@ -618,12 +616,12 @@ bool LibraryCallKit::inline_vector_shuffle_iota() { } if (!start_val->is_con() || start_val->get_con() != 0) { - Node* bcast_start = gvn().transform(VectorNode::scalar2vector(start, num_elem, type_bt)); + Node* bcast_start = gvn().transform(VectorNode::scalar2vector(start, num_elem, elem_bt)); res = gvn().transform(VectorNode::make(Op_AddVB, res, bcast_start, vt)); } - Node * mod_val = gvn().makecon(TypeInt::make(num_elem-1)); - Node * bcast_mod = gvn().transform(VectorNode::scalar2vector(mod_val, num_elem, type_bt)); + Node* mod_val = gvn().makecon(TypeInt::make(num_elem-1)); + Node* bcast_mod = gvn().transform(VectorNode::scalar2vector(mod_val, num_elem, elem_bt)); if (do_wrap) { // Wrap the indices greater than lane count. @@ -802,9 +800,8 @@ bool LibraryCallKit::inline_vector_wrap_shuffle_indexes() { Node* shuffle_vec = unbox_vector(shuffle, shuffle_box_type, shuffle_bt, num_elem, true); const TypeVect* vt = TypeVect::make(shuffle_bt, num_elem); - const Type* shuffle_type_bt = Type::get_const_basic_type(shuffle_bt); - Node* mod_mask = gvn().makecon(TypeInt::make(num_elem-1)); - Node* bcast_mod_mask = gvn().transform(VectorNode::scalar2vector(mod_mask, num_elem, shuffle_type_bt)); + Node* mod_mask = gvn().makecon(TypeInt::make(num_elem - 1)); + Node* bcast_mod_mask = gvn().transform(VectorNode::scalar2vector(mod_mask, num_elem, shuffle_bt)); // Wrap the indices greater than lane count. Node* res = gvn().transform(VectorNode::make(Op_AndV, shuffle_vec, bcast_mod_mask, vt)); @@ -908,7 +905,7 @@ bool LibraryCallKit::inline_vector_frombits_coerced() { } default: fatal("%s", type2name(elem_bt)); } - broadcast = VectorNode::scalar2vector(elem, num_elem, Type::get_const_basic_type(elem_bt), is_mask); + broadcast = VectorNode::scalar2vector(elem, num_elem, elem_bt, is_mask); broadcast = gvn().transform(broadcast); } @@ -1352,7 +1349,7 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { } else { // Use the vector blend to implement the masked load vector. The biased elements are zeros. Node* zero = gvn().transform(gvn().zerocon(mem_elem_bt)); - zero = gvn().transform(VectorNode::scalar2vector(zero, mem_num_elem, Type::get_const_basic_type(mem_elem_bt))); + zero = gvn().transform(VectorNode::scalar2vector(zero, mem_num_elem, mem_elem_bt)); vload = gvn().transform(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, mem_num_elem, mem_elem_bt)); vload = gvn().transform(new VectorBlendNode(zero, vload, mask)); } @@ -1678,7 +1675,7 @@ bool LibraryCallKit::inline_vector_reduction() { assert(mask != nullptr || !is_masked_op, "Masked op needs the mask value never null"); if (mask != nullptr && !use_predicate) { - Node* reduce_identity = gvn().transform(VectorNode::scalar2vector(init, num_elem, Type::get_const_basic_type(elem_bt))); + Node* reduce_identity = gvn().transform(VectorNode::scalar2vector(init, num_elem, elem_bt)); value = gvn().transform(new VectorBlendNode(reduce_identity, value, mask)); } @@ -2059,7 +2056,7 @@ bool LibraryCallKit::inline_vector_rearrange() { const TypeVect* vt = v1->bottom_type()->is_vect(); rearrange = gvn().transform(rearrange); Node* zero = gvn().makecon(Type::get_zero_type(elem_bt)); - Node* zerovec = gvn().transform(VectorNode::scalar2vector(zero, num_elem, Type::get_const_basic_type(elem_bt))); + Node* zerovec = gvn().transform(VectorNode::scalar2vector(zero, num_elem, elem_bt)); rearrange = new VectorBlendNode(zerovec, rearrange, mask); } } @@ -2215,18 +2212,17 @@ bool LibraryCallKit::inline_vector_select_from() { } // cast index vector from elem_bt vector to byte vector - const Type * byte_bt = Type::get_const_basic_type(T_BYTE); - const TypeVect * byte_vt = TypeVect::make(byte_bt, num_elem); + const TypeVect* byte_vt = TypeVect::make(T_BYTE, num_elem); Node* byte_shuffle = gvn().transform(VectorCastNode::make(cast_vopc, v1, T_BYTE, num_elem)); // wrap the byte vector lanes to (num_elem - 1) to form the shuffle vector where num_elem is vector length // this is a simple AND operation as we come here only for power of two vector length Node* mod_val = gvn().makecon(TypeInt::make(num_elem-1)); - Node* bcast_mod = gvn().transform(VectorNode::scalar2vector(mod_val, num_elem, byte_bt)); + Node* bcast_mod = gvn().transform(VectorNode::scalar2vector(mod_val, num_elem, T_BYTE)); byte_shuffle = gvn().transform(VectorNode::make(Op_AndV, byte_shuffle, bcast_mod, byte_vt)); // load the shuffle to use in rearrange - const TypeVect * shuffle_vt = TypeVect::make(elem_bt, num_elem); + const TypeVect* shuffle_vt = TypeVect::make(elem_bt, num_elem); Node* load_shuffle = gvn().transform(new VectorLoadShuffleNode(byte_shuffle, shuffle_vt)); // and finally rearrange @@ -2243,7 +2239,7 @@ bool LibraryCallKit::inline_vector_select_from() { // create a zero vector with each lane element set as zero Node* zero = gvn().makecon(Type::get_zero_type(elem_bt)); - Node* zerovec = gvn().transform(VectorNode::scalar2vector(zero, num_elem, Type::get_const_basic_type(elem_bt))); + Node* zerovec = gvn().transform(VectorNode::scalar2vector(zero, num_elem, elem_bt)); // For each lane for which mask is set, blend in the rearranged lane into zero vector rearrange = new VectorBlendNode(zerovec, rearrange, mask); @@ -2391,9 +2387,8 @@ bool LibraryCallKit::inline_vector_broadcast_int() { } else { assert(is_rotate, "unexpected operation"); if (!is_const_rotate) { - const Type * type_bt = Type::get_const_basic_type(elem_bt); cnt = elem_bt == T_LONG ? gvn().transform(new ConvI2LNode(cnt)) : cnt; - opd2 = gvn().transform(VectorNode::scalar2vector(cnt, num_elem, type_bt)); + opd2 = gvn().transform(VectorNode::scalar2vector(cnt, num_elem, elem_bt)); } else { // Constant shift value. opd2 = cnt; @@ -3051,7 +3046,7 @@ bool LibraryCallKit::inline_index_vector() { } default: fatal("%s", type2name(elem_bt)); } - scale = gvn().transform(VectorNode::scalar2vector(scale, num_elem, Type::get_const_basic_type(elem_bt))); + scale = gvn().transform(VectorNode::scalar2vector(scale, num_elem, elem_bt)); index = gvn().transform(VectorNode::make(vmul_op, index, scale, vt)); } @@ -3164,7 +3159,7 @@ bool LibraryCallKit::inline_index_partially_in_upper_range() { } default: fatal("%s", type2name(elem_bt)); } - indexLimit = gvn().transform(VectorNode::scalar2vector(indexLimit, num_elem, Type::get_const_basic_type(elem_bt))); + indexLimit = gvn().transform(VectorNode::scalar2vector(indexLimit, num_elem, elem_bt)); // Load the "iota" vector. const TypeVect* vt = TypeVect::make(elem_bt, num_elem); diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index 72b49c043b6..094d4dca564 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -799,15 +799,13 @@ VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, Node* n3, uint vlen, B } // Scalar promotion -VectorNode* VectorNode::scalar2vector(Node* s, uint vlen, const Type* opd_t, bool is_mask) { - BasicType bt = opd_t->array_element_basic_type(); +VectorNode* VectorNode::scalar2vector(Node* s, uint vlen, BasicType bt, bool is_mask) { if (is_mask && Matcher::match_rule_supported_vector(Op_MaskAll, vlen, bt)) { - const TypeVect* vt = TypeVect::make(opd_t, vlen, true); + const TypeVect* vt = TypeVect::make(bt, vlen, true); return new MaskAllNode(s, vt); } - const TypeVect* vt = opd_t->singleton() ? TypeVect::make(opd_t, vlen) - : TypeVect::make(bt, vlen); + const TypeVect* vt = TypeVect::make(bt, vlen); return new ReplicateNode(s, vt); } @@ -1626,8 +1624,6 @@ Node* VectorNode::degenerate_vector_rotate(Node* src, Node* cnt, bool is_rotate_ Node* const_one_node = nullptr; assert(cnt->bottom_type()->isa_vect(), "Unexpected shift"); - const Type* elem_ty = Type::get_const_basic_type(bt); - if (bt == T_LONG) { shift_mask_node = phase->longcon(shift_mask); const_one_node = phase->longcon(1L); @@ -1639,8 +1635,8 @@ Node* VectorNode::degenerate_vector_rotate(Node* src, Node* cnt, bool is_rotate_ subVopc = VectorNode::opcode(Op_SubI, bt); addVopc = VectorNode::opcode(Op_AddI, bt); } - Node* vector_mask = phase->transform(VectorNode::scalar2vector(shift_mask_node, vlen, elem_ty)); - Node* vector_one = phase->transform(VectorNode::scalar2vector(const_one_node, vlen, elem_ty)); + Node* vector_mask = phase->transform(VectorNode::scalar2vector(shift_mask_node, vlen, bt)); + Node* vector_one = phase->transform(VectorNode::scalar2vector(const_one_node, vlen, bt)); shiftRCnt = cnt; shiftRCnt = phase->transform(VectorNode::make(Op_AndV, shiftRCnt, vector_mask, vt)); @@ -1882,12 +1878,12 @@ Node* NegVNode::degenerate_integral_negate(PhaseGVN* phase, bool is_predicated) const_one = phase->intcon(1); add_opc = Op_AddI; } - const_minus_one = phase->transform(VectorNode::scalar2vector(const_minus_one, vlen, Type::get_const_basic_type(bt))); + const_minus_one = phase->transform(VectorNode::scalar2vector(const_minus_one, vlen, bt)); Node* xorv = VectorNode::make(Op_XorV, in(1), const_minus_one, vt); xorv->add_req(in(2)); xorv->add_flag(Node::Flag_is_predicated_vector); phase->transform(xorv); - const_one = phase->transform(VectorNode::scalar2vector(const_one, vlen, Type::get_const_basic_type(bt))); + const_one = phase->transform(VectorNode::scalar2vector(const_one, vlen, bt)); Node* addv = VectorNode::make(VectorNode::opcode(add_opc, bt), xorv, const_one, vt); addv->add_req(in(2)); addv->add_flag(Node::Flag_is_predicated_vector); @@ -1904,7 +1900,7 @@ Node* NegVNode::degenerate_integral_negate(PhaseGVN* phase, bool is_predicated) const_zero = phase->intcon(0); sub_opc = Op_SubI; } - const_zero = phase->transform(VectorNode::scalar2vector(const_zero, vlen, Type::get_const_basic_type(bt))); + const_zero = phase->transform(VectorNode::scalar2vector(const_zero, vlen, bt)); return VectorNode::make(VectorNode::opcode(sub_opc, bt), const_zero, in(1), vt); } @@ -2069,8 +2065,7 @@ Node* XorVNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (!is_predicated_vector() && (in(1) == in(2))) { BasicType bt = vect_type()->element_basic_type(); Node* zero = phase->transform(phase->zerocon(bt)); - return VectorNode::scalar2vector(zero, length(), Type::get_const_basic_type(bt), - bottom_type()->isa_vectmask() != nullptr); + return VectorNode::scalar2vector(zero, length(), bt, bottom_type()->isa_vectmask() != nullptr); } return nullptr; } diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index 23ddebaf338..d23e6b8c926 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -75,7 +75,7 @@ class VectorNode : public TypeNode { virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); - static VectorNode* scalar2vector(Node* s, uint vlen, const Type* opd_t, bool is_mask = false); + static VectorNode* scalar2vector(Node* s, uint vlen, BasicType bt, bool is_mask = false); static VectorNode* shift_count(int opc, Node* cnt, uint vlen, BasicType bt); static VectorNode* make(int opc, Node* n1, Node* n2, uint vlen, BasicType bt, bool is_var_shift = false); static VectorNode* make(int vopc, Node* n1, Node* n2, const TypeVect* vt, bool is_mask = false, bool is_var_shift = false); diff --git a/src/hotspot/share/opto/vtransform.cpp b/src/hotspot/share/opto/vtransform.cpp index e40157caa36..7c7aca3b90e 100644 --- a/src/hotspot/share/opto/vtransform.cpp +++ b/src/hotspot/share/opto/vtransform.cpp @@ -422,8 +422,7 @@ void VTransformScalarNode::print_spec() const { } void VTransformReplicateNode::print_spec() const { - tty->print("vlen=%d element_type=", _vlen); - _element_type->dump(); + tty->print("vlen=%d element_type=%s", _vlen, type2name(_element_type)); } void VTransformShiftCountNode::print_spec() const { diff --git a/src/hotspot/share/opto/vtransform.hpp b/src/hotspot/share/opto/vtransform.hpp index 071674533a7..ee298e7fe72 100644 --- a/src/hotspot/share/opto/vtransform.hpp +++ b/src/hotspot/share/opto/vtransform.hpp @@ -354,9 +354,9 @@ class VTransformInputScalarNode : public VTransformScalarNode { class VTransformReplicateNode : public VTransformNode { private: int _vlen; - const Type* _element_type; + BasicType _element_type; public: - VTransformReplicateNode(VTransform& vtransform, int vlen, const Type* element_type) : + VTransformReplicateNode(VTransform& vtransform, int vlen, BasicType element_type) : VTransformNode(vtransform, 2), _vlen(vlen), _element_type(element_type) {} virtual VTransformApplyResult apply(const VLoopAnalyzer& vloop_analyzer, const GrowableArray& vnode_idx_to_transformed_node) const override; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index a7c61f71050..8c4b3c93343 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -119,13 +119,13 @@ public class IRNode { public static final String VECTOR_SIZE_32 = VECTOR_SIZE + "32"; public static final String VECTOR_SIZE_64 = VECTOR_SIZE + "64"; - private static final String TYPE_BYTE = "byte"; - private static final String TYPE_CHAR = "char"; - private static final String TYPE_SHORT = "short"; - private static final String TYPE_INT = "int"; - private static final String TYPE_LONG = "long"; - private static final String TYPE_FLOAT = "float"; - private static final String TYPE_DOUBLE = "double"; + private static final String TYPE_BYTE = "B"; + private static final String TYPE_CHAR = "C"; + private static final String TYPE_SHORT = "S"; + private static final String TYPE_INT = "I"; + private static final String TYPE_LONG = "J"; + private static final String TYPE_FLOAT = "F"; + private static final String TYPE_DOUBLE = "D"; /** * IR placeholder string to regex-for-compile-phase map. diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.java index e7db6c4844c..bf3021a6868 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -133,7 +133,6 @@ private String regexForVectorIRNode(String nodeRegex, VMInfo vmInfo, Comparison. } } String sizeRegex = IRNode.parseVectorNodeSize(size, type, vmInfo); - return nodeRegex.replaceAll(IRNode.IS_REPLACED, - "vector[A-Za-z]\\\\[" + sizeRegex + "\\\\]:\\\\{" + type + "\\\\}"); + return nodeRegex.replaceAll(IRNode.IS_REPLACED, "vector[A-Za-z]<" + type + "," + sizeRegex + ">"); } } From 1f6bd0c3e509029bbf524b0ba34ce44601ac0b6a Mon Sep 17 00:00:00 2001 From: Damon Nguyen Date: Fri, 11 Oct 2024 20:03:14 +0000 Subject: [PATCH 24/95] 8341972: java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java timed out after JDK-8341257 Reviewed-by: azvegint --- test/jdk/ProblemList.txt | 1 + .../awt/dnd/DnDRemoveFocusOwnerCrashTest.java | 49 +++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index db0794f8f2d..034830a2575 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -201,6 +201,7 @@ java/awt/event/KeyEvent/ExtendedKeyCode/ExtendedKeyCodeTest.java 8169476 windows java/awt/event/KeyEvent/KeyChar/KeyCharTest.java 8169474,8224055 macosx-all,windows-all java/awt/event/KeyEvent/KeyTyped/CtrlASCII.java 8298910 linux-all +java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java 8242805 macosx-all java/awt/dnd/DnDCursorCrashTest.java 8242805 macosx-all java/awt/dnd/DnDClipboardDeadlockTest.java 8079553 linux-all java/awt/dnd/URIListToFileListBetweenJVMsTest/URIListToFileListBetweenJVMsTest.java 8194947 generic-all diff --git a/test/jdk/java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java b/test/jdk/java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java index 27cf54e1845..1fc7c2e82b8 100644 --- a/test/jdk/java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java +++ b/test/jdk/java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java @@ -64,10 +64,13 @@ public class DnDRemoveFocusOwnerCrashTest { public static Frame frame; public static Robot robot; public static DragSourceButton dragSourceButton; + static volatile Point p; public static void main(String[] args) throws Exception { try { robot = new Robot(); + robot.setAutoWaitForIdle(true); + robot.delay(FRAME_ACTIVATION_TIMEOUT); EventQueue.invokeAndWait(() -> { frame = new Frame(); dragSourceButton = new DragSourceButton(); @@ -79,37 +82,33 @@ public static void main(String[] args) throws Exception { frame.add(dropTargetPanel); frame.pack(); frame.setVisible(true); + }); - try { - robot.delay(FRAME_ACTIVATION_TIMEOUT); - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException("The test failed."); - } + robot.waitForIdle(); + robot.delay(FRAME_ACTIVATION_TIMEOUT); - Point p = dragSourceButton.getLocationOnScreen(); + EventQueue.invokeAndWait(() -> { + p = dragSourceButton.getLocationOnScreen(); p.translate(10, 10); + }); - try { - Robot robot = new Robot(); - robot.mouseMove(p.x, p.y); - robot.keyPress(KeyEvent.VK_CONTROL); - robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); - for (int dy = 0; dy < 50; dy++) { - robot.mouseMove(p.x, p.y + dy); - robot.delay(10); - } - robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); - robot.keyRelease(KeyEvent.VK_CONTROL); - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException("The test failed."); + robot.delay(FRAME_ACTIVATION_TIMEOUT); + robot.mouseMove(p.x, p.y); + robot.delay(FRAME_ACTIVATION_TIMEOUT); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (int dy = 0; dy < 50; dy++) { + robot.mouseMove(p.x, p.y + dy); + robot.delay(10); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); } }); - } finally { - if (frame != null) { - EventQueue.invokeAndWait(() -> frame.dispose()); - } } } From 0a57fe1df6f3431cfb2d5d868597c61ef6af3806 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Fri, 11 Oct 2024 21:11:12 +0000 Subject: [PATCH 25/95] 8341178: TypeRawPtr::add_offset may be "miscompiled" due to UB Reviewed-by: dlong, kvn --- src/hotspot/share/opto/type.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 828cea88d83..70cd46c900d 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -3111,8 +3111,8 @@ const TypeRawPtr *TypeRawPtr::make( enum PTR ptr ) { return (TypeRawPtr*)(new TypeRawPtr(ptr,nullptr))->hashcons(); } -const TypeRawPtr *TypeRawPtr::make( address bits ) { - assert( bits, "Use TypePtr for null" ); +const TypeRawPtr *TypeRawPtr::make(address bits) { + assert(bits != nullptr, "Use TypePtr for null"); return (TypeRawPtr*)(new TypeRawPtr(Constant,bits))->hashcons(); } @@ -3201,15 +3201,21 @@ const TypePtr* TypeRawPtr::add_offset(intptr_t offset) const { case TypePtr::BotPTR: case TypePtr::NotNull: return this; - case TypePtr::Null: case TypePtr::Constant: { - address bits = _bits+offset; - if ( bits == 0 ) return TypePtr::NULL_PTR; - return make( bits ); + uintptr_t bits = (uintptr_t)_bits; + uintptr_t sum = bits + offset; + if (( offset < 0 ) + ? ( sum > bits ) // Underflow? + : ( sum < bits )) { // Overflow? + return BOTTOM; + } else if ( sum == 0 ) { + return TypePtr::NULL_PTR; + } else { + return make( (address)sum ); + } } default: ShouldNotReachHere(); } - return nullptr; // Lint noise } //------------------------------eq--------------------------------------------- From 3f53d571343792341481f4d15970cdc0bcd76a5e Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Fri, 11 Oct 2024 21:16:41 +0000 Subject: [PATCH 26/95] 8340327: A common framework to support public key algorithms with standard parameter sets Reviewed-by: ascarpino, mullan --- .../sun/security/pkcs/NamedPKCS8Key.java | 143 +++++++++ .../sun/security/provider/NamedKEM.java | 223 ++++++++++++++ .../security/provider/NamedKeyFactory.java | 288 ++++++++++++++++++ .../provider/NamedKeyPairGenerator.java | 177 +++++++++++ .../sun/security/provider/NamedSignature.java | 222 ++++++++++++++ .../classes/sun/security/util/KeyUtil.java | 8 +- .../sun/security/util/SignatureUtil.java | 21 +- .../sun/security/x509/NamedX509Key.java | 120 ++++++++ .../jdk/sun/security/provider/NamedEdDSA.java | 239 +++++++++++++++ .../provider/NamedKeyFactoryTest.java | 261 ++++++++++++++++ 10 files changed, 1693 insertions(+), 9 deletions(-) create mode 100644 src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java create mode 100644 src/java.base/share/classes/sun/security/provider/NamedKEM.java create mode 100644 src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java create mode 100644 src/java.base/share/classes/sun/security/provider/NamedKeyPairGenerator.java create mode 100644 src/java.base/share/classes/sun/security/provider/NamedSignature.java create mode 100644 src/java.base/share/classes/sun/security/x509/NamedX509Key.java create mode 100644 test/jdk/sun/security/provider/NamedEdDSA.java create mode 100644 test/jdk/sun/security/provider/NamedKeyFactoryTest.java diff --git a/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java b/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java new file mode 100644 index 00000000000..88a2909cfac --- /dev/null +++ b/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs; + +import sun.security.util.DerInputStream; +import sun.security.util.DerValue; +import sun.security.x509.AlgorithmId; + +import javax.security.auth.DestroyFailedException; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serial; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.ProviderException; +import java.security.spec.NamedParameterSpec; +import java.util.Arrays; + +/// Represents a private key from an algorithm family that is specialized +/// with a named parameter set. +/// +/// This key is generated by either a [sun.security.provider.NamedKeyPairGenerator] +/// or [sun.security.provider.NamedKeyFactory]. Its [#getAlgorithm] method +/// returns the algorithm family name, while its [#getParams()] method returns +/// the parameter set name as a [NamedParameterSpec] object. The algorithm +/// identifier in the PKCS #8 encoding of the key is always a single OID derived +/// from the parameter set name. +/// +/// @see sun.security.provider.NamedKeyPairGenerator +public final class NamedPKCS8Key extends PKCS8Key { + @Serial + private static final long serialVersionUID = 1L; + + private final String fname; + private final transient NamedParameterSpec paramSpec; + private final byte[] rawBytes; + + private transient boolean destroyed = false; + + /// Ctor from family name, parameter set name, raw key bytes. + /// Key bytes won't be cloned, caller must relinquish ownership + public NamedPKCS8Key(String fname, String pname, byte[] rawBytes) { + this.fname = fname; + this.paramSpec = new NamedParameterSpec(pname); + try { + this.algid = AlgorithmId.get(pname); + } catch (NoSuchAlgorithmException e) { + throw new ProviderException(e); + } + this.rawBytes = rawBytes; + + DerValue val = new DerValue(DerValue.tag_OctetString, rawBytes); + try { + this.key = val.toByteArray(); + } finally { + val.clear(); + } + } + + /// Ctor from family name, and PKCS #8 bytes + public NamedPKCS8Key(String fname, byte[] encoded) throws InvalidKeyException { + super(encoded); + this.fname = fname; + try { + paramSpec = new NamedParameterSpec(algid.getName()); + if (algid.getEncodedParams() != null) { + throw new InvalidKeyException("algorithm identifier has params"); + } + rawBytes = new DerInputStream(key).getOctetString(); + } catch (IOException e) { + throw new InvalidKeyException("Cannot parse input", e); + } + } + + @Override + public String toString() { + // Do not modify: this can be used by earlier JDKs that + // do not have the getParams() method + return paramSpec.getName() + " private key"; + } + + /// Returns the reference to the internal key. Caller must not modify + /// the content or keep a reference. + public byte[] getRawBytes() { + return rawBytes; + } + + @Override + public NamedParameterSpec getParams() { + return paramSpec; + } + + @Override + public String getAlgorithm() { + return fname; + } + + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "NamedPKCS8Key keys are not directly deserializable"); + } + + @Override + public void destroy() throws DestroyFailedException { + Arrays.fill(rawBytes, (byte)0); + Arrays.fill(key, (byte)0); + if (encodedKey != null) { + Arrays.fill(encodedKey, (byte)0); + } + destroyed = true; + } + + @Override + public boolean isDestroyed() { + return destroyed; + } +} diff --git a/src/java.base/share/classes/sun/security/provider/NamedKEM.java b/src/java.base/share/classes/sun/security/provider/NamedKEM.java new file mode 100644 index 00000000000..2731b3460af --- /dev/null +++ b/src/java.base/share/classes/sun/security/provider/NamedKEM.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import sun.security.pkcs.NamedPKCS8Key; +import sun.security.x509.NamedX509Key; + +import javax.crypto.DecapsulateException; +import javax.crypto.KEM; +import javax.crypto.KEMSpi; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.ProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.NamedParameterSpec; +import java.util.Arrays; +import java.util.Objects; + +/// A base class for all `KEM` implementations that can be +/// configured with a named parameter set. See [NamedKeyPairGenerator] +/// for more details. +public abstract class NamedKEM implements KEMSpi { + + private final String fname; // family name + private final String[] pnames; // allowed parameter set name (at least one) + + /// Creates a new `NamedKEM` object. + /// + /// @param fname the family name + /// @param pnames the standard parameter set names, at least one is needed. + protected NamedKEM(String fname, String... pnames) { + if (fname == null) { + throw new AssertionError("fname cannot be null"); + } + if (pnames == null || pnames.length == 0) { + throw new AssertionError("pnames cannot be null or empty"); + } + this.fname = fname; + this.pnames = pnames; + } + + @Override + public EncapsulatorSpi engineNewEncapsulator(PublicKey publicKey, + AlgorithmParameterSpec spec, SecureRandom secureRandom) + throws InvalidAlgorithmParameterException, InvalidKeyException { + if (spec != null) { + throw new InvalidAlgorithmParameterException( + "The " + fname + " algorithm does not take any parameters"); + } + // translate also check the key + var nk = (NamedX509Key) new NamedKeyFactory(fname, pnames) + .engineTranslateKey(publicKey); + var pk = nk.getRawBytes(); + return getKeyConsumerImpl(this, nk.getParams(), pk, + implCheckPublicKey(nk.getParams().getName(), pk), secureRandom); + } + + @Override + public DecapsulatorSpi engineNewDecapsulator( + PrivateKey privateKey, AlgorithmParameterSpec spec) + throws InvalidAlgorithmParameterException, InvalidKeyException { + if (spec != null) { + throw new InvalidAlgorithmParameterException( + "The " + fname + " algorithm does not take any parameters"); + } + // translate also check the key + var nk = (NamedPKCS8Key) new NamedKeyFactory(fname, pnames) + .engineTranslateKey(privateKey); + var sk = nk.getRawBytes(); + return getKeyConsumerImpl(this, nk.getParams(), sk, + implCheckPrivateKey(nk.getParams().getName(), sk), null); + } + + // We don't have a flag on whether key is public key or private key. + // The correct method should always be called. + private record KeyConsumerImpl(NamedKEM kem, String name, int sslen, + int clen, byte[] key, Object k2, SecureRandom sr) + implements KEMSpi.EncapsulatorSpi, KEMSpi.DecapsulatorSpi { + @Override + public SecretKey engineDecapsulate(byte[] encapsulation, int from, int to, + String algorithm) throws DecapsulateException { + if (encapsulation.length != clen) { + throw new DecapsulateException("Invalid key encapsulation message length"); + } + var ss = kem.implDecapsulate(name, key, k2, encapsulation); + try { + return new SecretKeySpec(ss, + from, to - from, algorithm); + } finally { + Arrays.fill(ss, (byte)0); + } + } + + @Override + public KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm) { + var enc = kem.implEncapsulate(name, key, k2, sr); + try { + return new KEM.Encapsulated( + new SecretKeySpec(enc[1], + from, to - from, algorithm), + enc[0], + null); + } finally { + Arrays.fill(enc[1], (byte)0); + } + } + + @Override + public int engineSecretSize() { + return sslen; + } + + @Override + public int engineEncapsulationSize() { + return clen; + } + } + + private static KeyConsumerImpl getKeyConsumerImpl(NamedKEM kem, + NamedParameterSpec nps, byte[] key, Object k2, SecureRandom sr) { + String name = nps.getName(); + return new KeyConsumerImpl(kem, name, kem.implSecretSize(name), kem.implEncapsulationSize(name), + key, k2, sr); + } + + /// User-defined encap function. + /// + /// @param name parameter name + /// @param pk public key in raw bytes + /// @param pk2 parsed public key, `null` if none. See [#implCheckPublicKey]. + /// @param sr SecureRandom object, `null` if not initialized + /// @return the key encapsulation message and the shared key (in this order) + /// @throws ProviderException if there is an internal error + protected abstract byte[][] implEncapsulate(String name, byte[] pk, Object pk2, SecureRandom sr); + + /// User-defined decap function. + /// + /// @param name parameter name + /// @param sk private key in raw bytes + /// @param sk2 parsed private key, `null` if none. See [#implCheckPrivateKey]. + /// @param encap the key encapsulation message + /// @return the shared key + /// @throws ProviderException if there is an internal error + /// @throws DecapsulateException if there is another error + protected abstract byte[] implDecapsulate(String name, byte[] sk, Object sk2, byte[] encap) + throws DecapsulateException; + + /// User-defined function returning shared secret key length. + /// + /// @param name parameter name + /// @return shared secret key length + /// @throws ProviderException if there is an internal error + protected abstract int implSecretSize(String name); + + /// User-defined function returning key encapsulation message length. + /// + /// @param name parameter name + /// @return key encapsulation message length + /// @throws ProviderException if there is an internal error + protected abstract int implEncapsulationSize(String name); + + /// User-defined function to validate a public key. + /// + /// This method will be called in `newEncapsulator`. This gives the provider a chance to + /// reject the key so an `InvalidKeyException` can be thrown earlier. + /// An implementation can optionally return a "parsed key" as an `Object` value. + /// This object will be passed into the [#implEncapsulate] method along with the raw key. + /// + /// The default implementation returns `null`. + /// + /// @param name parameter name + /// @param pk public key in raw bytes + /// @return a parsed key, `null` if none. + /// @throws InvalidKeyException if the key is invalid + protected Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyException { + return null; + } + + /// User-defined function to validate a private key. + /// + /// This method will be called in `newDecapsulator`. This gives the provider a chance to + /// reject the key so an `InvalidKeyException` can be thrown earlier. + /// An implementation can optionally return a "parsed key" as an `Object` value. + /// This object will be passed into the [#implDecapsulate] method along with the raw key. + /// + /// The default implementation returns `null`. + /// + /// @param name parameter name + /// @param sk private key in raw bytes + /// @return a parsed key, `null` if none. + /// @throws InvalidKeyException if the key is invalid + protected Object implCheckPrivateKey(String name, byte[] sk) throws InvalidKeyException { + return null; + } +} diff --git a/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java b/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java new file mode 100644 index 00000000000..727358dd074 --- /dev/null +++ b/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import sun.security.pkcs.NamedPKCS8Key; +import sun.security.util.RawKeySpec; +import sun.security.x509.NamedX509Key; + +import java.security.AsymmetricKey; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactorySpi; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.EncodedKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.NamedParameterSpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; +import java.util.Objects; + +/// A base class for all `KeyFactory` implementations that can be +/// configured with a named parameter set. See [NamedKeyPairGenerator] +/// for more details. +/// +/// This factory supports reading and writing to RAW formats: +/// +/// 1. It reads from a RAW key using `translateKey` if `key.getFormat` is "RAW". +/// 2. It writes to a RAW [EncodedKeySpec] if `getKeySpec(key, EncodedKeySpec.class)` +/// is called. The format of the output is "RAW" and the algorithm is +/// intentionally left unspecified. +/// 3. It reads from and writes to the internal type [RawKeySpec]. +/// +/// When reading from a RAW format, it needs enough info to derive the +/// parameter set name. +public class NamedKeyFactory extends KeyFactorySpi { + + private final String fname; // family name + private final String[] pnames; // allowed parameter set name (at least one) + + /// Creates a new `NamedKeyFactory` object. + /// + /// @param fname the family name + /// @param pnames the standard parameter set names, at least one is needed. + protected NamedKeyFactory(String fname, String... pnames) { + if (fname == null) { + throw new AssertionError("fname cannot be null"); + } + if (pnames == null || pnames.length == 0) { + throw new AssertionError("pnames cannot be null or empty"); + } + this.fname = fname; + this.pnames = pnames; + } + + private String checkName(String name) throws InvalidKeyException { + for (var pname : pnames) { + if (pname.equalsIgnoreCase(name)) { + // return the stored standard name + return pname; + } + } + throw new InvalidKeyException("Unsupported parameter set name: " + name); + } + + @Override + protected PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException { + if (keySpec instanceof X509EncodedKeySpec xspec) { + try { + return fromX509(xspec.getEncoded()); + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof RawKeySpec rks) { + if (pnames.length == 1) { + return new NamedX509Key(fname, pnames[0], rks.getKeyArr()); + } else { + throw new InvalidKeySpecException("Parameter set name unavailable"); + } + } else if (keySpec instanceof EncodedKeySpec espec + && espec.getFormat().equalsIgnoreCase("RAW")) { + if (pnames.length == 1) { + return new NamedX509Key(fname, pnames[0], espec.getEncoded()); + } else { + throw new InvalidKeySpecException("Parameter set name unavailable"); + } + } else { + throw new InvalidKeySpecException("Unsupported keyspec: " + keySpec); + } + } + + @Override + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) + throws InvalidKeySpecException { + if (keySpec instanceof PKCS8EncodedKeySpec pspec) { + var bytes = pspec.getEncoded(); + try { + return fromPKCS8(bytes); + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException(e); + } finally { + Arrays.fill(bytes, (byte) 0); + } + } else if (keySpec instanceof RawKeySpec rks) { + if (pnames.length == 1) { + var bytes = rks.getKeyArr(); + try { + return new NamedPKCS8Key(fname, pnames[0], bytes); + } finally { + Arrays.fill(bytes, (byte) 0); + } + } else { + throw new InvalidKeySpecException("Parameter set name unavailable"); + } + } else if (keySpec instanceof EncodedKeySpec espec + && espec.getFormat().equalsIgnoreCase("RAW")) { + if (pnames.length == 1) { + var bytes = espec.getEncoded(); + try { + return new NamedPKCS8Key(fname, pnames[0], bytes); + } finally { + Arrays.fill(bytes, (byte) 0); + } + } else { + throw new InvalidKeySpecException("Parameter set name unavailable"); + } + } else { + throw new InvalidKeySpecException("Unsupported keyspec: " + keySpec); + } + } + + private PrivateKey fromPKCS8(byte[] bytes) + throws InvalidKeyException, InvalidKeySpecException { + var k = new NamedPKCS8Key(fname, bytes); + checkName(k.getParams().getName()); + return k; + } + + private PublicKey fromX509(byte[] bytes) + throws InvalidKeyException, InvalidKeySpecException { + var k = new NamedX509Key(fname, bytes); + checkName(k.getParams().getName()); + return k; + } + + private static class RawEncodedKeySpec extends EncodedKeySpec { + public RawEncodedKeySpec(byte[] encodedKey) { + super(encodedKey); + } + + @Override + public String getFormat() { + return "RAW"; + } + } + + @Override + protected T engineGetKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException { + try { + key = engineTranslateKey(key); + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException(e); + } + // key is now either NamedPKCS8Key or NamedX509Key of permitted param set + if (key instanceof NamedPKCS8Key nk) { + byte[] bytes = null; + try { + if (keySpec == PKCS8EncodedKeySpec.class) { + return keySpec.cast( + new PKCS8EncodedKeySpec(bytes = key.getEncoded())); + } else if (keySpec == RawKeySpec.class) { + return keySpec.cast(new RawKeySpec(nk.getRawBytes())); + } else if (keySpec.isAssignableFrom(EncodedKeySpec.class)) { + return keySpec.cast( + new RawEncodedKeySpec(nk.getRawBytes())); + } else { + throw new InvalidKeySpecException("Unsupported type: " + keySpec); + } + } finally { + if (bytes != null) { + Arrays.fill(bytes, (byte)0); + } + } + } else if (key instanceof NamedX509Key nk) { + if (keySpec == X509EncodedKeySpec.class + && key.getFormat().equalsIgnoreCase("X.509")) { + return keySpec.cast(new X509EncodedKeySpec(key.getEncoded())); + } else if (keySpec == RawKeySpec.class) { + return keySpec.cast(new RawKeySpec(nk.getRawBytes())); + } else if (keySpec.isAssignableFrom(EncodedKeySpec.class)) { + return keySpec.cast(new RawEncodedKeySpec(nk.getRawBytes())); + } else { + throw new InvalidKeySpecException("Unsupported type: " + keySpec); + } + } + throw new AssertionError("No " + keySpec.getName() + " for " + key.getClass()); + } + + @Override + protected Key engineTranslateKey(Key key) throws InvalidKeyException { + if (key == null) { + throw new InvalidKeyException("Key must not be null"); + } + if (key instanceof NamedX509Key nk) { + checkName(nk.getParams().getName()); + return key; + } + if (key instanceof NamedPKCS8Key nk) { + checkName(nk.getParams().getName()); + return key; + } + var format = key.getFormat(); + if (format == null) { + throw new InvalidKeyException("Unextractable key"); + } else if (format.equalsIgnoreCase("RAW")) { + var kAlg = key.getAlgorithm(); + if (key instanceof AsymmetricKey pk) { + String name; + // Three cases that we can find the parameter set name from a RAW key: + // 1. getParams() returns one + // 2. getAlgorithm() returns param set name (some provider does this) + // 3. getAlgorithm() returns family name but this KF is for param set name + if (pk.getParams() instanceof NamedParameterSpec nps) { + name = checkName(nps.getName()); + } else { + if (kAlg.equalsIgnoreCase(fname)) { + if (pnames.length == 1) { + name = pnames[0]; + } else { + throw new InvalidKeyException("No parameter set info"); + } + } else { + name = checkName(kAlg); + } + } + return key instanceof PrivateKey + ? new NamedPKCS8Key(fname, name, key.getEncoded()) + : new NamedX509Key(fname, name, key.getEncoded()); + } else { + throw new InvalidKeyException("Unsupported key type: " + key.getClass()); + } + } else if (format.equalsIgnoreCase("PKCS#8") && key instanceof PrivateKey) { + var bytes = key.getEncoded(); + try { + return fromPKCS8(bytes); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException("Invalid PKCS#8 key", e); + } finally { + Arrays.fill(bytes, (byte) 0); + } + } else if (format.equalsIgnoreCase("X.509") && key instanceof PublicKey) { + try { + return fromX509(key.getEncoded()); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException("Invalid X.509 key", e); + } + } else { + throw new InvalidKeyException("Unsupported key format: " + key.getFormat()); + } + } +} diff --git a/src/java.base/share/classes/sun/security/provider/NamedKeyPairGenerator.java b/src/java.base/share/classes/sun/security/provider/NamedKeyPairGenerator.java new file mode 100644 index 00000000000..5be2b2b2a08 --- /dev/null +++ b/src/java.base/share/classes/sun/security/provider/NamedKeyPairGenerator.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import sun.security.pkcs.NamedPKCS8Key; +import sun.security.x509.NamedX509Key; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.KeyPair; +import java.security.KeyPairGeneratorSpi; +import java.security.ProviderException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.NamedParameterSpec; +import java.util.Objects; + +/// A base class for all `KeyPairGenerator` implementations that can be +/// configured with a named parameter set. +/// +/// Together with [NamedKeyFactory], [NamedKEM], and [NamedSignature], these +/// classes form a compact framework designed to support any public key +/// algorithm standardized with named parameter sets. In this scenario, +/// the algorithm name is the "family name" and each standardized parameter +/// set has a "parameter set name". Implementations of these classes are able +/// to instantiate a `KeyPairGenerator`, `KeyFactory`, or `KEM` or `Signature` +/// object using either the family name or a parameter set name. All keys used +/// in this context will be of the type [NamedPKCS8Key] or [NamedX509Key], +/// with `getAlgorithm` returning the family name, and `getParams` returning +/// the parameter set name as a [NamedParameterSpec] object. +/// +/// An implementation must include a zero-argument public constructor that +/// calls `super(fname, pnames)`, where `fname` is the family name of the +/// algorithm and `pnames` are its supported parameter set names. `pnames` +/// must contain at least one element. For an implementation of +/// `NamedKeyPairGenerator`, the first element becomes its default parameter +/// set, i.e. the parameter set to be used in key pair generation unless +/// [#initialize(AlgorithmParameterSpec, java.security.SecureRandom)] +/// is called on a different parameter set. +/// +/// An implementation must implement all abstract methods. For all these +/// methods, the implementation must relinquish any "ownership" of any input +/// and output array argument. Precisely, the implementation must not retain +/// any reference to a returning array so that it won't be able to modify its +/// content later. Similarly, the implementation must not modify any input +/// array argument and must not retain any reference to an input array argument +/// after the call. +/// +/// Also, an implementation must not keep any extra copy of a private key. +/// For key generation, the only copy is the one returned in the +/// [#implGenerateKeyPair] call. For all other methods, it must not make +/// a copy of the input private key. A `KEM` implementation also must not +/// keep a copy of the shared secret key, no matter if it's an encapsulator +/// or a decapsulator. Only the code that owns these sensitive data can +/// choose to perform cleanup when it determines they are no longer needed. +/// +/// The `NamedSignature` and `NamedKEM` classes provide `implCheckPublicKey` +/// and `implCheckPrivateKey` methods that allow an implementation to validate +/// a key before using it. An implementation may return a parsed key in +/// a local type, and this parsed key will be passed to an operational method +/// (For example, `implSign`) later. An implementation must not retain +/// a reference of the parsed key. +/// +/// When constructing a [NamedX509Key] or [NamedPKCS8Key] object from raw key +/// bytes, the key bytes are directly referenced within the object, so the +/// caller must not modify them afterward. Similarly, the key's `getRawBytes` +/// method returns direct references to the underlying raw key bytes, meaning +/// the caller must not alter the contents of the returned value. +/// +/// Together, these measures ensure the classes are as efficient as possible, +/// preventing unnecessary array cloning and potential data leaks. While these +/// classes should not be considered immutable, strictly adhering to the rules +/// above will ensure data integrity is maintained. +/// +/// Note: A limitation of `NamedKeyPairGenerator` and `NamedKeyFactory` is +/// that the keys generated by their implementations will always be of type +/// `NamedX509Key` or `NamedPKCS8Key`. Existing implementations of algorithms +/// like EdDSA and XDH have been generating keys implementing `EdECKey` or +/// `XECKey` interfaces, and they are not rewritten with this framework. +/// `NamedParameterSpec` fields not implemented with this framework include +/// Ed25519, Ed448, X25519, and X448. +public abstract class NamedKeyPairGenerator extends KeyPairGeneratorSpi { + + private final String fname; // family name + private final String[] pnames; // allowed parameter set name (at least one) + + protected String name; // init as + private SecureRandom secureRandom; + + /// Creates a new `NamedKeyPairGenerator` object. + /// + /// @param fname the family name + /// @param pnames supported parameter set names, at least one is needed. + /// If multiple, the first one becomes the default parameter set name. + protected NamedKeyPairGenerator(String fname, String... pnames) { + if (fname == null) { + throw new AssertionError("fname cannot be null"); + } + if (pnames == null || pnames.length == 0) { + throw new AssertionError("pnames cannot be null or empty"); + } + this.fname = fname; + this.pnames = pnames; + } + + private String checkName(String name) throws InvalidAlgorithmParameterException { + for (var pname : pnames) { + if (pname.equalsIgnoreCase(name)) { + // return the stored standard name + return pname; + } + } + throw new InvalidAlgorithmParameterException( + "Unsupported parameter set name: " + name); + } + + @Override + public void initialize(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException { + if (params instanceof NamedParameterSpec spec) { + name = checkName(spec.getName()); + } else { + throw new InvalidAlgorithmParameterException( + "Unsupported AlgorithmParameterSpec: " + params); + } + this.secureRandom = random; + } + + @Override + public void initialize(int keysize, SecureRandom random) { + if (keysize != -1) { + // User can call initialize(-1, sr) to provide a SecureRandom + // without touching the parameter set currently used + throw new InvalidParameterException("keysize not supported"); + } + this.secureRandom = random; + } + + @Override + public KeyPair generateKeyPair() { + String pname = name != null ? name : pnames[0]; + var keys = implGenerateKeyPair(pname, secureRandom); + return new KeyPair(new NamedX509Key(fname, pname, keys[0]), + new NamedPKCS8Key(fname, pname, keys[1])); + } + + /// User-defined key pair generator. + /// + /// @param pname parameter set name + /// @param sr `SecureRandom` object, `null` if not initialized + /// @return public key and private key (in this order) in raw bytes + /// @throws ProviderException if there is an internal error + protected abstract byte[][] implGenerateKeyPair(String pname, SecureRandom sr); +} diff --git a/src/java.base/share/classes/sun/security/provider/NamedSignature.java b/src/java.base/share/classes/sun/security/provider/NamedSignature.java new file mode 100644 index 00000000000..921a39cfc92 --- /dev/null +++ b/src/java.base/share/classes/sun/security/provider/NamedSignature.java @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import sun.security.pkcs.NamedPKCS8Key; +import sun.security.x509.NamedX509Key; + +import java.io.ByteArrayOutputStream; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.PrivateKey; +import java.security.ProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.SignatureException; +import java.security.SignatureSpi; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Objects; + +/// A base class for all `Signature` implementations that can be +/// configured with a named parameter set. See [NamedKeyPairGenerator] +/// for more details. +/// +/// This class does not work with preHash signatures. +public abstract class NamedSignature extends SignatureSpi { + + private final String fname; // family name + private final String[] pnames; // allowed parameter set name (at least one) + + private final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + + // init with... + private String name; + private byte[] secKey; + private byte[] pubKey; + + private Object sk2; + private Object pk2; + + /// Creates a new `NamedSignature` object. + /// + /// @param fname the family name + /// @param pnames the standard parameter set names, at least one is needed. + protected NamedSignature(String fname, String... pnames) { + if (fname == null) { + throw new AssertionError("fname cannot be null"); + } + if (pnames == null || pnames.length == 0) { + throw new AssertionError("pnames cannot be null or empty"); + } + this.fname = fname; + this.pnames = pnames; + } + + @Override + protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { + // translate also check the key + var nk = (NamedX509Key) new NamedKeyFactory(fname, pnames) + .engineTranslateKey(publicKey); + name = nk.getParams().getName(); + pubKey = nk.getRawBytes(); + pk2 = implCheckPublicKey(name, pubKey); + secKey = null; + bout.reset(); + } + + @Override + protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { + // translate also check the key + var nk = (NamedPKCS8Key) new NamedKeyFactory(fname, pnames) + .engineTranslateKey(privateKey); + name = nk.getParams().getName(); + secKey = nk.getRawBytes(); + sk2 = implCheckPrivateKey(name, secKey); + pubKey = null; + bout.reset(); + } + + @Override + protected void engineUpdate(byte b) throws SignatureException { + bout.write(b); + } + + @Override + protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { + bout.write(b, off, len); + } + + @Override + protected byte[] engineSign() throws SignatureException { + if (secKey != null) { + var msg = bout.toByteArray(); + bout.reset(); + return implSign(name, secKey, sk2, msg, appRandom); + } else { + throw new SignatureException("No private key"); + } + } + + @Override + protected boolean engineVerify(byte[] sig) throws SignatureException { + if (pubKey != null) { + var msg = bout.toByteArray(); + bout.reset(); + return implVerify(name, pubKey, pk2, msg, sig); + } else { + throw new SignatureException("No public key"); + } + } + + @Override + @SuppressWarnings("deprecation") + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { + throw new InvalidParameterException("setParameter() not supported"); + } + + @Override + @SuppressWarnings("deprecation") + protected Object engineGetParameter(String param) throws InvalidParameterException { + throw new InvalidParameterException("getParameter() not supported"); + } + + @Override + protected void engineSetParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException( + "The " + fname + " algorithm does not take any parameters"); + } + } + + @Override + protected AlgorithmParameters engineGetParameters() { + return null; + } + + /// User-defined sign function. + /// + /// @param name parameter name + /// @param sk private key in raw bytes + /// @param sk2 parsed private key, `null` if none. See [#implCheckPrivateKey]. + /// @param msg the message + /// @param sr SecureRandom object, `null` if not initialized + /// @return the signature + /// @throws ProviderException if there is an internal error + /// @throws SignatureException if there is another error + protected abstract byte[] implSign(String name, byte[] sk, Object sk2, + byte[] msg, SecureRandom sr) throws SignatureException; + + /// User-defined verify function. + /// + /// @param name parameter name + /// @param pk public key in raw bytes + /// @param pk2 parsed public key, `null` if none. See [#implCheckPublicKey]. + /// @param msg the message + /// @param sig the signature + /// @return true if verified + /// @throws ProviderException if there is an internal error + /// @throws SignatureException if there is another error + protected abstract boolean implVerify(String name, byte[] pk, Object pk2, + byte[] msg, byte[] sig) throws SignatureException; + + /// User-defined function to validate a public key. + /// + /// This method will be called in `initVerify`. This gives the provider a chance to + /// reject the key so an `InvalidKeyException` can be thrown earlier. + /// An implementation can optionally return a "parsed key" as an `Object` value. + /// This object will be passed into the [#implVerify] method along with the raw key. + /// + /// The default implementation returns `null`. + /// + /// @param name parameter name + /// @param pk public key in raw bytes + /// @return a parsed key, `null` if none. + /// @throws InvalidKeyException if the key is invalid + protected Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyException { + return null; + } + + /// User-defined function to validate a private key. + /// + /// This method will be called in `initSign`. This gives the provider a chance to + /// reject the key so an `InvalidKeyException` can be thrown earlier. + /// An implementation can optionally return a "parsed key" as an `Object` value. + /// This object will be passed into the [#implSign] method along with the raw key. + /// + /// The default implementation returns `null`. + /// + /// @param name parameter name + /// @param sk private key in raw bytes + /// @return a parsed key, `null` if none. + /// @throws InvalidKeyException if the key is invalid + protected Object implCheckPrivateKey(String name, byte[] sk) throws InvalidKeyException { + return null; + } +} diff --git a/src/java.base/share/classes/sun/security/util/KeyUtil.java b/src/java.base/share/classes/sun/security/util/KeyUtil.java index 6884b9b201a..d057bb689e9 100644 --- a/src/java.base/share/classes/sun/security/util/KeyUtil.java +++ b/src/java.base/share/classes/sun/security/util/KeyUtil.java @@ -184,13 +184,13 @@ public static final int getKeySize(AlgorithmParameters parameters) { */ public static final String fullDisplayAlgName(Key key) { String result = key.getAlgorithm(); - if (key instanceof ECKey) { - ECParameterSpec paramSpec = ((ECKey) key).getParams(); + if (key instanceof AsymmetricKey ak) { + AlgorithmParameterSpec paramSpec = ak.getParams(); if (paramSpec instanceof NamedCurve nc) { result += " (" + nc.getNameAndAliases()[0] + ")"; + } else if (paramSpec instanceof NamedParameterSpec nps) { + result = nps.getName(); } - } else if (key instanceof EdECKey) { - result = ((EdECKey) key).getParams().getName(); } return result; } diff --git a/src/java.base/share/classes/sun/security/util/SignatureUtil.java b/src/java.base/share/classes/sun/security/util/SignatureUtil.java index 155adc198d0..c1ffd248f2a 100644 --- a/src/java.base/share/classes/sun/security/util/SignatureUtil.java +++ b/src/java.base/share/classes/sun/security/util/SignatureUtil.java @@ -33,6 +33,7 @@ import java.security.spec.*; import java.util.Locale; +import sun.security.pkcs.NamedPKCS8Key; import sun.security.rsa.RSAUtil; import jdk.internal.access.SharedSecrets; import sun.security.x509.AlgorithmId; @@ -274,7 +275,7 @@ public static String extractDigestAlgFromDwithE(String signatureAlgorithm) { return signatureAlgorithm.substring(0, with); } else { throw new IllegalArgumentException( - "Unknown algorithm: " + signatureAlgorithm); + "Cannot extract digest algorithm from " + signatureAlgorithm); } } @@ -390,8 +391,8 @@ private static Signature autoInitInternal(String alg, PrivateKey key, Signature public static AlgorithmId fromSignature(Signature sigEngine, PrivateKey key) throws SignatureException { try { - if (key instanceof EdECKey) { - return AlgorithmId.get(((EdECKey) key).getParams().getName()); + if (key.getParams() instanceof NamedParameterSpec nps) { + return AlgorithmId.get(nps.getName()); } AlgorithmParameters params = null; @@ -431,6 +432,14 @@ public static AlgorithmId fromSignature(Signature sigEngine, PrivateKey key) public static void checkKeyAndSigAlgMatch(PrivateKey key, String sAlg) { String kAlg = key.getAlgorithm().toUpperCase(Locale.ENGLISH); sAlg = checkName(sAlg); + if (key instanceof NamedPKCS8Key n8k) { + if (!sAlg.equalsIgnoreCase(n8k.getAlgorithm()) + && !sAlg.equalsIgnoreCase(n8k.getParams().getName())) { + throw new IllegalArgumentException( + "key algorithm not compatible with signature algorithm"); + } + return; + } switch (sAlg) { case "RSASSA-PSS" -> { if (!kAlg.equals("RSASSA-PSS") @@ -495,8 +504,10 @@ public static String getDefaultSigAlgForKey(PrivateKey k) { case "EDDSA" -> k instanceof EdECPrivateKey ? ((EdECPrivateKey) k).getParams().getName() : kAlg; - default -> kAlg; // All modern signature algorithms, - // RSASSA-PSS, ED25519, ED448, HSS/LMS, etc + default -> kAlg.contains("KEM") ? null : kAlg; + // All modern signature algorithms use the same name across + // key algorithms and signature algorithms, for example, + // RSASSA-PSS, ED25519, ED448, HSS/LMS, etc }; } diff --git a/src/java.base/share/classes/sun/security/x509/NamedX509Key.java b/src/java.base/share/classes/sun/security/x509/NamedX509Key.java new file mode 100644 index 00000000000..dc36bd3b9b3 --- /dev/null +++ b/src/java.base/share/classes/sun/security/x509/NamedX509Key.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import sun.security.util.BitArray; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serial; +import java.security.InvalidKeyException; +import java.security.KeyRep; +import java.security.NoSuchAlgorithmException; +import java.security.ProviderException; +import java.security.spec.NamedParameterSpec; + +/// Represents a public key from an algorithm family that is specialized +/// with a named parameter set. +/// +/// This key is generated by either a [sun.security.provider.NamedKeyPairGenerator] +/// or [sun.security.provider.NamedKeyFactory]. Its [#getAlgorithm] method +/// returns the algorithm family name, while its [#getParams()] method returns +/// the parameter set name as a [NamedParameterSpec] object. The algorithm +/// identifier in the X.509 encoding of the key is always a single OID derived +/// from the parameter set name. +/// +/// @see sun.security.provider.NamedKeyPairGenerator +public final class NamedX509Key extends X509Key { + @Serial + private static final long serialVersionUID = 1L; + + private final String fname; + private final transient NamedParameterSpec paramSpec; + private final byte[] rawBytes; + + /// Ctor from family name, parameter set name, raw key bytes. + /// Key bytes won't be cloned, caller must relinquish ownership + public NamedX509Key(String fname, String pname, byte[] rawBytes) { + this.fname = fname; + this.paramSpec = new NamedParameterSpec(pname); + try { + this.algid = AlgorithmId.get(pname); + } catch (NoSuchAlgorithmException e) { + throw new ProviderException(e); + } + this.rawBytes = rawBytes; + + setKey(new BitArray(rawBytes.length * 8, rawBytes)); + } + + /// Ctor from family name, and X.509 bytes + public NamedX509Key(String fname, byte[] encoded) throws InvalidKeyException { + this.fname = fname; + decode(encoded); + this.paramSpec = new NamedParameterSpec(algid.getName()); + if (algid.encodedParams != null) { + throw new InvalidKeyException("algorithm identifier has params"); + } + this.rawBytes = getKey().toByteArray(); + } + + @Override + public String toString() { + // Do not modify: this can be used by earlier JDKs that + // do not have the getParams() method + return paramSpec.getName() + " public key"; + } + + /// Returns the reference to the internal key. Caller must not modify + /// the content or keep a reference. + public byte[] getRawBytes() { + return rawBytes; + } + + @Override + public NamedParameterSpec getParams() { + return paramSpec; + } + + @Override + public String getAlgorithm() { + return fname; + } + + @java.io.Serial + private Object writeReplace() throws java.io.ObjectStreamException { + return new KeyRep(KeyRep.Type.PUBLIC, getAlgorithm(), getFormat(), + getEncoded()); + } + + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "NamedX509Key keys are not directly deserializable"); + } +} diff --git a/test/jdk/sun/security/provider/NamedEdDSA.java b/test/jdk/sun/security/provider/NamedEdDSA.java new file mode 100644 index 00000000000..4d0e3e9228a --- /dev/null +++ b/test/jdk/sun/security/provider/NamedEdDSA.java @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 8340327 + * @modules java.base/sun.security.ec.ed + * java.base/sun.security.ec.point + * java.base/sun.security.jca + * java.base/sun.security.provider + * @library /test/lib + */ + +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; +import sun.security.ec.ed.EdDSAOperations; +import sun.security.ec.ed.EdDSAParameters; +import sun.security.ec.point.AffinePoint; +import sun.security.jca.JCAUtil; +import sun.security.provider.NamedKeyFactory; +import sun.security.provider.NamedKeyPairGenerator; +import sun.security.provider.NamedSignature; + +import java.security.*; +import java.security.spec.EdDSAParameterSpec; +import java.security.spec.NamedParameterSpec; +import java.util.Arrays; +import java.util.List; + +public class NamedEdDSA { + + public static class ProviderImpl extends Provider { + public ProviderImpl() { + super("Named", "0", ""); + put("KeyPairGenerator.EdDSA", EdDSAKeyPairGenerator.class.getName()); + put("KeyPairGenerator.Ed25519", EdDSAKeyPairGenerator.Ed25519.class.getName()); + put("KeyPairGenerator.Ed448", EdDSAKeyPairGenerator.Ed448.class.getName()); + put("KeyFactory.EdDSA", EdDSAKeyFactory.class.getName()); + put("KeyFactory.Ed25519", EdDSAKeyFactory.Ed25519.class.getName()); + put("KeyFactory.Ed448", EdDSAKeyFactory.Ed448.class.getName()); + put("Signature.EdDSA", EdDSASignature.class.getName()); + put("Signature.Ed25519", EdDSASignature.Ed25519.class.getName()); + put("Signature.Ed448", EdDSASignature.Ed448.class.getName()); + } + } + + public static class EdDSASignature extends NamedSignature { + public EdDSASignature() { + super("EdDSA", "Ed25519", "Ed448"); + } + + protected EdDSASignature(String pname) { + super("EdDSA", pname); + } + + public static class Ed25519 extends EdDSASignature { + public Ed25519() { + super("Ed25519"); + } + } + + public static class Ed448 extends EdDSASignature { + public Ed448() { + super("Ed448"); + } + } + + @Override + public byte[] implSign(String name, byte[] sk, Object sk2, byte[] msg, SecureRandom sr) throws SignatureException { + return getOps(name).sign(plain, sk, msg); + } + + @Override + public boolean implVerify(String name, byte[] pk, Object pk2, byte[] msg, byte[] sig) throws SignatureException { + return getOps(name).verify(plain, (AffinePoint) pk2, pk, msg, sig); + } + + @Override + public Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyException { + return getOps(name).decodeAffinePoint(InvalidKeyException::new, pk); + } + } + + public static class EdDSAKeyFactory extends NamedKeyFactory { + public EdDSAKeyFactory() { + super("EdDSA", "Ed25519", "Ed448"); + } + + protected EdDSAKeyFactory(String pname) { + super("EdDSA", pname); + } + + public static class Ed25519 extends EdDSAKeyFactory { + public Ed25519() { + super("Ed25519"); + } + } + + public static class Ed448 extends EdDSAKeyFactory { + public Ed448() { + super("Ed448"); + } + } + } + + public static class EdDSAKeyPairGenerator extends NamedKeyPairGenerator { + public EdDSAKeyPairGenerator() { + super("EdDSA", "Ed25519", "Ed448"); + } + + protected EdDSAKeyPairGenerator(String pname) { + super("EdDSA", pname); + } + + public static class Ed25519 extends EdDSAKeyPairGenerator { + public Ed25519() { + super("Ed25519"); + } + } + + public static class Ed448 extends EdDSAKeyPairGenerator { + public Ed448() { + super("Ed448"); + } + } + + @Override + public byte[][] implGenerateKeyPair(String pname, SecureRandom sr) { + sr = sr == null ? JCAUtil.getDefSecureRandom() : sr; + var op = getOps(pname); + var sk = op.generatePrivate(sr); + var point = op.computePublic(sk); + byte[] encodedPoint = point.getY().toByteArray(); + reverse(encodedPoint); + // array may be too large or too small, depending on the value + encodedPoint = Arrays.copyOf(encodedPoint, op.getParameters().getKeyLength()); + // set the high-order bit of the encoded point + byte msb = (byte) (point.isXOdd() ? 0x80 : 0); + encodedPoint[encodedPoint.length - 1] |= msb; + return new byte[][] { encodedPoint, sk }; + } + + private static void swap(byte[] arr, int i, int j) { + byte tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + private static void reverse(byte [] arr) { + int i = 0; + int j = arr.length - 1; + + while (i < j) { + swap(arr, i, j); + i++; + j--; + } + } + } + + private static EdDSAOperations getOps(String pname) { + var op = switch (pname) { + case "Ed25519" -> e2; + case "Ed448" -> e4; + default -> throw new AssertionError("unknown pname " + pname); + }; + return op; + } + + static final EdDSAParameterSpec plain = new EdDSAParameterSpec(false); + static final EdDSAOperations e2, e4; + static { + try { + e2 = new EdDSAOperations(EdDSAParameters.getBySize(AssertionError::new, 255)); + e4 = new EdDSAOperations(EdDSAParameters.getBySize(AssertionError::new, 448)); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + public static void main(String[] args) throws Exception { + var ps = List.of(new ProviderImpl(), Security.getProvider("SunEC")); + for (var p1 : ps) { + for (var p2 : ps) { + for (var p3 : ps) { + test(p1, p2, p3); + } + } + } + } + + static void test(Provider p1, Provider p2, Provider p3) throws Exception { + System.out.println(p1.getName() + " " + p2.getName() + " " + p3.getName()); + var g = KeyPairGenerator.getInstance("EdDSA", p1); + g.initialize(NamedParameterSpec.ED448); + var kp = g.generateKeyPair(); + var s1 = Signature.getInstance("EdDSA", p2); + var s2 = Signature.getInstance("EdDSA", p3); + var f1 = KeyFactory.getInstance("EdDSA", p2); + var f2 = KeyFactory.getInstance("EdDSA", p3); + var sk = (PrivateKey) f1.translateKey(kp.getPrivate()); + var pk = (PublicKey) f2.translateKey(kp.getPublic()); + // sign and verify twice to make sure the key is intact + s1.initSign(sk); + var sig1 = s1.sign(); + s1.initSign(sk); + var sig2 = s1.sign(); + // EdDSA signing is deterministic + Asserts.assertEqualsByteArray(sig1, sig2); + s2.initVerify(pk); + Asserts.assertTrue(s2.verify(sig1)); + s2.initVerify(pk); + Asserts.assertTrue(s2.verify(sig2)); + // No parameters defined + s1.setParameter(null); + Utils.runAndCheckException(() -> s1.setParameter(NamedParameterSpec.ED448), + InvalidAlgorithmParameterException.class); + } +} diff --git a/test/jdk/sun/security/provider/NamedKeyFactoryTest.java b/test/jdk/sun/security/provider/NamedKeyFactoryTest.java new file mode 100644 index 00000000000..1ca179bc046 --- /dev/null +++ b/test/jdk/sun/security/provider/NamedKeyFactoryTest.java @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 8340327 + * @modules java.base/sun.security.x509 + * java.base/sun.security.pkcs + * java.base/sun.security.provider + * java.base/sun.security.util + * @library /test/lib + */ +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; +import jdk.test.lib.security.SeededSecureRandom; +import sun.security.pkcs.NamedPKCS8Key; +import sun.security.provider.NamedKeyFactory; +import sun.security.provider.NamedKeyPairGenerator; +import sun.security.util.RawKeySpec; +import sun.security.x509.NamedX509Key; + +import java.security.*; +import java.security.spec.*; + +public class NamedKeyFactoryTest { + + private static final SeededSecureRandom RAND = SeededSecureRandom.one(); + + public static void main(String[] args) throws Exception { + Security.addProvider(new ProviderImpl()); + + var g = KeyPairGenerator.getInstance("sHA"); + var g2 = KeyPairGenerator.getInstance("ShA-256"); + var g5 = KeyPairGenerator.getInstance("SHa-512"); + var kf = KeyFactory.getInstance("ShA"); + var kf2 = KeyFactory.getInstance("Sha-256"); + var kf5 = KeyFactory.getInstance("Sha-512"); + + checkKeyPair(g.generateKeyPair(), "SHA", "SHA-256"); + checkKeyPair(g2.generateKeyPair(), "SHA", "SHA-256"); + checkKeyPair(g5.generateKeyPair(), "SHA", "SHA-512"); + + checkKeyPair(g.generateKeyPair(), "SHA", "SHA-256"); + checkKeyPair(g2.generateKeyPair(), "SHA", "SHA-256"); + checkKeyPair(g5.generateKeyPair(), "SHA", "SHA-512"); + + Utils.runAndCheckException(() -> g.initialize(NamedParameterSpec.ED448), + InvalidAlgorithmParameterException.class); // wrong pname + Utils.runAndCheckException(() -> g.initialize(new NamedParameterSpec("SHA-384")), + InvalidAlgorithmParameterException.class); // wrong pname + + Utils.runAndCheckException(() -> g5.initialize(new NamedParameterSpec("SHA-256")), + InvalidAlgorithmParameterException.class); // diff pname + g5.initialize(new NamedParameterSpec("SHA-512")); + + g.initialize(new NamedParameterSpec("sHA-512")); + checkKeyPair(g.generateKeyPair(), "SHA", "SHA-512"); + g.initialize(new NamedParameterSpec("ShA-256")); + checkKeyPair(g.generateKeyPair(), "SHA", "SHA-256"); + + var pk = new NamedX509Key("sHa", "ShA-256", RAND.nBytes(2)); + var sk = new NamedPKCS8Key("sHa", "SHa-256", RAND.nBytes(2)); + checkKey(pk, "sHa", "ShA-256"); + checkKey(sk, "sHa", "SHa-256"); + + Asserts.assertEquals("X.509", pk.getFormat()); + Asserts.assertEquals("PKCS#8", sk.getFormat()); + + var pkSpec = kf.getKeySpec(pk, X509EncodedKeySpec.class); + var skSpec = kf.getKeySpec(sk, PKCS8EncodedKeySpec.class); + + kf2.getKeySpec(pk, X509EncodedKeySpec.class); + kf2.getKeySpec(sk, PKCS8EncodedKeySpec.class); + Utils.runAndCheckException(() -> kf5.getKeySpec(pk, X509EncodedKeySpec.class), + InvalidKeySpecException.class); // wrong KF + Utils.runAndCheckException(() -> kf5.getKeySpec(sk, PKCS8EncodedKeySpec.class), + InvalidKeySpecException.class); + Utils.runAndCheckException(() -> kf.getKeySpec(pk, PKCS8EncodedKeySpec.class), + InvalidKeySpecException.class); // wrong KeySpec + Utils.runAndCheckException(() -> kf.getKeySpec(sk, X509EncodedKeySpec.class), + InvalidKeySpecException.class); + + checkKey(kf.generatePublic(pkSpec), "SHA", "SHA-256"); + Utils.runAndCheckException(() -> kf.generatePrivate(pkSpec), + InvalidKeySpecException.class); + + checkKey(kf.generatePrivate(skSpec), "SHA", "SHA-256"); + Utils.runAndCheckException(() -> kf.generatePublic(skSpec), + InvalidKeySpecException.class); + + checkKey(kf2.generatePrivate(skSpec), "SHA", "SHA-256"); + checkKey(kf2.generatePublic(pkSpec), "SHA", "SHA-256"); + + Utils.runAndCheckException(() -> kf5.generatePublic(pkSpec), + InvalidKeySpecException.class); // wrong KF + Utils.runAndCheckException(() -> kf5.generatePublic(skSpec), + InvalidKeySpecException.class); + + // The private RawKeySpec and unnamed RAW EncodedKeySpec + var prk = kf.getKeySpec(pk, RawKeySpec.class); + Asserts.assertEqualsByteArray(prk.getKeyArr(), pk.getRawBytes()); + var prk2 = kf.getKeySpec(pk, EncodedKeySpec.class); + Asserts.assertEquals("RAW", prk2.getFormat()); + Asserts.assertEqualsByteArray(prk.getKeyArr(), prk2.getEncoded()); + + Asserts.assertEqualsByteArray(kf2.generatePublic(prk).getEncoded(), pk.getEncoded()); + Utils.runAndCheckException(() -> kf.generatePublic(prk), InvalidKeySpecException.class); // no pname + Asserts.assertEqualsByteArray(kf2.generatePublic(prk2).getEncoded(), pk.getEncoded()); + Utils.runAndCheckException(() -> kf.generatePublic(prk2), InvalidKeySpecException.class); // no pname + + var srk = kf.getKeySpec(sk, RawKeySpec.class); + Asserts.assertEqualsByteArray(srk.getKeyArr(), sk.getRawBytes()); + var srk2 = kf.getKeySpec(sk, EncodedKeySpec.class); + Asserts.assertEquals("RAW", srk2.getFormat()); + Asserts.assertEqualsByteArray(srk2.getEncoded(), sk.getRawBytes()); + + Asserts.assertEqualsByteArray(kf2.generatePrivate(srk).getEncoded(), sk.getEncoded()); + Utils.runAndCheckException(() -> kf.generatePrivate(srk), InvalidKeySpecException.class); // no pname + Asserts.assertEqualsByteArray(kf2.generatePrivate(srk2).getEncoded(), sk.getEncoded()); + Utils.runAndCheckException(() -> kf.generatePrivate(srk2), InvalidKeySpecException.class); // no pname + + var pk1 = new PublicKey() { + public String getAlgorithm() { return "SHA"; } + public String getFormat() { return "RAW"; } + public byte[] getEncoded() { return RAND.nBytes(2); } + }; + var pk2 = new PublicKey() { + public String getAlgorithm() { return "sHA-256"; } + public String getFormat() { return "RAW"; } + public byte[] getEncoded() { return RAND.nBytes(2); } + }; + var pk3 = new PublicKey() { + public String getAlgorithm() { return "SHA"; } + public String getFormat() { return "RAW"; } + public byte[] getEncoded() { return RAND.nBytes(2); } + public AlgorithmParameterSpec getParams() { return new NamedParameterSpec("sHA-256"); } + }; + + checkKey(kf2.translateKey(pk1), "SHA", "SHA-256"); + checkKey(kf.translateKey(pk2), "SHA", "SHA-256"); + checkKey(kf.translateKey(pk3), "SHA", "SHA-256"); + + Utils.runAndCheckException(() -> kf.translateKey(pk1), InvalidKeyException.class); + Utils.runAndCheckException(() -> kf5.translateKey(pk2), InvalidKeyException.class); + Utils.runAndCheckException(() -> kf5.translateKey(pk3), InvalidKeyException.class); + + var sk1 = new PrivateKey() { + public String getAlgorithm() { return "SHA"; } + public String getFormat() { return "RAW"; } + public byte[] getEncoded() { return RAND.nBytes(2); } + }; + var sk2 = new PrivateKey() { + public String getAlgorithm() { return "sHA-256"; } + public String getFormat() { return "RAW"; } + public byte[] getEncoded() { return RAND.nBytes(2); } + }; + var sk3 = new PrivateKey() { + public String getAlgorithm() { return "SHA"; } + public String getFormat() { return "RAW"; } + public byte[] getEncoded() { return RAND.nBytes(2); } + public AlgorithmParameterSpec getParams() { return new NamedParameterSpec("sHA-256"); } + }; + + checkKey(kf2.translateKey(sk1), "SHA", "SHA-256"); + checkKey(kf.translateKey(sk2), "SHA", "SHA-256"); + checkKey(kf.translateKey(sk3), "SHA", "SHA-256"); + + Utils.runAndCheckException(() -> kf.translateKey(sk1), InvalidKeyException.class); + Utils.runAndCheckException(() -> kf5.translateKey(sk2), InvalidKeyException.class); + Utils.runAndCheckException(() -> kf5.translateKey(sk3), InvalidKeyException.class); + } + + static void checkKeyPair(KeyPair kp, String algName, String toString) { + checkKey(kp.getPrivate(), algName, toString); + checkKey(kp.getPublic(), algName, toString); + } + + static void checkKey(Key k, String algName, String pname) { + Asserts.assertEquals(algName, k.getAlgorithm()); + Asserts.assertTrue(k.toString().contains(pname)); + if (k instanceof AsymmetricKey ak && ak.getParams() instanceof NamedParameterSpec nps) { + Asserts.assertEquals(pname, nps.getName()); + } + } + + // Provider + + public static class ProviderImpl extends Provider { + public ProviderImpl() { + super("P", "1", "..."); + put("KeyFactory.SHA", KF.class.getName()); + put("KeyFactory.SHA-256", KF1.class.getName()); + put("KeyFactory.SHA-512", KF2.class.getName()); + put("KeyPairGenerator.SHA", KPG.class.getName()); + put("KeyPairGenerator.SHA-256", KPG1.class.getName()); + put("KeyPairGenerator.SHA-512", KPG2.class.getName()); + } + } + public static class KF extends NamedKeyFactory { + public KF() { + super("SHA", "SHA-256", "SHA-512"); + } + } + public static class KF1 extends NamedKeyFactory { + public KF1() { + super("SHA", "SHA-256"); + } + } + public static class KF2 extends NamedKeyFactory { + public KF2() { + super("SHA", "SHA-512"); + } + } + public static class KPG extends NamedKeyPairGenerator { + public KPG() { + super("SHA", "SHA-256", "SHA-512"); + } + + public KPG(String pname) { + super("SHA", pname); + } + + @Override + public byte[][] implGenerateKeyPair(String name, SecureRandom sr) { + var out = new byte[2][]; + out[0] = RAND.nBytes(name.endsWith("256") ? 2 : 4); + out[1] = RAND.nBytes(name.endsWith("256") ? 2 : 4); + return out; + } + } + public static class KPG1 extends KPG { + public KPG1() { + super("SHA-256"); + } + } + public static class KPG2 extends KPG { + public KPG2() { + super("SHA-512"); + } + } +} From c4965d9b135b58e0b3604bc1cc60978ad4c8c11b Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Fri, 11 Oct 2024 21:30:53 +0000 Subject: [PATCH 27/95] 8341794: Fix ExceptionOccurred in jdk.attach Reviewed-by: amenkov, cjplummer --- .../windows/native/libattach/VirtualMachineImpl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c b/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c index a8b17c22d83..74d0e7bf7d5 100644 --- a/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c +++ b/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c @@ -432,7 +432,7 @@ JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_enqueue return; } } - if ((*env)->ExceptionOccurred(env)) return; + if ((*env)->ExceptionCheck(env)) return; } } for (i = argsLen; i < MAX_ARGS; i++) { @@ -463,7 +463,7 @@ JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_enqueue stubLen = (DWORD)(*env)->GetArrayLength(env, stub); stubCode = (*env)->GetByteArrayElements(env, stub, &isCopy); - if ((*env)->ExceptionOccurred(env)) return; + if ((*env)->ExceptionCheck(env)) return; pCode = (PDWORD) VirtualAllocEx( hProcess, 0, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); if (pCode == NULL) { @@ -636,7 +636,7 @@ static jboolean jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, size_t cstr[0] = '\0'; } else { str = JNU_GetStringPlatformChars(env, jstr, &isCopy); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return result; } if (strlen(str) >= cstr_buf_size) { From 2db3397187563d1821d24578247f764c372fbb4b Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Fri, 11 Oct 2024 21:31:58 +0000 Subject: [PATCH 28/95] 8341797: Fix ExceptionOccurred in jdk.jdi Reviewed-by: amenkov, cjplummer --- .../native/libdt_shmem/SharedMemoryConnection.c | 12 ++++++------ .../share/native/libdt_shmem/SharedMemoryTransport.c | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryConnection.c b/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryConnection.c index af78969e316..b6d246d0433 100644 --- a/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryConnection.c +++ b/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryConnection.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -115,7 +115,7 @@ packetToByteArray(JNIEnv *env, jdwpPacket *str) /* total packet length is header + data */ array = (*env)->NewByteArray(env, total_length); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return NULL; } @@ -144,7 +144,7 @@ packetToByteArray(JNIEnv *env, jdwpPacket *str) if (data_length > 0) { (*env)->SetByteArrayRegion(env, array, JDWP_HEADER_SIZE, data_length, str->type.cmd.data); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return NULL; } } @@ -174,7 +174,7 @@ byteArrayToPacket(JNIEnv *env, jbyteArray b, jdwpPacket *str) * Get the packet header */ (*env)->GetByteArrayRegion(env, b, 0, sizeof(pktHeader), pktHeader); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { /* b shorter than sizeof(pktHeader) */ return; } @@ -221,7 +221,7 @@ byteArrayToPacket(JNIEnv *env, jbyteArray b, jdwpPacket *str) } (*env)->GetByteArrayRegion(env, b, sizeof(pktHeader), /*sizeof(CmdPacket)+4*/ data_length, data); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { free(data); return; } @@ -326,7 +326,7 @@ JNIEXPORT void JNICALL Java_com_sun_tools_jdi_SharedMemoryConnection_sendPacket0 jint rc; byteArrayToPacket(env, b, &packet); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return; } diff --git a/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryTransport.c b/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryTransport.c index 7258503f7e1..867ed1d8567 100644 --- a/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryTransport.c +++ b/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryTransport.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * 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 @@ void throwException(JNIEnv *env, char *exceptionClassName, char *message) { jclass excClass = (*env)->FindClass(env, exceptionClassName); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return; } (*env)->ThrowNew(env, excClass, message); @@ -109,7 +109,7 @@ JNIEXPORT jlong JNICALL Java_com_sun_tools_jdi_SharedMemoryTransportService_atta const char *addrChars; addrChars = (*env)->GetStringUTFChars(env, address, NULL); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return CONNECTION_TO_ID(connection); } else if (addrChars == NULL) { throwException(env, "java/lang/InternalError", "GetStringUTFChars failed"); @@ -143,7 +143,7 @@ JNIEXPORT jstring JNICALL Java_com_sun_tools_jdi_SharedMemoryTransportService_na throwShmemException(env, "shmemBase_name failed", rc); } else { nameString = (*env)->NewStringUTF(env, namePtr); - if ((nameString == NULL) && !(*env)->ExceptionOccurred(env)) { + if ((nameString == NULL) && !(*env)->ExceptionCheck(env)) { throwException(env, "java/lang/InternalError", "Unable to create string"); } } @@ -190,7 +190,7 @@ JNIEXPORT jlong JNICALL Java_com_sun_tools_jdi_SharedMemoryTransportService_star if (address != NULL) { addrChars = (*env)->GetStringUTFChars(env, address, NULL); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return TRANSPORT_TO_ID(transport); } else if (addrChars == NULL) { throwException(env, "java/lang/InternalError", "GetStringUTFChars failed"); From 2e1c1936e183fe8329bb6247523bd73ad9b3ca88 Mon Sep 17 00:00:00 2001 From: "Daniel D. Daugherty" Date: Sat, 12 Oct 2024 02:45:50 +0000 Subject: [PATCH 29/95] 8342003: ProblemList sun/security/tools/keytool/GenKeyPairSigner.java Reviewed-by: jpai --- test/jdk/ProblemList.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 034830a2575..0aa27e4ac0c 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -644,6 +644,8 @@ sun/security/provider/PolicyParser/PrincipalExpansionError.java 8039280 generic- sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java 8316183 linux-ppc64le +sun/security/tools/keytool/GenKeyPairSigner.java 8342002 generic-all + ############################################################################ # jdk_sound From 41ee582df8c65f2f26b21e46784cf0bc4ece0585 Mon Sep 17 00:00:00 2001 From: Ramkumar Sunderbabu Date: Sat, 12 Oct 2024 03:25:42 +0000 Subject: [PATCH 30/95] 8341138: Rename jtreg property docker.support as container.support Reviewed-by: sgehwolf, mseledtsov --- test/hotspot/jtreg/TEST.ROOT | 2 +- .../containers/docker/DockerBasicTest.java | 4 ++-- .../jtreg/containers/docker/ShareTmpDir.java | 4 ++-- .../containers/docker/TestCPUAwareness.java | 4 ++-- .../jtreg/containers/docker/TestCPUSets.java | 4 ++-- .../containers/docker/TestContainerInfo.java | 2 +- .../jtreg/containers/docker/TestJFREvents.java | 2 +- .../docker/TestJFRNetworkEvents.java | 4 ++-- .../containers/docker/TestJFRWithJMX.java | 4 ++-- .../jtreg/containers/docker/TestJcmd.java | 4 ++-- .../containers/docker/TestJcmdWithSideCar.java | 4 ++-- .../containers/docker/TestLimitsUpdating.java | 3 ++- .../containers/docker/TestMemoryAwareness.java | 4 ++-- .../jtreg/containers/docker/TestMisc.java | 4 ++-- .../jtreg/containers/docker/TestPids.java | 2 +- test/jdk/TEST.ROOT | 2 +- .../platform/docker/TestDockerBasic.java | 3 ++- .../platform/docker/TestDockerCpuMetrics.java | 4 ++-- .../docker/TestDockerMemoryMetrics.java | 2 +- .../docker/TestGetFreeSwapSpaceSize.java | 3 ++- .../platform/docker/TestLimitsUpdating.java | 3 ++- .../platform/docker/TestPidsLimit.java | 4 ++-- .../platform/docker/TestSystemMetrics.java | 4 ++-- .../docker/TestUseContainerSupport.java | 3 ++- test/jtreg-ext/requires/VMProps.java | 18 +++++++++--------- test/lib/jdk/test/lib/Container.java | 5 +++-- 26 files changed, 54 insertions(+), 48 deletions(-) diff --git a/test/hotspot/jtreg/TEST.ROOT b/test/hotspot/jtreg/TEST.ROOT index 5ed0227068c..962fc36838c 100644 --- a/test/hotspot/jtreg/TEST.ROOT +++ b/test/hotspot/jtreg/TEST.ROOT @@ -86,7 +86,7 @@ requires.properties= \ vm.compiler2.enabled \ vm.musl \ vm.flagless \ - docker.support \ + container.support \ systemd.support \ jdk.containerized diff --git a/test/hotspot/jtreg/containers/docker/DockerBasicTest.java b/test/hotspot/jtreg/containers/docker/DockerBasicTest.java index 357eb3db497..9233b199532 100644 --- a/test/hotspot/jtreg/containers/docker/DockerBasicTest.java +++ b/test/hotspot/jtreg/containers/docker/DockerBasicTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * 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 * @summary Basic (sanity) test for JDK-under-test inside a docker image. - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/containers/docker/ShareTmpDir.java b/test/hotspot/jtreg/containers/docker/ShareTmpDir.java index 08493a75398..43cd6ec5152 100644 --- a/test/hotspot/jtreg/containers/docker/ShareTmpDir.java +++ b/test/hotspot/jtreg/containers/docker/ShareTmpDir.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,7 @@ * @bug 8286030 * @key cgroups * @summary Test for hsperfdata file name conflict when two containers share the same /tmp directory - * @requires docker.support + * @requires container.support * @library /test/lib * @build WaitForFlagFile * @run driver ShareTmpDir diff --git a/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java b/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java index a41dc9c3939..c51bfa1abbb 100644 --- a/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java +++ b/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ * @test * @key cgroups * @summary Test JVM's CPU resource awareness when running inside docker container - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.base/jdk.internal.platform diff --git a/test/hotspot/jtreg/containers/docker/TestCPUSets.java b/test/hotspot/jtreg/containers/docker/TestCPUSets.java index de35388f5a8..aabe82e131f 100644 --- a/test/hotspot/jtreg/containers/docker/TestCPUSets.java +++ b/test/hotspot/jtreg/containers/docker/TestCPUSets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ * @test * @key cgroups * @summary Test JVM's awareness of cpu sets (cpus and mems) - * @requires docker.support + * @requires container.support * @requires (os.arch != "s390x") * @library /test/lib * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/containers/docker/TestContainerInfo.java b/test/hotspot/jtreg/containers/docker/TestContainerInfo.java index 6bcb706fa7a..5db3c2af098 100644 --- a/test/hotspot/jtreg/containers/docker/TestContainerInfo.java +++ b/test/hotspot/jtreg/containers/docker/TestContainerInfo.java @@ -27,7 +27,7 @@ * @test * @summary Test container info for cgroup v2 * @key cgroups - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/containers/docker/TestJFREvents.java b/test/hotspot/jtreg/containers/docker/TestJFREvents.java index abc9de45db8..77c735cde00 100644 --- a/test/hotspot/jtreg/containers/docker/TestJFREvents.java +++ b/test/hotspot/jtreg/containers/docker/TestJFREvents.java @@ -29,7 +29,7 @@ * when run inside Docker container, such as available CPU and memory. * Also make sure that PIDs are based on value provided by container, * not by the host system. - * @requires (docker.support & os.maxMemory >= 2g) + * @requires (container.support & os.maxMemory >= 2g) * @modules java.base/jdk.internal.platform * @library /test/lib * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java b/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java index 54ffff4e60e..9f9497d9c63 100644 --- a/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java +++ b/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,7 @@ * @summary Test JFR network related events inside a container; make sure * the reported host ip and host name are correctly reported within * the container. - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java b/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java index 61fad4f86be..b7517254281 100644 --- a/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java +++ b/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * 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 * @summary Test JFR recording controlled via JMX across container boundary. - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/containers/docker/TestJcmd.java b/test/hotspot/jtreg/containers/docker/TestJcmd.java index 60d4f4a9e5b..ca8f1659fe9 100644 --- a/test/hotspot/jtreg/containers/docker/TestJcmd.java +++ b/test/hotspot/jtreg/containers/docker/TestJcmd.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ * @test * @summary Test JCMD across container boundary. The JCMD runs on a host system, * while sending commands to a JVM that runs inside a container. - * @requires docker.support + * @requires container.support * @requires vm.flagless * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java b/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java index 5dea864f427..de27f4d24e2 100644 --- a/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java +++ b/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,7 @@ * and other uses. In side car pattern the main application/service container * is paired with a sidecar container by sharing certain aspects of container * namespace such as PID namespace, specific sub-directories, IPC and more. - * @requires docker.support + * @requires container.support * @requires vm.flagless * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/containers/docker/TestLimitsUpdating.java b/test/hotspot/jtreg/containers/docker/TestLimitsUpdating.java index e15ab9b2b81..14227a71068 100644 --- a/test/hotspot/jtreg/containers/docker/TestLimitsUpdating.java +++ b/test/hotspot/jtreg/containers/docker/TestLimitsUpdating.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2023, Red Hat, Inc. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,7 +29,7 @@ * @bug 8308090 * @key cgroups * @summary Test container limits updating as they get updated at runtime without restart - * @requires docker.support + * @requires container.support * @library /test/lib * @build jdk.test.whitebox.WhiteBox LimitUpdateChecker * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar whitebox.jar jdk.test.whitebox.WhiteBox diff --git a/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java b/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java index 20354cf934d..06a874e008a 100644 --- a/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java +++ b/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,7 @@ * @bug 8146115 8292083 * @key cgroups * @summary Test JVM's memory resource awareness when running inside docker container - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.base/jdk.internal.platform diff --git a/test/hotspot/jtreg/containers/docker/TestMisc.java b/test/hotspot/jtreg/containers/docker/TestMisc.java index 5b7f96112b9..a811666999b 100644 --- a/test/hotspot/jtreg/containers/docker/TestMisc.java +++ b/test/hotspot/jtreg/containers/docker/TestMisc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * 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 * @summary Test miscellanous functionality related to JVM running in docker container - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/containers/docker/TestPids.java b/test/hotspot/jtreg/containers/docker/TestPids.java index 2b3b2594cfa..9b65a1b1ee8 100644 --- a/test/hotspot/jtreg/containers/docker/TestPids.java +++ b/test/hotspot/jtreg/containers/docker/TestPids.java @@ -27,7 +27,7 @@ * @test * @key cgroups * @summary Test JVM's awareness of pids controller - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.management diff --git a/test/jdk/TEST.ROOT b/test/jdk/TEST.ROOT index 6198d332142..c8db6b89a71 100644 --- a/test/jdk/TEST.ROOT +++ b/test/jdk/TEST.ROOT @@ -100,7 +100,7 @@ requires.properties= \ vm.jvmci.enabled \ vm.jvmti \ vm.cpu.features \ - docker.support \ + container.support \ systemd.support \ release.implementor \ jdk.containerized \ diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerBasic.java b/test/jdk/jdk/internal/platform/docker/TestDockerBasic.java index 5518943a6e6..e236292de98 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerBasic.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerBasic.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, Red Hat, Inc. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +27,7 @@ * @bug 8293540 * @summary Verify that -XshowSettings:system works * @key cgroups - * @requires docker.support + * @requires container.support * @library /test/lib * @run main/timeout=360 TestDockerBasic */ diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java b/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java index f08e80c76f4..4d452f20eef 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * 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,7 +34,7 @@ * @test * @key cgroups * @summary Test JDK Metrics class when running inside docker container - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.platform * @build MetricsCpuTester diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java index 416e91bec5c..e8dc616b5e7 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java @@ -32,7 +32,7 @@ * @test * @key cgroups * @summary Test JDK Metrics class when running inside docker container - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.platform * @build MetricsMemoryTester diff --git a/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java b/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java index 92f3364da10..204d7a215d8 100644 --- a/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java +++ b/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2020, 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * 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 +26,7 @@ * @test * @key cgroups * @bug 8242480 - * @requires docker.support + * @requires container.support * @library /test/lib * @build GetFreeSwapSpaceSize * @run driver TestGetFreeSwapSpaceSize diff --git a/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java b/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java index 22e03293c48..1544088f688 100644 --- a/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java +++ b/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2023, Red Hat, Inc. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,7 +29,7 @@ * @bug 8308090 * @key cgroups * @summary Test container limits updating as they get updated at runtime without restart - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.platform * @build LimitUpdateChecker diff --git a/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java b/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java index 6c6ff76fa99..9fedeb55234 100644 --- a/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java +++ b/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,7 +27,7 @@ * @key cgroups * @summary Test JDK Metrics class when running inside a docker container with limited pids * @bug 8266490 - * @requires docker.support + * @requires container.support * @library /test/lib * @build TestPidsLimit * @run driver TestPidsLimit diff --git a/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java b/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java index 854d24b2279..93efff64cc4 100644 --- a/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * 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 * @key cgroups * @summary Test JDK Metrics class when running inside docker container - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.platform * @run main TestSystemMetrics diff --git a/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java b/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java index 7a2f77b3ce4..6a96514771c 100644 --- a/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java +++ b/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Red Hat, Inc. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +25,7 @@ /* * @test * @summary UseContainerSupport flag should reflect Metrics being available - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.platform * @build CheckUseContainerSupport diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index 0457fb7e3c5..465c641d442 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -131,7 +131,7 @@ public Map call() { map.put("vm.libgraal.jit", this::isLibgraalJIT); map.put("vm.compiler1.enabled", this::isCompiler1Enabled); map.put("vm.compiler2.enabled", this::isCompiler2Enabled); - map.put("docker.support", this::dockerSupport); + map.put("container.support", this::containerSupport); map.put("systemd.support", this::systemdSupport); map.put("vm.musl", this::isMusl); map.put("release.implementor", this::implementor); @@ -585,16 +585,16 @@ protected String isCompiler2Enabled() { } /** - * A simple check for docker support + * A simple check for container support * - * @return true if docker is supported in a given environment + * @return true if container is supported in a given environment */ - protected String dockerSupport() { - log("Entering dockerSupport()"); + protected String containerSupport() { + log("Entering containerSupport()"); boolean isSupported = false; if (Platform.isLinux()) { - // currently docker testing is only supported for Linux, + // currently container testing is only supported for Linux, // on certain platforms String arch = System.getProperty("os.arch"); @@ -610,17 +610,17 @@ protected String dockerSupport() { } } - log("dockerSupport(): platform check: isSupported = " + isSupported); + log("containerSupport(): platform check: isSupported = " + isSupported); if (isSupported) { try { - isSupported = checkProgramSupport("checkDockerSupport()", Container.ENGINE_COMMAND); + isSupported = checkProgramSupport("checkContainerSupport()", Container.ENGINE_COMMAND); } catch (Exception e) { isSupported = false; } } - log("dockerSupport(): returning isSupported = " + isSupported); + log("containerSupport(): returning isSupported = " + isSupported); return "" + isSupported; } diff --git a/test/lib/jdk/test/lib/Container.java b/test/lib/jdk/test/lib/Container.java index e0ca4851e14..83fd265980f 100644 --- a/test/lib/jdk/test/lib/Container.java +++ b/test/lib/jdk/test/lib/Container.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Red Hat Inc. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * 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,9 +24,9 @@ package jdk.test.lib; public class Container { - // Use this property to specify docker location on your system. + // Use this property to specify container runtime location (e.g. docker) on your system. // E.g.: "/usr/local/bin/docker". We define this constant here so - // that it can be used in VMProps as well which checks docker support + // that it can be used in VMProps as well which checks container support // via this command public static final String ENGINE_COMMAND = System.getProperty("jdk.test.container.command", "docker"); From 5c4f1ef746b0c373a131726d74d27f0e0cc6e40d Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 14 Oct 2024 04:45:12 +0000 Subject: [PATCH 31/95] 8226933: [TEST_BUG]GTK L&F: There is no swatches or RGB tab in JColorChooser Reviewed-by: aivanov, psadhukhan, tr --- .../swing/JColorChooser/Test4887836.java | 13 +++- .../plaf/basic/BasicSliderUI/bug4419255.java | 65 +++++++++++++++++++ 2 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 test/jdk/javax/swing/plaf/basic/BasicSliderUI/bug4419255.java diff --git a/test/jdk/javax/swing/JColorChooser/Test4887836.java b/test/jdk/javax/swing/JColorChooser/Test4887836.java index 4043dbdd88a..82a09c46085 100644 --- a/test/jdk/javax/swing/JColorChooser/Test4887836.java +++ b/test/jdk/javax/swing/JColorChooser/Test4887836.java @@ -26,10 +26,12 @@ import javax.swing.JColorChooser; import javax.swing.UIManager; +import jtreg.SkippedException; + /* * @test * @bug 4887836 - * @library /java/awt/regtesthelpers + * @library /java/awt/regtesthelpers /test/lib * @build PassFailJFrame * @summary Checks for white area under the JColorChooser Swatch tab * @run main/manual Test4887836 @@ -38,6 +40,13 @@ public class Test4887836 { public static void main(String[] args) throws Exception { + + // ColorChooser UI design is different for GTK L&F. + // There is no Swatches tab available for GTK L&F, skip the testing. + if (UIManager.getLookAndFeel().getName().contains("GTK")) { + throw new SkippedException("Test not applicable for GTK L&F"); + } + String instructions = """ If you do not see white area under the \"Swatches\" tab, then test passed, otherwise it failed."""; @@ -45,9 +54,7 @@ public static void main(String[] args) throws Exception { PassFailJFrame.builder() .title("Test4759306") .instructions(instructions) - .rows(5) .columns(40) - .testTimeOut(10) .testUI(Test4887836::createColorChooser) .build() .awaitAndCheck(); diff --git a/test/jdk/javax/swing/plaf/basic/BasicSliderUI/bug4419255.java b/test/jdk/javax/swing/plaf/basic/BasicSliderUI/bug4419255.java new file mode 100644 index 00000000000..eb23901f60d --- /dev/null +++ b/test/jdk/javax/swing/plaf/basic/BasicSliderUI/bug4419255.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * 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; +import javax.swing.JColorChooser; +import javax.swing.UIManager; + +import jtreg.SkippedException; + +/* + * @test + * @bug 4419255 + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame + * @summary Tests if Metal Slider's thumb isn't clipped + * @run main/manual bug4419255 + */ + +public class bug4419255 { + + public static void main(String[] args) throws Exception { + + // ColorChooser UI design is different for GTK L&F. + // There is no RGB tab available for GTK L&F, skip the testing. + if (UIManager.getLookAndFeel().getName().contains("GTK")) { + throw new SkippedException("Test not applicable for GTK L&F"); + } + String instructions = """ + Choose RGB tab. If sliders' thumbs are painted correctly + (top is not clipped, black line is visible), + then test passed. Otherwise it failed."""; + + PassFailJFrame.builder() + .title("bug4419255") + .instructions(instructions) + .columns(40) + .testUI(bug4419255::createColorChooser) + .build() + .awaitAndCheck(); + } + + private static JColorChooser createColorChooser() { + return new JColorChooser(Color.BLUE); + } +} From fe98f86b5792cbb17d47871452d27ab87d72b342 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 14 Oct 2024 04:45:24 +0000 Subject: [PATCH 32/95] 8226938: [TEST_BUG]GTK L&F: There is no Details button in FileChooser Dialog Reviewed-by: honkar, prr --- .../javax/swing/JFileChooser/bug4587721.java | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 test/jdk/javax/swing/JFileChooser/bug4587721.java diff --git a/test/jdk/javax/swing/JFileChooser/bug4587721.java b/test/jdk/javax/swing/JFileChooser/bug4587721.java new file mode 100644 index 00000000000..408f4e47f49 --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/bug4587721.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 4587721 + * @summary Tests if JFileChooser details view chops off text + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4587721 + */ + +import java.awt.Font; +import java.util.Enumeration; + +import javax.swing.JFileChooser; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.plaf.FontUIResource; +import javax.swing.plaf.metal.MetalLookAndFeel; + +public class bug4587721 { + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel(new MetalLookAndFeel()); + + String instructions = """ + Click on the Details button in JFileChooser Window. + If the filename text is chopped off by height, + then Press FAIL else Press PASS. + """; + + PassFailJFrame.builder() + .title("bug4587721") + .instructions(instructions) + .columns(40) + .testUI(bug4587721::createUI) + .build() + .awaitAndCheck(); + } + + public static JFileChooser createUI() { + setFonts(); + JFileChooser fc = new JFileChooser(); + return fc; + } + + public static void setFonts() { + UIDefaults defaults = UIManager.getDefaults(); + Enumeration keys = defaults.keys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + if (defaults.get(key) instanceof Font) + UIManager.put(key, new FontUIResource(new Font("Courier", Font.BOLD, 30))); + } + } +} From 8d0975a27d826f7aa487a612131827586abaefd5 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Mon, 14 Oct 2024 05:26:48 +0000 Subject: [PATCH 33/95] 8336726: C2: assert(!do_asserts || projs->fallthrough_ioproj != nullptr) failed: must be found Reviewed-by: chagedorn, kvn, vlivanov --- src/hotspot/share/opto/callGenerator.cpp | 9 ++- src/hotspot/share/opto/graphKit.cpp | 4 +- src/hotspot/share/opto/graphKit.hpp | 2 +- ...tCallDevirtualizationWithInfiniteLoop.java | 76 +++++++++++++++++++ 4 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/TestCallDevirtualizationWithInfiniteLoop.java diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index afd9ca25a56..36fca9f61b6 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -623,7 +623,10 @@ void CallGenerator::do_late_inline_helper() { // check for unreachable loop CallProjections callprojs; - call->extract_projections(&callprojs, true); + // Similar to incremental inlining, don't assert that all call + // projections are still there for post-parse call devirtualization. + bool do_asserts = !is_mh_late_inline() && !is_virtual_late_inline(); + call->extract_projections(&callprojs, true, do_asserts); if ((callprojs.fallthrough_catchproj == call->in(0)) || (callprojs.catchall_catchproj == call->in(0)) || (callprojs.fallthrough_memproj == call->in(TypeFunc::Memory)) || @@ -647,7 +650,7 @@ void CallGenerator::do_late_inline_helper() { if (is_pure_call() && result_not_used) { GraphKit kit(call->jvms()); - kit.replace_call(call, C->top(), true); + kit.replace_call(call, C->top(), true, do_asserts); } else { // Make a clone of the JVMState that appropriate to use for driving a parse JVMState* old_jvms = call->jvms(); @@ -729,7 +732,7 @@ void CallGenerator::do_late_inline_helper() { } C->set_inlining_progress(true); C->set_do_cleanup(kit.stopped()); // path is dead; needs cleanup - kit.replace_call(call, result, true); + kit.replace_call(call, result, true, do_asserts); } } diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 27120c5ea1e..fe8ca76e318 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -1930,7 +1930,7 @@ static void add_mergemem_users_to_worklist(Unique_Node_List& wl, Node* mem) { } // Replace the call with the current state of the kit. -void GraphKit::replace_call(CallNode* call, Node* result, bool do_replaced_nodes) { +void GraphKit::replace_call(CallNode* call, Node* result, bool do_replaced_nodes, bool do_asserts) { JVMState* ejvms = nullptr; if (has_exceptions()) { ejvms = transfer_exceptions_into_jvms(); @@ -1944,7 +1944,7 @@ void GraphKit::replace_call(CallNode* call, Node* result, bool do_replaced_nodes // Find all the needed outputs of this call CallProjections callprojs; - call->extract_projections(&callprojs, true); + call->extract_projections(&callprojs, true, do_asserts); Unique_Node_List wl; Node* init_mem = call->in(TypeFunc::Memory); diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index 421ce933ed1..3333b7d1bd9 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -731,7 +731,7 @@ class GraphKit : public Phase { // Replace the call with the current state of the kit. Requires // that the call was generated with separate io_projs so that // exceptional control flow can be handled properly. - void replace_call(CallNode* call, Node* result, bool do_replaced_nodes = false); + void replace_call(CallNode* call, Node* result, bool do_replaced_nodes = false, bool do_asserts = true); // helper functions for statistics void increment_counter(address counter_addr); // increment a debug counter diff --git a/test/hotspot/jtreg/compiler/c2/TestCallDevirtualizationWithInfiniteLoop.java b/test/hotspot/jtreg/compiler/c2/TestCallDevirtualizationWithInfiniteLoop.java new file mode 100644 index 00000000000..1afbe8d9465 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestCallDevirtualizationWithInfiniteLoop.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 8336726 + * @summary Test that post-parse call devirtualization works when call does not have an IO projection. + * @run main/othervm -XX:-TieredCompilation -Xcomp -XX:CompileCommand=compileonly,TestCallDevirtualizationWithInfiniteLoop::test + * TestCallDevirtualizationWithInfiniteLoop + */ + +public class TestCallDevirtualizationWithInfiniteLoop { + + static interface I { + public void method(); + } + + static final class A implements I { + @Override + public void method() { }; + } + + static final class B implements I { + @Override + public void method() { }; + } + + static final A a = new A(); + static final B b = new B(); + + public static void test(boolean flag) { + // Avoid executing endless loop + if (flag) { + return; + } + + // We only know after loop opts that the receiver type is B. + I recv = a; + for (int i = 0; i < 3; ++i) { + if (i > 1) { + recv = b; + } + } + // Post-parse call devirtualization will then convert below + // virtual call to a static call. + recv.method(); + + // Endless loop which does not use IO. As a result the IO + // projection of the call is removed unexpectedly. + while (true) { } + } + + public static void main(String[] args) { + test(true); + } +} From 037f11b864734734dd7fbce029b2e8b4bc17f3ab Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 14 Oct 2024 06:02:00 +0000 Subject: [PATCH 34/95] 8341708: Optimize safepoint poll encoding with smaller poll data offset Reviewed-by: kvn, qamai --- src/hotspot/share/jvmci/vmStructs_jvmci.cpp | 2 +- src/hotspot/share/runtime/javaThread.hpp | 20 +++++++++++++++---- src/hotspot/share/runtime/thread.hpp | 7 +++++++ .../compiler/c2/irTests/TestPadding.java | 2 +- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 5452cca96b8..05081197c4b 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -241,7 +241,6 @@ nonstatic_field(JavaThread, _jvmci_reserved_oop0, oop) \ nonstatic_field(JavaThread, _should_post_on_exceptions_flag, int) \ nonstatic_field(JavaThread, _jni_environment, JNIEnv) \ - nonstatic_field(JavaThread, _poll_data, SafepointMechanism::ThreadData) \ nonstatic_field(JavaThread, _stack_overflow_state._reserved_stack_activation, address) \ nonstatic_field(JavaThread, _held_monitor_count, intx) \ nonstatic_field(JavaThread, _lock_stack, LockStack) \ @@ -409,6 +408,7 @@ static_field(StubRoutines, _cont_thaw, address) \ static_field(StubRoutines, _lookup_secondary_supers_table_slow_path_stub, address) \ \ + nonstatic_field(Thread, _poll_data, SafepointMechanism::ThreadData) \ nonstatic_field(Thread, _tlab, ThreadLocalAllocBuffer) \ nonstatic_field(Thread, _allocated_bytes, jlong) \ JFR_ONLY(nonstatic_field(Thread, _jfr_thread_local, JfrThreadLocal)) \ diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp index 20bb08a4acb..bda438612e3 100644 --- a/src/hotspot/share/runtime/javaThread.hpp +++ b/src/hotspot/share/runtime/javaThread.hpp @@ -238,8 +238,6 @@ class JavaThread: public Thread { // Safepoint support public: // Expose _thread_state for SafeFetchInt() volatile JavaThreadState _thread_state; - private: - SafepointMechanism::ThreadData _poll_data; ThreadSafepointState* _safepoint_state; // Holds information about a thread during a safepoint address _saved_exception_pc; // Saved pc of instruction where last implicit exception happened NOT_PRODUCT(bool _requires_cross_modify_fence;) // State used by VerifyCrossModifyFence @@ -598,6 +596,22 @@ class JavaThread: public Thread { SafepointMechanism::ThreadData* poll_data() { return &_poll_data; } + static ByteSize polling_word_offset() { + ByteSize offset = byte_offset_of(Thread, _poll_data) + + byte_offset_of(SafepointMechanism::ThreadData, _polling_word); + // At least on x86_64, safepoint polls encode the offset as disp8 imm. + assert(in_bytes(offset) < 128, "Offset >= 128"); + return offset; + } + + static ByteSize polling_page_offset() { + ByteSize offset = byte_offset_of(Thread, _poll_data) + + byte_offset_of(SafepointMechanism::ThreadData, _polling_page); + // At least on x86_64, safepoint polls encode the offset as disp8 imm. + assert(in_bytes(offset) < 128, "Offset >= 128"); + return offset; + } + void set_requires_cross_modify_fence(bool val) PRODUCT_RETURN NOT_PRODUCT({ _requires_cross_modify_fence = val; }) // Continuation support @@ -787,8 +801,6 @@ class JavaThread: public Thread { static ByteSize vm_result_offset() { return byte_offset_of(JavaThread, _vm_result); } static ByteSize vm_result_2_offset() { return byte_offset_of(JavaThread, _vm_result_2); } static ByteSize thread_state_offset() { return byte_offset_of(JavaThread, _thread_state); } - static ByteSize polling_word_offset() { return byte_offset_of(JavaThread, _poll_data) + byte_offset_of(SafepointMechanism::ThreadData, _polling_word);} - static ByteSize polling_page_offset() { return byte_offset_of(JavaThread, _poll_data) + byte_offset_of(SafepointMechanism::ThreadData, _polling_page);} static ByteSize saved_exception_pc_offset() { return byte_offset_of(JavaThread, _saved_exception_pc); } static ByteSize osthread_offset() { return byte_offset_of(JavaThread, _osthread); } #if INCLUDE_JVMCI diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 45c39eae151..e2dfce7b255 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -33,6 +33,7 @@ #include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "runtime/os.hpp" +#include "runtime/safepointMechanism.hpp" #include "runtime/threadHeapSampler.hpp" #include "runtime/threadLocalStorage.hpp" #include "runtime/threadStatisticalInfo.hpp" @@ -109,6 +110,7 @@ class Thread: public ThreadShadow { friend class VMErrorCallbackMark; friend class VMStructs; friend class JVMCIVMStructs; + friend class JavaThread; private: #ifndef USE_LIBRARY_BASED_TLS_ONLY @@ -135,6 +137,11 @@ class Thread: public ThreadShadow { } private: + // Poll data is used in generated code for safepoint polls. + // It is important for performance to put this at lower offset + // in Thread. The accessors are in JavaThread. + SafepointMechanism::ThreadData _poll_data; + // Thread local data area available to the GC. The internal // structure and contents of this data area is GC-specific. // Only GC and GC barrier code should access this data area. diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestPadding.java b/test/hotspot/jtreg/compiler/c2/irTests/TestPadding.java index 25225e86b28..17b2817a9a2 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestPadding.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestPadding.java @@ -48,7 +48,7 @@ public static void test_runner() { } @Test - @IR(counts = { IRNode.NOP, "1" }) + @IR(counts = { IRNode.NOP, "<=1" }) static int test(int i) { TestPadding tp = tpf; if (tp.b1 > 42) { // Big 'cmpb' instruction at offset 0x30 From e3f650393744790e24820b2d6d99dd1da1a44de6 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 14 Oct 2024 07:55:38 +0000 Subject: [PATCH 35/95] 8341893: AArch64: Micro-optimize compressed ptr decoding Reviewed-by: aph, fyang --- src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 16473b09fff..9835fb5aca1 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -5011,8 +5011,10 @@ void MacroAssembler::decode_heap_oop(Register d, Register s) { verify_heapbase("MacroAssembler::decode_heap_oop: heap base corrupted?"); #endif if (CompressedOops::base() == nullptr) { - if (CompressedOops::shift() != 0 || d != s) { + if (CompressedOops::shift() != 0) { lsl(d, s, CompressedOops::shift()); + } else if (d != s) { + mov(d, s); } } else { Label done; From dcac4b0a532f2ca6cb374da7ece331e8266ab351 Mon Sep 17 00:00:00 2001 From: Fei Gao Date: Mon, 14 Oct 2024 08:40:03 +0000 Subject: [PATCH 36/95] 8341471: Reversed field layout caused by unstable sorting Reviewed-by: jwaters, jsjolen --- .../share/classfile/fieldLayoutBuilder.hpp | 10 +-- .../runtime/FieldLayout/TestFieldLayout.java | 76 +++++++++++++++++++ 2 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/FieldLayout/TestFieldLayout.java diff --git a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp index cda64788acf..9b0d80b2a55 100644 --- a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp +++ b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * 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,17 +101,13 @@ class LayoutRawBlock : public ResourceObj { // sort fields in decreasing order. // Note: with line types, the comparison should include alignment constraint if sizes are equals static int compare_size_inverted(LayoutRawBlock** x, LayoutRawBlock** y) { -#ifdef _WINDOWS - // qsort() on Windows reverse the order of fields with the same size - // the extension of the comparison function below preserves this order int diff = (*y)->size() - (*x)->size(); + // qsort() may reverse the order of fields with the same size. + // The extension is to ensure stable sort. if (diff == 0) { diff = (*x)->field_index() - (*y)->field_index(); } return diff; -#else - return (*y)->size() - (*x)->size(); -#endif // _WINDOWS } }; diff --git a/test/hotspot/jtreg/runtime/FieldLayout/TestFieldLayout.java b/test/hotspot/jtreg/runtime/FieldLayout/TestFieldLayout.java new file mode 100644 index 00000000000..4066d6dd743 --- /dev/null +++ b/test/hotspot/jtreg/runtime/FieldLayout/TestFieldLayout.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024, Arm 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.reflect.Field; +import jdk.internal.misc.Unsafe; + +/* + * @test + * @bug 8341471 + * @summary Reversed field layout caused by unstable sorting + * @modules java.base/jdk.internal.misc + * @run main/othervm TestFieldLayout + */ + +public class TestFieldLayout { + + private static final Unsafe U = Unsafe.getUnsafe(); + + public static void main(String[] args) throws Exception { + + boolean endResult = true; + long previous = 0; + + for (Field f : Test.class.getDeclaredFields()) { + long current = U.objectFieldOffset(f); + if (current < previous) { + System.out.printf("FAILED: field %s offset %d previous %d\n", + f.getName(), current, previous); + endResult = false; + } + previous = current; + } + + System.out.println(endResult ? "Test PASSES" : "Test FAILS"); + if (!endResult) { + throw new Error("Test failed"); + } + } + + public class Test { + char a000; + char a001; + char a002; + char a003; + char a004; + char a005; + char a006; + char a007; + char a008; + char a009; + char a00a; + char a00b; + } + +} + From b20c5c79def1ef2cc4b587b7688f8f1d4c81d699 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Mon, 14 Oct 2024 10:34:19 +0000 Subject: [PATCH 37/95] 8341967: Unify os::current_file_offset and os::seek_to_file_offset across posix platforms Reviewed-by: jsjolen, mdoerr --- src/hotspot/os/aix/os_aix.cpp | 10 ---------- src/hotspot/os/bsd/os_bsd.cpp | 10 ---------- src/hotspot/os/linux/os_linux.cpp | 10 ---------- src/hotspot/os/posix/os_posix.cpp | 10 ++++++++++ 4 files changed, 10 insertions(+), 30 deletions(-) diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index fd16a7984a6..63aa53f0a23 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -2483,16 +2483,6 @@ int os::open(const char *path, int oflag, int mode) { return fd; } -// return current position of file pointer -jlong os::current_file_offset(int fd) { - return (jlong)::lseek(fd, (off_t)0, SEEK_CUR); -} - -// move file pointer to the specified offset -jlong os::seek_to_file_offset(int fd, jlong offset) { - return (jlong)::lseek(fd, (off_t)offset, SEEK_SET); -} - // current_thread_cpu_time(bool) and thread_cpu_time(Thread*, bool) // are used by JVM M&M and JVMTI to get user+sys or user CPU time // of a thread. diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 9ad7c35e6bd..18818268c1f 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -2400,16 +2400,6 @@ int os::open(const char *path, int oflag, int mode) { return fd; } -// return current position of file pointer -jlong os::current_file_offset(int fd) { - return (jlong)::lseek(fd, (off_t)0, SEEK_CUR); -} - -// move file pointer to the specified offset -jlong os::seek_to_file_offset(int fd, jlong offset) { - return (jlong)::lseek(fd, (off_t)offset, SEEK_SET); -} - // current_thread_cpu_time(bool) and thread_cpu_time(Thread*, bool) // are used by JVM M&M and JVMTI to get user+sys or user CPU time // of a thread. diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index c9968fc9f35..c80663fec3d 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -5053,16 +5053,6 @@ int os::open(const char *path, int oflag, int mode) { return fd; } -// return current position of file pointer -jlong os::current_file_offset(int fd) { - return (jlong)::lseek(fd, (off_t)0, SEEK_CUR); -} - -// move file pointer to the specified offset -jlong os::seek_to_file_offset(int fd, jlong offset) { - return (jlong)::lseek(fd, (off_t)offset, SEEK_SET); -} - static jlong slow_thread_cpu_time(Thread *thread, bool user_sys_cpu_time); static jlong fast_cpu_time(Thread *thread) { diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index 60efdeb2ef5..cc29f209160 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -348,6 +348,16 @@ int os::create_file_for_heap(const char* dir) { return fd; } +// return current position of file pointer +jlong os::current_file_offset(int fd) { + return (jlong)::lseek(fd, (off_t)0, SEEK_CUR); +} + +// move file pointer to the specified offset +jlong os::seek_to_file_offset(int fd, jlong offset) { + return (jlong)::lseek(fd, (off_t)offset, SEEK_SET); +} + // Is a (classpath) directory empty? bool os::dir_is_empty(const char* path) { DIR *dir = nullptr; From 1581508988141bfb420d97759138203f30926b35 Mon Sep 17 00:00:00 2001 From: Afshin Zafari Date: Mon, 14 Oct 2024 10:51:37 +0000 Subject: [PATCH 38/95] 8335091: NMT: VMATree reserve_mapping and commit_mapping APIs need MEMFLAGS while un/-committing API has no MEMFLAGS arg Reviewed-by: jsjolen, gziemski --- src/hotspot/share/nmt/vmatree.cpp | 20 +++++++++++++++- src/hotspot/share/nmt/vmatree.hpp | 32 ++++++++++++++++--------- test/hotspot/gtest/nmt/test_vmatree.cpp | 18 +++++++++++++- 3 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/hotspot/share/nmt/vmatree.cpp b/src/hotspot/share/nmt/vmatree.cpp index 3795376d476..65a5bdb94ae 100644 --- a/src/hotspot/share/nmt/vmatree.cpp +++ b/src/hotspot/share/nmt/vmatree.cpp @@ -24,6 +24,7 @@ */ #include "precompiled.hpp" +#include "logging/log.hpp" #include "nmt/vmatree.hpp" #include "utilities/growableArray.hpp" @@ -34,7 +35,9 @@ const char* VMATree::statetype_strings[3] = { }; VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType state, - const RegionData& metadata) { + const RegionData& metadata, bool use_tag_inplace) { + assert(!use_tag_inplace || metadata.mem_tag == mtNone, + "If using use_tag_inplace, then the supplied tag should be mtNone, was instead: %s", NMTUtil::tag_to_name(metadata.mem_tag)); if (A == B) { // A 0-sized mapping isn't worth recording. return SummaryDiff(); @@ -55,6 +58,10 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType AddressState LEQ_A; TreapNode* leqA_n = _tree.closest_leq(A); if (leqA_n == nullptr) { + assert(!use_tag_inplace, "Cannot use the tag inplace if no pre-existing tag exists. From: " PTR_FORMAT " To: " PTR_FORMAT, A, B); + if (use_tag_inplace) { + log_debug(nmt)("Cannot use the tag inplace if no pre-existing tag exists. From: " PTR_FORMAT " To: " PTR_FORMAT, A, B); + } // No match. We add the A node directly, unless it would have no effect. if (!stA.is_noop()) { _tree.upsert(A, stA); @@ -62,6 +69,17 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType } else { LEQ_A_found = true; LEQ_A = AddressState{leqA_n->key(), leqA_n->val()}; + StateType leqA_state = leqA_n->val().out.type(); + StateType new_state = stA.out.type(); + // If we specify use_tag_inplace then the new region takes over the current tag instead of the tag in metadata. + // This is important because the VirtualMemoryTracker API doesn't require supplying the tag for some operations. + if (use_tag_inplace) { + assert(leqA_n->val().out.type() != StateType::Released, "Should not use inplace the tag of a released region"); + MemTag tag = leqA_n->val().out.mem_tag(); + stA.out.set_tag(tag); + stB.in.set_tag(tag); + } + // Unless we know better, let B's outgoing state be the outgoing state of the node at or preceding A. // Consider the case where the found node is the start of a region enclosing [A,B) stB.out = leqA_n->val().out; diff --git a/src/hotspot/share/nmt/vmatree.hpp b/src/hotspot/share/nmt/vmatree.hpp index 55399e51b9d..cfb3c8ab524 100644 --- a/src/hotspot/share/nmt/vmatree.hpp +++ b/src/hotspot/share/nmt/vmatree.hpp @@ -66,7 +66,7 @@ class VMATree { return statetype_strings[static_cast(type)]; } - // Each point has some stack and a flag associated with it. + // Each point has some stack and a tag associated with it. struct RegionData { const NativeCallStackStorage::StackIndex stack_idx; const MemTag mem_tag; @@ -88,30 +88,34 @@ class VMATree { struct IntervalState { private: // Store the type and mem_tag as two bytes - uint8_t type_flag[2]; + uint8_t type_tag[2]; NativeCallStackStorage::StackIndex sidx; public: - IntervalState() : type_flag{0,0}, sidx() {} + IntervalState() : type_tag{0,0}, sidx() {} IntervalState(const StateType type, const RegionData data) { assert(!(type == StateType::Released) || data.mem_tag == mtNone, "Released type must have memory tag mtNone"); - type_flag[0] = static_cast(type); - type_flag[1] = static_cast(data.mem_tag); + type_tag[0] = static_cast(type); + type_tag[1] = static_cast(data.mem_tag); sidx = data.stack_idx; } StateType type() const { - return static_cast(type_flag[0]); + return static_cast(type_tag[0]); } MemTag mem_tag() const { - return static_cast(type_flag[1]); + return static_cast(type_tag[1]); } RegionData regiondata() const { return RegionData{sidx, mem_tag()}; } + void set_tag(MemTag tag) { + type_tag[1] = static_cast(tag); + } + NativeCallStackStorage::StackIndex stack() const { return sidx; } @@ -167,14 +171,20 @@ class VMATree { } }; - SummaryDiff register_mapping(position A, position B, StateType state, const RegionData& metadata); + private: + SummaryDiff register_mapping(position A, position B, StateType state, const RegionData& metadata, bool use_tag_inplace = false); + public: SummaryDiff reserve_mapping(position from, position sz, const RegionData& metadata) { - return register_mapping(from, from + sz, StateType::Reserved, metadata); + return register_mapping(from, from + sz, StateType::Reserved, metadata, false); + } + + SummaryDiff commit_mapping(position from, position sz, const RegionData& metadata, bool use_tag_inplace = false) { + return register_mapping(from, from + sz, StateType::Committed, metadata, use_tag_inplace); } - SummaryDiff commit_mapping(position from, position sz, const RegionData& metadata) { - return register_mapping(from, from + sz, StateType::Committed, metadata); + SummaryDiff uncommit_mapping(position from, position sz, const RegionData& metadata) { + return register_mapping(from, from + sz, StateType::Reserved, metadata, true); } SummaryDiff release_mapping(position from, position sz) { diff --git a/test/hotspot/gtest/nmt/test_vmatree.cpp b/test/hotspot/gtest/nmt/test_vmatree.cpp index 08b4340ae4f..7a5a98b7863 100644 --- a/test/hotspot/gtest/nmt/test_vmatree.cpp +++ b/test/hotspot/gtest/nmt/test_vmatree.cpp @@ -171,7 +171,6 @@ class NMTVMATreeTest : public testing::Test { }; - TEST_VM_F(NMTVMATreeTest, OverlappingReservationsResultInTwoNodes) { VMATree::RegionData rd{si[0], mtTest}; Tree tree; @@ -181,6 +180,23 @@ TEST_VM_F(NMTVMATreeTest, OverlappingReservationsResultInTwoNodes) { EXPECT_EQ(2, count_nodes(tree)); } +TEST_VM_F(NMTVMATreeTest, UseFlagInplace) { + Tree tree; + VMATree::RegionData rd1(si[0], mtTest); + VMATree::RegionData rd2(si[1], mtNone); + tree.reserve_mapping(0, 100, rd1); + tree.commit_mapping(20, 50, rd2, true); + tree.uncommit_mapping(30, 10, rd2); + tree.visit_in_order([&](Node* node) { + if (node->key() != 100) { + EXPECT_EQ(mtTest, node->val().out.mem_tag()) << "failed at: " << node->key(); + if (node->key() != 20 && node->key() != 40) { + EXPECT_EQ(VMATree::StateType::Reserved, node->val().out.type()); + } + } + }); +} + // Low-level tests inspecting the state of the tree. TEST_VM_F(NMTVMATreeTest, LowLevel) { adjacent_2_nodes(VMATree::empty_regiondata); From ba3774dc5d27e762dfd61f8acf842ae11dec0cb7 Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Mon, 14 Oct 2024 12:29:06 +0000 Subject: [PATCH 39/95] 8341637: java/net/Socket/UdpSocket.java fails with "java.net.BindException: Address already in use" (macos-aarch64) Reviewed-by: jpai --- test/jdk/java/net/Socket/UdpSocket.java | 27 ++++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/test/jdk/java/net/Socket/UdpSocket.java b/test/jdk/java/net/Socket/UdpSocket.java index a15f9255b45..5d13c1f916a 100644 --- a/test/jdk/java/net/Socket/UdpSocket.java +++ b/test/jdk/java/net/Socket/UdpSocket.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * 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,6 +47,8 @@ @Test public class UdpSocket { + private static final int MAX_RETRIES = 3; + /** * Test using the Socket API to send/receive datagrams */ @@ -133,16 +135,21 @@ public void testMaxSockets() throws Exception { } - private Socket newUdpSocket() throws IOException { - Socket s = null; - - try { - s = new Socket(InetAddress.getLoopbackAddress(), 8000, false); - } catch (BindException unexpected) { - System.out.println("BindException caught retry Socket creation"); - s = new Socket(InetAddress.getLoopbackAddress(), 8000, false); + private Socket newUdpSocket() throws IOException, InterruptedException { + BindException unexpected = null; + for (int i=0; i < MAX_RETRIES; i++) { + try { + return new Socket(InetAddress.getLoopbackAddress(), 8000, false); + } catch (BindException be) { + unexpected = be; + if (i != MAX_RETRIES - 1) { + System.out.printf("BindException caught: retry Socket creation [%s/%s]%n", + i + 1, MAX_RETRIES); + Thread.sleep(10 + 10 * i); + } + } } - return s; + throw unexpected; } private void closeAll(Deque sockets) throws IOException { From f56a154132f7e66b1b65adfa2aa937119999b14a Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Mon, 14 Oct 2024 12:32:55 +0000 Subject: [PATCH 40/95] 8341881: [REDO] java/nio/file/attribute/BasicFileAttributeView/CreationTime.java#tmp fails on alinux3 Reviewed-by: liach, sgehwolf, ihse, bpb --- make/test/JtregNativeJdk.gmk | 2 + .../BasicFileAttributeView/CreationTime.java | 38 +++--- .../CreationTimeHelper.java | 61 +++++++++ .../libCreationTimeHelper.c | 122 ++++++++++++++++++ 4 files changed, 205 insertions(+), 18 deletions(-) create mode 100644 test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTimeHelper.java create mode 100644 test/jdk/java/nio/file/attribute/BasicFileAttributeView/libCreationTimeHelper.c diff --git a/make/test/JtregNativeJdk.gmk b/make/test/JtregNativeJdk.gmk index d9f1e334a5c..90055cb5c01 100644 --- a/make/test/JtregNativeJdk.gmk +++ b/make/test/JtregNativeJdk.gmk @@ -115,6 +115,8 @@ ifeq ($(call isTargetOs, linux), true) # stripping during the test libraries' build. BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libFib := -g BUILD_JDK_JTREG_LIBRARIES_STRIP_SYMBOLS_libFib := false + # nio tests' libCreationTimeHelper native needs -ldl linker flag + BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libCreationTimeHelper := -ldl endif ifeq ($(ASAN_ENABLED), true) diff --git a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java index ad85da7ae63..65e801b0a9f 100644 --- a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java +++ b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024 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 @@ -25,18 +26,18 @@ * @bug 8011536 8151430 8316304 8334339 * @summary Basic test for creationTime attribute on platforms/file systems * that support it, tests using /tmp directory. - * @library ../.. /test/lib - * @build jdk.test.lib.Platform - * @run main CreationTime + * @library ../.. /test/lib /java/foreign + * @build jdk.test.lib.Platform NativeTestHelper + * @run main/othervm/native --enable-native-access=ALL-UNNAMED CreationTime */ /* @test id=cwd * @summary Basic test for creationTime attribute on platforms/file systems * that support it, tests using the test scratch directory, the test * scratch directory maybe at diff disk partition to /tmp on linux. - * @library ../.. /test/lib - * @build jdk.test.lib.Platform - * @run main CreationTime . + * @library ../.. /test/lib /java/foreign + * @build jdk.test.lib.Platform NativeTestHelper + * @run main/othervm/native --enable-native-access=ALL-UNNAMED CreationTime . */ import java.lang.foreign.Linker; @@ -51,8 +52,6 @@ public class CreationTime { - private static final java.io.PrintStream err = System.err; - /** * Reads the creationTime attribute */ @@ -78,14 +77,9 @@ static void test(Path top) throws IOException { FileTime creationTime = creationTime(file); Instant now = Instant.now(); if (Math.abs(creationTime.toMillis()-now.toEpochMilli()) > 10000L) { - System.out.println("creationTime.toMillis() == " + creationTime.toMillis()); - // If the file system doesn't support birth time, then skip this test - if (creationTime.toMillis() == 0) { - throw new SkippedException("birth time not support for: " + file); - } else { - err.println("File creation time reported as: " + creationTime); - throw new RuntimeException("Expected to be close to: " + now); - } + System.err.println("creationTime.toMillis() == " + creationTime.toMillis()); + System.err.println("File creation time reported as: " + creationTime); + throw new RuntimeException("Expected to be close to: " + now); } /** @@ -107,7 +101,12 @@ static void test(Path top) throws IOException { } } else if (Platform.isLinux()) { // Creation time read depends on statx system call support - supportsCreationTimeRead = Linker.nativeLinker().defaultLookup().find("statx").isPresent(); + try { + supportsCreationTimeRead = CreationTimeHelper. + linuxIsCreationTimeSupported(file.toAbsolutePath().toString()); + } catch (Throwable e) { + supportsCreationTimeRead = false; + } // Creation time updates are not supported on Linux supportsCreationTimeWrite = false; } @@ -122,8 +121,11 @@ static void test(Path top) throws IOException { Instant plusHour = Instant.now().plusSeconds(60L * 60L); Files.setLastModifiedTime(file, FileTime.from(plusHour)); FileTime current = creationTime(file); - if (!current.equals(creationTime)) + if (!current.equals(creationTime)) { + System.err.println("current = " + current); + System.err.println("creationTime = " + creationTime); throw new RuntimeException("Creation time should not have changed"); + } } /** diff --git a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTimeHelper.java b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTimeHelper.java new file mode 100644 index 00000000000..592aeba322d --- /dev/null +++ b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTimeHelper.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SymbolLookup; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.MethodHandle; + +public class CreationTimeHelper extends NativeTestHelper { + + static { + System.loadLibrary("CreationTimeHelper"); + } + + final static Linker abi = Linker.nativeLinker(); + static final SymbolLookup lookup = SymbolLookup.loaderLookup(); + final static MethodHandle methodHandle = abi. + downcallHandle(lookup.findOrThrow("linuxIsCreationTimeSupported"), + FunctionDescriptor.of(C_BOOL, C_POINTER)); + + // Helper so as to determine birth time support or not on Linux. + // Support is determined in a two-step process: + // 1. Determine if `statx` system call is available. If available proceed, + // otherwise return false. + // 2. Perform an actual `statx` call on the given file and check for birth + // time support in the mask returned from the call. This is needed, + // since some file systems, like nfs/tmpfs etc., don't support birth + // time even though the `statx` system call is available. + static boolean linuxIsCreationTimeSupported(String file) throws Throwable { + if (!abi.defaultLookup().find("statx").isPresent()) { + return false; + } + try (var arena = Arena.ofConfined()) { + MemorySegment s = arena.allocateFrom(file); + return (boolean)methodHandle.invokeExact(s); + } + } +} diff --git a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/libCreationTimeHelper.c b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/libCreationTimeHelper.c new file mode 100644 index 00000000000..fb518b3b701 --- /dev/null +++ b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/libCreationTimeHelper.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2024 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 + * 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 "export.h" +#include +#if defined(__linux__) +#include +#include +#include +#include +#include +#ifndef STATX_BASIC_STATS +#define STATX_BASIC_STATS 0x000007ffU +#endif +#ifndef STATX_BTIME +#define STATX_BTIME 0x00000800U +#endif +#ifndef RTLD_DEFAULT +#define RTLD_DEFAULT RTLD_LOCAL +#endif +#ifndef AT_SYMLINK_NOFOLLOW +#define AT_SYMLINK_NOFOLLOW 0x100 +#endif +#ifndef AT_FDCWD +#define AT_FDCWD -100 +#endif + +/* + * Timestamp structure for the timestamps in struct statx. + */ +struct my_statx_timestamp { + __int64_t tv_sec; + __uint32_t tv_nsec; + __int32_t __reserved; +}; + +/* + * struct statx used by statx system call on >= glibc 2.28 + * systems + */ +struct my_statx +{ + __uint32_t stx_mask; + __uint32_t stx_blksize; + __uint64_t stx_attributes; + __uint32_t stx_nlink; + __uint32_t stx_uid; + __uint32_t stx_gid; + __uint16_t stx_mode; + __uint16_t __statx_pad1[1]; + __uint64_t stx_ino; + __uint64_t stx_size; + __uint64_t stx_blocks; + __uint64_t stx_attributes_mask; + struct my_statx_timestamp stx_atime; + struct my_statx_timestamp stx_btime; + struct my_statx_timestamp stx_ctime; + struct my_statx_timestamp stx_mtime; + __uint32_t stx_rdev_major; + __uint32_t stx_rdev_minor; + __uint32_t stx_dev_major; + __uint32_t stx_dev_minor; + __uint64_t __statx_pad2[14]; +}; + +typedef int statx_func(int dirfd, const char *restrict pathname, int flags, + unsigned int mask, struct my_statx *restrict statxbuf); + +static statx_func* my_statx_func = NULL; +#endif //#defined(__linux__) + +// static boolean linuxIsCreationTimeSupported(char* file) +EXPORT bool linuxIsCreationTimeSupported(char* file) { +#if defined(__linux__) + struct my_statx stx = {0}; + int ret, atflag = AT_SYMLINK_NOFOLLOW; + unsigned int mask = STATX_BASIC_STATS | STATX_BTIME; + + my_statx_func = (statx_func*) dlsym(RTLD_DEFAULT, "statx"); + if (my_statx_func == NULL) { + return false; + } + + if (file == NULL) { + printf("input file error!\n"); + return false; + } + + ret = my_statx_func(AT_FDCWD, file, atflag, mask, &stx); + if (ret != 0) { + return false; + } + // On some systems where statx is available but birth time might still not + // be supported as it's file system specific. The only reliable way to + // check for supported or not is looking at the filled in STATX_BTIME bit + // in the returned statx buffer mask. + if ((stx.stx_mask & STATX_BTIME) != 0) + return true; + return false; +#else + return false; +#endif +} From 9e262df813874043f72b60695a88c28fc7d5cccb Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Mon, 14 Oct 2024 16:00:04 +0000 Subject: [PATCH 41/95] 8342002: sun/security/tools/keytool/GenKeyPairSigner.java failed due to missing certificate output Reviewed-by: mullan --- test/jdk/ProblemList.txt | 2 -- .../security/tools/keytool/GenKeyPairSigner.java | 14 +++++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 0aa27e4ac0c..034830a2575 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -644,8 +644,6 @@ sun/security/provider/PolicyParser/PrincipalExpansionError.java 8039280 generic- sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java 8316183 linux-ppc64le -sun/security/tools/keytool/GenKeyPairSigner.java 8342002 generic-all - ############################################################################ # jdk_sound diff --git a/test/jdk/sun/security/tools/keytool/GenKeyPairSigner.java b/test/jdk/sun/security/tools/keytool/GenKeyPairSigner.java index 52ca1ead82c..84cfcd7cb17 100644 --- a/test/jdk/sun/security/tools/keytool/GenKeyPairSigner.java +++ b/test/jdk/sun/security/tools/keytool/GenKeyPairSigner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -84,7 +84,7 @@ static void testSignerPKCS12() throws Exception { System.out.println("Generating an XDH cert with -signer option"); SecurityTools.keytool("-keystore ks -storepass changeit " + "-genkeypair -keyalg XDH -alias e1 -dname CN=E1 -signer ca") - .shouldContain("Generating 255 bit XDH key pair and a certificate (Ed25519) issued by with a validity of 90 days") + .shouldContain("Generating 255 bit X25519 key pair and a certificate (Ed25519) issued by with a validity of 90 days") .shouldContain("for: CN=E1") .shouldHaveExitValue(0); @@ -118,7 +118,7 @@ static void testSignerPKCS12() throws Exception { .shouldContain("Alias name: e1") .shouldContain("Certificate chain length: 2") .shouldContain("Signature algorithm name: Ed25519") - .shouldContain("Subject Public Key Algorithm: 255-bit XDH key") + .shouldContain("Subject Public Key Algorithm: 255-bit X25519 key") .shouldHaveExitValue(0); // check to make sure that cert's AKID is created from the SKID of the signing cert @@ -150,7 +150,7 @@ static void testSignerPKCS12() throws Exception { System.out.println("Generating an X448 cert with -signer option"); SecurityTools.keytool("-keystore ks -storepass changeit " + "-genkeypair -keyalg X448 -alias e2 -dname CN=E2 -sigalg SHA384withRSA -signer ca2") - .shouldContain("Generating 448 bit XDH key pair and a certificate (SHA384withRSA) issued by with a validity of 90 days") + .shouldContain("Generating 448 bit X448 key pair and a certificate (SHA384withRSA) issued by with a validity of 90 days") .shouldContain("for: CN=E2") .shouldHaveExitValue(0); @@ -177,7 +177,7 @@ static void testSignerPKCS12() throws Exception { "-list -v") .shouldContain("Alias name: e2") .shouldContain("Signature algorithm name: SHA384withRSA") - .shouldContain("Subject Public Key Algorithm: 448-bit XDH key") + .shouldContain("Subject Public Key Algorithm: 448-bit X448 key") .shouldHaveExitValue(0); kt("-genkeypair -keyalg DSA -alias ca3 -dname CN=CA3 -ext bc:c ", @@ -249,7 +249,7 @@ static void testSignerJKS() throws Exception { SecurityTools.keytool("-keystore ksjks -storepass changeit -storetype jks " + "-genkeypair -keyalg XDH -alias e1 -dname CN=E1 " + "-keypass e1keypass -signer ca1 -signerkeypass ca1keypass") - .shouldContain("Generating 255 bit XDH key pair and a certificate (SHA256withDSA) issued by with a validity of 90 days") + .shouldContain("Generating 255 bit X25519 key pair and a certificate (SHA256withDSA) issued by with a validity of 90 days") .shouldContain("for: CN=E1") .shouldContain("The generated certificate #2 of 3 uses a 1024-bit DSA key which is considered a security risk") .shouldContain("The generated certificate #3 of 3 uses a 1024-bit RSA key which is considered a security risk") @@ -285,7 +285,7 @@ static void testSignerJKS() throws Exception { .shouldContain("Alias name: e1") .shouldContain("Certificate chain length: 3") .shouldContain("Signature algorithm name: SHA256withDSA") - .shouldContain("Subject Public Key Algorithm: 255-bit XDH key") + .shouldContain("Subject Public Key Algorithm: 255-bit X25519 key") .shouldHaveExitValue(0); } From a8a8b2deba854ac105ed760c09e65701c4d0f6fc Mon Sep 17 00:00:00 2001 From: Igor Veresov Date: Mon, 14 Oct 2024 16:44:53 +0000 Subject: [PATCH 42/95] 8341831: PhaseCFG::insert_anti_dependences asserts with "no loads" Reviewed-by: dlong, kvn --- src/hotspot/share/opto/gcm.cpp | 12 +++++++++++- test/hotspot/jtreg/ProblemList.txt | 2 -- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/opto/gcm.cpp b/src/hotspot/share/opto/gcm.cpp index dd51bb47094..c46d69058e9 100644 --- a/src/hotspot/share/opto/gcm.cpp +++ b/src/hotspot/share/opto/gcm.cpp @@ -763,7 +763,17 @@ Block* PhaseCFG::insert_anti_dependences(Block* LCA, Node* load, bool verify) { worklist_def_use_mem_states.pop(); uint op = use_mem_state->Opcode(); - assert(!use_mem_state->needs_anti_dependence_check(), "no loads"); + +#ifdef ASSERT + // CacheWB nodes are peculiar in a sense that they both are anti-dependent and produce memory. + // Allow them to be treated as a store. + bool is_cache_wb = false; + if (use_mem_state->is_Mach()) { + int ideal_op = use_mem_state->as_Mach()->ideal_Opcode(); + is_cache_wb = (ideal_op == Op_CacheWB); + } + assert(!use_mem_state->needs_anti_dependence_check() || is_cache_wb, "no loads"); +#endif // MergeMems do not directly have anti-deps. // Treat them as internal nodes in a forward tree of memory states, diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index e85b742a53b..3ff450dc3ad 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -43,8 +43,6 @@ # :hotspot_compiler -applications/ctw/modules/java_base_2.java 8341831 linux-x64 - compiler/ciReplay/TestSAServer.java 8029528 generic-all compiler/compilercontrol/jcmd/ClearDirectivesFileStackTest.java 8225370 generic-all From 60713463c7014e4e15da73023e82ef58d7134b48 Mon Sep 17 00:00:00 2001 From: Alisen Chung Date: Mon, 14 Oct 2024 16:45:59 +0000 Subject: [PATCH 43/95] 8339879: Open some dialog awt tests Reviewed-by: honkar, prr --- test/jdk/java/awt/Dialog/DefaultIconTest.java | 71 ++++ .../awt/Dialog/DialogInitialResizability.java | 96 ++++++ .../jdk/java/awt/Dialog/NestedDialogTest.java | 312 ++++++++++++++++++ .../ShownModalDialogSerializationTest.java | 92 ++++++ 4 files changed, 571 insertions(+) create mode 100644 test/jdk/java/awt/Dialog/DefaultIconTest.java create mode 100644 test/jdk/java/awt/Dialog/DialogInitialResizability.java create mode 100644 test/jdk/java/awt/Dialog/NestedDialogTest.java create mode 100644 test/jdk/java/awt/Dialog/ShownModalDialogSerializationTest.java diff --git a/test/jdk/java/awt/Dialog/DefaultIconTest.java b/test/jdk/java/awt/Dialog/DefaultIconTest.java new file mode 100644 index 00000000000..8d2ec8c406f --- /dev/null +++ b/test/jdk/java/awt/Dialog/DefaultIconTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dialog; +import java.awt.Frame; + +/* + * @test + * @bug 4964237 + * @requires (os.family == "windows") + * @summary Win: Changing theme changes java dialogs title icon + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DefaultIconTest + */ + +public class DefaultIconTest { + static String instructions = """ + This test shows frame and two dialogs + Change windows theme. Resizable dialog should retain default icon + Non-resizable dialog should retain no icon + Press PASS if icons look correct, FAIL otherwise + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ShownModalDialogSerializationTest Instructions") + .instructions(instructions) + .testTimeOut(5) + .rows(10) + .columns(35) + .testUI(DefaultIconTest::createGUIs) + .build() + .awaitAndCheck(); + } + + public static Frame createGUIs() { + Frame f = new Frame("DefaultIconTest"); + f.setSize(200, 100); + Dialog d1 = new Dialog(f, "Resizable Dialog, should show default icon"); + d1.setSize(200, 100); + d1.setVisible(true); + d1.setLocation(0, 150); + Dialog d2 = new Dialog(f, "Non-resizable dialog, should have no icon"); + d2.setSize(200, 100); + d2.setVisible(true); + d2.setResizable(false); + d2.setLocation(0, 300); + return f; + } +} diff --git a/test/jdk/java/awt/Dialog/DialogInitialResizability.java b/test/jdk/java/awt/Dialog/DialogInitialResizability.java new file mode 100644 index 00000000000..7ecde39c4ad --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogInitialResizability.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; + +/* + * @test + * @bug 4912551 + * @summary Checks that with resizable set to false before show() + * dialog can not be resized. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogInitialResizability + */ + +public class DialogInitialResizability { + static String instructions = """ + When this test is run a dialog will display (setResizable Test). + This dialog should not be resizable. + + Additionally ensure that there are NO componentResized events in the log section. + If the above conditions are true, then Press PASS else FAIL. + """; + + private static final Dimension INITIAL_SIZE = new Dimension(400, 150); + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("DialogInitialResizability") + .instructions(instructions) + .testTimeOut(5) + .rows((int) instructions.lines().count() + 2) + .columns(40) + .testUI(DialogInitialResizability::createGUI) + .logArea() + .build() + .awaitAndCheck(); + } + + public static MyDialog createGUI() { + Frame f = new Frame("invisible dialog owner"); + + MyDialog ld = new MyDialog(f); + ld.setBounds(100, 100, INITIAL_SIZE.width, INITIAL_SIZE.height); + ld.setResizable(false); + + PassFailJFrame.log("Dialog isResizable is set to: " + ld.isResizable()); + PassFailJFrame.log("Dialog Initial Size " + ld.getSize()); + return ld; + } + + private static class MyDialog extends Dialog implements ComponentListener { + public MyDialog(Frame f) { + super(f, "setResizable test", false); + this.addComponentListener(this); + } + + public void componentResized(ComponentEvent e) { + if (!e.getComponent().getSize().equals(INITIAL_SIZE)) { + PassFailJFrame.log("Component Resized. Test Failed!!"); + } + } + + public void componentMoved(ComponentEvent e) { + } + + public void componentShown(ComponentEvent e) { + } + + public void componentHidden(ComponentEvent e) { + } + } +} diff --git a/test/jdk/java/awt/Dialog/NestedDialogTest.java b/test/jdk/java/awt/Dialog/NestedDialogTest.java new file mode 100644 index 00000000000..28fb1bc919e --- /dev/null +++ b/test/jdk/java/awt/Dialog/NestedDialogTest.java @@ -0,0 +1,312 @@ +/* + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Choice; +import java.awt.Dialog; +import java.awt.FileDialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.List; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.Vector; +import java.util.Enumeration; + +/* + * @test + * @bug 4110094 4178930 4178390 + * @summary Test: Rewrite of Win modal dialogs + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual NestedDialogTest + */ + +public class NestedDialogTest { + private static Vector windows = new Vector(); + static String instructions = """ + To solve various race conditions, windows modal dialogs were rewritten. This + test exercises various modal dialog boundary conditions and checks that + previous fixes to modality are incorporated in the rewrite. + + Check the following: + - No IllegalMonitorStateException is thrown when a dialog closes + + - Open multiple nested dialogs and verify that all other windows + are disabled when modal dialog is active. + + - Check that the proper window is activated when a modal dialog closes. + + - Close nested dialogs out of order (e.g. close dialog1 before dialog2) + and verify that this works and no deadlock occurs. + + - Check that all other windows are disabled when a FileDialog is open. + + - Check that the proper window is activated when a FileDialog closes. + + - Verify that the active window nevers switches to another application + when closing dialogs, even temporarily. + + - Check that choosing Hide always sucessfully hides a dialog. You should + try this multiple times to catch any race conditions. + + - Check that the scrollbar on the Choice component in the dialog works, as opposed + to just using drag-scrolling or the cursor keys + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("NestedDialogTest") + .instructions(instructions) + .testTimeOut(5) + .rows((int) instructions.lines().count() + 2) + .columns(35) + .testUI(NestedDialogTest::createGUI) + .build() + .awaitAndCheck(); + } + + public static Frame createGUI() { + Frame frame1 = new NestedDialogTestFrame("frame0"); + Frame frame2 = new NestedDialogTestFrame("frame1"); + frame2.setLocation(100, 100); + return frame1; + } + + public static void addWindow(Window window) { + // System.out.println("Pushing window " + window); + windows.removeElement(window); + windows.addElement(window); + } + + public static void removeWindow(Window window) { + // System.out.println("Popping window " + window); + windows.removeElement(window); + } + + public static Window getWindow(int index) { + return (Window) windows.elementAt(index); + } + + public static Enumeration enumWindows() { + return windows.elements(); + } + + public static int getWindowIndex(Window win) { + return windows.indexOf(win); + } +} + +class NestedDialogTestFrame extends Frame { + NestedDialogTestFrame(String name) { + super(name); + setSize(200, 200); + show(); + + setLayout(new FlowLayout()); + Button btnDlg = new Button("Dialog..."); + add(btnDlg); + Button btnFileDlg = new Button("FileDialog..."); + add(btnFileDlg); + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent ev) { + System.exit(0); + } + }); + + btnDlg.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + Dialog d1 = new SimpleDialog(NestedDialogTestFrame.this, null, true); + System.out.println("Returned from showing dialog: " + d1); + } + } + ); + + btnFileDlg.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + FileDialog dlg = new FileDialog(NestedDialogTestFrame.this); + dlg.show(); + } + } + ); + + validate(); + } + + public void show() { + if (!isVisible()) { + NestedDialogTest.addWindow(this); + } + super.show(); + } + + public void dispose() { + NestedDialogTest.removeWindow(this); + super.dispose(); + } +} + +class SimpleDialog extends Dialog { + Button btnNested; + Button btnFileDlg; + Button btnShow; + Button btnHide; + Button btnDispose; + Button btnExit; + List listWins; + Dialog dlgPrev; + + public SimpleDialog(Frame frame, Dialog prev, boolean isModal) { + super(frame, "", isModal); + + dlgPrev = prev; + + addWindowListener(new WindowAdapter() { + public void windowActivated(WindowEvent ev) { + populateListWin(); + } + }); + + setTitle(getName()); + + Panel panelNorth = new Panel(); + panelNorth.setLayout(new GridLayout(1, 1)); + listWins = new List(); + panelNorth.add(listWins); + + Panel panelSouth = new Panel(); + panelSouth.setLayout(new FlowLayout()); + btnNested = new Button("Dialog..."); + panelSouth.add(btnNested); + btnFileDlg = new Button("FileDialog..."); + panelSouth.add(btnFileDlg); + btnShow = new Button("Show"); + panelSouth.add(btnShow); + btnHide = new Button("Hide"); + panelSouth.add(btnHide); + btnDispose = new Button("Dispose"); + panelSouth.add(btnDispose); + + Choice cbox = new Choice(); + cbox.add("Test1"); + cbox.add("Test2"); + cbox.add("Test3"); + cbox.add("Test4"); + cbox.add("Test5"); + cbox.add("Test6"); + cbox.add("Test7"); + cbox.add("Test8"); + cbox.add("Test9"); + cbox.add("Test10"); + cbox.add("Test11"); + panelSouth.add(cbox); + + validate(); + + add("Center", panelNorth); + add("South", panelSouth); + + btnNested.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Dialog dlg = new SimpleDialog((Frame) getParent(), SimpleDialog.this, true); + System.out.println("Returned from showing dialog: " + dlg); + } + }); + + btnFileDlg.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + FileDialog dlg = new FileDialog((Frame) getParent()); + dlg.show(); + } + }); + + btnHide.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Window wnd = getSelectedWindow(); + System.out.println(wnd); + wnd.hide(); + } + }); + + btnShow.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + getSelectedWindow().show(); + } + }); + + btnDispose.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + getSelectedWindow().dispose(); + populateListWin(); + } + }); + + pack(); + setSize(getSize().width, getSize().height * 2); + if (dlgPrev != null) { + Point pt = dlgPrev.getLocation(); + setLocation(pt.x + 30, pt.y + 50); + } + show(); + } + + private Window getSelectedWindow() { + Window window; + int index = listWins.getSelectedIndex(); + + window = NestedDialogTest.getWindow(index); + return window; + } + + private void populateListWin() { + Enumeration enumWindows = NestedDialogTest.enumWindows(); + + listWins.removeAll(); + while (enumWindows.hasMoreElements()) { + Window win = (Window) enumWindows.nextElement(); + listWins.add(win.getName()); + } + listWins.select(NestedDialogTest.getWindowIndex(this)); + } + + public void show() { + if (!isVisible()) { + NestedDialogTest.addWindow(this); + } + super.show(); + } + + public void dispose() { + NestedDialogTest.removeWindow(this); + super.dispose(); + } +} diff --git a/test/jdk/java/awt/Dialog/ShownModalDialogSerializationTest.java b/test/jdk/java/awt/Dialog/ShownModalDialogSerializationTest.java new file mode 100644 index 00000000000..b57dd2cf8f2 --- /dev/null +++ b/test/jdk/java/awt/Dialog/ShownModalDialogSerializationTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Label; + +import java.awt.TextArea; +import java.io.File; +import java.io.FileOutputStream; +import java.io.ObjectOutputStream; + +/* + * @test + * @bug 4739757 + * @summary REGRESSION: Modal Dialog is not serializable after showing + * @key headful + * @run main ShownModalDialogSerializationTest + */ + +public class ShownModalDialogSerializationTest { + static volatile Frame frame; + static volatile Frame outputFrame; + static volatile Dialog dialog; + + public static void main(String[] args) throws Exception { + + EventQueue.invokeLater(ShownModalDialogSerializationTest::createTestUI); + + while (dialog == null || !dialog.isShowing()) { + Thread.sleep(500); + } + File file = new File("dialog.ser"); + FileOutputStream fos = new FileOutputStream(file); + ObjectOutputStream oos = new ObjectOutputStream(fos); + oos.writeObject(dialog); + oos.flush(); + file.delete(); + + EventQueue.invokeAndWait(ShownModalDialogSerializationTest::deleteTestUI); + } + + static void deleteTestUI() { + if (dialog != null) { + dialog.setVisible(false); + dialog.dispose(); + } + if (frame != null) { + frame.setVisible(false); + frame.dispose(); + } + if (outputFrame != null) { + outputFrame.setVisible(false); + outputFrame.dispose(); + } + } + + private static void createTestUI() { + outputFrame = new Frame("ShownModalDialogSerializationTest"); + TextArea output = new TextArea(40, 50); + outputFrame.add(output); + + frame = new Frame("invisible dialog owner"); + dialog = new Dialog(frame, "Dialog for Close", true); + dialog.add(new Label("Close This Dialog")); + outputFrame.setSize(200, 200); + outputFrame.setVisible(true); + dialog.pack(); + dialog.setVisible(true); + } +} From a2c775222e17a3ba2e388c9a0eb9ffd33efad219 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Mon, 14 Oct 2024 17:21:10 +0000 Subject: [PATCH 44/95] 8317356: Fix missing null checks in the ClassFile API Co-authored-by: Nizar Benalla Reviewed-by: asotona --- .../java/lang/classfile/AnnotationValue.java | 17 ++- .../lang/classfile/AttributedElement.java | 4 + .../classfile/ClassHierarchyResolver.java | 10 +- .../java/lang/classfile/ClassTransform.java | 14 ++- .../java/lang/classfile/CodeTransform.java | 7 +- .../java/lang/classfile/FieldTransform.java | 8 +- .../java/lang/classfile/MethodTransform.java | 9 +- .../classfile/components/ClassRemapper.java | 7 +- .../classfile/components/CodeRelabeler.java | 5 +- .../instruction/DiscontinuedInstruction.java | 2 +- .../java/lang/invoke/MethodHandleProxies.java | 4 +- .../classfile/impl/AbstractInstruction.java | 31 ++--- .../classfile/impl/AbstractPoolEntry.java | 4 +- .../impl/AbstractPseudoInstruction.java | 25 ++-- .../classfile/impl/AnnotationImpl.java | 8 ++ .../classfile/impl/BlockCodeBuilderImpl.java | 4 +- .../classfile/impl/BufferedCodeBuilder.java | 4 +- .../classfile/impl/BufferedFieldBuilder.java | 8 +- .../classfile/impl/BufferedMethodBuilder.java | 8 +- .../classfile/impl/BytecodeHelpers.java | 5 +- .../classfile/impl/ChainedClassBuilder.java | 4 +- .../classfile/impl/ChainedCodeBuilder.java | 6 +- .../classfile/impl/ChainedFieldBuilder.java | 4 +- .../classfile/impl/ChainedMethodBuilder.java | 4 +- .../classfile/impl/ClassFileImpl.java | 4 +- .../classfile/impl/ClassHierarchyImpl.java | 4 +- .../classfile/impl/ClassPrinterImpl.java | 2 + .../classfile/impl/DirectClassBuilder.java | 8 +- .../classfile/impl/DirectCodeBuilder.java | 3 +- .../classfile/impl/DirectFieldBuilder.java | 8 +- .../classfile/impl/DirectMethodBuilder.java | 8 +- .../classfile/impl/SplitConstantPool.java | 2 + .../classfile/impl/StackMapDecoder.java | 8 ++ .../classfile/impl/TargetInfoImpl.java | 4 +- .../classfile/impl/TemporaryConstantPool.java | 2 + .../classfile/impl/UnboundAttribute.java | 110 ++++++++++++------ 36 files changed, 249 insertions(+), 116 deletions(-) diff --git a/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java b/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java index 50bfa0b7aa6..fe768e93b13 100644 --- a/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java +++ b/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java @@ -42,6 +42,8 @@ import jdk.internal.classfile.impl.Util; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * Models an {@code element_value} structure, or a value of an element-value * pair of an annotation, as defined in JVMS {@jvms 4.7.16.1}. @@ -488,6 +490,8 @@ default ClassDesc classSymbol() { */ static OfEnum ofEnum(Utf8Entry className, Utf8Entry constantName) { + requireNonNull(className); + requireNonNull(constantName); return new AnnotationImpl.OfEnumImpl(className, constantName); } @@ -506,6 +510,7 @@ static OfEnum ofEnum(ClassDesc className, String constantName) { * @param className the descriptor string of the class */ static OfClass ofClass(Utf8Entry className) { + requireNonNull(className); return new AnnotationImpl.OfClassImpl(className); } @@ -522,6 +527,7 @@ static OfClass ofClass(ClassDesc className) { * @param value the string */ static OfString ofString(Utf8Entry value) { + requireNonNull(value); return new AnnotationImpl.OfStringImpl(value); } @@ -538,6 +544,7 @@ static OfString ofString(String value) { * @param value the double value */ static OfDouble ofDouble(DoubleEntry value) { + requireNonNull(value); return new AnnotationImpl.OfDoubleImpl(value); } @@ -554,6 +561,7 @@ static OfDouble ofDouble(double value) { * @param value the float value */ static OfFloat ofFloat(FloatEntry value) { + requireNonNull(value); return new AnnotationImpl.OfFloatImpl(value); } @@ -570,6 +578,7 @@ static OfFloat ofFloat(float value) { * @param value the long value */ static OfLong ofLong(LongEntry value) { + requireNonNull(value); return new AnnotationImpl.OfLongImpl(value); } @@ -586,6 +595,7 @@ static OfLong ofLong(long value) { * @param value the int value */ static OfInt ofInt(IntegerEntry value) { + requireNonNull(value); return new AnnotationImpl.OfIntImpl(value); } @@ -602,6 +612,7 @@ static OfInt ofInt(int value) { * @param value the short value */ static OfShort ofShort(IntegerEntry value) { + requireNonNull(value); return new AnnotationImpl.OfShortImpl(value); } @@ -618,6 +629,7 @@ static OfShort ofShort(short value) { * @param value the char value */ static OfChar ofChar(IntegerEntry value) { + requireNonNull(value); return new AnnotationImpl.OfCharImpl(value); } @@ -634,6 +646,7 @@ static OfChar ofChar(char value) { * @param value the byte value */ static OfByte ofByte(IntegerEntry value) { + requireNonNull(value); return new AnnotationImpl.OfByteImpl(value); } @@ -650,6 +663,7 @@ static OfByte ofByte(byte value) { * @param value the boolean value */ static OfBoolean ofBoolean(IntegerEntry value) { + requireNonNull(value); return new AnnotationImpl.OfBooleanImpl(value); } @@ -667,6 +681,7 @@ static OfBoolean ofBoolean(boolean value) { * @param value the annotation */ static OfAnnotation ofAnnotation(Annotation value) { + requireNonNull(value); return new AnnotationImpl.OfAnnotationImpl(value); } @@ -784,6 +799,6 @@ static AnnotationValue of(Object value) { } else if (value instanceof Enum e) { return ofEnum(ClassDesc.ofDescriptor(e.getDeclaringClass().descriptorString()), e.name()); } - throw new IllegalArgumentException("Illegal annotation constant value type " + (value == null ? null : value.getClass())); + throw new IllegalArgumentException("Illegal annotation constant value type " + requireNonNull(value).getClass()); } } diff --git a/src/java.base/share/classes/java/lang/classfile/AttributedElement.java b/src/java.base/share/classes/java/lang/classfile/AttributedElement.java index d66806ca93b..0caf231ec2a 100644 --- a/src/java.base/share/classes/java/lang/classfile/AttributedElement.java +++ b/src/java.base/share/classes/java/lang/classfile/AttributedElement.java @@ -33,6 +33,8 @@ import jdk.internal.classfile.impl.AbstractUnboundModel; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * A {@link ClassFileElement} describing an entity that has attributes, such * as a class, field, method, code attribute, or record component. @@ -58,6 +60,7 @@ public sealed interface AttributedElement extends ClassFileElement * is not present */ default > Optional findAttribute(AttributeMapper attr) { + requireNonNull(attr); for (Attribute la : attributes()) { if (la.attributeMapper() == attr) { @SuppressWarnings("unchecked") @@ -76,6 +79,7 @@ default > Optional findAttribute(AttributeMapper at * is not present */ default > List findAttributes(AttributeMapper attr) { + requireNonNull(attr); var list = new ArrayList(); for (var a : attributes()) { if (a.attributeMapper() == attr) { diff --git a/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java b/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java index 589713c8e95..3f16ce84024 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * 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,8 @@ import jdk.internal.classfile.impl.Util; import static java.lang.constant.ConstantDescs.CD_Object; +import static java.util.Objects.requireNonNull; + import jdk.internal.javac.PreviewFeature; /** @@ -106,6 +108,7 @@ static ClassHierarchyInfo ofInterface() { * other resolver in cases where this resolver returns {@code null}. */ default ClassHierarchyResolver orElse(ClassHierarchyResolver other) { + requireNonNull(other); return new ClassHierarchyResolver() { @Override public ClassHierarchyInfo getClassInfo(ClassDesc classDesc) { @@ -170,7 +173,7 @@ public Map get() { * @return the {@linkplain ClassHierarchyResolver} */ static ClassHierarchyResolver ofResourceParsing(Function classStreamResolver) { - return new ClassHierarchyImpl.ResourceParsingClassHierarchyResolver(classStreamResolver); + return new ClassHierarchyImpl.ResourceParsingClassHierarchyResolver(requireNonNull(classStreamResolver)); } /** @@ -181,6 +184,7 @@ static ClassHierarchyResolver ofResourceParsing(Function * @return the {@linkplain ClassHierarchyResolver} */ static ClassHierarchyResolver ofResourceParsing(ClassLoader loader) { + requireNonNull(loader); return ofResourceParsing(new Function<>() { @Override public InputStream apply(ClassDesc classDesc) { @@ -210,6 +214,7 @@ static ClassHierarchyResolver of(Collection interfaces, * @return the class hierarchy resolver */ static ClassHierarchyResolver ofClassLoading(ClassLoader loader) { + requireNonNull(loader); return new ClassLoadingClassHierarchyResolver(new Function<>() { @Override public Class apply(ClassDesc cd) { @@ -232,6 +237,7 @@ public Class apply(ClassDesc cd) { * @return the class hierarchy resolver */ static ClassHierarchyResolver ofClassLoading(MethodHandles.Lookup lookup) { + requireNonNull(lookup); return new ClassLoadingClassHierarchyResolver(new Function<>() { @Override public Class apply(ClassDesc cd) { diff --git a/src/java.base/share/classes/java/lang/classfile/ClassTransform.java b/src/java.base/share/classes/java/lang/classfile/ClassTransform.java index 743a3985114..230b436b138 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassTransform.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassTransform.java @@ -32,6 +32,8 @@ import jdk.internal.classfile.impl.TransformImpl; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * A transformation on streams of {@link ClassElement}. * @@ -63,7 +65,7 @@ public void accept(ClassBuilder builder, ClassElement element) { * @return the stateful class transform */ static ClassTransform ofStateful(Supplier supplier) { - return new TransformImpl.SupplierClassTransform(supplier); + return new TransformImpl.SupplierClassTransform(requireNonNull(supplier)); } /** @@ -74,6 +76,7 @@ static ClassTransform ofStateful(Supplier supplier) { * @return the class transform */ static ClassTransform endHandler(Consumer finisher) { + requireNonNull(finisher); return new ClassTransform() { @Override public void accept(ClassBuilder builder, ClassElement element) { @@ -95,6 +98,7 @@ public void atEnd(ClassBuilder builder) { * @return the class transform */ static ClassTransform dropping(Predicate filter) { + requireNonNull(filter); return (b, e) -> { if (!filter.test(e)) b.with(e); @@ -111,7 +115,7 @@ static ClassTransform dropping(Predicate filter) { */ static ClassTransform transformingMethods(Predicate filter, MethodTransform xform) { - return new TransformImpl.ClassMethodTransform(xform, filter); + return new TransformImpl.ClassMethodTransform(requireNonNull(xform), requireNonNull(filter)); } /** @@ -122,7 +126,7 @@ static ClassTransform transformingMethods(Predicate filter, * @return the class transform */ static ClassTransform transformingMethods(MethodTransform xform) { - return transformingMethods(mm -> true, xform); + return transformingMethods(_ -> true, xform); } /** @@ -157,7 +161,7 @@ static ClassTransform transformingMethodBodies(CodeTransform xform) { * @return the class transform */ static ClassTransform transformingFields(FieldTransform xform) { - return new TransformImpl.ClassFieldTransform(xform, f -> true); + return new TransformImpl.ClassFieldTransform(requireNonNull(xform), _ -> true); } /** @@ -169,6 +173,6 @@ static ClassTransform transformingFields(FieldTransform xform) { */ @Override default ClassTransform andThen(ClassTransform t) { - return new TransformImpl.ChainedClassTransform(this, t); + return new TransformImpl.ChainedClassTransform(this, requireNonNull(t)); } } diff --git a/src/java.base/share/classes/java/lang/classfile/CodeTransform.java b/src/java.base/share/classes/java/lang/classfile/CodeTransform.java index cdc7a3b1434..0474e0c9c67 100644 --- a/src/java.base/share/classes/java/lang/classfile/CodeTransform.java +++ b/src/java.base/share/classes/java/lang/classfile/CodeTransform.java @@ -30,6 +30,8 @@ import jdk.internal.classfile.impl.TransformImpl; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * A transformation on streams of {@link CodeElement}. * @@ -61,7 +63,7 @@ public void accept(CodeBuilder builder, CodeElement element) { * @return the stateful code transform */ static CodeTransform ofStateful(Supplier supplier) { - return new TransformImpl.SupplierCodeTransform(supplier); + return new TransformImpl.SupplierCodeTransform(requireNonNull(supplier)); } /** @@ -72,6 +74,7 @@ static CodeTransform ofStateful(Supplier supplier) { * @return the code transform */ static CodeTransform endHandler(Consumer finisher) { + requireNonNull(finisher); return new CodeTransform() { @Override public void accept(CodeBuilder builder, CodeElement element) { @@ -94,6 +97,6 @@ public void atEnd(CodeBuilder builder) { */ @Override default CodeTransform andThen(CodeTransform t) { - return new TransformImpl.ChainedCodeTransform(this, t); + return new TransformImpl.ChainedCodeTransform(this, requireNonNull(t)); } } diff --git a/src/java.base/share/classes/java/lang/classfile/FieldTransform.java b/src/java.base/share/classes/java/lang/classfile/FieldTransform.java index 4e39f1e9c7f..78a6f5ead2f 100644 --- a/src/java.base/share/classes/java/lang/classfile/FieldTransform.java +++ b/src/java.base/share/classes/java/lang/classfile/FieldTransform.java @@ -31,6 +31,8 @@ import jdk.internal.classfile.impl.TransformImpl; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * A transformation on streams of {@link FieldElement}. * @@ -62,7 +64,7 @@ public void accept(FieldBuilder builder, FieldElement element) { * @return the stateful field transform */ static FieldTransform ofStateful(Supplier supplier) { - return new TransformImpl.SupplierFieldTransform(supplier); + return new TransformImpl.SupplierFieldTransform(requireNonNull(supplier)); } /** @@ -73,6 +75,7 @@ static FieldTransform ofStateful(Supplier supplier) { * @return the field transform */ static FieldTransform endHandler(Consumer finisher) { + requireNonNull(finisher); return new FieldTransform() { @Override public void accept(FieldBuilder builder, FieldElement element) { @@ -94,6 +97,7 @@ public void atEnd(FieldBuilder builder) { * @return the field transform */ static FieldTransform dropping(Predicate filter) { + requireNonNull(filter); return (b, e) -> { if (!filter.test(e)) b.with(e); @@ -109,6 +113,6 @@ static FieldTransform dropping(Predicate filter) { */ @Override default FieldTransform andThen(FieldTransform t) { - return new TransformImpl.ChainedFieldTransform(this, t); + return new TransformImpl.ChainedFieldTransform(this, requireNonNull(t)); } } diff --git a/src/java.base/share/classes/java/lang/classfile/MethodTransform.java b/src/java.base/share/classes/java/lang/classfile/MethodTransform.java index e7e024ebc34..bf5786f3dc7 100644 --- a/src/java.base/share/classes/java/lang/classfile/MethodTransform.java +++ b/src/java.base/share/classes/java/lang/classfile/MethodTransform.java @@ -31,6 +31,8 @@ import jdk.internal.classfile.impl.TransformImpl; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * A transformation on streams of {@link MethodElement}. * @@ -62,6 +64,7 @@ public void accept(MethodBuilder builder, MethodElement element) { * @return the stateful method transform */ static MethodTransform ofStateful(Supplier supplier) { + requireNonNull(supplier); return new TransformImpl.SupplierMethodTransform(supplier); } @@ -73,6 +76,7 @@ static MethodTransform ofStateful(Supplier supplier) { * @return the method transform */ static MethodTransform endHandler(Consumer finisher) { + requireNonNull(finisher); return new MethodTransform() { @Override public void accept(MethodBuilder builder, MethodElement element) { @@ -94,6 +98,7 @@ public void atEnd(MethodBuilder builder) { * @return the method transform */ static MethodTransform dropping(Predicate filter) { + requireNonNull(filter); return (b, e) -> { if (!filter.test(e)) b.with(e); @@ -108,7 +113,7 @@ static MethodTransform dropping(Predicate filter) { * @return the class transform */ static MethodTransform transformingCode(CodeTransform xform) { - return new TransformImpl.MethodCodeTransform(xform); + return new TransformImpl.MethodCodeTransform(requireNonNull(xform)); } /** @@ -120,6 +125,6 @@ static MethodTransform transformingCode(CodeTransform xform) { */ @Override default MethodTransform andThen(MethodTransform t) { - return new TransformImpl.ChainedMethodTransform(this, t); + return new TransformImpl.ChainedMethodTransform(this, requireNonNull(t)); } } diff --git a/src/java.base/share/classes/java/lang/classfile/components/ClassRemapper.java b/src/java.base/share/classes/java/lang/classfile/components/ClassRemapper.java index d3ae180dde5..bcda3635587 100644 --- a/src/java.base/share/classes/java/lang/classfile/components/ClassRemapper.java +++ b/src/java.base/share/classes/java/lang/classfile/components/ClassRemapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * 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,8 @@ import jdk.internal.classfile.impl.ClassRemapperImpl; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * {@code ClassRemapper} is a {@link ClassTransform}, {@link FieldTransform}, * {@link MethodTransform} and {@link CodeTransform} @@ -64,6 +66,7 @@ public sealed interface ClassRemapper extends ClassTransform permits ClassRemapp * @return new instance of {@code ClassRemapper} */ static ClassRemapper of(Map classMap) { + requireNonNull(classMap); return of(desc -> classMap.getOrDefault(desc, desc)); } @@ -75,7 +78,7 @@ static ClassRemapper of(Map classMap) { * @return new instance of {@code ClassRemapper} */ static ClassRemapper of(Function mapFunction) { - return new ClassRemapperImpl(mapFunction); + return new ClassRemapperImpl(requireNonNull(mapFunction)); } /** diff --git a/src/java.base/share/classes/java/lang/classfile/components/CodeRelabeler.java b/src/java.base/share/classes/java/lang/classfile/components/CodeRelabeler.java index ca5ad90389c..6ec3f9f792b 100644 --- a/src/java.base/share/classes/java/lang/classfile/components/CodeRelabeler.java +++ b/src/java.base/share/classes/java/lang/classfile/components/CodeRelabeler.java @@ -33,6 +33,8 @@ import jdk.internal.classfile.impl.CodeRelabelerImpl; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * A code relabeler is a {@link CodeTransform} replacing all occurrences * of {@link java.lang.classfile.Label} in the transformed code with new instances. @@ -62,6 +64,7 @@ static CodeRelabeler of() { * @return a new instance of CodeRelabeler */ static CodeRelabeler of(Map map) { + requireNonNull(map); return of((l, cob) -> map.computeIfAbsent(l, ll -> cob.newLabel())); } @@ -72,6 +75,6 @@ static CodeRelabeler of(Map map) { * @return a new instance of CodeRelabeler */ static CodeRelabeler of(BiFunction mapFunction) { - return new CodeRelabelerImpl(mapFunction); + return new CodeRelabelerImpl(requireNonNull(mapFunction)); } } diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/DiscontinuedInstruction.java b/src/java.base/share/classes/java/lang/classfile/instruction/DiscontinuedInstruction.java index 84bead6d8cc..fc87dd274d3 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/DiscontinuedInstruction.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/DiscontinuedInstruction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java index dc4133ae244..9709c881863 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java @@ -54,6 +54,7 @@ import java.lang.classfile.TypeKind; import jdk.internal.constant.ConstantUtils; +import jdk.internal.loader.ClassLoaders; import jdk.internal.module.Modules; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; @@ -371,7 +372,8 @@ private static Class getProxyClass(Class intfc) { */ private static byte[] createTemplate(ClassLoader loader, ClassDesc proxyDesc, ClassDesc ifaceDesc, String methodName, List methods) { - return ClassFile.of(ClassHierarchyResolverOption.of(ClassHierarchyResolver.ofClassLoading(loader))) + return ClassFile.of(ClassHierarchyResolverOption.of(ClassHierarchyResolver.ofClassLoading(loader == null ? + ClassLoaders.platformClassLoader() : loader))) .build(proxyDesc, clb -> { clb.withSuperclass(CD_Object) .withFlags(ACC_FINAL | ACC_SYNTHETIC) diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java index 6b77f6ff1ad..23dad36b1b1 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java @@ -66,6 +66,8 @@ import java.lang.classfile.Opcode; import java.lang.classfile.TypeKind; +import static java.util.Objects.requireNonNull; + public abstract sealed class AbstractInstruction extends AbstractElement implements Instruction { @@ -247,6 +249,9 @@ public String toString() { public record SwitchCaseImpl(int caseValue, Label target) implements SwitchCase { + public SwitchCaseImpl { + requireNonNull(target); + } } public static final class BoundLookupSwitchInstruction @@ -892,7 +897,7 @@ public static final class UnboundBranchInstruction public UnboundBranchInstruction(Opcode op, Label target) { super(op); - this.target = target; + this.target = requireNonNull(target); } @Override @@ -919,7 +924,7 @@ public static final class UnboundLookupSwitchInstruction public UnboundLookupSwitchInstruction(Label defaultTarget, List cases) { super(Opcode.LOOKUPSWITCH); - this.defaultTarget = defaultTarget; + this.defaultTarget = requireNonNull(defaultTarget); this.cases = List.copyOf(cases); } @@ -955,7 +960,7 @@ public UnboundTableSwitchInstruction(int lowValue, int highValue, Label defaultT super(Opcode.TABLESWITCH); this.lowValue = lowValue; this.highValue = highValue; - this.defaultTarget = defaultTarget; + this.defaultTarget = requireNonNull(defaultTarget); this.cases = List.copyOf(cases); } @@ -1030,7 +1035,7 @@ public static final class UnboundFieldInstruction public UnboundFieldInstruction(Opcode op, FieldRefEntry fieldEntry) { super(op); - this.fieldEntry = fieldEntry; + this.fieldEntry = requireNonNull(fieldEntry); } @Override @@ -1055,7 +1060,7 @@ public static final class UnboundInvokeInstruction public UnboundInvokeInstruction(Opcode op, MemberRefEntry methodEntry) { super(op); - this.methodEntry = methodEntry; + this.methodEntry = requireNonNull(methodEntry); } @Override @@ -1095,7 +1100,7 @@ public static final class UnboundInvokeDynamicInstruction public UnboundInvokeDynamicInstruction(InvokeDynamicEntry indyEntry) { super(Opcode.INVOKEDYNAMIC); - this.indyEntry = indyEntry; + this.indyEntry = requireNonNull(indyEntry); } @Override @@ -1120,7 +1125,7 @@ public static final class UnboundNewObjectInstruction public UnboundNewObjectInstruction(ClassEntry classEntry) { super(Opcode.NEW); - this.classEntry = classEntry; + this.classEntry = requireNonNull(classEntry); } @Override @@ -1145,7 +1150,7 @@ public static final class UnboundNewPrimitiveArrayInstruction public UnboundNewPrimitiveArrayInstruction(TypeKind typeKind) { super(Opcode.NEWARRAY); - this.typeKind = typeKind; + this.typeKind = requireNonNull(typeKind); } @Override @@ -1170,7 +1175,7 @@ public static final class UnboundNewReferenceArrayInstruction public UnboundNewReferenceArrayInstruction(ClassEntry componentTypeEntry) { super(Opcode.ANEWARRAY); - this.componentTypeEntry = componentTypeEntry; + this.componentTypeEntry = requireNonNull(componentTypeEntry); } @Override @@ -1197,7 +1202,7 @@ public static final class UnboundNewMultidimensionalArrayInstruction public UnboundNewMultidimensionalArrayInstruction(ClassEntry arrayTypeEntry, int dimensions) { super(Opcode.MULTIANEWARRAY); - this.arrayTypeEntry = arrayTypeEntry; + this.arrayTypeEntry = requireNonNull(arrayTypeEntry); this.dimensions = dimensions; } @@ -1255,7 +1260,7 @@ public static final class UnboundTypeCheckInstruction public UnboundTypeCheckInstruction(Opcode op, ClassEntry typeEntry) { super(op); - this.typeEntry = typeEntry; + this.typeEntry = requireNonNull(typeEntry); } @Override @@ -1357,7 +1362,7 @@ public static final class UnboundLoadConstantInstruction public UnboundLoadConstantInstruction(Opcode op, LoadableConstantEntry constant) { super(op); - this.constant = constant; + this.constant = requireNonNull(constant); } @Override @@ -1405,7 +1410,7 @@ public static final class UnboundJsrInstruction public UnboundJsrInstruction(Opcode op, Label target) { super(op); - this.target = target; + this.target = requireNonNull(target); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java index 447e7e25c45..15b52621764 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java @@ -56,6 +56,8 @@ import jdk.internal.util.ArraysSupport; import jdk.internal.vm.annotation.Stable; +import static java.util.Objects.requireNonNull; + public abstract sealed class AbstractPoolEntry { /* Invariant: a {CP,BSM} entry for pool P refer only to {CP,BSM} entries @@ -439,7 +441,7 @@ public boolean equalsString(String s) { inflate(); switch (state) { case STRING: - return stringValue.equals(s); + return stringValue.equals(requireNonNull(s)); case CHAR: if (charLen != s.length() || contentHash != s.hashCode()) return false; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPseudoInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPseudoInstruction.java index ffba8ec7a5d..596379305a9 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPseudoInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPseudoInstruction.java @@ -36,6 +36,8 @@ import java.lang.classfile.Label; import java.lang.classfile.PseudoInstruction; +import static java.util.Objects.requireNonNull; + public abstract sealed class AbstractPseudoInstruction extends AbstractElement implements PseudoInstruction { @@ -55,17 +57,14 @@ public static final class ExceptionCatchImpl public ExceptionCatchImpl(Label handler, Label tryStart, Label tryEnd, ClassEntry catchTypeEntry) { this.catchTypeEntry = catchTypeEntry; - this.handler = handler; - this.tryStart = tryStart; - this.tryEnd = tryEnd; + this.handler = requireNonNull(handler); + this.tryStart = requireNonNull(tryStart); + this.tryEnd = requireNonNull(tryEnd); } public ExceptionCatchImpl(Label handler, Label tryStart, Label tryEnd, Optional catchTypeEntry) { - this.catchTypeEntry = catchTypeEntry.orElse(null); - this.handler = handler; - this.tryStart = tryStart; - this.tryEnd = tryEnd; + this(handler, tryStart, tryEnd, catchTypeEntry.orElse(null)); } @Override @@ -115,8 +114,8 @@ public static final class UnboundCharacterRange public UnboundCharacterRange(Label startScope, Label endScope, int characterRangeStart, int characterRangeEnd, int flags) { - this.startScope = startScope; - this.endScope = endScope; + this.startScope = requireNonNull(startScope); + this.endScope = requireNonNull(endScope); this.characterRangeStart = characterRangeStart; this.characterRangeEnd = characterRangeEnd; this.flags = flags; @@ -165,10 +164,10 @@ private abstract static sealed class AbstractLocalPseudo extends AbstractPseudoI public AbstractLocalPseudo(int slot, Utf8Entry name, Utf8Entry descriptor, Label startScope, Label endScope) { BytecodeHelpers.validateSlot(slot); this.slot = slot; - this.name = name; - this.descriptor = descriptor; - this.startScope = startScope; - this.endScope = endScope; + this.name = requireNonNull(name); + this.descriptor = requireNonNull(descriptor); + this.startScope = requireNonNull(startScope); + this.endScope = requireNonNull(endScope); } public int slot() { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java index 6aeda552b47..f1c7e6aaf65 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java @@ -29,9 +29,12 @@ import java.util.List; +import static java.util.Objects.requireNonNull; + public record AnnotationImpl(Utf8Entry className, List elements) implements Annotation { public AnnotationImpl { + requireNonNull(className); elements = List.copyOf(elements); } @@ -50,6 +53,11 @@ public String toString() { public record AnnotationElementImpl(Utf8Entry name, AnnotationValue value) implements AnnotationElement { + public AnnotationElementImpl { + requireNonNull(name); + requireNonNull(value); + } + @Override public String toString() { return name + "=" + value; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BlockCodeBuilderImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BlockCodeBuilderImpl.java index b28bcc0b4b5..2fdc9f36426 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BlockCodeBuilderImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BlockCodeBuilderImpl.java @@ -33,6 +33,8 @@ import java.util.Objects; import java.lang.classfile.Instruction; +import static java.util.Objects.requireNonNull; + public final class BlockCodeBuilderImpl extends NonterminalCodeBuilder implements CodeBuilder.BlockCodeBuilder { @@ -80,7 +82,7 @@ private int topLocal(CodeBuilder parent) { @Override public CodeBuilder with(CodeElement element) { - parent.with(element); + parent.with(requireNonNull(element)); hasInstructions |= element instanceof Instruction; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java index c506d265f68..4ed458c3983 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java @@ -38,6 +38,8 @@ import java.util.Optional; import java.util.function.Consumer; +import static java.util.Objects.requireNonNull; + public final class BufferedCodeBuilder implements TerminalCodeBuilder { private final SplitConstantPool constantPool; @@ -121,7 +123,7 @@ public ConstantPoolBuilder constantPool() { public CodeBuilder with(CodeElement element) { if (finished) throw new IllegalStateException("Can't add elements after traversal"); - elements.add(element); + elements.add(requireNonNull(element)); return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java index 8cf274d746c..0578cf85c4c 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java @@ -34,6 +34,8 @@ import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.lang.classfile.constantpool.Utf8Entry; +import static java.util.Objects.requireNonNull; + public final class BufferedFieldBuilder implements TerminalFieldBuilder { private final SplitConstantPool constantPool; @@ -49,8 +51,8 @@ public BufferedFieldBuilder(SplitConstantPool constantPool, Utf8Entry type) { this.constantPool = constantPool; this.context = context; - this.name = name; - this.desc = type; + this.name = requireNonNull(name); + this.desc = requireNonNull(type); this.flags = new AccessFlagsImpl(AccessFlag.Location.FIELD); } @@ -61,7 +63,7 @@ public ConstantPoolBuilder constantPool() { @Override public FieldBuilder with(FieldElement element) { - elements.add(element); + elements.add(requireNonNull(element)); if (element instanceof AccessFlags f) this.flags = f; return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java index bc6ab555ae5..8f511218d1e 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java @@ -43,6 +43,8 @@ import java.lang.classfile.MethodModel; import java.lang.classfile.constantpool.Utf8Entry; +import static java.util.Objects.requireNonNull; + public final class BufferedMethodBuilder implements TerminalMethodBuilder { private final List elements; @@ -63,15 +65,15 @@ public BufferedMethodBuilder(SplitConstantPool constantPool, this.elements = new ArrayList<>(); this.constantPool = constantPool; this.context = context; - this.name = nameInfo; - this.desc = typeInfo; + this.name = requireNonNull(nameInfo); + this.desc = requireNonNull(typeInfo); this.flags = new AccessFlagsImpl(AccessFlag.Location.METHOD, flags); this.original = original; } @Override public MethodBuilder with(MethodElement element) { - elements.add(element); + elements.add(requireNonNull(element)); if (element instanceof AccessFlags f) this.flags = checkFlags(f); return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java index bca80c1ed4b..fde8905abc1 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java @@ -47,6 +47,7 @@ import java.lang.classfile.constantpool.NameAndTypeEntry; import java.util.Objects; +import static java.util.Objects.requireNonNull; import static jdk.internal.classfile.impl.RawBytecodeHelper.*; /** @@ -463,7 +464,7 @@ public static void validateRet(Opcode opcode, int slot) { if (opcode == Opcode.RET && (slot & ~0xFF) == 0 || opcode == Opcode.RET_W && (slot & ~0xFFFF) == 0) return; - Objects.requireNonNull(opcode); + requireNonNull(opcode); throw slotOutOfBounds(opcode, slot); } @@ -551,7 +552,7 @@ public static LoadableConstantEntry constantEntry(ConstantPoolBuilder constantPo } if (constantValue instanceof DynamicConstantDesc value) { return handleConstantDescToHandleInfo(constantPool, value); } - throw new UnsupportedOperationException("not yet: " + constantValue); + throw new UnsupportedOperationException("not yet: " + requireNonNull(constantValue)); } public static ConstantDesc intrinsicConstantValue(Opcode opcode) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java index 8f05f20d739..ebf803f5f27 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java @@ -31,6 +31,8 @@ import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.lang.classfile.constantpool.Utf8Entry; +import static java.util.Objects.requireNonNull; + public final class ChainedClassBuilder implements ClassBuilder, Consumer { private final DirectClassBuilder terminal; @@ -47,7 +49,7 @@ public ChainedClassBuilder(ClassBuilder downstream, @Override public ClassBuilder with(ClassElement element) { - consumer.accept(element); + consumer.accept(requireNonNull(element)); return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedCodeBuilder.java index 8c6c80b3013..fa02a346fab 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedCodeBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * 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,8 @@ import java.util.function.Consumer; +import static java.util.Objects.requireNonNull; + public final class ChainedCodeBuilder extends NonterminalCodeBuilder implements CodeBuilder { @@ -59,7 +61,7 @@ public int allocateLocal(TypeKind typeKind) { @Override public CodeBuilder with(CodeElement element) { - consumer.accept(element); + consumer.accept(requireNonNull(element)); return this; } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedFieldBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedFieldBuilder.java index b3a30b5351b..f9c2b50f414 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedFieldBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedFieldBuilder.java @@ -30,6 +30,8 @@ import java.lang.classfile.FieldElement; import java.lang.classfile.constantpool.ConstantPoolBuilder; +import static java.util.Objects.requireNonNull; + public final class ChainedFieldBuilder implements FieldBuilder { private final TerminalFieldBuilder terminal; private final Consumer consumer; @@ -50,7 +52,7 @@ public ConstantPoolBuilder constantPool() { @Override public FieldBuilder with(FieldElement element) { - consumer.accept(element); + consumer.accept(requireNonNull(element)); return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java index a7084116b9b..5bab6806b71 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java @@ -33,6 +33,8 @@ import java.lang.classfile.MethodElement; import java.lang.classfile.constantpool.ConstantPoolBuilder; +import static java.util.Objects.requireNonNull; + public final class ChainedMethodBuilder implements MethodBuilder { final TerminalMethodBuilder terminal; final Consumer consumer; @@ -48,7 +50,7 @@ public ChainedMethodBuilder(MethodBuilder downstream, @Override public MethodBuilder with(MethodElement element) { - consumer.accept(element); + consumer.accept(requireNonNull(element)); return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassFileImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassFileImpl.java index c6d9e55d8db..ed81bcea009 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassFileImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassFileImpl.java @@ -40,6 +40,8 @@ import java.lang.classfile.constantpool.Utf8Entry; import jdk.internal.classfile.impl.verifier.VerifierImpl; +import static java.util.Objects.requireNonNull; + public final class ClassFileImpl implements ClassFile { private Option stackMapsOption; @@ -123,7 +125,7 @@ public ClassFileImpl withOptions(Option... options) { } else if (o instanceof AttributeMapperOption oo) { amo = oo; } else { // null or unknown Option type - throw new IllegalArgumentException("Invalid option: " + o); + throw new IllegalArgumentException("Invalid option: " + requireNonNull(o)); } } return new ClassFileImpl(smo, deo, lno, apo, cpso, sjo, dco, dlo, chro, amo); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java index 34e6f5f7c1a..9c2dd895538 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java @@ -205,9 +205,9 @@ public StaticClassHierarchyResolver(Collection interfaceNames, Map convertVTIs(CodeAttribute lr, List model, Verbosity verbosity) { + requireNonNull(verbosity); // we are using == checks in implementations return switch(model) { case ClassModel cm -> classToTree(cm, verbosity); case FieldModel fm -> fieldToTree(fm, verbosity); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java index afa7ebac8ba..d1131d94db7 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java @@ -26,11 +26,7 @@ package jdk.internal.classfile.impl; -import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; -import java.lang.constant.MethodTypeDesc; -import java.lang.reflect.AccessFlag; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -50,6 +46,8 @@ import java.lang.classfile.MethodTransform; import java.lang.classfile.constantpool.Utf8Entry; +import static java.util.Objects.requireNonNull; + public final class DirectClassBuilder extends AbstractDirectBuilder implements ClassBuilder { @@ -87,7 +85,7 @@ public ClassBuilder with(ClassElement element) { if (element instanceof AbstractElement ae) { ae.writeTo(this); } else { - writeAttribute((CustomAttribute) element); + writeAttribute((CustomAttribute) requireNonNull(element)); } return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java index b3c106c461d..c00025e2a7e 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java @@ -64,6 +64,7 @@ import java.util.function.Consumer; import java.util.function.Function; +import static java.util.Objects.requireNonNull; import static jdk.internal.classfile.impl.BytecodeHelpers.*; import static jdk.internal.classfile.impl.RawBytecodeHelper.*; @@ -147,7 +148,7 @@ public CodeBuilder with(CodeElement element) { if (element instanceof AbstractElement ae) { ae.writeTo(this); } else { - writeAttribute((CustomAttribute) element); + writeAttribute((CustomAttribute) requireNonNull(element)); } return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java index d1fafc626f9..222ed6b9792 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java @@ -33,6 +33,8 @@ import java.lang.classfile.FieldModel; import java.lang.classfile.constantpool.Utf8Entry; +import static java.util.Objects.requireNonNull; + public final class DirectFieldBuilder extends AbstractDirectBuilder implements TerminalFieldBuilder, Util.Writable { @@ -48,8 +50,8 @@ public DirectFieldBuilder(SplitConstantPool constantPool, FieldModel original) { super(constantPool, context); setOriginal(original); - this.name = name; - this.desc = type; + this.name = requireNonNull(name); + this.desc = requireNonNull(type); this.flags = flags; } @@ -58,7 +60,7 @@ public FieldBuilder with(FieldElement element) { if (element instanceof AbstractElement ae) { ae.writeTo(this); } else { - writeAttribute((CustomAttribute) element); + writeAttribute((CustomAttribute) requireNonNull(element)); } return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java index a841cbf47f4..cdd5f8155e1 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java @@ -38,6 +38,8 @@ import java.lang.classfile.MethodModel; import java.lang.classfile.constantpool.Utf8Entry; +import static java.util.Objects.requireNonNull; + public final class DirectMethodBuilder extends AbstractDirectBuilder implements TerminalMethodBuilder, Util.Writable { @@ -55,8 +57,8 @@ public DirectMethodBuilder(SplitConstantPool constantPool, MethodModel original) { super(constantPool, context); setOriginal(original); - this.name = nameInfo; - this.desc = typeInfo; + this.name = requireNonNull(nameInfo); + this.desc = requireNonNull(typeInfo); this.flags = flags; } @@ -114,7 +116,7 @@ public MethodBuilder with(MethodElement element) { if (element instanceof AbstractElement ae) { ae.writeTo(this); } else { - writeAttribute((CustomAttribute) element); + writeAttribute((CustomAttribute) requireNonNull(element)); } return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java index 0b99766b385..4c760338857 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java @@ -38,6 +38,7 @@ import jdk.internal.constant.ConstantUtils; import static java.lang.classfile.constantpool.PoolEntry.*; +import static java.util.Objects.requireNonNull; public final class SplitConstantPool implements ConstantPoolBuilder { @@ -122,6 +123,7 @@ public BootstrapMethodEntryImpl bootstrapMethodEntry(int index) { @Override public boolean canWriteDirect(ConstantPool other) { + requireNonNull(other); return this == other || parent == other; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java index 2cad163b177..28b877cd83c 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java @@ -42,6 +42,7 @@ import static java.lang.classfile.ClassFile.*; import static java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo.*; +import static java.util.Objects.requireNonNull; public class StackMapDecoder { @@ -245,6 +246,9 @@ private VerificationTypeInfo readVerificationTypeInfo() { public static record ObjectVerificationTypeInfoImpl( ClassEntry className) implements ObjectVerificationTypeInfo { + public ObjectVerificationTypeInfoImpl { + requireNonNull(className); + } @Override public int tag() { return ITEM_OBJECT; } @@ -270,6 +274,9 @@ public String toString() { } public static record UninitializedVerificationTypeInfoImpl(Label newTarget) implements UninitializedVerificationTypeInfo { + public UninitializedVerificationTypeInfoImpl { + requireNonNull(newTarget); + } @Override public int tag() { return ITEM_UNINITIALIZED; } @@ -292,6 +299,7 @@ public static record StackMapFrameImpl(int frameType, List stack) implements StackMapFrameInfo { public StackMapFrameImpl { + requireNonNull(target); locals = List.copyOf(locals); stack = List.copyOf(stack); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/TargetInfoImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/TargetInfoImpl.java index b2d5cd9f62c..f30597cb3c4 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/TargetInfoImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/TargetInfoImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,6 @@ package jdk.internal.classfile.impl; import java.util.List; -import java.util.Objects; import java.lang.classfile.Label; import java.lang.classfile.TypeAnnotation.*; @@ -37,7 +36,6 @@ private TargetInfoImpl() { } private static TargetType checkValid(TargetType targetType, int rangeFrom, int rangeTo) { - Objects.requireNonNull(targetType); if (targetType.targetTypeValue() < rangeFrom || targetType.targetTypeValue() > rangeTo) throw new IllegalArgumentException("Wrong target type specified " + targetType); return targetType; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/TemporaryConstantPool.java b/src/java.base/share/classes/jdk/internal/classfile/impl/TemporaryConstantPool.java index 784e844b712..4fee9ba3d95 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/TemporaryConstantPool.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/TemporaryConstantPool.java @@ -50,6 +50,7 @@ import java.lang.constant.MethodTypeDesc; import java.util.List; +import java.util.Objects; public final class TemporaryConstantPool implements ConstantPoolBuilder { @@ -187,6 +188,7 @@ public int bootstrapMethodCount() { @Override public boolean canWriteDirect(ConstantPool constantPool) { + Objects.requireNonNull(constantPool); return false; } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java b/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java index 3119cfa0f49..a979a31c593 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java @@ -92,6 +92,8 @@ import jdk.internal.access.SharedSecrets; +import static java.util.Objects.requireNonNull; + public abstract sealed class UnboundAttribute> extends AbstractElement implements Attribute, Util.Writable { @@ -149,7 +151,7 @@ public static final class UnboundConstantValueAttribute public UnboundConstantValueAttribute(ConstantValueEntry entry) { super(Attributes.constantValue()); - this.entry = entry; + this.entry = requireNonNull(entry); } @Override @@ -182,7 +184,7 @@ public static final class UnboundSignatureAttribute public UnboundSignatureAttribute(Utf8Entry signature) { super(Attributes.signature()); - this.signature = signature; + this.signature = requireNonNull(signature); } @Override @@ -214,7 +216,7 @@ public static final class UnboundAnnotationDefaultAttribute public UnboundAnnotationDefaultAttribute(AnnotationValue annotationDefault) { super(Attributes.annotationDefault()); - this.annotationDefault = annotationDefault; + this.annotationDefault = requireNonNull(annotationDefault); } @Override @@ -229,7 +231,7 @@ public static final class UnboundSourceFileAttribute extends UnboundAttribute hashes) { super(Attributes.moduleHashes()); - this.algorithm = algorithm; + this.algorithm = requireNonNull(algorithm); this.hashes = List.copyOf(hashes); } @@ -451,7 +453,7 @@ public static final class UnboundNestHostAttribute public UnboundNestHostAttribute(ClassEntry hostEntry) { super(Attributes.nestHost()); - this.hostEntry = hostEntry; + this.hostEntry = requireNonNull(hostEntry); } @Override @@ -467,7 +469,7 @@ public static final class UnboundCompilationIDAttribute public UnboundCompilationIDAttribute(Utf8Entry idEntry) { super(Attributes.compilationId()); - this.idEntry = idEntry; + this.idEntry = requireNonNull(idEntry); } @Override @@ -483,7 +485,7 @@ public static final class UnboundSourceIDAttribute public UnboundSourceIDAttribute(Utf8Entry idEntry) { super(Attributes.sourceId()); - this.idEntry = idEntry; + this.idEntry = requireNonNull(idEntry); } @Override @@ -499,7 +501,7 @@ public static final class UnboundSourceDebugExtensionAttribute public UnboundSourceDebugExtensionAttribute(byte[] contents) { super(Attributes.sourceDebugExtension()); - this.contents = contents; + this.contents = requireNonNull(contents); } @Override @@ -611,7 +613,13 @@ public static final class UnboundRuntimeVisibleParameterAnnotationsAttribute public UnboundRuntimeVisibleParameterAnnotationsAttribute(List> elements) { super(Attributes.runtimeVisibleParameterAnnotations()); - this.elements = List.copyOf(elements); + // deep copy + var array = elements.toArray().clone(); + for (int i = 0; i < array.length; i++) { + array[i] = List.copyOf((List) array[i]); + } + + this.elements = SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(array); } @Override @@ -684,7 +692,13 @@ public record UnboundInnerClassInfo(ClassEntry innerClass, Optional outerClass, Optional innerName, int flagsMask) - implements InnerClassInfo {} + implements InnerClassInfo { + public UnboundInnerClassInfo { + requireNonNull(innerClass); + requireNonNull(outerClass); + requireNonNull(innerName); + } + } public record UnboundLineNumberInfo(int startPc, int lineNumber) implements LineNumberInfo { } @@ -693,64 +707,84 @@ public record UnboundLocalVariableInfo(int startPc, int length, Utf8Entry name, Utf8Entry type, int slot) - implements LocalVariableInfo { } + implements LocalVariableInfo { + public UnboundLocalVariableInfo { + requireNonNull(name); + requireNonNull(type); + } + } public record UnboundLocalVariableTypeInfo(int startPc, int length, Utf8Entry name, Utf8Entry signature, int slot) - implements LocalVariableTypeInfo { } + implements LocalVariableTypeInfo { + public UnboundLocalVariableTypeInfo { + requireNonNull(name); + requireNonNull(signature); + } + } public record UnboundMethodParameterInfo(Optional name, int flagsMask) - implements MethodParameterInfo {} + implements MethodParameterInfo { + public UnboundMethodParameterInfo { + requireNonNull(name); + } + } public record UnboundModuleExportInfo(PackageEntry exportedPackage, int exportsFlagsMask, List exportsTo) implements ModuleExportInfo { - public UnboundModuleExportInfo(PackageEntry exportedPackage, int exportsFlagsMask, - List exportsTo) { - this.exportedPackage = exportedPackage; - this.exportsFlagsMask = exportsFlagsMask; - this.exportsTo = List.copyOf(exportsTo); + public UnboundModuleExportInfo { + requireNonNull(exportedPackage); + exportsTo = List.copyOf(exportsTo); } } public record UnboundModuleHashInfo(ModuleEntry moduleName, - byte[] hash) implements ModuleHashInfo { } + byte[] hash) implements ModuleHashInfo { + public UnboundModuleHashInfo { + requireNonNull(moduleName); + requireNonNull(hash); + } + } public record UnboundModuleOpenInfo(PackageEntry openedPackage, int opensFlagsMask, List opensTo) implements ModuleOpenInfo { - public UnboundModuleOpenInfo(PackageEntry openedPackage, int opensFlagsMask, - List opensTo) { - this.openedPackage = openedPackage; - this.opensFlagsMask = opensFlagsMask; - this.opensTo = List.copyOf(opensTo); + public UnboundModuleOpenInfo { + requireNonNull(openedPackage); + opensTo = List.copyOf(opensTo); } } public record UnboundModuleProvideInfo(ClassEntry provides, List providesWith) implements ModuleProvideInfo { - public UnboundModuleProvideInfo(ClassEntry provides, List providesWith) { - this.provides = provides; - this.providesWith = List.copyOf(providesWith); + public UnboundModuleProvideInfo { + requireNonNull(provides); + providesWith = List.copyOf(providesWith); } } public record UnboundModuleRequiresInfo(ModuleEntry requires, int requiresFlagsMask, Optional requiresVersion) - implements ModuleRequireInfo {} + implements ModuleRequireInfo { + public UnboundModuleRequiresInfo { + requireNonNull(requires); + requireNonNull(requiresVersion); + } + } public record UnboundRecordComponentInfo(Utf8Entry name, Utf8Entry descriptor, List> attributes) implements RecordComponentInfo { - public UnboundRecordComponentInfo(Utf8Entry name, Utf8Entry descriptor, List> attributes) { - this.name = name; - this.descriptor = descriptor; - this.attributes = List.copyOf(attributes); + public UnboundRecordComponentInfo { + requireNonNull(name); + requireNonNull(descriptor); + attributes = List.copyOf(attributes); } } @@ -759,7 +793,9 @@ public record UnboundTypeAnnotation(TargetInfo targetInfo, Annotation annotation) implements TypeAnnotation { public UnboundTypeAnnotation { + requireNonNull(targetInfo); targetPath = List.copyOf(targetPath); + requireNonNull(annotation); } } @@ -786,7 +822,7 @@ public UnboundModuleAttribute(ModuleEntry moduleName, Collection provides) { super(Attributes.module()); - this.moduleName = moduleName; + this.moduleName = requireNonNull(moduleName); this.moduleFlags = moduleFlags; this.moduleVersion = moduleVersion; this.requires = List.copyOf(requires); From bd6264420b9f248999dd8387c25c549b08bd193a Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Mon, 14 Oct 2024 17:22:11 +0000 Subject: [PATCH 45/95] 8341924: Improve error message with structurally malformed Code array Reviewed-by: asotona --- .../classes/com/sun/tools/javap/CodeWriter.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/CodeWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/CodeWriter.java index 519a5adcf62..8f6b9b1d2ed 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/CodeWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/CodeWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -92,20 +92,22 @@ public void writeVerboseHeader(CodeAttribute attr) { public void writeInstrs(CodeAttribute attr) { List detailWriters = getDetailWriters(attr); - int pc = 0; + int[] pcState = {0}; try { - for (var coe: attr) { + attr.forEach(coe -> { if (coe instanceof Instruction instr) { - for (InstructionDetailWriter w: detailWriters) + int pc = pcState[0]; + for (InstructionDetailWriter w : detailWriters) w.writeDetails(pc, instr); writeInstr(pc, instr, attr); - pc += instr.sizeInBytes(); + pcState[0] = pc + instr.sizeInBytes(); } - } + }); } catch (IllegalArgumentException e) { - report("error at or after byte " + pc); + report("error at or after address " + pcState[0] + ": " + e.getMessage()); } + int pc = pcState[0]; for (InstructionDetailWriter w: detailWriters) w.flush(pc); } From a601cd2e100958e3f37ae65e32e4b3cac246c079 Mon Sep 17 00:00:00 2001 From: Fei Yang Date: Tue, 15 Oct 2024 02:53:44 +0000 Subject: [PATCH 46/95] 8342014: RISC-V: ZStoreBarrierStubC2 clobbers rflags Reviewed-by: rehn, aboldtch, mli --- src/hotspot/cpu/riscv/gc/g1/g1_riscv.ad | 68 ++++++++++++------------- src/hotspot/cpu/riscv/gc/x/x_riscv.ad | 28 +++++----- src/hotspot/cpu/riscv/gc/z/z_riscv.ad | 32 ++++++------ 3 files changed, 64 insertions(+), 64 deletions(-) diff --git a/src/hotspot/cpu/riscv/gc/g1/g1_riscv.ad b/src/hotspot/cpu/riscv/gc/g1/g1_riscv.ad index 1dc5834dbdc..7a525323021 100644 --- a/src/hotspot/cpu/riscv/gc/g1/g1_riscv.ad +++ b/src/hotspot/cpu/riscv/gc/g1/g1_riscv.ad @@ -74,11 +74,11 @@ static void write_barrier_post(MacroAssembler* masm, %} -instruct g1StoreP(indirect mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3) +instruct g1StoreP(indirect mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) %{ predicate(UseG1GC && n->as_Store()->barrier_data() != 0); match(Set mem (StoreP mem src)); - effect(TEMP tmp1, TEMP tmp2, TEMP tmp3); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); ins_cost(STORE_COST); format %{ "sd $src, $mem\t# ptr" %} ins_encode %{ @@ -99,11 +99,11 @@ instruct g1StoreP(indirect mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegP ins_pipe(istore_reg_mem); %} -instruct g1StoreN(indirect mem, iRegN src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3) +instruct g1StoreN(indirect mem, iRegN src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) %{ predicate(UseG1GC && n->as_Store()->barrier_data() != 0); match(Set mem (StoreN mem src)); - effect(TEMP tmp1, TEMP tmp2, TEMP tmp3); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); ins_cost(STORE_COST); format %{ "sw $src, $mem\t# compressed ptr" %} ins_encode %{ @@ -131,11 +131,11 @@ instruct g1StoreN(indirect mem, iRegN src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegP ins_pipe(istore_reg_mem); %} -instruct g1EncodePAndStoreN(indirect mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3) +instruct g1EncodePAndStoreN(indirect mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) %{ predicate(UseG1GC && n->as_Store()->barrier_data() != 0); match(Set mem (StoreN mem (EncodeP src))); - effect(TEMP tmp1, TEMP tmp2, TEMP tmp3); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); ins_cost(STORE_COST); format %{ "encode_heap_oop $tmp1, $src\n\t" "sw $tmp1, $mem\t# compressed ptr" %} @@ -162,11 +162,11 @@ instruct g1EncodePAndStoreN(indirect mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp t ins_pipe(istore_reg_mem); %} -instruct g1CompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2) +instruct g1CompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, rFlagsReg cr) %{ predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); match(Set res (CompareAndExchangeP mem (Binary oldval newval))); - effect(TEMP res, TEMP tmp1, TEMP tmp2); + effect(TEMP res, TEMP tmp1, TEMP tmp2, KILL cr); ins_cost(2 * VOLATILE_REF_COST); format %{ "cmpxchg $res = $mem, $oldval, $newval\t# ptr" %} ins_encode %{ @@ -194,11 +194,11 @@ instruct g1CompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP ins_pipe(pipe_slow); %} -instruct g1CompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2) +instruct g1CompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, rFlagsReg cr) %{ predicate(UseG1GC && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); match(Set res (CompareAndExchangeP mem (Binary oldval newval))); - effect(TEMP res, TEMP tmp1, TEMP tmp2); + effect(TEMP res, TEMP tmp1, TEMP tmp2, KILL cr); ins_cost(VOLATILE_REF_COST); format %{ "cmpxchg_acq $res = $mem, $oldval, $newval\t# ptr" %} ins_encode %{ @@ -226,11 +226,11 @@ instruct g1CompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRe ins_pipe(pipe_slow); %} -instruct g1CompareAndExchangeN(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3) +instruct g1CompareAndExchangeN(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) %{ predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); match(Set res (CompareAndExchangeN mem (Binary oldval newval))); - effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); ins_cost(2 * VOLATILE_REF_COST); format %{ "cmpxchg $res = $mem, $oldval, $newval\t# narrow oop" %} ins_encode %{ @@ -256,11 +256,11 @@ instruct g1CompareAndExchangeN(iRegNNoSp res, indirect mem, iRegN oldval, iRegN ins_pipe(pipe_slow); %} -instruct g1CompareAndExchangeNAcq(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3) +instruct g1CompareAndExchangeNAcq(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) %{ predicate(UseG1GC && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); match(Set res (CompareAndExchangeN mem (Binary oldval newval))); - effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); ins_cost(VOLATILE_REF_COST); format %{ "cmpxchg_acq $res = $mem, $oldval, $newval\t# narrow oop" %} ins_encode %{ @@ -286,12 +286,12 @@ instruct g1CompareAndExchangeNAcq(iRegNNoSp res, indirect mem, iRegN oldval, iRe ins_pipe(pipe_slow); %} -instruct g1CompareAndSwapP(iRegINoSp res, indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegP oldval) +instruct g1CompareAndSwapP(iRegINoSp res, indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegP oldval, rFlagsReg cr) %{ predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); match(Set res (CompareAndSwapP mem (Binary oldval newval))); match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); - effect(TEMP res, TEMP tmp1, TEMP tmp2); + effect(TEMP res, TEMP tmp1, TEMP tmp2, KILL cr); ins_cost(2 * VOLATILE_REF_COST); format %{ "cmpxchg $mem, $oldval, $newval\t# (ptr)\n\t" "mv $res, $res == $oldval" %} @@ -318,12 +318,12 @@ instruct g1CompareAndSwapP(iRegINoSp res, indirect mem, iRegP newval, iRegPNoSp ins_pipe(pipe_slow); %} -instruct g1CompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegP oldval) +instruct g1CompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegP oldval, rFlagsReg cr) %{ predicate(UseG1GC && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); match(Set res (CompareAndSwapP mem (Binary oldval newval))); match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); - effect(TEMP res, TEMP tmp1, TEMP tmp2); + effect(TEMP res, TEMP tmp1, TEMP tmp2, KILL cr); ins_cost(VOLATILE_REF_COST); format %{ "cmpxchg_acq $mem, $oldval, $newval\t# (ptr)\n\t" "mv $res, $res == $oldval" %} @@ -350,12 +350,12 @@ instruct g1CompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP newval, iRegPNo ins_pipe(pipe_slow); %} -instruct g1CompareAndSwapN(iRegINoSp res, indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegN oldval) +instruct g1CompareAndSwapN(iRegINoSp res, indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegN oldval, rFlagsReg cr) %{ predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); match(Set res (CompareAndSwapN mem (Binary oldval newval))); match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); - effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); ins_cost(2 * VOLATILE_REF_COST); format %{ "cmpxchg $mem, $oldval, $newval\t# (narrow oop)\n\t" "mv $res, $res == $oldval" %} @@ -383,12 +383,12 @@ instruct g1CompareAndSwapN(iRegINoSp res, indirect mem, iRegN newval, iRegPNoSp ins_pipe(pipe_slow); %} -instruct g1CompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegN oldval) +instruct g1CompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegN oldval, rFlagsReg cr) %{ predicate(UseG1GC && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); match(Set res (CompareAndSwapN mem (Binary oldval newval))); match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); - effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); ins_cost(VOLATILE_REF_COST); format %{ "cmpxchg_acq $mem, $oldval, $newval\t# (narrow oop)\n\t" "mv $res, $res == $oldval" %} @@ -416,11 +416,11 @@ instruct g1CompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN newval, iRegPNo ins_pipe(pipe_slow); %} -instruct g1GetAndSetP(indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp preval) +instruct g1GetAndSetP(indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp preval, rFlagsReg cr) %{ predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); match(Set preval (GetAndSetP mem newval)); - effect(TEMP preval, TEMP tmp1, TEMP tmp2); + effect(TEMP preval, TEMP tmp1, TEMP tmp2, KILL cr); ins_cost(2 * VOLATILE_REF_COST); format %{ "atomic_xchg $preval, $newval, [$mem]" %} ins_encode %{ @@ -442,11 +442,11 @@ instruct g1GetAndSetP(indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2 ins_pipe(pipe_serial); %} -instruct g1GetAndSetPAcq(indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp preval) +instruct g1GetAndSetPAcq(indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp preval, rFlagsReg cr) %{ predicate(UseG1GC && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); match(Set preval (GetAndSetP mem newval)); - effect(TEMP preval, TEMP tmp1, TEMP tmp2); + effect(TEMP preval, TEMP tmp1, TEMP tmp2, KILL cr); ins_cost(VOLATILE_REF_COST); format %{ "atomic_xchg_acq $preval, $newval, [$mem]" %} ins_encode %{ @@ -468,11 +468,11 @@ instruct g1GetAndSetPAcq(indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp t ins_pipe(pipe_serial); %} -instruct g1GetAndSetN(indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegNNoSp preval) +instruct g1GetAndSetN(indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegNNoSp preval, rFlagsReg cr) %{ predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); match(Set preval (GetAndSetN mem newval)); - effect(TEMP preval, TEMP tmp1, TEMP tmp2, TEMP tmp3); + effect(TEMP preval, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); ins_cost(2 * VOLATILE_REF_COST); format %{ "atomic_xchgwu $preval, $newval, [$mem]" %} ins_encode %{ @@ -495,11 +495,11 @@ instruct g1GetAndSetN(indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2 ins_pipe(pipe_serial); %} -instruct g1GetAndSetNAcq(indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegNNoSp preval) +instruct g1GetAndSetNAcq(indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegNNoSp preval, rFlagsReg cr) %{ predicate(UseG1GC && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); match(Set preval (GetAndSetN mem newval)); - effect(TEMP preval, TEMP tmp1, TEMP tmp2, TEMP tmp3); + effect(TEMP preval, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); ins_cost(VOLATILE_REF_COST); format %{ "atomic_xchgwu_acq $preval, $newval, [$mem]" %} ins_encode %{ @@ -522,11 +522,11 @@ instruct g1GetAndSetNAcq(indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp t ins_pipe(pipe_serial); %} -instruct g1LoadP(iRegPNoSp dst, indirect mem, iRegPNoSp tmp1, iRegPNoSp tmp2) +instruct g1LoadP(iRegPNoSp dst, indirect mem, iRegPNoSp tmp1, iRegPNoSp tmp2, rFlagsReg cr) %{ predicate(UseG1GC && n->as_Load()->barrier_data() != 0); match(Set dst (LoadP mem)); - effect(TEMP dst, TEMP tmp1, TEMP tmp2); + effect(TEMP dst, TEMP tmp1, TEMP tmp2, KILL cr); ins_cost(LOAD_COST + BRANCH_COST); format %{ "ld $dst, $mem\t# ptr" %} ins_encode %{ @@ -541,11 +541,11 @@ instruct g1LoadP(iRegPNoSp dst, indirect mem, iRegPNoSp tmp1, iRegPNoSp tmp2) ins_pipe(iload_reg_mem); %} -instruct g1LoadN(iRegNNoSp dst, indirect mem, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3) +instruct g1LoadN(iRegNNoSp dst, indirect mem, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) %{ predicate(UseG1GC && n->as_Load()->barrier_data() != 0); match(Set dst (LoadN mem)); - effect(TEMP dst, TEMP tmp1, TEMP tmp2, TEMP tmp3); + effect(TEMP dst, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); ins_cost(LOAD_COST + BRANCH_COST); format %{ "lwu $dst, $mem\t# compressed ptr" %} ins_encode %{ diff --git a/src/hotspot/cpu/riscv/gc/x/x_riscv.ad b/src/hotspot/cpu/riscv/gc/x/x_riscv.ad index ef02f301c6a..b93b7066425 100644 --- a/src/hotspot/cpu/riscv/gc/x/x_riscv.ad +++ b/src/hotspot/cpu/riscv/gc/x/x_riscv.ad @@ -52,11 +52,11 @@ static void x_load_barrier_slow_path(MacroAssembler* masm, const MachNode* node, %} // Load Pointer -instruct xLoadP(iRegPNoSp dst, memory mem, iRegPNoSp tmp) +instruct xLoadP(iRegPNoSp dst, memory mem, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set dst (LoadP mem)); predicate(UseZGC && !ZGenerational && (n->as_Load()->barrier_data() != 0)); - effect(TEMP dst, TEMP tmp); + effect(TEMP dst, TEMP tmp, KILL cr); ins_cost(4 * DEFAULT_COST); @@ -71,11 +71,11 @@ instruct xLoadP(iRegPNoSp dst, memory mem, iRegPNoSp tmp) ins_pipe(iload_reg_mem); %} -instruct xCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp) %{ +instruct xCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set res (CompareAndSwapP mem (Binary oldval newval))); match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); predicate(UseZGC && !ZGenerational && !needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() == XLoadBarrierStrong); - effect(TEMP_DEF res, TEMP tmp); + effect(TEMP_DEF res, TEMP tmp, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -105,11 +105,11 @@ instruct xCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newva ins_pipe(pipe_slow); %} -instruct xCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp) %{ +instruct xCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set res (CompareAndSwapP mem (Binary oldval newval))); match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); predicate(UseZGC && !ZGenerational && needs_acquiring_load_reserved(n) && (n->as_LoadStore()->barrier_data() == XLoadBarrierStrong)); - effect(TEMP_DEF res, TEMP tmp); + effect(TEMP_DEF res, TEMP tmp, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -139,10 +139,10 @@ instruct xCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP ne ins_pipe(pipe_slow); %} -instruct xCompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp) %{ +instruct xCompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set res (CompareAndExchangeP mem (Binary oldval newval))); predicate(UseZGC && !ZGenerational && !needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() == XLoadBarrierStrong); - effect(TEMP_DEF res, TEMP tmp); + effect(TEMP_DEF res, TEMP tmp, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -167,10 +167,10 @@ instruct xCompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP n ins_pipe(pipe_slow); %} -instruct xCompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp) %{ +instruct xCompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set res (CompareAndExchangeP mem (Binary oldval newval))); predicate(UseZGC && !ZGenerational && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() == XLoadBarrierStrong); - effect(TEMP_DEF res, TEMP tmp); + effect(TEMP_DEF res, TEMP tmp, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -195,10 +195,10 @@ instruct xCompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iReg ins_pipe(pipe_slow); %} -instruct xGetAndSetP(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp) %{ +instruct xGetAndSetP(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set prev (GetAndSetP mem newv)); predicate(UseZGC && !ZGenerational && !needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP_DEF prev, TEMP tmp); + effect(TEMP_DEF prev, TEMP tmp, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -212,10 +212,10 @@ instruct xGetAndSetP(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp) %{ ins_pipe(pipe_serial); %} -instruct xGetAndSetPAcq(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp) %{ +instruct xGetAndSetPAcq(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set prev (GetAndSetP mem newv)); predicate(UseZGC && !ZGenerational && needs_acquiring_load_reserved(n) && (n->as_LoadStore()->barrier_data() != 0)); - effect(TEMP_DEF prev, TEMP tmp); + effect(TEMP_DEF prev, TEMP tmp, KILL cr); ins_cost(VOLATILE_REF_COST); diff --git a/src/hotspot/cpu/riscv/gc/z/z_riscv.ad b/src/hotspot/cpu/riscv/gc/z/z_riscv.ad index 4c94e504475..5b545fe8012 100644 --- a/src/hotspot/cpu/riscv/gc/z/z_riscv.ad +++ b/src/hotspot/cpu/riscv/gc/z/z_riscv.ad @@ -90,11 +90,11 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Address %} // Load Pointer -instruct zLoadP(iRegPNoSp dst, memory mem, iRegPNoSp tmp) +instruct zLoadP(iRegPNoSp dst, memory mem, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set dst (LoadP mem)); predicate(UseZGC && ZGenerational && n->as_Load()->barrier_data() != 0); - effect(TEMP dst, TEMP tmp); + effect(TEMP dst, TEMP tmp, KILL cr); ins_cost(4 * DEFAULT_COST); @@ -110,11 +110,11 @@ instruct zLoadP(iRegPNoSp dst, memory mem, iRegPNoSp tmp) %} // Store Pointer -instruct zStoreP(memory mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2) +instruct zStoreP(memory mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2, rFlagsReg cr) %{ predicate(UseZGC && ZGenerational && n->as_Store()->barrier_data() != 0); match(Set mem (StoreP mem src)); - effect(TEMP tmp1, TEMP tmp2); + effect(TEMP tmp1, TEMP tmp2, KILL cr); ins_cost(125); // XXX format %{ "sd $mem, $src\t# ptr" %} @@ -127,11 +127,11 @@ instruct zStoreP(memory mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2) %} instruct zCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, - iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1) %{ + iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1, rFlagsReg cr) %{ match(Set res (CompareAndSwapP mem (Binary oldval newval))); match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); predicate(UseZGC && ZGenerational && !needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res); + effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -150,11 +150,11 @@ instruct zCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newva %} instruct zCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, - iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1) %{ + iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1, rFlagsReg cr) %{ match(Set res (CompareAndSwapP mem (Binary oldval newval))); match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); predicate(UseZGC && ZGenerational && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res); + effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -173,10 +173,10 @@ instruct zCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP ne %} instruct zCompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, - iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1) %{ + iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1, rFlagsReg cr) %{ match(Set res (CompareAndExchangeP mem (Binary oldval newval))); predicate(UseZGC && ZGenerational && !needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res); + effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -195,10 +195,10 @@ instruct zCompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP n %} instruct zCompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, - iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1) %{ + iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1, rFlagsReg cr) %{ match(Set res (CompareAndExchangeP mem (Binary oldval newval))); predicate(UseZGC && ZGenerational && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res); + effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -216,10 +216,10 @@ instruct zCompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iReg ins_pipe(pipe_slow); %} -instruct zGetAndSetP(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp) %{ +instruct zGetAndSetP(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set prev (GetAndSetP mem newv)); predicate(UseZGC && ZGenerational && !needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP_DEF prev, TEMP tmp); + effect(TEMP_DEF prev, TEMP tmp, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -234,10 +234,10 @@ instruct zGetAndSetP(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp) %{ ins_pipe(pipe_serial); %} -instruct zGetAndSetPAcq(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp) %{ +instruct zGetAndSetPAcq(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set prev (GetAndSetP mem newv)); predicate(UseZGC && ZGenerational && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP_DEF prev, TEMP tmp); + effect(TEMP_DEF prev, TEMP tmp, KILL cr); ins_cost(2 * VOLATILE_REF_COST); From e6698f51a1977d553ce4e1dd14b43b0d325224fa Mon Sep 17 00:00:00 2001 From: "t.ogata" Date: Tue, 15 Oct 2024 07:47:42 +0000 Subject: [PATCH 47/95] 8337851: Some tests have name which confuse jtreg Reviewed-by: aivanov, prr, cstein --- test/jdk/java/awt/dnd/{ => URLDragTest}/URLDragTest.java | 0 .../generics/{ => parametricException}/ParametricException.java | 0 test/langtools/tools/javac/warnings/{ => Serial}/Serial.java | 0 test/langtools/tools/javac/warnings/{ => Serial}/Serial.out | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename test/jdk/java/awt/dnd/{ => URLDragTest}/URLDragTest.java (100%) rename test/langtools/tools/javac/generics/{ => parametricException}/ParametricException.java (100%) rename test/langtools/tools/javac/warnings/{ => Serial}/Serial.java (100%) rename test/langtools/tools/javac/warnings/{ => Serial}/Serial.out (100%) diff --git a/test/jdk/java/awt/dnd/URLDragTest.java b/test/jdk/java/awt/dnd/URLDragTest/URLDragTest.java similarity index 100% rename from test/jdk/java/awt/dnd/URLDragTest.java rename to test/jdk/java/awt/dnd/URLDragTest/URLDragTest.java diff --git a/test/langtools/tools/javac/generics/ParametricException.java b/test/langtools/tools/javac/generics/parametricException/ParametricException.java similarity index 100% rename from test/langtools/tools/javac/generics/ParametricException.java rename to test/langtools/tools/javac/generics/parametricException/ParametricException.java diff --git a/test/langtools/tools/javac/warnings/Serial.java b/test/langtools/tools/javac/warnings/Serial/Serial.java similarity index 100% rename from test/langtools/tools/javac/warnings/Serial.java rename to test/langtools/tools/javac/warnings/Serial/Serial.java diff --git a/test/langtools/tools/javac/warnings/Serial.out b/test/langtools/tools/javac/warnings/Serial/Serial.out similarity index 100% rename from test/langtools/tools/javac/warnings/Serial.out rename to test/langtools/tools/javac/warnings/Serial/Serial.out From 521effe017b9b6322036f1851220056a637d6b1c Mon Sep 17 00:00:00 2001 From: Per Minborg Date: Tue, 15 Oct 2024 07:59:33 +0000 Subject: [PATCH 48/95] 8340189: 8339531 incorrect for Big Endian platforms Reviewed-by: mdoerr, amitkumar --- .../foreign/SegmentBulkOperations.java | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/foreign/SegmentBulkOperations.java b/src/java.base/share/classes/jdk/internal/foreign/SegmentBulkOperations.java index 30e146a82f4..d5a5954fa0e 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SegmentBulkOperations.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SegmentBulkOperations.java @@ -200,8 +200,8 @@ private static long mismatch(AbstractMemorySegmentImpl src, long srcFromOffset, int offset = 0; final int limit = length & (NATIVE_THRESHOLD_MISMATCH - 8); for (; offset < limit; offset += 8) { - final long s = SCOPED_MEMORY_ACCESS.getLongUnaligned(src.sessionImpl(), src.unsafeGetBase(), src.unsafeGetOffset() + srcFromOffset + offset, !Architecture.isLittleEndian()); - final long d = SCOPED_MEMORY_ACCESS.getLongUnaligned(dst.sessionImpl(), dst.unsafeGetBase(), dst.unsafeGetOffset() + dstFromOffset + offset, !Architecture.isLittleEndian()); + final long s = SCOPED_MEMORY_ACCESS.getLongUnaligned(src.sessionImpl(), src.unsafeGetBase(), src.unsafeGetOffset() + srcFromOffset + offset, false); + final long d = SCOPED_MEMORY_ACCESS.getLongUnaligned(dst.sessionImpl(), dst.unsafeGetBase(), dst.unsafeGetOffset() + dstFromOffset + offset, false); if (s != d) { return start + offset + mismatch(s, d); } @@ -210,8 +210,8 @@ private static long mismatch(AbstractMemorySegmentImpl src, long srcFromOffset, // 0...0X00 if (remaining >= 4) { - final int s = SCOPED_MEMORY_ACCESS.getIntUnaligned(src.sessionImpl(), src.unsafeGetBase(), src.unsafeGetOffset() + srcFromOffset + offset, !Architecture.isLittleEndian()); - final int d = SCOPED_MEMORY_ACCESS.getIntUnaligned(dst.sessionImpl(), dst.unsafeGetBase(), dst.unsafeGetOffset() + dstFromOffset + offset, !Architecture.isLittleEndian()); + final int s = SCOPED_MEMORY_ACCESS.getIntUnaligned(src.sessionImpl(), src.unsafeGetBase(), src.unsafeGetOffset() + srcFromOffset + offset, false); + final int d = SCOPED_MEMORY_ACCESS.getIntUnaligned(dst.sessionImpl(), dst.unsafeGetBase(), dst.unsafeGetOffset() + dstFromOffset + offset, false); if (s != d) { return start + offset + mismatch(s, d); } @@ -220,8 +220,8 @@ private static long mismatch(AbstractMemorySegmentImpl src, long srcFromOffset, } // 0...00X0 if (remaining >= 2) { - final short s = SCOPED_MEMORY_ACCESS.getShortUnaligned(src.sessionImpl(), src.unsafeGetBase(), src.unsafeGetOffset() + srcFromOffset + offset, !Architecture.isLittleEndian()); - final short d = SCOPED_MEMORY_ACCESS.getShortUnaligned(dst.sessionImpl(), dst.unsafeGetBase(), dst.unsafeGetOffset() + dstFromOffset + offset, !Architecture.isLittleEndian()); + final short s = SCOPED_MEMORY_ACCESS.getShortUnaligned(src.sessionImpl(), src.unsafeGetBase(), src.unsafeGetOffset() + srcFromOffset + offset, false); + final short d = SCOPED_MEMORY_ACCESS.getShortUnaligned(dst.sessionImpl(), dst.unsafeGetBase(), dst.unsafeGetOffset() + dstFromOffset + offset, false); if (s != d) { return start + offset + mismatch(s, d); } @@ -243,26 +243,18 @@ private static long mismatch(AbstractMemorySegmentImpl src, long srcFromOffset, @ForceInline private static int mismatch(long first, long second) { final long x = first ^ second; - return (Architecture.isLittleEndian() - ? Long.numberOfTrailingZeros(x) - : Long.numberOfLeadingZeros(x)) / 8; + return Long.numberOfTrailingZeros(x) / 8; } @ForceInline private static int mismatch(int first, int second) { final int x = first ^ second; - return (Architecture.isLittleEndian() - ? Integer.numberOfTrailingZeros(x) - : Integer.numberOfLeadingZeros(x)) / 8; + return Integer.numberOfTrailingZeros(x) / 8; } @ForceInline private static int mismatch(short first, short second) { - if (Architecture.isLittleEndian()) { - return ((0xff & first) == (0xff & second)) ? 1 : 0; - } else { - return ((0xff & first) == (0xff & second)) ? 0 : 1; - } + return ((0xff & first) == (0xff & second)) ? 1 : 0; } /** From 3b8a2f8c45ffd0bdb48db805cf70b4652525d891 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 15 Oct 2024 08:46:23 +0000 Subject: [PATCH 49/95] 8337269: G1ConfidencePercent interpreted inconsistently Reviewed-by: kbarrett, iwalulya --- src/hotspot/share/gc/g1/g1Policy.cpp | 2 +- src/hotspot/share/gc/g1/g1Predictions.hpp | 17 ++++++++--------- src/hotspot/share/gc/g1/g1_globals.hpp | 3 ++- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 6d0864f032c..1b71901f0fe 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -53,7 +53,7 @@ #include "gc/shared/gcTraceTime.inline.hpp" G1Policy::G1Policy(STWGCTimer* gc_timer) : - _predictor(G1ConfidencePercent / 100.0), + _predictor((100 - G1ConfidencePercent) / 100.0), _analytics(new G1Analytics(&_predictor)), _remset_tracker(), _mmu_tracker(new G1MMUTracker(GCPauseIntervalMillis / 1000.0, MaxGCPauseMillis / 1000.0)), diff --git a/src/hotspot/share/gc/g1/g1Predictions.hpp b/src/hotspot/share/gc/g1/g1Predictions.hpp index 510f296a9f3..ae2a8f41880 100644 --- a/src/hotspot/share/gc/g1/g1Predictions.hpp +++ b/src/hotspot/share/gc/g1/g1Predictions.hpp @@ -29,8 +29,9 @@ // Utility class containing various helper methods for prediction. class G1Predictions { - private: - double _sigma; +private: + // Scale factor indicating to which degree stddev should be taking into account in predictions. + double _stddev_scale; // This function is used to estimate the stddev of sample sets. There is some // special consideration of small sample sets: the actual stddev for them is @@ -46,16 +47,14 @@ class G1Predictions { } return estimate; } - public: - G1Predictions(double sigma) : _sigma(sigma) { - assert(sigma >= 0.0, "Confidence must be larger than or equal to zero"); - } - // Confidence factor. - double sigma() const { return _sigma; } +public: + G1Predictions(double stddev_scale) : _stddev_scale(stddev_scale) { + assert(stddev_scale >= 0.0, "must be"); + } double predict(TruncatedSeq const* seq) const { - return seq->davg() + _sigma * stddev_estimate(seq); + return seq->davg() + _stddev_scale * stddev_estimate(seq); } double predict_in_unit_interval(TruncatedSeq const* seq) const { diff --git a/src/hotspot/share/gc/g1/g1_globals.hpp b/src/hotspot/share/gc/g1/g1_globals.hpp index c8016ddc0dd..ed02ba2dc5c 100644 --- a/src/hotspot/share/gc/g1/g1_globals.hpp +++ b/src/hotspot/share/gc/g1/g1_globals.hpp @@ -111,7 +111,8 @@ range(1, max_intx) \ \ product(uint, G1ConfidencePercent, 50, \ - "Confidence level for MMU/pause predictions") \ + "Confidence level for MMU/pause predictions. A higher value " \ + "means that G1 will use less safety margin for its predictions.") \ range(1, 100) \ \ product(uintx, G1SummarizeRSetStatsPeriod, 0, DIAGNOSTIC, \ From f4dccfd4cf354f360b823c8cce15bb54ef90e9ca Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Tue, 15 Oct 2024 09:10:13 +0000 Subject: [PATCH 50/95] 8338596: Clarify handling of restricted and caller-sensitive methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hannes Wallnöfer Reviewed-by: alanb, jvernee --- .../lang/doc-files/RestrictedMethods.html | 53 +++++++++++++++++++ .../java/lang/foreign/package-info.java | 40 -------------- .../formats/html/HtmlDocletWriter.java | 28 +++++++--- .../html/resources/standard.properties | 3 +- .../doclets/toolkit/util/DocPaths.java | 3 ++ 5 files changed, 78 insertions(+), 49 deletions(-) create mode 100644 src/java.base/share/classes/java/lang/doc-files/RestrictedMethods.html diff --git a/src/java.base/share/classes/java/lang/doc-files/RestrictedMethods.html b/src/java.base/share/classes/java/lang/doc-files/RestrictedMethods.html new file mode 100644 index 00000000000..413adf8e063 --- /dev/null +++ b/src/java.base/share/classes/java/lang/doc-files/RestrictedMethods.html @@ -0,0 +1,53 @@ + + + + + Restricted methods + + +

Restricted methods

+

Various methods in the Java SE API allow Java code to interoperate with resources outside the Java runtime + in such a way that the runtime cannot prove correct or safe use of the resources. These methods can, + when used incorrectly, violate the integrity of the Java Virtual Machine, but are conditionally made available + to users, as they provide essential functionality. They are known as restricted methods.

+

Given the potential danger of restricted methods, the Java runtime issues a warning on + the standard error stream every time a restricted method is invoked. Such warnings can + be disabled by granting access to restricted methods to selected modules. This can be + done either via implementation-specific command line options or programmatically, e.g. + by calling ModuleLayer.Controller.enableNativeAccess(java.lang.Module).

+

When a restricted method is invoked by JNI code, + or from an upcall stub + and there is no caller class on the stack, it is as if the restricted method call occurred in an unnamed module.

+

In the reference implementation, access to restricted methods can be granted to + specific modules using the command line option --enable-native-access=M1,M2, ... Mn, + where M1, M2, ... Mn are module names (for the unnamed module, + the special value ALL-UNNAMED can be used). Access to restricted methods + from modules not listed by that option is deemed illegal. Clients can + control how access to restricted methods is handled, using the command line + option --illegal-native-access. If this option is not specified, + illegal access to restricted methods will result in runtime warnings.

+ + diff --git a/src/java.base/share/classes/java/lang/foreign/package-info.java b/src/java.base/share/classes/java/lang/foreign/package-info.java index 1f31301638e..18419ba1877 100644 --- a/src/java.base/share/classes/java/lang/foreign/package-info.java +++ b/src/java.base/share/classes/java/lang/foreign/package-info.java @@ -128,49 +128,9 @@ * {@linkplain java.lang.foreign.SegmentAllocator#allocateFrom(java.lang.String) converting} * Java strings into zero-terminated, UTF-8 strings, as demonstrated in the above example. * - *

Restricted methods

- * - * Some methods in this package are considered restricted. Restricted methods - * are typically used to bind native foreign data and/or functions to first-class - * Java API elements which can then be used directly by clients. For instance the - * restricted method {@link java.lang.foreign.MemorySegment#reinterpret(long)} can be - * used to create a fresh segment with the same address and temporal bounds, but with - * the provided size. This can be useful to resize memory segments obtained when - * interacting with native functions. - *

- * Binding foreign data and/or functions is generally unsafe and, if done incorrectly, - * can result in VM crashes, or memory corruption when the bound Java API element - * is accessed. For instance, incorrectly resizing a native memory segment using - * {@link java.lang.foreign.MemorySegment#reinterpret(long)} can lead to a JVM crash, or, - * worse, lead to silent memory corruption when attempting to access the resized segment. - * For these reasons, it is crucial for code that calls a restricted method to never pass - * arguments that might cause incorrect binding of foreign data and/or functions to - * a Java API. - *

- * Given the potential danger of restricted methods, the Java runtime issues a warning on - * the standard error stream every time a restricted method is invoked. Such warnings can - * be disabled by granting access to restricted methods to selected modules. This can be - * done either via implementation-specific command line options or programmatically, e.g. - * by calling {@link java.lang.ModuleLayer.Controller#enableNativeAccess(java.lang.Module)}. - *

- * For every class in this package, unless specified otherwise, any method arguments of - * reference type must not be {@code null}, and any null argument will elicit a - * {@code NullPointerException}. This fact is not individually documented for methods of - * this API. - * * @apiNote Usual memory model guarantees (see {@jls 17.4}) do not apply when accessing * native memory segments as these segments are backed by off-heap regions of memory. * - * @implNote - * In the reference implementation, access to restricted methods can be granted to - * specific modules using the command line option {@code --enable-native-access=M1,M2, ... Mn}, - * where {@code M1}, {@code M2}, {@code ... Mn} are module names (for the unnamed module, - * the special value {@code ALL-UNNAMED} can be used). Access to restricted methods - * from modules not listed by that option is deemed illegal. Clients can - * control how access to restricted methods is handled, using the command line - * option {@code --illegal-native-access}. If this option is not specified, - * illegal access to restricted methods will result in runtime warnings. - * * @spec jni/index.html Java Native Interface Specification * * @since 22 diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java index fdf05489168..9bb1ebaaf62 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java @@ -2642,19 +2642,31 @@ public void addRestrictedInfo(ExecutableElement forWhat, Content target) { //in Java platform: var restrictedDiv = HtmlTree.DIV(HtmlStyles.restrictedBlock); restrictedDiv.setId(htmlIds.forRestrictedSection(forWhat)); - String name = forWhat.getSimpleName().toString(); + var name = forWhat.getSimpleName().toString(); var nameCode = HtmlTree.CODE(Text.of(name)); - String leadingNoteKey = "doclet.RestrictedLeadingNote"; - Content leadingNote = - contents.getContent(leadingNoteKey, nameCode); - restrictedDiv.add(HtmlTree.SPAN(HtmlStyles.restrictedLabel, - leadingNote)); - Content note1 = contents.getContent("doclet.RestrictedTrailingNote1", nameCode); + var restrictedMethodLink = getRestrictedMethodDocLink(); + var leadingNoteKey = "doclet.RestrictedLeadingNote"; + var leadingNote = contents.getContent(leadingNoteKey, nameCode, restrictedMethodLink); + restrictedDiv.add(HtmlTree.SPAN(HtmlStyles.restrictedLabel, leadingNote)); + var note1 = contents.getContent("doclet.RestrictedTrailingNote1", nameCode); restrictedDiv.add(HtmlTree.DIV(HtmlStyles.restrictedComment, note1)); - Content note2 = contents.getContent("doclet.RestrictedTrailingNote2", nameCode); + var note2 = contents.getContent("doclet.RestrictedTrailingNote2", nameCode); restrictedDiv.add(HtmlTree.DIV(HtmlStyles.restrictedComment, note2)); target.add(restrictedDiv); } } + private Content getRestrictedMethodDocLink() { + var restrictedMethodLabel = contents.getContent("doclet.RestrictedMethod"); + var javaLang = utils.elementUtils.getPackageElement("java.lang"); + if (utils.isIncluded(javaLang)) { + var restrictedDocPath = pathToRoot + .resolve(docPaths.forPackage(javaLang)) + .resolve(DocPaths.DOC_FILES) + .resolve(DocPaths.RESTRICTED_DOC); + return links.createLink(restrictedDocPath, restrictedMethodLabel); + } + return restrictedMethodLabel; + } + } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties index 6141ce46fe8..bb001912229 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties @@ -424,7 +424,8 @@ doclet.ReflectivePreviewAPI={0} refers to one or more reflective preview APIs: doclet.UsesDeclaredUsingPreview={0} refers to one or more types which are declared using a preview feature of the Java language: {1}. doclet.PreviewTrailingNote1=Programs can only use {0} when preview features are enabled. doclet.PreviewTrailingNote2=Preview features may be removed in a future release, or upgraded to permanent features of the Java platform. -doclet.RestrictedLeadingNote={0} is a restricted method of the Java platform. +doclet.RestrictedMethod=restricted method +doclet.RestrictedLeadingNote={0} is a {1} of the Java platform. doclet.RestrictedTrailingNote1=Programs can only use {0} when access to restricted methods is enabled. doclet.RestrictedTrailingNote2=Restricted methods are unsafe, and, if used incorrectly, might crash \ the JVM or result in memory corruption. 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 5ae2d159072..47cf3e81ebe 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 @@ -163,6 +163,9 @@ public static DocPath indexN(int n) { /** The name of the file for restricted methods. */ public static final DocPath RESTRICTED_LIST = DocPath.create("restricted-list.html"); + /** The name of the doc-file for restricted methods. */ + public static final DocPath RESTRICTED_DOC = DocPath.create("RestrictedMethods.html"); + /** The name of the directory for the resource files. */ public static final DocPath RESOURCE_FILES = DocPath.create("resource-files"); From c9a536c330d37632139a1d71b0c054352eae5aa0 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 15 Oct 2024 09:15:40 +0000 Subject: [PATCH 51/95] 8337339: gc/arguments/Test*SizeFlags.java timing out with Xcomp Reviewed-by: shade, iwalulya --- test/hotspot/jtreg/gc/arguments/TestG1HeapSizeFlags.java | 1 + test/hotspot/jtreg/gc/arguments/TestParallelHeapSizeFlags.java | 1 + test/hotspot/jtreg/gc/arguments/TestSerialHeapSizeFlags.java | 1 + 3 files changed, 3 insertions(+) diff --git a/test/hotspot/jtreg/gc/arguments/TestG1HeapSizeFlags.java b/test/hotspot/jtreg/gc/arguments/TestG1HeapSizeFlags.java index 24e542881c0..4b69421e6e3 100644 --- a/test/hotspot/jtreg/gc/arguments/TestG1HeapSizeFlags.java +++ b/test/hotspot/jtreg/gc/arguments/TestG1HeapSizeFlags.java @@ -29,6 +29,7 @@ * @summary Tests argument processing for initial and maximum heap size for the G1 collector * @key flag-sensitive * @requires vm.gc.G1 & vm.opt.MinHeapSize == null & vm.opt.MaxHeapSize == null & vm.opt.InitialHeapSize == null + * @requires vm.compMode != "Xcomp" * @library /test/lib * @library / * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/gc/arguments/TestParallelHeapSizeFlags.java b/test/hotspot/jtreg/gc/arguments/TestParallelHeapSizeFlags.java index 544064953df..ca8d1e74895 100644 --- a/test/hotspot/jtreg/gc/arguments/TestParallelHeapSizeFlags.java +++ b/test/hotspot/jtreg/gc/arguments/TestParallelHeapSizeFlags.java @@ -30,6 +30,7 @@ * parallel collectors. * @key flag-sensitive * @requires vm.gc.Parallel & vm.opt.MinHeapSize == null & vm.opt.MaxHeapSize == null & vm.opt.InitialHeapSize == null + * @requires vm.compMode != "Xcomp" * @library /test/lib * @library / * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/gc/arguments/TestSerialHeapSizeFlags.java b/test/hotspot/jtreg/gc/arguments/TestSerialHeapSizeFlags.java index c580245a2a3..b9cc0f231f2 100644 --- a/test/hotspot/jtreg/gc/arguments/TestSerialHeapSizeFlags.java +++ b/test/hotspot/jtreg/gc/arguments/TestSerialHeapSizeFlags.java @@ -29,6 +29,7 @@ * @summary Tests argument processing for initial and maximum heap size for the Serial collector * @key flag-sensitive * @requires vm.gc.Serial & vm.opt.MinHeapSize == null & vm.opt.MaxHeapSize == null & vm.opt.InitialHeapSize == null + * @requires vm.compMode != "Xcomp" * @library /test/lib * @library / * @modules java.base/jdk.internal.misc From df7d6e081ff9513fbd6cff5d033a307e6798418b Mon Sep 17 00:00:00 2001 From: Kevin Walls Date: Tue, 15 Oct 2024 10:50:45 +0000 Subject: [PATCH 52/95] 8338603: DiagnosticCommandMBean operations should standardize types for parameters Reviewed-by: cjplummer, egahlin --- .../internal/DiagnosticCommandImpl.java | 20 +++++++- .../DiagnosticCommandMBean/DcmdMBeanTest.java | 46 ++++++++++++++++--- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandImpl.java b/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandImpl.java index be84beb03bb..eee0ea051d4 100644 --- a/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandImpl.java +++ b/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -317,7 +317,7 @@ private Descriptor commandDescriptor(Wrapper w) throws IllegalArgumentException for (DiagnosticCommandArgumentInfo arginfo : w.info.getArgumentsInfo()) { HashMap argmap = new HashMap<>(); argmap.put("dcmd.arg.name", arginfo.getName()); - argmap.put("dcmd.arg.type", arginfo.getType()); + argmap.put("dcmd.arg.type", sanitiseType(arginfo.getType())); argmap.put("dcmd.arg.description", arginfo.getDescription()); argmap.put("dcmd.arg.isMandatory", arginfo.isMandatory()); argmap.put("dcmd.arg.isMultiple", arginfo.isMultiple()); @@ -335,6 +335,22 @@ private Descriptor commandDescriptor(Wrapper w) throws IllegalArgumentException return new ImmutableDescriptor(map); } + // Type names that will be published in dcmd.arg.type: + private static final String [] publicTypes = new String [] { "INT", "STRING", "BOOLEAN", "STRING SET", "MEMORY SIZE", "NANOTIME" }; + + private static final String sanitiseType(String typeName) { + // For any typeName not in the set to be made public, return "STRING". + if (typeName == null) { + return null; + } + for (String t : publicTypes) { + if (typeName.equals(t)) { + return t; + } + } + return "STRING"; + } + private static final String notifName = "javax.management.Notification"; diff --git a/test/jdk/com/sun/management/DiagnosticCommandMBean/DcmdMBeanTest.java b/test/jdk/com/sun/management/DiagnosticCommandMBean/DcmdMBeanTest.java index 3cc9f40b895..ba6fffd0670 100644 --- a/test/jdk/com/sun/management/DiagnosticCommandMBean/DcmdMBeanTest.java +++ b/test/jdk/com/sun/management/DiagnosticCommandMBean/DcmdMBeanTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * 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 7150256 + * @bug 7150256 8338603 * @summary Basic Test for the DiagnosticCommandMBean * @author Frederic Parain, Shanliang JIANG * @@ -68,10 +68,14 @@ public static void main(String[] args) throws Exception { System.out.println("Description:" + info.getDescription()); MBeanOperationInfo[] opInfo = info.getOperations(); System.out.println("Operations:"); + int operationFailures = 0; for (int i = 0; i < opInfo.length; i++) { - printOperation(opInfo[i]); + operationFailures += printOperation(opInfo[i]); System.out.println("\n@@@@@@\n"); } + if (operationFailures > 0) { + throw new RuntimeException("FAILED. " + operationFailures + " operations found with non-standard parameter types."); + } } finally { try { cc.close(); @@ -83,7 +87,12 @@ public static void main(String[] args) throws Exception { System.out.println("Test passed"); } - static void printOperation(MBeanOperationInfo info) { + /** + * Print an Operation, and check for any non-standard parameter types. + * Return the number of failed parameters, so the caller can signal to fail the test. + */ + static int printOperation(MBeanOperationInfo info) { + int failures = 0; System.out.println("Name: "+info.getName()); System.out.println("Description: "+info.getDescription()); System.out.println("Return Type: "+info.getReturnType()); @@ -100,8 +109,16 @@ static void printOperation(MBeanOperationInfo info) { Descriptor desc3 = (Descriptor)desc2.getFieldValue(desc2.getFieldNames()[j]); for(int k=0; k Date: Mon, 5 Feb 2024 22:53:51 +0000 Subject: [PATCH 53/95] 8302111: Serialization considerations Reviewed-by: skoivu, rhalade, weijun, wetmore --- .../com/sun/crypto/provider/DHPrivateKey.java | 291 ++++++++++-------- .../com/sun/crypto/provider/DHPublicKey.java | 243 +++++++++------ .../provider/TlsMasterSecretGenerator.java | 38 +-- .../classes/java/security/Permissions.java | 7 +- .../classes/java/security/SignedObject.java | 54 ++-- .../classes/java/security/Timestamp.java | 14 +- .../UnresolvedPermissionCollection.java | 38 ++- .../cert/CertificateRevokedException.java | 23 +- .../javax/crypto/spec/SecretKeySpec.java | 30 +- .../auth/callback/ChoiceCallback.java | 59 ++-- .../auth/callback/ConfirmationCallback.java | 139 +++++---- .../auth/callback/PasswordCallback.java | 6 +- .../classes/sun/security/provider/DRBG.java | 12 +- .../sun/security/util/ObjectIdentifier.java | 18 +- .../classes/sun/security/x509/AlgIdDSA.java | 123 ++++---- .../sun/security/jgss/krb5/Krb5Context.java | 19 +- .../jgss/krb5/Krb5InitCredential.java | 17 +- .../sun/security/krb5/internal/KRBError.java | 44 +-- .../classes/sun/security/pkcs11/P11Key.java | 13 + .../sun/security/pkcs11/P11SecureRandom.java | 16 +- .../sun/security/pkcs11/SunPKCS11.java | 13 + 21 files changed, 748 insertions(+), 469 deletions(-) diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java b/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java index 0d5363e866a..fcdc3fbea49 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * 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,7 @@ final class DHPrivateKey implements PrivateKey, private final BigInteger x; // the key bytes, without the algorithm information + // cannot be final as it's re-assigned for deserialization private byte[] key; // the encoded key @@ -70,115 +71,85 @@ final class DHPrivateKey implements PrivateKey, // the private-value length (optional) private final int l; - /** - * Make a DH private key out of a private value x, a prime - * modulus p, and a base generator g. - * - * @param x the private value - * @param p the prime modulus - * @param g the base generator - */ - DHPrivateKey(BigInteger x, BigInteger p, BigInteger g) - throws InvalidKeyException { - this(x, p, g, 0); - } - - /** - * Make a DH private key out of a private value x, a prime - * modulus p, a base generator g, and a - * private-value length l. - * - * @param x the private value - * @param p the prime modulus - * @param g the base generator - * @param l the private-value length - */ - DHPrivateKey(BigInteger x, BigInteger p, BigInteger g, int l) { - this.x = x; - this.p = p; - this.g = g; - this.l = l; - byte[] xbytes = x.toByteArray(); - DerValue val = new DerValue(DerValue.tag_Integer, xbytes); - this.key = val.toByteArray(); - val.clear(); - Arrays.fill(xbytes, (byte) 0); - encode(); + private static class DHComponents { + final BigInteger x; + final BigInteger p; + final BigInteger g; + final int l; + final byte[] key; + + DHComponents(BigInteger x, BigInteger p, BigInteger g, int l, + byte[] key) { + this.x = x; + this.p = p; + this.g = g; + this.l = l; + this.key = key; + } } - /** - * Make a DH private key from its DER encoding (PKCS #8). - * - * @param encodedKey the encoded key - * - * @throws InvalidKeyException if the encoded key does not represent - * a Diffie-Hellman private key - */ - DHPrivateKey(byte[] encodedKey) throws InvalidKeyException { + // parses the specified encoding into a DHComponents object + private static DHComponents decode(byte[] encodedKey) + throws IOException { DerValue val = null; + try { val = new DerValue(encodedKey); if (val.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException ("Key not a SEQUENCE"); + throw new IOException("Key not a SEQUENCE"); } - // // version - // BigInteger parsedVersion = val.data.getBigInteger(); if (!parsedVersion.equals(PKCS8_VERSION)) { throw new IOException("version mismatch: (supported: " + - PKCS8_VERSION + ", parsed: " + - parsedVersion); + PKCS8_VERSION + ", parsed: " + parsedVersion); } - // // privateKeyAlgorithm - // DerValue algid = val.data.getDerValue(); if (algid.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("AlgId is not a SEQUENCE"); + throw new IOException("AlgId is not a SEQUENCE"); } DerInputStream derInStream = algid.toDerInputStream(); ObjectIdentifier oid = derInStream.getOID(); if (oid == null) { - throw new InvalidKeyException("Null OID"); + throw new IOException("Null OID"); } if (derInStream.available() == 0) { - throw new InvalidKeyException("Parameters missing"); + throw new IOException("Parameters missing"); } // parse the parameters DerValue params = derInStream.getDerValue(); if (params.tag == DerValue.tag_Null) { - throw new InvalidKeyException("Null parameters"); + throw new IOException("Null parameters"); } if (params.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("Parameters not a SEQUENCE"); + throw new IOException("Parameters not a SEQUENCE"); } params.data.reset(); - this.p = params.data.getBigInteger(); - this.g = params.data.getBigInteger(); + BigInteger p = params.data.getBigInteger(); + BigInteger g = params.data.getBigInteger(); // Private-value length is OPTIONAL + int l = (params.data.available() != 0 ? + params.data.getInteger() : 0); + // should have no trailing data if (params.data.available() != 0) { - this.l = params.data.getInteger(); - } else { - this.l = 0; - } - if (params.data.available() != 0) { - throw new InvalidKeyException("Extra parameter data"); + throw new IOException("Extra parameter data"); } - // // privateKey - // - this.key = val.data.getOctetString(); - - DerInputStream in = new DerInputStream(this.key); - this.x = in.getBigInteger(); + byte[] key = val.data.getOctetString(); + DerInputStream in = new DerInputStream(key); + BigInteger x = in.getBigInteger(); - this.encodedKey = encodedKey.clone(); - } catch (IOException | NumberFormatException e) { - throw new InvalidKeyException("Error parsing key encoding", e); + // should have no trailing data + if (val.data.available() != 0) { + throw new IOException("Excess trailing data"); + } + return new DHComponents(x, p, g, l, key); + } catch (NumberFormatException e) { + throw new IOException("Error parsing key encoding", e); } finally { if (val != null) { val.clear(); @@ -186,6 +157,108 @@ final class DHPrivateKey implements PrivateKey, } } + // Generates the ASN.1 encoding + private static byte[] encode(BigInteger p, BigInteger g, int l, + byte[] key) { + DerOutputStream tmp = new DerOutputStream(); + + // version + tmp.putInteger(PKCS8_VERSION); + + // privateKeyAlgorithm + DerOutputStream algid = new DerOutputStream(); + + // store OID + algid.putOID(DHPublicKey.DH_OID); + // encode parameters + DerOutputStream params = new DerOutputStream(); + params.putInteger(p); + params.putInteger(g); + if (l != 0) { + params.putInteger(l); + } + // wrap parameters into SEQUENCE + DerValue paramSequence = new DerValue(DerValue.tag_Sequence, + params.toByteArray()); + // store parameter SEQUENCE in algid + algid.putDerValue(paramSequence); + // wrap algid into SEQUENCE + tmp.write(DerValue.tag_Sequence, algid); + + // privateKey + tmp.putOctetString(key); + + // make it a SEQUENCE + DerValue val = DerValue.wrap(DerValue.tag_Sequence, tmp); + byte[] encoded = val.toByteArray(); + val.clear(); + + return encoded; + } + + /** + * Make a DH private key out of a private value x, a prime + * modulus p, and a base generator g. + * + * @param x the private value + * @param p the prime modulus + * @param g the base generator + */ + DHPrivateKey(BigInteger x, BigInteger p, BigInteger g) + throws InvalidKeyException { + this(x, p, g, 0); + } + + /** + * Make a DH private key out of a private value x, a prime + * modulus p, a base generator g, and a + * private-value length l. + * + * @param x the private value + * @param p the prime modulus + * @param g the base generator + * @param l the private-value length + */ + DHPrivateKey(BigInteger x, BigInteger p, BigInteger g, int l) { + this.x = x; + this.p = p; + this.g = g; + this.l = l; + + byte[] xbytes = x.toByteArray(); + DerValue val = new DerValue(DerValue.tag_Integer, xbytes); + try { + this.key = val.toByteArray(); + } finally { + val.clear(); + Arrays.fill(xbytes, (byte) 0); + } + this.encodedKey = encode(p, g, l, key); + } + + /** + * Make a DH private key from its DER encoding (PKCS #8). + * + * @param encodedKey the encoded key + * + * @throws InvalidKeyException if the encoded key does not represent + * a Diffie-Hellman private key + */ + DHPrivateKey(byte[] encodedKey) throws InvalidKeyException { + this.encodedKey = encodedKey.clone(); + DHComponents dc; + try { + dc = decode(this.encodedKey); + } catch (IOException e) { + throw new InvalidKeyException("Invalid encoding", e); + } + this.x = dc.x; + this.p = dc.p; + this.g = dc.g; + this.l = dc.l; + this.key = dc.key; + } + /** * Returns the encoding format of this key: "PKCS#8" */ @@ -204,55 +277,9 @@ public String getAlgorithm() { * Get the encoding of the key. */ public synchronized byte[] getEncoded() { - encode(); return encodedKey.clone(); } - /** - * Generate the encodedKey field if it has not been calculated. - * Could generate null. - */ - private void encode() { - if (this.encodedKey == null) { - DerOutputStream tmp = new DerOutputStream(); - - // - // version - // - tmp.putInteger(PKCS8_VERSION); - - // - // privateKeyAlgorithm - // - DerOutputStream algid = new DerOutputStream(); - - // store OID - algid.putOID(DHPublicKey.DH_OID); - // encode parameters - DerOutputStream params = new DerOutputStream(); - params.putInteger(this.p); - params.putInteger(this.g); - if (this.l != 0) { - params.putInteger(this.l); - } - // wrap parameters into SEQUENCE - DerValue paramSequence = new DerValue(DerValue.tag_Sequence, - params.toByteArray()); - // store parameter SEQUENCE in algid - algid.putDerValue(paramSequence); - // wrap algid into SEQUENCE - tmp.write(DerValue.tag_Sequence, algid); - - // privateKey - tmp.putOctetString(this.key); - - // make it a SEQUENCE - DerValue val = DerValue.wrap(DerValue.tag_Sequence, tmp); - this.encodedKey = val.toByteArray(); - val.clear(); - } - } - /** * Returns the private value, x. * @@ -307,10 +334,7 @@ public boolean equals(Object obj) { */ @java.io.Serial private Object writeReplace() throws java.io.ObjectStreamException { - encode(); - return new KeyRep(KeyRep.Type.PRIVATE, - getAlgorithm(), - getFormat(), + return new KeyRep(KeyRep.Type.PRIVATE, getAlgorithm(), getFormat(), encodedKey); } @@ -330,11 +354,28 @@ private void readObject(ObjectInputStream stream) if ((key == null) || (key.length == 0)) { throw new InvalidObjectException("key not deserializable"); } - this.key = key.clone(); if ((encodedKey == null) || (encodedKey.length == 0)) { throw new InvalidObjectException( "encoded key not deserializable"); } - this.encodedKey = encodedKey.clone(); + // check if the "encodedKey" value matches the deserialized fields + DHComponents c; + byte[] encodedKeyIntern = encodedKey.clone(); + try { + c = decode(encodedKeyIntern); + } catch (IOException e) { + throw new InvalidObjectException("Invalid encoding", e); + } + if (!Arrays.equals(c.key, key) || !c.x.equals(x) || !c.p.equals(p) + || !c.g.equals(g) || c.l != l) { + throw new InvalidObjectException( + "encoded key not matching internal fields"); + } + // zero out external arrays + Arrays.fill(key, (byte)0x00); + Arrays.fill(encodedKey, (byte)0x00); + // use self-created internal copies + this.key = c.key; + this.encodedKey = encodedKeyIntern; } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java b/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java index 47727c432a6..c95b40482d5 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * 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 @@ package com.sun.crypto.provider; import java.io.*; +import java.util.Arrays; import java.util.Objects; import java.math.BigInteger; import java.security.KeyRep; @@ -70,6 +71,116 @@ final class DHPublicKey implements PublicKey, static final ObjectIdentifier DH_OID = ObjectIdentifier.of(KnownOIDs.DiffieHellman); + private static class DHComponents { + final BigInteger y; + final BigInteger p; + final BigInteger g; + final int l; + final byte[] key; + + DHComponents(BigInteger y, BigInteger p, BigInteger g, int l, + byte[] key) { + this.y = y; + this.p = p; + this.g = g; + this.l = l; + this.key = key; + } + } + + // parses the specified encoding into a DHComponents object + private static DHComponents decode(byte[] encodedKey) + throws IOException { + DerValue val = null; + + try { + val = new DerValue(encodedKey); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid key format"); + } + + // algorithm identifier + DerValue algid = val.data.getDerValue(); + if (algid.tag != DerValue.tag_Sequence) { + throw new IOException("AlgId is not a SEQUENCE"); + } + DerInputStream derInStream = algid.toDerInputStream(); + ObjectIdentifier oid = derInStream.getOID(); + if (oid == null) { + throw new IOException("Null OID"); + } + if (derInStream.available() == 0) { + throw new IOException("Parameters missing"); + } + + // parse the parameters + DerValue params = derInStream.getDerValue(); + if (params.tag == DerValue.tag_Null) { + throw new IOException("Null parameters"); + } + if (params.tag != DerValue.tag_Sequence) { + throw new IOException("Parameters not a SEQUENCE"); + } + params.data.reset(); + + BigInteger p = params.data.getBigInteger(); + BigInteger g = params.data.getBigInteger(); + // Private-value length is OPTIONAL + int l = (params.data.available() != 0 ? params.data.getInteger() : + 0); + if (params.data.available() != 0) { + throw new IOException("Extra parameter data"); + } + + // publickey + byte[] key = val.data.getBitString(); + DerInputStream in = new DerInputStream(key); + BigInteger y = in.getBigInteger(); + + if (val.data.available() != 0) { + throw new IOException("Excess key data"); + } + return new DHComponents(y, p, g, l, key); + } catch (NumberFormatException e) { + throw new IOException("Error parsing key encoding", e); + } + } + + // generates the ASN.1 encoding + private static byte[] encode(BigInteger p, BigInteger g, int l, + byte[] key) { + DerOutputStream algid = new DerOutputStream(); + + // store oid in algid + algid.putOID(DH_OID); + + // encode parameters + DerOutputStream params = new DerOutputStream(); + params.putInteger(p); + params.putInteger(g); + if (l != 0) { + params.putInteger(l); + } + + // wrap parameters into SEQUENCE + DerValue paramSequence = new DerValue(DerValue.tag_Sequence, + params.toByteArray()); + // store parameter SEQUENCE in algid + algid.putDerValue(paramSequence); + + // wrap algid into SEQUENCE, and store it in key encoding + DerOutputStream tmpDerKey = new DerOutputStream(); + tmpDerKey.write(DerValue.tag_Sequence, algid); + + // store key data + tmpDerKey.putBitString(key); + + // wrap algid and key into SEQUENCE + DerOutputStream derKey = new DerOutputStream(); + derKey.write(DerValue.tag_Sequence, tmpDerKey); + return derKey.toByteArray(); + } + /** * Make a DH public key out of a public value y, a prime * modulus p, and a base generator g. @@ -102,7 +213,7 @@ final class DHPublicKey implements PublicKey, this.l = l; this.key = new DerValue(DerValue.tag_Integer, this.y.toByteArray()).toByteArray(); - this.encodedKey = getEncoded(); + this.encodedKey = encode(p, g, l, key); } /** @@ -114,68 +225,19 @@ final class DHPublicKey implements PublicKey, * a Diffie-Hellman public key */ DHPublicKey(byte[] encodedKey) throws InvalidKeyException { - InputStream inStream = new ByteArrayInputStream(encodedKey); - try { - DerValue derKeyVal = new DerValue(inStream); - if (derKeyVal.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException ("Invalid key format"); - } - - /* - * Parse the algorithm identifier - */ - DerValue algid = derKeyVal.data.getDerValue(); - if (algid.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("AlgId is not a SEQUENCE"); - } - DerInputStream derInStream = algid.toDerInputStream(); - ObjectIdentifier oid = derInStream.getOID(); - if (oid == null) { - throw new InvalidKeyException("Null OID"); - } - if (derInStream.available() == 0) { - throw new InvalidKeyException("Parameters missing"); - } - - /* - * Parse the parameters - */ - DerValue params = derInStream.getDerValue(); - if (params.tag == DerValue.tag_Null) { - throw new InvalidKeyException("Null parameters"); - } - if (params.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("Parameters not a SEQUENCE"); - } - params.data.reset(); - this.p = params.data.getBigInteger(); - this.g = params.data.getBigInteger(); - // Private-value length is OPTIONAL - if (params.data.available() != 0) { - this.l = params.data.getInteger(); - } else { - this.l = 0; - } - if (params.data.available() != 0) { - throw new InvalidKeyException("Extra parameter data"); - } - - /* - * Parse the key - */ - this.key = derKeyVal.data.getBitString(); - - DerInputStream in = new DerInputStream(this.key); - this.y = in.getBigInteger(); - - if (derKeyVal.data.available() != 0) { - throw new InvalidKeyException("Excess key data"); - } + this.encodedKey = encodedKey.clone(); - this.encodedKey = encodedKey.clone(); - } catch (IOException | NumberFormatException e) { - throw new InvalidKeyException("Error parsing key encoding", e); + DHComponents dc; + try { + dc = decode(this.encodedKey); + } catch (IOException e) { + throw new InvalidKeyException("Invalid encoding", e); } + this.y = dc.y; + this.p = dc.p; + this.g = dc.g; + this.l = dc.l; + this.key = dc.key; } /** @@ -196,37 +258,6 @@ public String getAlgorithm() { * Get the encoding of the key. */ public synchronized byte[] getEncoded() { - if (this.encodedKey == null) { - DerOutputStream algid = new DerOutputStream(); - - // store oid in algid - algid.putOID(DH_OID); - - // encode parameters - DerOutputStream params = new DerOutputStream(); - params.putInteger(this.p); - params.putInteger(this.g); - if (this.l != 0) { - params.putInteger(this.l); - } - // wrap parameters into SEQUENCE - DerValue paramSequence = new DerValue(DerValue.tag_Sequence, - params.toByteArray()); - // store parameter SEQUENCE in algid - algid.putDerValue(paramSequence); - - // wrap algid into SEQUENCE, and store it in key encoding - DerOutputStream tmpDerKey = new DerOutputStream(); - tmpDerKey.write(DerValue.tag_Sequence, algid); - - // store key data - tmpDerKey.putBitString(this.key); - - // wrap algid and key into SEQUENCE - DerOutputStream derKey = new DerOutputStream(); - derKey.write(DerValue.tag_Sequence, tmpDerKey); - this.encodedKey = derKey.toByteArray(); - } return this.encodedKey.clone(); } @@ -263,8 +294,9 @@ public String toString() { + Debug.toHexString(this.p) + LINE_SEP + "g:" + LINE_SEP + Debug.toHexString(this.g)); - if (this.l != 0) + if (this.l != 0) { sb.append(LINE_SEP + "l:" + LINE_SEP + " " + this.l); + } return sb.toString(); } @@ -304,7 +336,7 @@ private Object writeReplace() throws java.io.ObjectStreamException { return new KeyRep(KeyRep.Type.PUBLIC, getAlgorithm(), getFormat(), - getEncoded()); + encodedKey); } /** @@ -323,11 +355,28 @@ private void readObject(ObjectInputStream stream) if ((key == null) || (key.length == 0)) { throw new InvalidObjectException("key not deserializable"); } - this.key = key.clone(); if ((encodedKey == null) || (encodedKey.length == 0)) { throw new InvalidObjectException( "encoded key not deserializable"); } - this.encodedKey = encodedKey.clone(); + // check if the "encodedKey" value matches the deserialized fields + DHComponents c; + byte[] encodedKeyIntern = encodedKey.clone(); + try { + c = decode(encodedKeyIntern); + } catch (IOException e) { + throw new InvalidObjectException("Invalid encoding", e); + } + if (!Arrays.equals(c.key, key) || !c.y.equals(y) || !c.p.equals(p) + || !c.g.equals(g) || c.l != l) { + throw new InvalidObjectException( + "encoded key not matching internal fields"); + } + // zero out external arrays + Arrays.fill(key, (byte)0x00); + Arrays.fill(encodedKey, (byte)0x00); + // use self-created internal copies + this.key = c.key; + this.encodedKey = encodedKeyIntern; } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java b/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java index 14ada1699c1..2762fb3751c 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -194,22 +194,24 @@ public byte[] getEncoded() { return key.clone(); } - /** - * Restores the state of this object from the stream. - * - * @param stream the {@code ObjectInputStream} from which data is read - * @throws IOException if an I/O error occurs - * @throws ClassNotFoundException if a serialized class cannot be loaded - */ - @java.io.Serial - private void readObject(ObjectInputStream stream) - throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - if ((key == null) || (key.length == 0)) { - throw new InvalidObjectException("TlsMasterSecretKey is null"); - } - key = key.clone(); - } - } + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + if (key == null || key.length == 0) { + throw new InvalidObjectException("TlsMasterSecretKey is null"); + } + byte[] temp = key; + this.key = temp.clone(); + Arrays.fill(temp, (byte)0); + } + } } diff --git a/src/java.base/share/classes/java/security/Permissions.java b/src/java.base/share/classes/java/security/Permissions.java index 42c1adc9002..3bdeac6f929 100644 --- a/src/java.base/share/classes/java/security/Permissions.java +++ b/src/java.base/share/classes/java/security/Permissions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -407,6 +407,11 @@ private void readObject(ObjectInputStream in) throws IOException, @SuppressWarnings("unchecked") Hashtable, PermissionCollection> perms = (Hashtable, PermissionCollection>)gfields.get("perms", null); + + if (perms == null) { + throw new InvalidObjectException("perms can't be null"); + } + permsMap = new ConcurrentHashMap<>(perms.size()*2); permsMap.putAll(perms); diff --git a/src/java.base/share/classes/java/security/SignedObject.java b/src/java.base/share/classes/java/security/SignedObject.java index e2f9c764ec2..f65300fc808 100644 --- a/src/java.base/share/classes/java/security/SignedObject.java +++ b/src/java.base/share/classes/java/security/SignedObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -152,20 +152,20 @@ public final class SignedObject implements Serializable { */ public SignedObject(Serializable object, PrivateKey signingKey, Signature signingEngine) - throws IOException, InvalidKeyException, SignatureException { - // creating a stream pipe-line, from a to b - ByteArrayOutputStream b = new ByteArrayOutputStream(); - ObjectOutput a = new ObjectOutputStream(b); + throws IOException, InvalidKeyException, SignatureException { + // creating a stream pipe-line, from a to b + ByteArrayOutputStream b = new ByteArrayOutputStream(); + ObjectOutput a = new ObjectOutputStream(b); - // write and flush the object content to byte array - a.writeObject(object); - a.flush(); - a.close(); - this.content = b.toByteArray(); - b.close(); + // write and flush the object content to byte array + a.writeObject(object); + a.flush(); + a.close(); + this.content = b.toByteArray(); + b.close(); - // now sign the encapsulated object - this.sign(signingKey, signingEngine); + // now sign the encapsulated object + this.sign(signingKey, signingEngine); } /** @@ -245,12 +245,12 @@ public boolean verify(PublicKey verificationKey, * @throws SignatureException if signing fails. */ private void sign(PrivateKey signingKey, Signature signingEngine) - throws InvalidKeyException, SignatureException { - // initialize the signing engine - signingEngine.initSign(signingKey); - signingEngine.update(this.content.clone()); - this.signature = signingEngine.sign().clone(); - this.thealgorithm = signingEngine.getAlgorithm(); + throws InvalidKeyException, SignatureException { + // initialize the signing engine + signingEngine.initSign(signingKey); + signingEngine.update(this.content.clone()); + this.signature = signingEngine.sign(); + this.thealgorithm = signingEngine.getAlgorithm(); } /** @@ -263,10 +263,16 @@ private void sign(PrivateKey signingKey, Signature signingEngine) */ @Serial private void readObject(ObjectInputStream s) - throws IOException, ClassNotFoundException { - ObjectInputStream.GetField fields = s.readFields(); - content = ((byte[])fields.get("content", null)).clone(); - signature = ((byte[])fields.get("signature", null)).clone(); - thealgorithm = (String)fields.get("thealgorithm", null); + throws IOException, ClassNotFoundException { + ObjectInputStream.GetField fields = s.readFields(); + byte[] c = (byte[]) fields.get("content", null); + byte[] sig = (byte[]) fields.get("signature", null); + String a = (String) fields.get("thealgorithm", null); + if (c == null || sig == null || a == null) { + throw new InvalidObjectException("One or more null fields"); + } + content = c.clone(); + signature = sig.clone(); + thealgorithm = a; } } diff --git a/src/java.base/share/classes/java/security/Timestamp.java b/src/java.base/share/classes/java/security/Timestamp.java index 10a93a9b180..96df37a8c1f 100644 --- a/src/java.base/share/classes/java/security/Timestamp.java +++ b/src/java.base/share/classes/java/security/Timestamp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * 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.io.IOException; import java.io.ObjectInputStream; +import java.io.InvalidObjectException; import java.io.Serializable; import java.security.cert.CertPath; import java.security.cert.Certificate; @@ -78,7 +79,7 @@ public final class Timestamp implements Serializable { * {@code null}. */ public Timestamp(Date timestamp, CertPath signerCertPath) { - if (timestamp == null || signerCertPath == null) { + if (isNull(timestamp, signerCertPath)) { throw new NullPointerException(); } this.timestamp = new Date(timestamp.getTime()); // clone @@ -166,9 +167,16 @@ public String toString() { */ @java.io.Serial private void readObject(ObjectInputStream ois) - throws IOException, ClassNotFoundException { + throws IOException, ClassNotFoundException { ois.defaultReadObject(); + if (isNull(timestamp, signerCertPath)) { + throw new InvalidObjectException("Invalid null field(s)"); + } myhash = -1; timestamp = new Date(timestamp.getTime()); } + + private static boolean isNull(Date d, CertPath c) { + return (d == null || c == null); + } } diff --git a/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java b/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java index a5f4de22d89..c0bdf5fc2a1 100644 --- a/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java +++ b/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * 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.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; +import java.io.InvalidObjectException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; @@ -196,23 +197,32 @@ private void readObject(ObjectInputStream in) throws IOException, ObjectInputStream.GetField gfields = in.readFields(); // Get permissions - @SuppressWarnings("unchecked") // writeObject writes a Hashtable> // for the permissions key, so this cast is safe, unless the data is corrupt. - Hashtable> permissions = - (Hashtable>) - gfields.get("permissions", null); - perms = new ConcurrentHashMap<>(permissions.size()*2); + try { + @SuppressWarnings("unchecked") + Hashtable> permissions = + (Hashtable>) + gfields.get("permissions", null); + + if (permissions == null) { + throw new InvalidObjectException("Invalid null permissions"); + } - // Convert each entry (Vector) into a List - Set>> set = permissions.entrySet(); - for (Map.Entry> e : set) { - // Convert Vector into ArrayList - Vector vec = e.getValue(); - List list = new CopyOnWriteArrayList<>(vec); + perms = new ConcurrentHashMap<>(permissions.size()*2); - // Add to Hashtable being serialized - perms.put(e.getKey(), list); + // Convert each entry (Vector) into a List + Set>> set = permissions.entrySet(); + for (Map.Entry> e : set) { + // Convert Vector into ArrayList + Vector vec = e.getValue(); + List list = new CopyOnWriteArrayList<>(vec); + + // Add to Hashtable being serialized + perms.put(e.getKey(), list); + } + } catch (ClassCastException cce) { + throw new InvalidObjectException("Invalid type for permissions"); } } } diff --git a/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java b/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java index 70083033fc6..6649dcda6cc 100644 --- a/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java +++ b/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.IOException; +import java.io.InvalidObjectException; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -70,6 +71,13 @@ public class CertificateRevokedException extends CertificateException { private transient Map extensions; + private static boolean isNull(Date revocationDate, + CRLReason reason, X500Principal authority, + Map extensions) { + return (revocationDate == null || reason == null || authority == null + || extensions == null); + } + /** * Constructs a {@code CertificateRevokedException} with * the specified revocation date, reason code, authority name, and map @@ -92,8 +100,7 @@ public class CertificateRevokedException extends CertificateException { */ public CertificateRevokedException(Date revocationDate, CRLReason reason, X500Principal authority, Map extensions) { - if (revocationDate == null || reason == null || authority == null || - extensions == null) { + if (isNull(revocationDate, reason, authority, extensions)) { throw new NullPointerException(); } this.revocationDate = new Date(revocationDate.getTime()); @@ -234,9 +241,6 @@ private void readObject(ObjectInputStream ois) // (revocationDate, reason, authority) ois.defaultReadObject(); - // Defensively copy the revocation date - revocationDate = new Date(revocationDate.getTime()); - // Read in the size (number of mappings) of the extensions map // and create the extensions map int size = ois.readInt(); @@ -247,6 +251,13 @@ private void readObject(ObjectInputStream ois) } else { extensions = HashMap.newHashMap(Math.min(size, 20)); } + // make sure all fields are set before checking + if (isNull(revocationDate, reason, authority, extensions)) { + throw new InvalidObjectException("Invalid null field(s)"); + } + + // Defensively copy the revocation date + revocationDate = new Date(revocationDate.getTime()); // Read in the extensions and put the mappings in the extensions map for (int i = 0; i < size; i++) { diff --git a/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java b/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java index e76a51e5d68..2ad9a7748f2 100644 --- a/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java +++ b/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * 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,11 +100,9 @@ public class SecretKeySpec implements KeySpec, SecretKey { * is null or key is null or empty. */ public SecretKeySpec(byte[] key, String algorithm) { - if (key == null || algorithm == null) { - throw new IllegalArgumentException("Missing argument"); - } - if (key.length == 0) { - throw new IllegalArgumentException("Empty key"); + String errMsg = doSanityCheck(key, algorithm); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); } this.key = key.clone(); this.algorithm = algorithm; @@ -266,14 +264,22 @@ void clear() { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - - if (key == null || algorithm == null) { - throw new InvalidObjectException("Missing argument"); + String errMsg = doSanityCheck(key, algorithm); + if (errMsg != null) { + throw new InvalidObjectException(errMsg); } + byte[] temp = key; + this.key = temp.clone(); + Arrays.fill(temp, (byte) 0); + } - this.key = key.clone(); - if (key.length == 0) { - throw new InvalidObjectException("Invalid key length"); + private static String doSanityCheck(byte[] key, String algorithm) { + String errMsg = null; + if (key == null || algorithm == null) { + errMsg = "Missing argument"; + } else if (key.length == 0) { + errMsg = "Empty key"; } + return errMsg; } } diff --git a/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java b/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java index c005b4ea02b..1c35491e4e2 100644 --- a/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java +++ b/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -102,20 +102,18 @@ public class ChoiceCallback implements Callback, java.io.Serializable { public ChoiceCallback(String prompt, String[] choices, int defaultChoice, boolean multipleSelectionsAllowed) { - if (prompt == null || prompt.isEmpty() || - choices == null || choices.length == 0 || - defaultChoice < 0 || defaultChoice >= choices.length) - throw new IllegalArgumentException(); - + choices = (choices == null || choices.length == 0 ? choices : + choices.clone()); + String errMsg = doSanityCheck(prompt, choices, defaultChoice, + multipleSelectionsAllowed); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); + } this.prompt = prompt; this.defaultChoice = defaultChoice; this.multipleSelectionsAllowed = multipleSelectionsAllowed; - this.choices = choices.clone(); - for (int i = 0; i < choices.length; i++) { - if (choices[i] == null || choices[i].isEmpty()) - throw new IllegalArgumentException(); - } + this.choices = choices; } /** @@ -183,9 +181,11 @@ public void setSelectedIndex(int selection) { * @see #getSelectedIndexes */ public void setSelectedIndexes(int[] selections) { - if (!multipleSelectionsAllowed) + if (!multipleSelectionsAllowed) { throw new UnsupportedOperationException(); - this.selections = selections == null ? null : selections.clone(); + } + this.selections = ((selections == null || selections.length == 0) ? + selections : selections.clone()); } /** @@ -211,26 +211,35 @@ public int[] getSelectedIndexes() { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); + choices = (choices == null || choices.length == 0 ? + choices : choices.clone()); + String errMsg = doSanityCheck(prompt, choices, defaultChoice, + multipleSelectionsAllowed); + if (errMsg != null) { + throw new InvalidObjectException(errMsg); + } + selections = (selections == null || selections.length == 0 ? + selections : selections.clone()); + if (selections != null && selections.length > 1 && + !multipleSelectionsAllowed) { + throw new InvalidObjectException("Multiple selections not allowed"); + } + } + + private static String doSanityCheck(String prompt, String[] choices, + int defaultChoice, boolean allowMultiple) { if ((prompt == null) || prompt.isEmpty() || (choices == null) || (choices.length == 0) || (defaultChoice < 0) || (defaultChoice >= choices.length)) { - throw new InvalidObjectException( - "Missing/invalid prompt/choices"); + return "Missing/invalid prompt/choices"; } - choices = choices.clone(); for (int i = 0; i < choices.length; i++) { - if ((choices[i] == null) || choices[i].isEmpty()) - throw new InvalidObjectException("Null/empty choices"); - } - - if (selections != null) { - selections = selections.clone(); - if (!multipleSelectionsAllowed && (selections.length != 1)) { - throw new InvalidObjectException( - "Multiple selections not allowed"); + if ((choices[i] == null) || choices[i].isEmpty()) { + return "Null/empty choices value"; } } + return null; } } diff --git a/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java b/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java index 437ce7041a7..a00fc7013ec 100644 --- a/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java +++ b/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * 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 @@ package javax.security.auth.callback; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectInputStream; /** @@ -189,25 +190,10 @@ public class ConfirmationCallback implements Callback, java.io.Serializable { */ public ConfirmationCallback(int messageType, int optionType, int defaultOption) { - - if (messageType < INFORMATION || messageType > ERROR || - optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION) - throw new IllegalArgumentException(); - - switch (optionType) { - case YES_NO_OPTION: - if (defaultOption != YES && defaultOption != NO) - throw new IllegalArgumentException(); - break; - case YES_NO_CANCEL_OPTION: - if (defaultOption != YES && defaultOption != NO && - defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; - case OK_CANCEL_OPTION: - if (defaultOption != OK && defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; + String errMsg = doSanityCheck(messageType, optionType, false, null, + defaultOption, null, false); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); } this.prompt = null; @@ -250,21 +236,20 @@ public ConfirmationCallback(int messageType, public ConfirmationCallback(int messageType, String[] options, int defaultOption) { - if (messageType < INFORMATION || messageType > ERROR || - options == null || options.length == 0 || - defaultOption < 0 || defaultOption >= options.length) - throw new IllegalArgumentException(); + if (options != null) { + options = options.clone(); + } + String errMsg = doSanityCheck(messageType, UNSPECIFIED_OPTION, true, + options, defaultOption, null, false); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); + } this.prompt = null; this.messageType = messageType; this.optionType = UNSPECIFIED_OPTION; this.defaultOption = defaultOption; - - this.options = options.clone(); - for (int i = 0; i < options.length; i++) { - if (options[i] == null || options[i].isEmpty()) - throw new IllegalArgumentException(); - } + this.options = options; } /** @@ -304,27 +289,11 @@ public ConfirmationCallback(int messageType, public ConfirmationCallback(String prompt, int messageType, int optionType, int defaultOption) { - if (prompt == null || prompt.isEmpty() || - messageType < INFORMATION || messageType > ERROR || - optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION) - throw new IllegalArgumentException(); - - switch (optionType) { - case YES_NO_OPTION: - if (defaultOption != YES && defaultOption != NO) - throw new IllegalArgumentException(); - break; - case YES_NO_CANCEL_OPTION: - if (defaultOption != YES && defaultOption != NO && - defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; - case OK_CANCEL_OPTION: - if (defaultOption != OK && defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; + String errMsg = doSanityCheck(messageType, optionType, false, null, + defaultOption, prompt, true); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); } - this.prompt = prompt; this.messageType = messageType; this.optionType = optionType; @@ -369,22 +338,20 @@ public ConfirmationCallback(String prompt, int messageType, public ConfirmationCallback(String prompt, int messageType, String[] options, int defaultOption) { - if (prompt == null || prompt.isEmpty() || - messageType < INFORMATION || messageType > ERROR || - options == null || options.length == 0 || - defaultOption < 0 || defaultOption >= options.length) - throw new IllegalArgumentException(); + if (options != null) { + options = options.clone(); + } + String errMsg = doSanityCheck(messageType, UNSPECIFIED_OPTION, true, + options, defaultOption, prompt, true); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); + } this.prompt = prompt; this.messageType = messageType; this.optionType = UNSPECIFIED_OPTION; this.defaultOption = defaultOption; - - this.options = options.clone(); - for (int i = 0; i < options.length; i++) { - if (options[i] == null || options[i].isEmpty()) - throw new IllegalArgumentException(); - } + this.options = options; } /** @@ -491,6 +458,49 @@ public int getSelectedIndex() { return selection; } + private static String doSanityCheck(int msgType, int optionType, + boolean isUnspecifiedOption, String[] options, int defOption, + String prompt, boolean checkPrompt) { + // validate msgType + if (msgType < INFORMATION || msgType > ERROR) { + return "Invalid msgType"; + } + // validate prompt if checkPrompt == true + if (checkPrompt && (prompt == null || prompt.isEmpty())) { + return "Invalid prompt"; + } + // validate optionType + if (isUnspecifiedOption) { + if (optionType != UNSPECIFIED_OPTION) { + return "Invalid optionType"; + } + // check options + if (options == null || options.length == 0 || + defOption < 0 || defOption >= options.length) { + return "Invalid options and/or default option"; + } + for (String ov : options) { + if (ov == null || ov.isEmpty()) { + return "Invalid option value"; + } + } + } else { + if (optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION) { + return "Invalid optionType"; + } + // validate defOption based on optionType + if ((optionType == YES_NO_OPTION && (defOption != YES && + defOption != NO)) || + (optionType == YES_NO_CANCEL_OPTION && (defOption != YES && + defOption != NO && defOption != CANCEL)) || + (optionType == OK_CANCEL_OPTION && (defOption != OK && + defOption != CANCEL))) { + return "Invalid default option"; + } + } + return null; + } + /** * Restores the state of this object from the stream. * @@ -502,8 +512,15 @@ public int getSelectedIndex() { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); + if (options != null) { options = options.clone(); } + String errMsg = doSanityCheck(messageType, optionType, + (optionType == UNSPECIFIED_OPTION), options, defaultOption, + prompt, false); + if (errMsg != null) { + throw new InvalidObjectException(errMsg); + } } } diff --git a/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java b/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java index bbe7ab882a6..2bee38ceaaa 100644 --- a/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java +++ b/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -178,7 +178,9 @@ private void readObject(ObjectInputStream stream) } if (inputPassword != null) { - inputPassword = inputPassword.clone(); + char[] temp = inputPassword; + inputPassword = temp.clone(); + Arrays.fill(temp, '0'); cleanable = CleanerFactory.cleaner().register( this, cleanerFor(inputPassword)); } diff --git a/src/java.base/share/classes/sun/security/provider/DRBG.java b/src/java.base/share/classes/sun/security/provider/DRBG.java index 923c8c3aa54..01958285e43 100644 --- a/src/java.base/share/classes/sun/security/provider/DRBG.java +++ b/src/java.base/share/classes/sun/security/provider/DRBG.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * 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 @@ package sun.security.provider; import java.io.IOException; +import java.io.InvalidObjectException; import java.security.AccessController; import java.security.DrbgParameters; import java.security.PrivilegedAction; @@ -272,11 +273,18 @@ private static void checkTwice(boolean flag, String name) { } } + /** + * Restores the state of this object from the stream. + * + * @param s the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ @java.io.Serial private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); - if (mdp.mech == null) { + if (mdp == null || mdp.mech == null) { throw new IllegalArgumentException("Input data is corrupted"); } createImpl(); diff --git a/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java b/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java index 6181bf223e9..306d34d7149 100644 --- a/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java +++ b/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * 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,13 +127,24 @@ public final class ObjectIdentifier implements Serializable { // Is the component's field calculated? private transient boolean componentsCalculated = false; + /** + * Restores the state of this object from the stream. + * + * @param is the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ @java.io.Serial private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { is.defaultReadObject(); if (encoding == null) { // from an old version - int[] comp = (int[])components; + if (components == null) { + throw new InvalidObjectException("OID components is null"); + } + + int[] comp = ((int[]) components).clone(); if (componentLen > comp.length) { componentLen = comp.length; } @@ -142,7 +153,9 @@ private void readObject(ObjectInputStream is) // will be performed again in init(). checkOidSize(componentLen); init(comp, componentLen); + components = comp; } else { + encoding = encoding.clone(); // defensive copying checkOidSize(encoding.length); check(encoding); } @@ -261,6 +274,7 @@ public ObjectIdentifier(DerInputStream in) throws IOException { encoding = in.getDerValue().getOID().encoding; } + // set 'encoding' field based on the specified 'components' and 'length' private void init(int[] components, int length) throws IOException { int pos = 0; byte[] tmp = new byte[length * 5 + 1]; // +1 for empty input diff --git a/src/java.base/share/classes/sun/security/x509/AlgIdDSA.java b/src/java.base/share/classes/sun/security/x509/AlgIdDSA.java index 3a3d384a0e8..764d77e6da8 100644 --- a/src/java.base/share/classes/sun/security/x509/AlgIdDSA.java +++ b/src/java.base/share/classes/sun/security/x509/AlgIdDSA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * 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,13 @@ package sun.security.x509; +import java.io.ObjectInputStream; import java.io.IOException; +import java.io.InvalidObjectException; import java.math.BigInteger; import java.security.*; import java.security.interfaces.DSAParams; - +import java.util.Arrays; import sun.security.util.*; @@ -72,33 +74,42 @@ * * @author David Brownell */ -public final -class AlgIdDSA extends AlgorithmId implements DSAParams -{ +public final class AlgIdDSA extends AlgorithmId implements DSAParams { @java.io.Serial private static final long serialVersionUID = 3437177836797504046L; + private static class DSAComponents { + private final BigInteger p; + private final BigInteger q; + private final BigInteger g; + DSAComponents(BigInteger p, BigInteger q, BigInteger g) { + this.p = p; + this.q = q; + this.g = g; + } + } + /* * The three unsigned integer parameters. */ - private BigInteger p , q, g; + private BigInteger p, q, g; /** Returns the DSS/DSA parameter "P" */ - public BigInteger getP () { return p; } + public BigInteger getP() { return p; } /** Returns the DSS/DSA parameter "Q" */ - public BigInteger getQ () { return q; } + public BigInteger getQ() { return q; } /** Returns the DSS/DSA parameter "G" */ - public BigInteger getG () { return g; } + public BigInteger getG() { return g; } /** * Default constructor. The OID and parameters must be * deserialized before this algorithm ID is used. */ @Deprecated - public AlgIdDSA () {} + public AlgIdDSA() {} /** * Constructs a DSS/DSA Algorithm ID from numeric parameters. @@ -109,7 +120,7 @@ public AlgIdDSA () {} * @param q the DSS/DSA parameter "Q" * @param g the DSS/DSA parameter "G" */ - public AlgIdDSA (BigInteger p, BigInteger q, BigInteger g) { + public AlgIdDSA(BigInteger p, BigInteger q, BigInteger g) { super (DSA_oid); if (p != null || q != null || g != null) { @@ -120,8 +131,10 @@ public AlgIdDSA (BigInteger p, BigInteger q, BigInteger g) { this.p = p; this.q = q; this.g = g; - initializeParams (); - + // For algorithm IDs which haven't been created from a DER + // encoded value, need to create DER encoding and store it + // into "encodedParams" + encodedParams = encode(p, q, g); } catch (IOException e) { /* this should not happen */ throw new ProviderException ("Construct DSS/DSA Algorithm ID"); @@ -133,50 +146,10 @@ public AlgIdDSA (BigInteger p, BigInteger q, BigInteger g) { * Returns "DSA", indicating the Digital Signature Algorithm (DSA) as * defined by the Digital Signature Standard (DSS), FIPS 186. */ - public String getName () - { return "DSA"; } - - - /* - * For algorithm IDs which haven't been created from a DER encoded - * value, "params" must be created. - */ - private void initializeParams () throws IOException { - DerOutputStream out = new DerOutputStream(); - out.putInteger(p); - out.putInteger(q); - out.putInteger(g); - DerOutputStream result = new DerOutputStream(); - result.write(DerValue.tag_Sequence, out); - encodedParams = result.toByteArray(); - } - - /** - * Parses algorithm parameters P, Q, and G. They're found - * in the "params" member, which never needs to be changed. - */ - protected void decodeParams () throws IOException { - if (encodedParams == null) { - throw new IOException("DSA alg params are null"); - } - - DerValue params = new DerValue(encodedParams); - if (params.tag != DerValue.tag_Sequence) { - throw new IOException("DSA alg parsing error"); - } - - params.data.reset (); - - this.p = params.data.getBigInteger(); - this.q = params.data.getBigInteger(); - this.g = params.data.getBigInteger(); - - if (params.data.available () != 0) - throw new IOException ("AlgIdDSA params, extra="+ - params.data.available ()); + public String getName() { + return "DSA"; } - /* * Returns a formatted string describing the parameters. */ @@ -197,4 +170,44 @@ protected String paramsToString () { "\n"; } } + + /** + * Restores the state of this object from the stream. Override to check + * on the 'p', 'q', 'g', and 'encodedParams'. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) throws IOException { + try { + stream.defaultReadObject(); + // if any of the 'p', 'q', 'g', 'encodedParams' is non-null, + // then they must be all non-null w/ matching encoding + if ((p != null || q != null || g != null || encodedParams != null) + && !Arrays.equals(encodedParams, encode(p, q, g))) { + throw new InvalidObjectException("Invalid DSA alg params"); + } + } catch (ClassNotFoundException e) { + throw new IOException(e); + } + } + + /* + * Create the DER encoding w/ the specified 'p', 'q', 'g' + */ + private static byte[] encode(BigInteger p, BigInteger q, + BigInteger g) throws IOException { + if (p == null || q == null || g == null) { + throw new InvalidObjectException("invalid null value"); + } + DerOutputStream out = new DerOutputStream(); + out.putInteger(p); + out.putInteger(q); + out.putInteger(g); + DerOutputStream result = new DerOutputStream(); + result.write(DerValue.tag_Sequence, out); + return result.toByteArray(); + } } 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 fa596fc6a1e..cb9b8746994 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 @@ -33,8 +33,10 @@ import sun.security.jgss.TokenTracker; import sun.security.krb5.*; import java.io.InputStream; -import java.io.OutputStream; +import java.io.InvalidObjectException; import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.OutputStream; import java.security.*; import javax.security.auth.Subject; import javax.security.auth.kerberos.ServicePermission; @@ -1408,6 +1410,20 @@ public String toString() { return "Kerberos session key: etype: " + key.getEType() + "\n" + new HexDumpEncoder().encodeBuffer(key.getBytes()); } + + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException + ("KerberosSessionKey not directly deserializable"); + } } /** @@ -1477,5 +1493,4 @@ public void setAuthTime(String authTime) { public void setAuthzData(AuthorizationData authzData) { this.authzData = authzData; } - } 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 1b109cc881f..4cc306282e6 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * 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,9 @@ import javax.security.auth.kerberos.KerberosPrincipal; import java.io.Serial; import java.net.InetAddress; +import java.io.InvalidObjectException; import java.io.IOException; +import java.io.ObjectInputStream; import java.util.Date; import java.security.AccessController; import java.security.PrivilegedExceptionAction; @@ -400,4 +402,17 @@ public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException { throw ge; } } + + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.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/internal/KRBError.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java index b93ced00c65..db6192ce9ee 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 @@ -83,28 +83,36 @@ * * http://www.ietf.org/rfc/rfc4120.txt. */ -// The instance fields not statically typed as Serializable are ASN.1 -// encoded and written by the writeObject method. -@SuppressWarnings("serial") + public class KRBError implements java.io.Serializable { static final long serialVersionUID = 3643809337475284503L; - private int pvno; - private int msgType; - private KerberosTime cTime; //optional - private Integer cuSec; //optional - private KerberosTime sTime; - private Integer suSec; - private int errorCode; - private Realm crealm; //optional - private PrincipalName cname; //optional - private PrincipalName sname; - private String eText; //optional - private byte[] eData; //optional - private Checksum eCksum; //optional - - private PAData[] pa; // PA-DATA in eData + private transient int pvno; + private transient int msgType; + private transient KerberosTime cTime; //optional + private transient Integer cuSec; //optional + private transient KerberosTime sTime; + private transient Integer suSec; + private transient int errorCode; + private transient Realm crealm; //optional + private transient PrincipalName cname; //optional + private transient PrincipalName sname; + private transient String eText; //optional + private transient byte[] eData; //optional + private transient Checksum eCksum; //optional + + private transient PAData[] pa; // PA-DATA in eData + + + /** + * Restores the state of this object from the stream. + * + * @param is the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { try { diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java index d528f1b8485..49718e254b3 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java @@ -240,6 +240,19 @@ protected Object writeReplace() throws ObjectStreamException { return new KeyRep(type, getAlgorithm(), format, getEncodedInternal()); } + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException("P11Key not directly deserializable"); + } + public String toString() { token.ensureValid(); String s1 = token.provider.getName() + " " + algorithm + " " + type diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java index 70effc141bc..7ef8510ddee 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -187,9 +187,23 @@ private void implNextBytes(byte[] bytes) { } } + /** + * Restores the state of this object from the stream. + * + * @param in the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); + if (token == null) { + throw new InvalidObjectException("token is null"); + } + if (mixBuffer != null) { + mixBuffer = mixBuffer.clone(); + } // assign default values to non-null transient fields iBuffer = new byte[IBUFFER_SIZE]; ibuffered = 0; diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java index 44a2c4efb06..e77edc98399 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java @@ -1947,6 +1947,19 @@ private Object writeReplace() throws ObjectStreamException { return new SunPKCS11Rep(this); } + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException("SunPKCS11 not directly deserializable"); + } + /** * Serialized representation of the SunPKCS11 provider. */ From 893e7bc894e2828f8d02db06302485d02f2cfa5a Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Wed, 17 Apr 2024 22:38:46 +0000 Subject: [PATCH 54/95] 8328726: Better Kerberos support Reviewed-by: ahgross, rhalade, valeriep, coffeys --- .../security/auth/kerberos/EncryptionKey.java | 4 ++-- .../auth/kerberos/KerberosCredMessage.java | 6 ++--- .../security/auth/kerberos/KerberosKey.java | 8 +++---- .../javax/security/auth/kerberos/KeyImpl.java | 16 +++++--------- .../sun/security/jgss/krb5/Krb5Context.java | 22 +++++-------------- .../sun/security/jgss/krb5/Krb5Util.java | 15 +++++++++++++ .../sun/security/krb5/EncryptionKey.java | 8 ++----- .../sun/security/krb5/internal/Krb5.java | 3 --- .../security/krb5/internal/tools/Kinit.java | 4 ---- .../pkcs11/wrapper/CK_PBE_PARAMS.java | 5 ----- .../security/auth/module/Krb5LoginModule.java | 10 +++------ 11 files changed, 39 insertions(+), 62 deletions(-) diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java index 5a6f74e130c..6140168ebbb 100644 --- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java +++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -170,7 +170,7 @@ public String toString() { if (destroyed) { return "Destroyed EncryptionKey"; } - return "key " + key.toString(); + return "EncryptionKey: " + key.toString(); } /** diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java index c6f3f083a66..c331833e9f0 100644 --- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java +++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ import javax.security.auth.Destroyable; import java.util.Arrays; -import java.util.Base64; import java.util.Objects; /** @@ -140,8 +139,7 @@ public String toString() { if (destroyed) { return "Destroyed KerberosCredMessage"; } else { - return "KRB_CRED from " + sender + " to " + recipient + ":\n" - + Base64.getUrlEncoder().encodeToString(message); + return "KRB_CRED from " + sender + " to " + recipient; } } diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java index 55c1be3c0d8..71aaddda9de 100644 --- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java +++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -273,9 +273,9 @@ public String toString() { if (destroyed) { return "Destroyed KerberosKey"; } - return "Kerberos Principal " + principal + - "Key Version " + versionNum + - "key " + key.toString(); + return "KerberosKey: principal " + principal + + ", version " + versionNum + + ", key " + key.toString(); } /** diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.java index 46168cf8377..b18f7d8eae1 100644 --- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.java +++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * 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,8 @@ import javax.crypto.SecretKey; import javax.security.auth.Destroyable; import javax.security.auth.DestroyFailedException; -import sun.security.util.HexDumpEncoder; + +import sun.security.jgss.krb5.Krb5Util; import sun.security.krb5.Asn1Exception; import sun.security.krb5.PrincipalName; import sun.security.krb5.EncryptionKey; @@ -225,15 +226,8 @@ private void readObject(ObjectInputStream ois) } public String toString() { - HexDumpEncoder hd = new HexDumpEncoder(); - return "EncryptionKey: keyType=" + keyType - + " keyBytes (hex dump)=" - + (keyBytes == null || keyBytes.length == 0 ? - " Empty Key" : - '\n' + hd.encodeBuffer(keyBytes) - + '\n'); - - + return "keyType=" + keyType + + ", " + Krb5Util.keyInfo(keyBytes); } public int hashCode() { 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 cb9b8746994..92b694efb86 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 @@ -901,15 +901,11 @@ public final int getWrapSizeLimit(int qop, boolean confReq, public final byte[] wrap(byte[] inBuf, int offset, int len, MessageProp msgProp) throws GSSException { - if (DEBUG != null) { - DEBUG.println("Krb5Context.wrap: data=[" - + getHexBytes(inBuf, offset, len) - + "]"); - } - if (state != STATE_DONE) - throw new GSSException(GSSException.NO_CONTEXT, -1, - "Wrap called in invalid state!"); + if (state != STATE_DONE) { + throw new GSSException(GSSException.NO_CONTEXT, -1, + "Wrap called in invalid state!"); + } byte[] encToken = null; try { @@ -1052,12 +1048,6 @@ public final byte[] unwrap(byte[] inBuf, int offset, int len, setSequencingAndReplayProps(token, msgProp); } - if (DEBUG != null) { - DEBUG.println("Krb5Context.unwrap: data=[" - + getHexBytes(data, 0, data.length) - + "]"); - } - return data; } @@ -1407,8 +1397,8 @@ public byte[] getEncoded() { @Override public String toString() { - return "Kerberos session key: etype: " + key.getEType() + "\n" + - new HexDumpEncoder().encodeBuffer(key.getBytes()); + return "Kerberos session key: etype=" + key.getEType() + + ", " + Krb5Util.keyInfo(key.getBytes()); } /** diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java index 10dea6749e5..e784b7b33ca 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java @@ -187,4 +187,19 @@ public static EncryptionKey[] keysFromJavaxKeyTab( KeyTab ktab, PrincipalName cname) { return snapshotFromJavaxKeyTab(ktab).readServiceKeys(cname); } + + public static String keyInfo(byte[] data) { + if (data == null) { + return "null key"; + } else if (data.length == 0) { + return "empty key"; + } else { + for (byte b : data) { + if (b != 0) { + return data.length + "-byte key"; + } + } + return data.length + "-byte zero key"; + } + } } diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java b/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java index f975ba15a67..b5453fae916 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java @@ -31,6 +31,7 @@ package sun.security.krb5; +import sun.security.jgss.krb5.Krb5Util; import sun.security.util.*; import sun.security.krb5.internal.*; import sun.security.krb5.internal.crypto.*; @@ -498,12 +499,7 @@ public synchronized void writeKey(CCacheOutputStream cos) public String toString() { return "EncryptionKey: keyType=" + keyType - + " kvno=" + kvno - + " keyValue (hex dump)=" - + (keyValue == null || keyValue.length == 0 ? - " Empty Key" : '\n' - + Krb5.hexDumper.encodeBuffer(keyValue) - + '\n'); + + ", kvno=" + kvno + ", " + Krb5Util.keyInfo(keyValue); } /** diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java index c3cac113c40..0850abb53c8 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java @@ -320,9 +320,6 @@ public static String getErrorMessage(int i) { public static final Debug DEBUG = Debug.of("krb5", GetPropertyAction .privilegedGetProperty("sun.security.krb5.debug")); - public static final sun.security.util.HexDumpEncoder hexDumper = - new sun.security.util.HexDumpEncoder(); - static { errMsgList = new Hashtable (); errMsgList.put(KDC_ERR_NONE, "No error"); diff --git a/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Kinit.java b/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Kinit.java index 9d24de5c2e6..6b9f5de0c3d 100644 --- a/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Kinit.java +++ b/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Kinit.java @@ -195,10 +195,6 @@ private void acquire() System.out.print("Password for " + princName + ":"); System.out.flush(); psswd = Password.readPassword(System.in); - if (DEBUG != null) { - DEBUG.println(">>> Kinit console input " + - new String(psswd)); - } } builder = new KrbAsReqBuilder(principal, psswd); } else { diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java index a25fa1c39e5..d6c291ebc57 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java @@ -127,11 +127,6 @@ public String toString() { sb.append(pPassword.length); sb.append(Constants.NEWLINE); - sb.append(Constants.INDENT); - sb.append("pPassword: "); - sb.append(pPassword); - sb.append(Constants.NEWLINE); - sb.append(Constants.INDENT); sb.append("ulSaltLen: "); sb.append(pSalt.length); diff --git a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java index 24f26d4364b..18016a07260 100644 --- a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java +++ b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java @@ -43,7 +43,7 @@ import sun.security.jgss.krb5.Krb5Util; import sun.security.krb5.Credentials; import sun.security.util.Debug; -import sun.security.util.HexDumpEncoder; + import static sun.security.util.ResourcesMgr.getAuthResourceString; /** @@ -769,15 +769,11 @@ private void attemptAuthentication(boolean getPasswdFromSharedState) if (debug != null) { debug.println("principal is " + principal); - HexDumpEncoder hd = new HexDumpEncoder(); if (ktab != null) { debug.println("Will use keytab"); } else if (storeKey) { for (int i = 0; i < encKeys.length; i++) { - debug.println("EncryptionKey: keyType=" + - encKeys[i].getEType() + - " keyBytes (hex dump)=" + - hd.encodeBuffer(encKeys[i].getBytes())); + debug.println(encKeys[i].toString()); } } } @@ -868,7 +864,7 @@ private void promptForPass(boolean getPasswdFromSharedState) } if (debug != null) { debug.println - ("password is " + new String(password)); + ("Get password from shared state"); } return; } From 03bc6b359fc5ff4fa2b569723903cdf1c9c5775b Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Tue, 7 May 2024 19:29:49 +0000 Subject: [PATCH 55/95] 8328286: Enhance HTTP client Reviewed-by: aefimov, michaelm --- .../java/net/doc-files/net-properties.html | 9 + .../classes/sun/net/www/MessageHeader.java | 55 ++++ .../www/protocol/http/HttpURLConnection.java | 23 +- src/java.base/share/conf/net.properties | 17 + .../jdk/internal/net/http/Exchange.java | 45 ++- .../internal/net/http/Http1HeaderParser.java | 49 ++- .../internal/net/http/Http2ClientImpl.java | 78 +++-- .../internal/net/http/Http2Connection.java | 292 ++++++++++++++---- .../jdk/internal/net/http/HttpClientImpl.java | 4 +- .../internal/net/http/HttpRequestImpl.java | 4 +- .../net/http/ResponseBodyHandlers.java | 12 +- .../classes/jdk/internal/net/http/Stream.java | 118 +++++-- .../net/http/common/HeaderDecoder.java | 6 +- .../jdk/internal/net/http/common/Utils.java | 13 + .../jdk/internal/net/http/hpack/Decoder.java | 80 ++++- .../net/http/hpack/DecodingCallback.java | 14 + .../jdk/internal/net/http/hpack/Encoder.java | 14 +- .../share/classes/module-info.java | 30 +- .../share/classes/module-info.java | 12 +- .../classes/sun/net/httpserver/Request.java | 35 ++- .../sun/net/httpserver/ServerConfig.java | 18 +- .../net/httpclient/ExpectContinueTest.java | 3 +- test/jdk/java/net/httpclient/ShutdownNow.java | 1 + .../http2/PushPromiseContinuation.java | 9 +- .../test/lib/common/HttpServerAdapters.java | 12 +- .../test/lib/http2/HpackTestEncoder.java | 174 +++++++++++ .../test/lib/http2/Http2TestExchange.java | 13 + .../test/lib/http2/Http2TestExchangeImpl.java | 16 +- .../lib/http2/Http2TestServerConnection.java | 153 +++++++-- 29 files changed, 1125 insertions(+), 184 deletions(-) create mode 100644 test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/HpackTestEncoder.java diff --git a/src/java.base/share/classes/java/net/doc-files/net-properties.html b/src/java.base/share/classes/java/net/doc-files/net-properties.html index a61844cca6e..a67df0c0d00 100644 --- a/src/java.base/share/classes/java/net/doc-files/net-properties.html +++ b/src/java.base/share/classes/java/net/doc-files/net-properties.html @@ -253,6 +253,15 @@

Misc HTTP URL stream protocol handler properties

The channel binding tokens generated are of the type "tls-server-end-point" as defined in RFC 5929.

+ +
  • {@systemProperty jdk.http.maxHeaderSize} (default: 393216 or 384kB)
    + This is the maximum header field section size that a client is prepared to accept. + This is computed as the sum of the size of the uncompressed header name, plus + the size of the uncompressed header value, plus an overhead of 32 bytes for + each field section line. If a peer sends a field section that exceeds this + size a {@link java.net.ProtocolException ProtocolException} will be raised. + This applies to all versions of the HTTP protocol. A value of zero or a negative + value means no limit. If left unspecified, the default value is 393216 bytes.

    All these properties are checked only once at startup.

    diff --git a/src/java.base/share/classes/sun/net/www/MessageHeader.java b/src/java.base/share/classes/sun/net/www/MessageHeader.java index 6af23e43ad2..5095507d968 100644 --- a/src/java.base/share/classes/sun/net/www/MessageHeader.java +++ b/src/java.base/share/classes/sun/net/www/MessageHeader.java @@ -30,6 +30,8 @@ package sun.net.www; import java.io.*; +import java.lang.reflect.Array; +import java.net.ProtocolException; import java.util.Collections; import java.util.*; @@ -45,11 +47,32 @@ public final class MessageHeader { private String[] values; private int nkeys; + // max number of bytes for headers, <=0 means unlimited; + // this corresponds to the length of the names, plus the length + // of the values, plus an overhead of 32 bytes per name: value + // pair. + // Note: we use the same definition as HTTP/2 SETTINGS_MAX_HEADER_LIST_SIZE + // see RFC 9113, section 6.5.2. + // https://www.rfc-editor.org/rfc/rfc9113.html#SETTINGS_MAX_HEADER_LIST_SIZE + private final int maxHeaderSize; + + // Aggregate size of the field lines (name + value + 32) x N + // that have been parsed and accepted so far. + // This is defined as a long to force promotion to long + // and avoid overflows; see checkNewSize; + private long size; + public MessageHeader () { + this(0); + } + + public MessageHeader (int maxHeaderSize) { + this.maxHeaderSize = maxHeaderSize; grow(); } public MessageHeader (InputStream is) throws java.io.IOException { + maxHeaderSize = 0; parseHeader(is); } @@ -476,10 +499,28 @@ public static String canonicalID(String id) { public void parseHeader(InputStream is) throws java.io.IOException { synchronized (this) { nkeys = 0; + size = 0; } mergeHeader(is); } + private void checkMaxHeaderSize(int sz) throws ProtocolException { + if (maxHeaderSize > 0) checkNewSize(size, sz, 0); + } + + private long checkNewSize(long size, int name, int value) throws ProtocolException { + // See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2. + long newSize = size + name + value + 32; + if (maxHeaderSize > 0 && newSize > maxHeaderSize) { + Arrays.fill(keys, 0, nkeys, null); + Arrays.fill(values,0, nkeys, null); + nkeys = 0; + throw new ProtocolException(String.format("Header size too big: %s > %s", + newSize, maxHeaderSize)); + } + return newSize; + } + /** Parse and merge a MIME header from an input stream. */ @SuppressWarnings("fallthrough") public void mergeHeader(InputStream is) throws java.io.IOException { @@ -493,7 +534,15 @@ public void mergeHeader(InputStream is) throws java.io.IOException { int c; boolean inKey = firstc > ' '; s[len++] = (char) firstc; + checkMaxHeaderSize(len); parseloop:{ + // We start parsing for a new name value pair here. + // The max header size includes an overhead of 32 bytes per + // name value pair. + // See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2. + long maxRemaining = maxHeaderSize > 0 + ? maxHeaderSize - size - 32 + : Long.MAX_VALUE; while ((c = is.read()) >= 0) { switch (c) { case ':': @@ -527,6 +576,9 @@ public void mergeHeader(InputStream is) throws java.io.IOException { s = ns; } s[len++] = (char) c; + if (maxHeaderSize > 0 && len > maxRemaining) { + checkMaxHeaderSize(len); + } } firstc = -1; } @@ -548,6 +600,9 @@ public void mergeHeader(InputStream is) throws java.io.IOException { v = new String(); else v = String.copyValueOf(s, keyend, len - keyend); + int klen = k == null ? 0 : k.length(); + + size = checkNewSize(size, klen, v.length()); add(k, v); } } diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index f47261f4491..83511853502 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -172,6 +172,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection { */ private static final int bufSize4ES; + private static final int maxHeaderSize; + /* * Restrict setting of request headers through the public api * consistent with JavaScript XMLHttpRequest2 with a few @@ -288,6 +290,19 @@ private static Set schemesListToSet(String list) { } else { restrictedHeaderSet = null; } + + int defMaxHeaderSize = 384 * 1024; + String maxHeaderSizeStr = getNetProperty("jdk.http.maxHeaderSize"); + int maxHeaderSizeVal = defMaxHeaderSize; + if (maxHeaderSizeStr != null) { + try { + maxHeaderSizeVal = Integer.parseInt(maxHeaderSizeStr); + } catch (NumberFormatException n) { + maxHeaderSizeVal = defMaxHeaderSize; + } + } + if (maxHeaderSizeVal < 0) maxHeaderSizeVal = 0; + maxHeaderSize = maxHeaderSizeVal; } static final String httpVersion = "HTTP/1.1"; @@ -754,7 +769,7 @@ private void writeRequests() throws IOException { } ps = (PrintStream) http.getOutputStream(); connected=true; - responses = new MessageHeader(); + responses = new MessageHeader(maxHeaderSize); setRequests=false; writeRequests(); } @@ -912,7 +927,7 @@ protected HttpURLConnection(URL u, Proxy p, Handler handler) throws IOException { super(checkURL(u)); requests = new MessageHeader(); - responses = new MessageHeader(); + responses = new MessageHeader(maxHeaderSize); userHeaders = new MessageHeader(); this.handler = handler; instProxy = p; @@ -2810,7 +2825,7 @@ private boolean followRedirect0(String loc, int stat, URL locUrl) } // clear out old response headers!!!! - responses = new MessageHeader(); + responses = new MessageHeader(maxHeaderSize); if (stat == HTTP_USE_PROXY) { /* This means we must re-request the resource through the * proxy denoted in the "Location:" field of the response. @@ -3000,7 +3015,7 @@ private void reset() throws IOException { } catch (IOException e) { } } responseCode = -1; - responses = new MessageHeader(); + responses = new MessageHeader(maxHeaderSize); connected = false; } diff --git a/src/java.base/share/conf/net.properties b/src/java.base/share/conf/net.properties index 67f294355a1..2aa9a9630be 100644 --- a/src/java.base/share/conf/net.properties +++ b/src/java.base/share/conf/net.properties @@ -130,3 +130,20 @@ jdk.http.auth.tunneling.disabledSchemes=Basic #jdk.http.ntlm.transparentAuth=trustedHosts # jdk.http.ntlm.transparentAuth=disabled + +# +# Maximum HTTP field section size that a client is prepared to accept +# +# jdk.http.maxHeaderSize=393216 +# +# This is the maximum header field section size that a client is prepared to accept. +# This is computed as the sum of the size of the uncompressed header name, plus +# the size of the uncompressed header value, plus an overhead of 32 bytes for +# each field section line. If a peer sends a field section that exceeds this +# size a {@link java.net.ProtocolException ProtocolException} will be raised. +# This applies to all versions of the HTTP protocol. A value of zero or a negative +# value means no limit. If left unspecified, the default value is 393216 bytes +# or 384kB. +# +# Note: This property is currently used by the JDK Reference implementation. It +# is not guaranteed to be examined and used by other implementations. diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java b/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java index eb30dc85e9c..1ff1e5f4733 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java @@ -41,6 +41,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.net.http.HttpClient; import java.net.http.HttpHeaders; @@ -69,6 +70,8 @@ */ final class Exchange { + static final int MAX_NON_FINAL_RESPONSES = + Utils.getIntegerNetProperty("jdk.httpclient.maxNonFinalResponses", 8); final Logger debug = Utils.getDebugLogger(this::dbgString, Utils.DEBUG); final HttpRequestImpl request; @@ -93,6 +96,8 @@ final class Exchange { // exchange so that it can be aborted/timed out mid setup. final ConnectionAborter connectionAborter = new ConnectionAborter(); + final AtomicInteger nonFinalResponses = new AtomicInteger(); + Exchange(HttpRequestImpl request, MultiExchange multi) { this.request = request; this.upgrading = false; @@ -359,7 +364,7 @@ CompletableFuture checkCancelled(CompletableFuture cf, HttpConnection public void h2Upgrade() { upgrading = true; - request.setH2Upgrade(client.client2()); + request.setH2Upgrade(this); } synchronized IOException getCancelCause() { @@ -482,9 +487,9 @@ private CompletableFuture expectContinue(ExchangeImpl ex) { Log.logResponse(r1::toString); int rcode = r1.statusCode(); if (rcode == 100) { + nonFinalResponses.incrementAndGet(); Log.logTrace("Received 100-Continue: sending body"); - if (debug.on()) - debug.log("Received 100-Continue for %s", r1); + if (debug.on()) debug.log("Received 100-Continue for %s", r1); CompletableFuture cf = exchImpl.sendBodyAsync() .thenCompose(exIm -> exIm.getResponseAsync(parentExecutor)); @@ -492,9 +497,9 @@ private CompletableFuture expectContinue(ExchangeImpl ex) { cf = wrapForLog(cf); return cf; } else { - Log.logTrace("Expectation failed: Received {0}", rcode); - if (debug.on()) - debug.log("Expect-Continue failed (%d) for: %s", rcode, r1); + Log.logTrace("Expectation failed: Received {0}", + rcode); + if (debug.on()) debug.log("Expect-Continue failed (%d) for: %s", rcode, r1); if (upgrading && rcode == 101) { IOException failed = new IOException( "Unable to handle 101 while waiting for 100"); @@ -559,12 +564,20 @@ private CompletableFuture ignore1xxResponse(final Response rsp) { + rsp.statusCode()); } assert exchImpl != null : "Illegal state - current exchange isn't set"; - // ignore this Response and wait again for the subsequent response headers - final CompletableFuture cf = exchImpl.getResponseAsync(parentExecutor); - // we recompose the CF again into the ignore1xxResponse check/function because - // the 1xx response is allowed to be sent multiple times for a request, before - // a final response arrives - return cf.thenCompose(this::ignore1xxResponse); + int count = nonFinalResponses.incrementAndGet(); + if (MAX_NON_FINAL_RESPONSES > 0 && (count < 0 || count > MAX_NON_FINAL_RESPONSES)) { + return MinimalFuture.failedFuture( + new ProtocolException(String.format( + "Too many interim responses received: %s > %s", + count, MAX_NON_FINAL_RESPONSES))); + } else { + // ignore this Response and wait again for the subsequent response headers + final CompletableFuture cf = exchImpl.getResponseAsync(parentExecutor); + // we recompose the CF again into the ignore1xxResponse check/function because + // the 1xx response is allowed to be sent multiple times for a request, before + // a final response arrives + return cf.thenCompose(this::ignore1xxResponse); + } } else { // return the already completed future return MinimalFuture.completedFuture(rsp); @@ -829,6 +842,14 @@ HttpClient.Version version() { return multi.version(); } + boolean pushEnabled() { + return pushGroup != null; + } + + String h2cSettingsStrings() { + return client.client2().getSettingsString(pushEnabled()); + } + String dbgString() { return dbgTag; } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Http1HeaderParser.java b/src/java.net.http/share/classes/jdk/internal/net/http/Http1HeaderParser.java index 669c173e3f8..8c796193015 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http1HeaderParser.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http1HeaderParser.java @@ -25,6 +25,7 @@ package jdk.internal.net.http; +import java.io.IOException; import java.net.ProtocolException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; @@ -53,6 +54,12 @@ class Http1HeaderParser { private int responseCode; private HttpHeaders headers; private Map> privateMap = new HashMap<>(); + private long size; + + private static final int K = 1024; + private static final int MAX_HTTP_HEADER_SIZE = Utils.getIntegerNetProperty( + "jdk.http.maxHeaderSize", + Integer.MIN_VALUE, Integer.MAX_VALUE, 384 * K, true); enum State { INITIAL, STATUS_LINE, @@ -164,11 +171,16 @@ private char get(ByteBuffer input) { return (char)(input.get() & 0xFF); } - private void readResumeStatusLine(ByteBuffer input) { + private void readResumeStatusLine(ByteBuffer input) throws ProtocolException { + final long max = MAX_HTTP_HEADER_SIZE - size - 32 - sb.length(); + int count = 0; char c = 0; while (input.hasRemaining() && (c = get(input)) != CR) { if (c == LF) break; sb.append(c); + if (++count > max) { + checkMaxHeaderSize(sb.length()); + } } if (c == CR) { state = State.STATUS_LINE_FOUND_CR; @@ -185,6 +197,7 @@ private void readStatusLineFeed(ByteBuffer input) throws ProtocolException { } statusLine = sb.toString(); + size = size + 32 + statusLine.length(); sb = new StringBuilder(); if (!statusLine.startsWith("HTTP/1.")) { throw protocolException("Invalid status line: \"%s\"", statusLine); @@ -205,7 +218,23 @@ private void readStatusLineFeed(ByteBuffer input) throws ProtocolException { state = State.STATUS_LINE_END; } - private void maybeStartHeaders(ByteBuffer input) { + private void checkMaxHeaderSize(int sz) throws ProtocolException { + long s = size + sz + 32; + if (MAX_HTTP_HEADER_SIZE > 0 && s > MAX_HTTP_HEADER_SIZE) { + throw new ProtocolException(String.format("Header size too big: %s > %s", + s, MAX_HTTP_HEADER_SIZE)); + } + } + static private long newSize(long size, int name, int value) throws ProtocolException { + long newSize = size + name + value + 32; + if (MAX_HTTP_HEADER_SIZE > 0 && newSize > MAX_HTTP_HEADER_SIZE) { + throw new ProtocolException(String.format("Header size too big: %s > %s", + newSize, MAX_HTTP_HEADER_SIZE)); + } + return newSize; + } + + private void maybeStartHeaders(ByteBuffer input) throws ProtocolException { assert state == State.STATUS_LINE_END; assert sb.length() == 0; char c = get(input); @@ -215,6 +244,7 @@ private void maybeStartHeaders(ByteBuffer input) { state = State.STATUS_LINE_END_LF; } else { sb.append(c); + checkMaxHeaderSize(sb.length()); state = State.HEADER; } } @@ -232,9 +262,11 @@ private void maybeEndHeaders(ByteBuffer input) throws ProtocolException { } } - private void readResumeHeader(ByteBuffer input) { + private void readResumeHeader(ByteBuffer input) throws ProtocolException { assert state == State.HEADER; assert input.hasRemaining(); + final long max = MAX_HTTP_HEADER_SIZE - size - 32 - sb.length(); + int count = 0; while (input.hasRemaining()) { char c = get(input); if (c == CR) { @@ -248,6 +280,9 @@ private void readResumeHeader(ByteBuffer input) { if (c == HT) c = SP; sb.append(c); + if (++count > max) { + checkMaxHeaderSize(sb.length()); + } } } @@ -268,12 +303,12 @@ private void addHeaderFromString(String headerString) throws ProtocolException { if (!Utils.isValidValue(value)) { throw protocolException("Invalid header value \"%s: %s\"", name, value); } - + size = newSize(size, name.length(), value.length()); privateMap.computeIfAbsent(name.toLowerCase(Locale.US), k -> new ArrayList<>()).add(value); } - private void resumeOrLF(ByteBuffer input) { + private void resumeOrLF(ByteBuffer input) throws ProtocolException { assert state == State.HEADER_FOUND_CR || state == State.HEADER_FOUND_LF; char c = state == State.HEADER_FOUND_LF ? LF : get(input); if (c == LF) { @@ -283,10 +318,12 @@ private void resumeOrLF(ByteBuffer input) { state = State.HEADER_FOUND_CR_LF; } else if (c == SP || c == HT) { sb.append(SP); // parity with MessageHeaders + checkMaxHeaderSize(sb.length()); state = State.HEADER; } else { sb = new StringBuilder(); sb.append(c); + checkMaxHeaderSize(1); state = State.HEADER; } } @@ -312,6 +349,7 @@ private void resumeOrSecondCR(ByteBuffer input) throws ProtocolException { } else if (c == SP || c == HT) { assert sb.length() != 0; sb.append(SP); // continuation line + checkMaxHeaderSize(sb.length()); state = State.HEADER; } else { if (sb.length() > 0) { @@ -322,6 +360,7 @@ private void resumeOrSecondCR(ByteBuffer input) throws ProtocolException { addHeaderFromString(headerString); } sb.append(c); + checkMaxHeaderSize(sb.length()); state = State.HEADER; } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java index 50338f94c1d..022442b5371 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * 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,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.locks.ReentrantLock; -import jdk.internal.net.http.common.Log; import jdk.internal.net.http.common.Logger; import jdk.internal.net.http.common.MinimalFuture; import jdk.internal.net.http.common.Utils; @@ -46,6 +45,7 @@ import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE; import static jdk.internal.net.http.frame.SettingsFrame.MAX_CONCURRENT_STREAMS; import static jdk.internal.net.http.frame.SettingsFrame.MAX_FRAME_SIZE; +import static jdk.internal.net.http.frame.SettingsFrame.MAX_HEADER_LIST_SIZE; /** * Http2 specific aspects of HttpClientImpl @@ -98,16 +98,20 @@ class Http2ClientImpl { CompletableFuture getConnectionFor(HttpRequestImpl req, Exchange exchange) { String key = Http2Connection.keyFor(req); + boolean pushEnabled = exchange.pushEnabled(); connectionPoolLock.lock(); try { Http2Connection connection = connections.get(key); if (connection != null) { try { - if (!connection.tryReserveForPoolCheckout() || !connection.reserveStream(true)) { + if (!connection.tryReserveForPoolCheckout() + || !connection.reserveStream(true, pushEnabled)) { if (debug.on()) debug.log("removing connection from pool since it couldn't be" + - " reserved for use: %s", connection); + " reserved for use%s: %s", + pushEnabled ? " with server push enabled" : "", + connection); removeFromPool(connection); } else { // fast path if connection already exists @@ -137,7 +141,7 @@ CompletableFuture getConnectionFor(HttpRequestImpl req, try { if (conn != null) { try { - conn.reserveStream(true); + conn.reserveStream(true, exchange.pushEnabled()); } catch (IOException e) { throw new UncheckedIOException(e); // shouldn't happen } @@ -183,10 +187,21 @@ boolean offerConnection(Http2Connection c) { } Http2Connection c1 = connections.putIfAbsent(key, c); if (c1 != null) { - c.setFinalStream(); - if (debug.on()) - debug.log("existing entry in connection pool for %s", key); - return false; + if (c.serverPushEnabled() && !c1.serverPushEnabled()) { + c1.setFinalStream(); + connections.remove(key, c1); + connections.put(key, c); + if (debug.on()) { + debug.log("Replacing %s with %s in connection pool", c1, c); + } + if (c1.shouldClose()) c1.close(); + return true; + } else { + c.setFinalStream(); + if (debug.on()) + debug.log("existing entry in connection pool for %s", key); + return false; + } } if (debug.on()) debug.log("put in the connection pool: %s", c); @@ -250,8 +265,8 @@ HttpClientImpl client() { } /** Returns the client settings as a base64 (url) encoded string */ - String getSettingsString() { - SettingsFrame sf = getClientSettings(); + String getSettingsString(boolean defaultServerPush) { + SettingsFrame sf = getClientSettings(defaultServerPush); byte[] settings = sf.toByteArray(); // without the header Base64.Encoder encoder = Base64.getUrlEncoder() .withoutPadding(); @@ -261,14 +276,7 @@ String getSettingsString() { private static final int K = 1024; private static int getParameter(String property, int min, int max, int defaultValue) { - int value = Utils.getIntegerNetProperty(property, defaultValue); - // use default value if misconfigured - if (value < min || value > max) { - Log.logError("Property value for {0}={1} not in [{2}..{3}]: " + - "using default={4}", property, value, min, max, defaultValue); - value = defaultValue; - } - return value; + return Utils.getIntegerNetProperty(property, min, max, defaultValue, true); } // used for the connection window, to have a connection window size @@ -288,7 +296,18 @@ int getConnectionWindowSize(SettingsFrame clientSettings) { streamWindow, Integer.MAX_VALUE, defaultValue); } - SettingsFrame getClientSettings() { + /** + * This method is used to test whether pushes are globally + * disabled on all connections. + * @return true if pushes are globally disabled on all connections + */ + boolean serverPushDisabled() { + return getParameter( + "jdk.httpclient.enablepush", + 0, 1, 1) == 0; + } + + SettingsFrame getClientSettings(boolean defaultServerPush) { SettingsFrame frame = new SettingsFrame(); // default defined for HTTP/2 is 4 K, we use 16 K. frame.setParameter(HEADER_TABLE_SIZE, getParameter( @@ -297,14 +316,15 @@ SettingsFrame getClientSettings() { // O: does not accept push streams. 1: accepts push streams. frame.setParameter(ENABLE_PUSH, getParameter( "jdk.httpclient.enablepush", - 0, 1, 1)); + 0, 1, defaultServerPush ? 1 : 0)); // HTTP/2 recommends to set the number of concurrent streams - // no lower than 100. We use 100. 0 means no stream would be - // accepted. That would render the client to be non functional, - // so we won't let 0 be configured for our Http2ClientImpl. + // no lower than 100. We use 100, unless push promises are + // disabled. + int initialServerStreams = frame.getParameter(ENABLE_PUSH) == 0 + ? 0 : 100; frame.setParameter(MAX_CONCURRENT_STREAMS, getParameter( "jdk.httpclient.maxstreams", - 1, Integer.MAX_VALUE, 100)); + 0, Integer.MAX_VALUE, initialServerStreams)); // Maximum size is 2^31-1. Don't allow window size to be less // than the minimum frame size as this is likely to be a // configuration error. HTTP/2 specify a default of 64 * K -1, @@ -317,6 +337,14 @@ SettingsFrame getClientSettings() { frame.setParameter(MAX_FRAME_SIZE, getParameter( "jdk.httpclient.maxframesize", 16 * K, 16 * K * K -1, 16 * K)); + // Maximum field section size we're prepared to accept + // This is the uncompressed name + value size + 32 per field line + int maxHeaderSize = getParameter( + "jdk.http.maxHeaderSize", + Integer.MIN_VALUE, Integer.MAX_VALUE, 384 * K); + // If the property is <= 0 the value is unlimited + if (maxHeaderSize <= 0) maxHeaderSize = -1; + frame.setParameter(MAX_HEADER_LIST_SIZE, maxHeaderSize); return frame; } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java b/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java index 9457ff69988..080905222c3 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java @@ -31,6 +31,7 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.net.InetSocketAddress; +import java.net.ProtocolException; import java.net.http.HttpClient; import java.net.http.HttpHeaders; import java.nio.ByteBuffer; @@ -49,6 +50,7 @@ import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; @@ -88,10 +90,12 @@ import jdk.internal.net.http.hpack.Encoder; import static java.nio.charset.StandardCharsets.UTF_8; import static jdk.internal.net.http.frame.SettingsFrame.DEFAULT_INITIAL_WINDOW_SIZE; +import static jdk.internal.net.http.frame.SettingsFrame.ENABLE_PUSH; import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE; import static jdk.internal.net.http.frame.SettingsFrame.INITIAL_WINDOW_SIZE; import static jdk.internal.net.http.frame.SettingsFrame.MAX_CONCURRENT_STREAMS; import static jdk.internal.net.http.frame.SettingsFrame.MAX_FRAME_SIZE; +import static jdk.internal.net.http.frame.SettingsFrame.MAX_HEADER_LIST_SIZE; /** * An Http2Connection. Encapsulates the socket(channel) and any SSLEngine used @@ -327,6 +331,45 @@ void markPrefaceSent() { } } + private final class PushPromiseDecoder extends HeaderDecoder implements DecodingCallback { + + final int parentStreamId; + final int pushPromiseStreamId; + final Stream parent; + final AtomicReference errorRef = new AtomicReference<>(); + + PushPromiseDecoder(int parentStreamId, int pushPromiseStreamId, Stream parent) { + this.parentStreamId = parentStreamId; + this.pushPromiseStreamId = pushPromiseStreamId; + this.parent = parent; + } + + @Override + protected void addHeader(String name, String value) { + if (errorRef.get() == null) { + super.addHeader(name, value); + } + } + + @Override + public void onMaxHeaderListSizeReached(long size, int maxHeaderListSize) throws ProtocolException { + try { + DecodingCallback.super.onMaxHeaderListSizeReached(size, maxHeaderListSize); + } catch (ProtocolException pe) { + if (parent != null) { + if (errorRef.compareAndSet(null, pe)) { + // cancel the parent stream + resetStream(pushPromiseStreamId, ResetFrame.REFUSED_STREAM); + parent.onProtocolError(pe); + } + } else { + // interrupt decoding and closes the connection + throw pe; + } + } + } + } + private static final int HALF_CLOSED_LOCAL = 1; private static final int HALF_CLOSED_REMOTE = 2; @@ -355,7 +398,7 @@ void markPrefaceSent() { private final Decoder hpackIn; final SettingsFrame clientSettings; private volatile SettingsFrame serverSettings; - private record PushContinuationState(HeaderDecoder pushContDecoder, PushPromiseFrame pushContFrame) {} + private record PushContinuationState(PushPromiseDecoder pushContDecoder, PushPromiseFrame pushContFrame) {} private volatile PushContinuationState pushContinuationState; private final String key; // for HttpClientImpl.connections map private final FramesDecoder framesDecoder; @@ -370,12 +413,24 @@ private record PushContinuationState(HeaderDecoder pushContDecoder, PushPromiseF private final FramesController framesController = new FramesController(); private final Http2TubeSubscriber subscriber; final ConnectionWindowUpdateSender windowUpdater; - private volatile Throwable cause; + private final AtomicReference cause = new AtomicReference<>(); private volatile Supplier initial; private volatile Stream initialStream; + private ValidatingHeadersConsumer orphanedConsumer; + private final AtomicInteger orphanedHeaders = new AtomicInteger(); + static final int DEFAULT_FRAME_SIZE = 16 * 1024; + static final int MAX_LITERAL_WITH_INDEXING = + Utils.getIntegerNetProperty("jdk.httpclient.maxLiteralWithIndexing",512); + // The maximum number of HEADER frames, CONTINUATION frames, or PUSH_PROMISE frames + // referring to an already closed or non-existent stream that a client will accept to + // process. Receiving frames referring to non-existent or closed streams doesn't necessarily + // constitute an HTTP/2 protocol error, but receiving too many may indicate a problem + // with the connection. If this limit is reached, a {@link java.net.ProtocolException + // ProtocolException} will be raised and the connection will be closed. + static final int MAX_ORPHANED_HEADERS = 1024; // TODO: need list of control frames from other threads // that need to be sent @@ -383,19 +438,21 @@ private record PushContinuationState(HeaderDecoder pushContDecoder, PushPromiseF private Http2Connection(HttpConnection connection, Http2ClientImpl client2, int nextstreamid, - String key) { + String key, + boolean defaultServerPush) { this.connection = connection; this.client2 = client2; this.subscriber = new Http2TubeSubscriber(client2.client()); this.nextstreamid = nextstreamid; this.key = key; - this.clientSettings = this.client2.getClientSettings(); + this.clientSettings = this.client2.getClientSettings(defaultServerPush); this.framesDecoder = new FramesDecoder(this::processFrame, clientSettings.getParameter(SettingsFrame.MAX_FRAME_SIZE)); // serverSettings will be updated by server this.serverSettings = SettingsFrame.defaultRFCSettings(); this.hpackOut = new Encoder(serverSettings.getParameter(HEADER_TABLE_SIZE)); - this.hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE)); + this.hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE), + clientSettings.getParameter(MAX_HEADER_LIST_SIZE), MAX_LITERAL_WITH_INDEXING); if (debugHpack.on()) { debugHpack.log("For the record:" + super.toString()); debugHpack.log("Decoder created: %s", hpackIn); @@ -414,14 +471,16 @@ private Http2Connection(HttpConnection connection, private Http2Connection(HttpConnection connection, Http2ClientImpl client2, Exchange exchange, - Supplier initial) + Supplier initial, + boolean defaultServerPush) throws IOException, InterruptedException { this(connection, client2, 3, // stream 1 is registered during the upgrade - keyFor(connection)); - reserveStream(true); + keyFor(connection), + defaultServerPush); + reserveStream(true, clientSettings.getFlag(ENABLE_PUSH)); Log.logTrace("Connection send window size {0} ", windowController.connectionWindowSize()); Stream initialStream = createStream(exchange); @@ -454,7 +513,8 @@ static CompletableFuture createAsync(HttpConnection connection, Exchange exchange, Supplier initial) { - return MinimalFuture.supply(() -> new Http2Connection(connection, client2, exchange, initial)); + return MinimalFuture.supply(() -> new Http2Connection(connection, client2, exchange, initial, + exchange.pushEnabled())); } // Requires TLS handshake. So, is really async @@ -478,7 +538,8 @@ static CompletableFuture createAsync(HttpRequestImpl request, .thenCompose(notused-> { CompletableFuture cf = new MinimalFuture<>(); try { - Http2Connection hc = new Http2Connection(request, h2client, connection); + Http2Connection hc = new Http2Connection(request, h2client, + connection, exchange.pushEnabled()); cf.complete(hc); } catch (IOException e) { cf.completeExceptionally(e); @@ -493,13 +554,15 @@ static CompletableFuture createAsync(HttpRequestImpl request, */ private Http2Connection(HttpRequestImpl request, Http2ClientImpl h2client, - HttpConnection connection) + HttpConnection connection, + boolean defaultServerPush) throws IOException { this(connection, h2client, 1, - keyFor(request)); + keyFor(request), + defaultServerPush); Log.logTrace("Connection send window size {0} ", windowController.connectionWindowSize()); @@ -522,24 +585,30 @@ final HttpClientImpl client() { // if false returned then a new Http2Connection is required // if true, the stream may be assigned to this connection // for server push, if false returned, then the stream should be cancelled - boolean reserveStream(boolean clientInitiated) throws IOException { + boolean reserveStream(boolean clientInitiated, boolean pushEnabled) throws IOException { stateLock.lock(); try { - return reserveStream0(clientInitiated); + return reserveStream0(clientInitiated, pushEnabled); } finally { stateLock.unlock(); } } - private boolean reserveStream0(boolean clientInitiated) throws IOException { + private boolean reserveStream0(boolean clientInitiated, boolean pushEnabled) throws IOException { if (finalStream()) { return false; } - if (clientInitiated && (lastReservedClientStreamid + 2) >= MAX_CLIENT_STREAM_ID) { + // If requesting to reserve a stream for an exchange for which push is enabled, + // we will reserve the stream in this connection only if this connection is also + // push enabled, unless pushes are globally disabled. + boolean pushCompatible = !clientInitiated || !pushEnabled + || this.serverPushEnabled() + || client2.serverPushDisabled(); + if (clientInitiated && (lastReservedClientStreamid >= MAX_CLIENT_STREAM_ID -2 || !pushCompatible)) { setFinalStream(); client2.removeFromPool(this); return false; - } else if (!clientInitiated && (lastReservedServerStreamid + 2) >= MAX_SERVER_STREAM_ID) { + } else if (!clientInitiated && (lastReservedServerStreamid >= MAX_SERVER_STREAM_ID - 2)) { setFinalStream(); client2.removeFromPool(this); return false; @@ -564,6 +633,15 @@ private boolean reserveStream0(boolean clientInitiated) throws IOException { return true; } + boolean shouldClose() { + stateLock.lock(); + try { + return finalStream() && streams.isEmpty(); + } finally { + stateLock.unlock(); + } + } + /** * Throws an IOException if h2 was not negotiated */ @@ -691,6 +769,10 @@ String key() { return this.key; } + public boolean serverPushEnabled() { + return clientSettings.getParameter(SettingsFrame.ENABLE_PUSH) == 1; + } + boolean offerConnection() { return client2.offerConnection(this); } @@ -795,7 +877,7 @@ final void asyncReceive(ByteBuffer buffer) { } Throwable getRecordedCause() { - return cause; + return cause.get(); } void shutdown(Throwable t) { @@ -804,11 +886,11 @@ void shutdown(Throwable t) { stateLock.lock(); try { if (!markShutdownRequested()) return; - Throwable initialCause = this.cause; - if (initialCause == null && t != null) this.cause = t; + cause.compareAndSet(null, t); } finally { stateLock.unlock(); } + if (Log.errors()) { if (t!= null && (!(t instanceof EOFException) || isActive())) { Log.logError(t); @@ -819,6 +901,7 @@ void shutdown(Throwable t) { } } client2.removeFromPool(this); + subscriber.stop(cause.get()); for (Stream s : streams.values()) { try { s.connectionClosing(t); @@ -872,17 +955,39 @@ void processFrame(Http2Frame frame) throws IOException { return; } + if (frame instanceof PushPromiseFrame && !serverPushEnabled()) { + String protocolError = "received a PUSH_PROMISE when SETTINGS_ENABLE_PUSH is 0"; + protocolError(ResetFrame.PROTOCOL_ERROR, protocolError); + return; + } + Stream stream = getStream(streamid); + var nextstreamid = this.nextstreamid; + if (stream == null && (streamid & 0x01) == 0x01 && streamid >= nextstreamid) { + String protocolError = String.format( + "received a frame for a non existing streamid(%s) >= nextstreamid(%s)", + streamid, nextstreamid); + protocolError(ResetFrame.PROTOCOL_ERROR, protocolError); + return; + } if (stream == null && pushContinuationState == null) { // Should never receive a frame with unknown stream id - if (frame instanceof HeaderFrame) { + if (frame instanceof HeaderFrame hf) { + String protocolError = checkMaxOrphanedHeadersExceeded(hf); + if (protocolError != null) { + protocolError(ResetFrame.PROTOCOL_ERROR, protocolError); + return; + } // always decode the headers as they may affect // connection-level HPACK decoding state - DecodingCallback decoder = new ValidatingHeadersConsumer()::onDecoded; + if (orphanedConsumer == null || frame.getClass() != ContinuationFrame.class) { + orphanedConsumer = new ValidatingHeadersConsumer(); + } + DecodingCallback decoder = orphanedConsumer::onDecoded; try { - decodeHeaders((HeaderFrame) frame, decoder); - } catch (UncheckedIOException e) { + decodeHeaders(hf, decoder); + } catch (IOException | UncheckedIOException e) { protocolError(ResetFrame.PROTOCOL_ERROR, e.getMessage()); return; } @@ -910,29 +1015,41 @@ void processFrame(Http2Frame frame) throws IOException { // While push frame is not null, the only acceptable frame on this // stream is a Continuation frame - if (pushContinuationState != null) { + PushContinuationState pcs = pushContinuationState; + if (pcs != null) { if (frame instanceof ContinuationFrame cf) { + if (stream == null) { + String protocolError = checkMaxOrphanedHeadersExceeded(cf); + if (protocolError != null) { + protocolError(ResetFrame.PROTOCOL_ERROR, protocolError); + return; + } + } try { - if (streamid == pushContinuationState.pushContFrame.streamid()) - handlePushContinuation(stream, cf); - else - protocolError(ErrorFrame.PROTOCOL_ERROR, "Received a Continuation Frame with an " + - "unexpected stream id"); - } catch (UncheckedIOException e) { + if (streamid == pcs.pushContFrame.streamid()) + handlePushContinuation(pcs, stream, cf); + else { + String protocolError = "Received a CONTINUATION with " + + "unexpected stream id: " + streamid + " != " + + pcs.pushContFrame.streamid(); + protocolError(ErrorFrame.PROTOCOL_ERROR, protocolError); + } + } catch (IOException | UncheckedIOException e) { debug.log("Error handling Push Promise with Continuation: " + e.getMessage(), e); protocolError(ErrorFrame.PROTOCOL_ERROR, e.getMessage()); return; } } else { pushContinuationState = null; - protocolError(ErrorFrame.PROTOCOL_ERROR, "Expected a Continuation frame but received " + frame); + String protocolError = "Expected a CONTINUATION frame but received " + frame; + protocolError(ErrorFrame.PROTOCOL_ERROR, protocolError); return; } } else { if (frame instanceof PushPromiseFrame pp) { try { handlePushPromise(stream, pp); - } catch (UncheckedIOException e) { + } catch (IOException | UncheckedIOException e) { protocolError(ErrorFrame.PROTOCOL_ERROR, e.getMessage()); return; } @@ -940,7 +1057,7 @@ void processFrame(Http2Frame frame) throws IOException { // decode headers try { decodeHeaders(hf, stream.rspHeadersConsumer()); - } catch (UncheckedIOException e) { + } catch (IOException | UncheckedIOException e) { debug.log("Error decoding headers: " + e.getMessage(), e); protocolError(ErrorFrame.PROTOCOL_ERROR, e.getMessage()); return; @@ -953,6 +1070,16 @@ void processFrame(Http2Frame frame) throws IOException { } } + private String checkMaxOrphanedHeadersExceeded(HeaderFrame hf) { + if (MAX_ORPHANED_HEADERS > 0 ) { + int orphaned = orphanedHeaders.incrementAndGet(); + if (orphaned < 0 || orphaned > MAX_ORPHANED_HEADERS) { + return "Too many orphaned header frames received on connection"; + } + } + return null; + } + final void dropDataFrame(DataFrame df) { if (isMarked(closedState, SHUTDOWN_REQUESTED)) return; if (debug.on()) { @@ -977,38 +1104,65 @@ final void ensureWindowUpdated(DataFrame df) { private void handlePushPromise(Stream parent, PushPromiseFrame pp) throws IOException { + int promisedStreamid = pp.getPromisedStream(); + if ((promisedStreamid & 0x01) != 0x00) { + throw new ProtocolException("Received PUSH_PROMISE for stream " + promisedStreamid); + } + int streamId = pp.streamid(); + if ((streamId & 0x01) != 0x01) { + throw new ProtocolException("Received PUSH_PROMISE on stream " + streamId); + } // always decode the headers as they may affect connection-level HPACK // decoding state assert pushContinuationState == null; - HeaderDecoder decoder = new HeaderDecoder(); - decodeHeaders(pp, decoder::onDecoded); - int promisedStreamid = pp.getPromisedStream(); + PushPromiseDecoder decoder = new PushPromiseDecoder(streamId, promisedStreamid, parent); + decodeHeaders(pp, decoder); if (pp.endHeaders()) { - completePushPromise(promisedStreamid, parent, decoder.headers()); + if (decoder.errorRef.get() == null) { + completePushPromise(promisedStreamid, parent, decoder.headers()); + } } else { pushContinuationState = new PushContinuationState(decoder, pp); } } - private void handlePushContinuation(Stream parent, ContinuationFrame cf) + private void handlePushContinuation(PushContinuationState pcs, Stream parent, ContinuationFrame cf) throws IOException { - var pcs = pushContinuationState; - decodeHeaders(cf, pcs.pushContDecoder::onDecoded); + assert pcs.pushContFrame.streamid() == cf.streamid() : String.format( + "Received CONTINUATION on a different stream %s != %s", + cf.streamid(), pcs.pushContFrame.streamid()); + decodeHeaders(cf, pcs.pushContDecoder); // if all continuations are sent, set pushWithContinuation to null if (cf.endHeaders()) { - completePushPromise(pcs.pushContFrame.getPromisedStream(), parent, - pcs.pushContDecoder.headers()); + if (pcs.pushContDecoder.errorRef.get() == null) { + completePushPromise(pcs.pushContFrame.getPromisedStream(), parent, + pcs.pushContDecoder.headers()); + } pushContinuationState = null; } } private void completePushPromise(int promisedStreamid, Stream parent, HttpHeaders headers) throws IOException { + if (parent == null) { + resetStream(promisedStreamid, ResetFrame.REFUSED_STREAM); + return; + } HttpRequestImpl parentReq = parent.request; + if (promisedStreamid < nextPushStream) { + // From RFC 9113 section 5.1.1: + // The identifier of a newly established stream MUST be numerically + // greater than all streams that the initiating endpoint has + // opened or reserved. + protocolError(ResetFrame.PROTOCOL_ERROR, String.format( + "Unexpected stream identifier: %s < %s", promisedStreamid, nextPushStream)); + return; + } if (promisedStreamid != nextPushStream) { + // we don't support skipping stream ids; resetStream(promisedStreamid, ResetFrame.PROTOCOL_ERROR); return; - } else if (!reserveStream(false)) { + } else if (!reserveStream(false, true)) { resetStream(promisedStreamid, ResetFrame.REFUSED_STREAM); return; } else { @@ -1177,11 +1331,17 @@ private void protocolError(int errorCode) private void protocolError(int errorCode, String msg) throws IOException { + String protocolError = "protocol error" + (msg == null?"":(": " + msg)); + ProtocolException protocolException = + new ProtocolException(protocolError); if (markHalfClosedLocal()) { + framesDecoder.close(protocolError); + subscriber.stop(protocolException); + if (debug.on()) debug.log("Sending GOAWAY due to " + protocolException); GoAwayFrame frame = new GoAwayFrame(0, errorCode); sendFrame(frame); } - shutdown(new IOException("protocol error" + (msg == null?"":(": " + msg)))); + shutdown(protocolException); } private void handleSettings(SettingsFrame frame) @@ -1356,7 +1516,7 @@ final Stream createStream(Exchange exchange) { Stream.PushedStream createPushStream(Stream parent, Exchange pushEx) { PushGroup pg = parent.exchange.getPushGroup(); - return new Stream.PushedStream<>(pg, this, pushEx); + return new Stream.PushedStream<>(parent, pg, this, pushEx); } /** @@ -1466,16 +1626,18 @@ private ByteBuffer getHeaderBuffer(int size) { private List encodeHeadersImpl(int bufferSize, HttpHeaders... headers) { ByteBuffer buffer = getHeaderBuffer(bufferSize); List buffers = new ArrayList<>(); - for(HttpHeaders header : headers) { + for (HttpHeaders header : headers) { for (Map.Entry> e : header.map().entrySet()) { String lKey = e.getKey().toLowerCase(Locale.US); List values = e.getValue(); for (String value : values) { hpackOut.header(lKey, value); while (!hpackOut.encode(buffer)) { - buffer.flip(); - buffers.add(buffer); - buffer = getHeaderBuffer(bufferSize); + if (!buffer.hasRemaining()) { + buffer.flip(); + buffers.add(buffer); + buffer = getHeaderBuffer(bufferSize); + } } } } @@ -1514,7 +1676,7 @@ private Stream registerNewStream(OutgoingHeaders> oh) { Throwable cause = null; synchronized (this) { if (isMarked(closedState, SHUTDOWN_REQUESTED)) { - cause = this.cause; + cause = this.cause.get(); if (cause == null) { cause = new IOException("Connection closed"); } @@ -1553,6 +1715,8 @@ void sendFrame(Http2Frame frame) { Stream stream = registerNewStream(oh); // provide protection from inserting unordered frames between Headers and Continuation if (stream != null) { + // we are creating a new stream: reset orphaned header count + orphanedHeaders.set(0); publisher.enqueue(encodeHeaders(oh, stream)); } } else { @@ -1621,7 +1785,7 @@ final class Http2TubeSubscriber implements TubeSubscriber { private volatile Flow.Subscription subscription; private volatile boolean completed; private volatile boolean dropped; - private volatile Throwable error; + private final AtomicReference errorRef = new AtomicReference<>(); private final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); private final SequentialScheduler scheduler = @@ -1642,10 +1806,9 @@ final void processQueue() { asyncReceive(buffer); } } catch (Throwable t) { - Throwable x = error; - if (x == null) error = t; + errorRef.compareAndSet(null, t); } finally { - Throwable x = error; + Throwable x = errorRef.get(); if (x != null) { if (debug.on()) debug.log("Stopping scheduler", x); scheduler.stop(); @@ -1680,6 +1843,7 @@ public void onSubscribe(Flow.Subscription subscription) { @Override public void onNext(List item) { + if (completed) return; if (debug.on()) debug.log(() -> "onNext: got " + Utils.remaining(item) + " bytes in " + item.size() + " buffers"); queue.addAll(item); @@ -1688,19 +1852,21 @@ public void onNext(List item) { @Override public void onError(Throwable throwable) { + if (completed) return; if (debug.on()) debug.log(() -> "onError: " + throwable); - error = throwable; + errorRef.compareAndSet(null, throwable); completed = true; runOrSchedule(); } @Override public void onComplete() { + if (completed) return; String msg = isActive() ? "EOF reached while reading" : "Idle connection closed by HTTP/2 peer"; if (debug.on()) debug.log(msg); - error = new EOFException(msg); + errorRef.compareAndSet(null, new EOFException(msg)); completed = true; runOrSchedule(); } @@ -1712,6 +1878,18 @@ public void dropSubscription() { // then we might not need the 'dropped' boolean? dropped = true; } + + void stop(Throwable error) { + if (errorRef.compareAndSet(null, error)) { + completed = true; + scheduler.stop(); + queue.clear(); + if (subscription != null) { + subscription.cancel(); + } + queue.clear(); + } + } } boolean isActive() { 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 3fe8efdd7af..e1f4ec16dcd 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 @@ -964,7 +964,9 @@ private void debugCompleted(String tag, long startNanos, HttpRequest req) { // SSLException throw new SSLException(msg, throwable); } else if (throwable instanceof ProtocolException) { - throw new ProtocolException(msg); + ProtocolException pe = new ProtocolException(msg); + pe.initCause(throwable); + throw pe; } else if (throwable instanceof IOException) { throw new IOException(msg, throwable); } else { diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestImpl.java index 68a27041d6b..f5b5ed54bf3 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestImpl.java @@ -287,10 +287,10 @@ public HttpHeaders headers() { InetSocketAddress authority() { return authority; } - void setH2Upgrade(Http2ClientImpl h2client) { + void setH2Upgrade(Exchange exchange) { systemHeadersBuilder.setHeader("Connection", "Upgrade, HTTP2-Settings"); systemHeadersBuilder.setHeader("Upgrade", Alpns.H2C); - systemHeadersBuilder.setHeader("HTTP2-Settings", h2client.getSettingsString()); + systemHeadersBuilder.setHeader("HTTP2-Settings", exchange.h2cSettingsStrings()); } @Override diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java b/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java index 66d89ae1fc5..22e03238d21 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * 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.security.AccessControlContext; import java.security.AccessController; import java.util.List; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; @@ -137,16 +138,21 @@ public void applyPushPromise( if (!initiatingURI.getHost().equalsIgnoreCase(pushRequestURI.getHost())) return; + String initiatingScheme = initiatingURI.getScheme(); + String pushRequestScheme = pushRequestURI.getScheme(); + + if (!initiatingScheme.equalsIgnoreCase(pushRequestScheme)) return; + int initiatingPort = initiatingURI.getPort(); if (initiatingPort == -1 ) { - if ("https".equalsIgnoreCase(initiatingURI.getScheme())) + if ("https".equalsIgnoreCase(initiatingScheme)) initiatingPort = 443; else initiatingPort = 80; } int pushPort = pushRequestURI.getPort(); if (pushPort == -1 ) { - if ("https".equalsIgnoreCase(pushRequestURI.getScheme())) + if ("https".equalsIgnoreCase(pushRequestScheme)) pushPort = 443; else pushPort = 80; diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java b/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java index 45633622923..84187678f94 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java @@ -30,6 +30,7 @@ import java.io.UncheckedIOException; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; +import java.net.ProtocolException; import java.net.URI; import java.net.http.HttpResponse.BodyHandler; import java.net.http.HttpResponse.ResponseInfo; @@ -43,6 +44,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.Flow; import java.util.concurrent.Flow.Subscription; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -52,10 +54,13 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodySubscriber; + import jdk.internal.net.http.common.*; import jdk.internal.net.http.frame.*; import jdk.internal.net.http.hpack.DecodingCallback; +import static jdk.internal.net.http.Exchange.MAX_NON_FINAL_RESPONSES; + /** * Http/2 Stream handling. * @@ -140,6 +145,9 @@ class Stream extends ExchangeImpl { private volatile boolean closed; private volatile boolean endStreamSent; private volatile boolean finalResponseCodeReceived; + private volatile boolean trailerReceived; + private AtomicInteger nonFinalResponseCount = new AtomicInteger(); + // Indicates the first reason that was invoked when sending a ResetFrame // to the server. A streamState of 0 indicates that no reset was sent. // (see markStream(int code) @@ -520,16 +528,38 @@ void otherFrame(Http2Frame frame) throws IOException { // The Hpack decoder decodes into one of these consumers of name,value pairs DecodingCallback rspHeadersConsumer() { - return rspHeadersConsumer::onDecoded; + return rspHeadersConsumer; + } + + String checkInterimResponseCountExceeded() { + // this is also checked by Exchange - but tracking it here too provides + // a more informative message. + int count = nonFinalResponseCount.incrementAndGet(); + if (MAX_NON_FINAL_RESPONSES > 0 && (count < 0 || count > MAX_NON_FINAL_RESPONSES)) { + return String.format( + "Stream %s PROTOCOL_ERROR: too many interim responses received: %s > %s", + streamid, count, MAX_NON_FINAL_RESPONSES); + } + return null; } protected void handleResponse(HeaderFrame hf) throws IOException { HttpHeaders responseHeaders = responseHeadersBuilder.build(); if (!finalResponseCodeReceived) { - responseCode = (int) responseHeaders - .firstValueAsLong(":status") - .orElseThrow(() -> new IOException("no statuscode in response")); + try { + responseCode = (int) responseHeaders + .firstValueAsLong(":status") + .orElseThrow(() -> new ProtocolException(String.format( + "Stream %s PROTOCOL_ERROR: no status code in response", + streamid))); + } catch (ProtocolException cause) { + cancelImpl(cause, ResetFrame.PROTOCOL_ERROR); + rspHeadersConsumer.reset(); + return; + } + + String protocolErrorMsg = null; // If informational code, response is partially complete if (responseCode < 100 || responseCode > 199) { this.finalResponseCodeReceived = true; @@ -537,23 +567,31 @@ protected void handleResponse(HeaderFrame hf) throws IOException { // see RFC 9113 section 8.1: // A HEADERS frame with the END_STREAM flag set that carries an // informational status code is malformed - String msg = ("Stream %s PROTOCOL_ERROR: " + - "HEADERS frame with status %s has END_STREAM flag set") - .formatted(streamid, responseCode); + protocolErrorMsg = String.format( + "Stream %s PROTOCOL_ERROR: " + + "HEADERS frame with status %s has END_STREAM flag set", + streamid, responseCode); + } else { + protocolErrorMsg = checkInterimResponseCountExceeded(); + } + + if (protocolErrorMsg != null) { if (debug.on()) { - debug.log(msg); + debug.log(protocolErrorMsg); } - cancelImpl(new IOException(msg), ResetFrame.PROTOCOL_ERROR); + cancelImpl(new ProtocolException(protocolErrorMsg), ResetFrame.PROTOCOL_ERROR); + rspHeadersConsumer.reset(); + return; } response = new Response( request, exchange, responseHeaders, connection(), responseCode, HttpClient.Version.HTTP_2); - /* TODO: review if needs to be removed - the value is not used, but in case `content-length` doesn't parse as - long, there will be NumberFormatException. If left as is, make sure - code up the stack handles NFE correctly. */ + /* TODO: review if needs to be removed + the value is not used, but in case `content-length` doesn't parse as + long, there will be NumberFormatException. If left as is, make sure + code up the stack handles NFE correctly. */ responseHeaders.firstValueAsLong("content-length"); if (Log.headers()) { @@ -572,6 +610,15 @@ request, exchange, responseHeaders, connection(), Log.dumpHeaders(sb, " ", responseHeaders); Log.logHeaders(sb.toString()); } + if (trailerReceived) { + String protocolErrorMsg = String.format( + "Stream %s PROTOCOL_ERROR: trailers already received", streamid); + if (debug.on()) { + debug.log(protocolErrorMsg); + } + cancelImpl(new ProtocolException(protocolErrorMsg), ResetFrame.PROTOCOL_ERROR); + } + trailerReceived = true; rspHeadersConsumer.reset(); } @@ -1182,7 +1229,7 @@ private DataFrame getEmptyEndStreamDataFrame() { /** * A List of responses relating to this stream. Normally there is only - * one response, but intermediate responses like 100 are allowed + * one response, but interim responses like 100 are allowed * and must be passed up to higher level before continuing. Deals with races * such as if responses are returned before the CFs get created by * getResponseAsync() @@ -1401,7 +1448,7 @@ void cancelImpl(Throwable e) { cancelImpl(e, ResetFrame.CANCEL); } - private void cancelImpl(final Throwable e, final int resetFrameErrCode) { + void cancelImpl(final Throwable e, final int resetFrameErrCode) { errorRef.compareAndSet(null, e); if (debug.on()) { if (streamid == 0) debug.log("cancelling stream: %s", (Object)e); @@ -1511,6 +1558,7 @@ void close() { } static class PushedStream extends Stream { + final Stream parent; final PushGroup pushGroup; // push streams need the response CF allocated up front as it is // given directly to user via the multi handler callback function. @@ -1520,16 +1568,17 @@ static class PushedStream extends Stream { volatile HttpResponse.BodyHandler pushHandler; private volatile boolean finalPushResponseCodeReceived; - PushedStream(PushGroup pushGroup, + PushedStream(Stream parent, + PushGroup pushGroup, Http2Connection connection, Exchange pushReq) { // ## no request body possible, null window controller super(connection, pushReq, null); + this.parent = parent; this.pushGroup = pushGroup; this.pushReq = pushReq.request(); this.pushCF = new MinimalFuture<>(); this.responseCF = new MinimalFuture<>(); - } CompletableFuture> responseCF() { @@ -1617,7 +1666,16 @@ protected void handleResponse(HeaderFrame hf) { .orElse(-1); if (responseCode == -1) { - completeResponseExceptionally(new IOException("No status code")); + cancelImpl(new ProtocolException("No status code"), ResetFrame.PROTOCOL_ERROR); + rspHeadersConsumer.reset(); + return; + } else if (responseCode >= 100 && responseCode < 200) { + String protocolErrorMsg = checkInterimResponseCountExceeded(); + if (protocolErrorMsg != null) { + cancelImpl(new ProtocolException(protocolErrorMsg), ResetFrame.PROTOCOL_ERROR); + rspHeadersConsumer.reset(); + return; + } } this.finalPushResponseCodeReceived = true; @@ -1727,7 +1785,9 @@ void closeAsUnprocessed() { } } - private class HeadersConsumer extends ValidatingHeadersConsumer { + private class HeadersConsumer extends ValidatingHeadersConsumer implements DecodingCallback { + + boolean maxHeaderListSizeReached; @Override public void reset() { @@ -1740,6 +1800,9 @@ public void reset() { public void onDecoded(CharSequence name, CharSequence value) throws UncheckedIOException { + if (maxHeaderListSizeReached) { + return; + } try { String n = name.toString(); String v = value.toString(); @@ -1762,6 +1825,23 @@ public void onDecoded(CharSequence name, CharSequence value) protected String formatMessage(String message, String header) { return "malformed response: " + super.formatMessage(message, header); } + + @Override + public void onMaxHeaderListSizeReached(long size, int maxHeaderListSize) throws ProtocolException { + if (maxHeaderListSizeReached) return; + try { + DecodingCallback.super.onMaxHeaderListSizeReached(size, maxHeaderListSize); + } catch (ProtocolException cause) { + maxHeaderListSizeReached = true; + // If this is a push stream: cancel the parent. + if (Stream.this instanceof Stream.PushedStream ps) { + ps.parent.onProtocolError(cause); + } + // cancel the stream, continue processing + onProtocolError(cause); + reset(); + } + } } final class Http2StreamResponseSubscriber extends HttpBodySubscriberWrapper { diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/HeaderDecoder.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/HeaderDecoder.java index 62d03844d2e..d81f52e6630 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/common/HeaderDecoder.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/HeaderDecoder.java @@ -39,7 +39,11 @@ public void onDecoded(CharSequence name, CharSequence value) { String n = name.toString(); String v = value.toString(); super.onDecoded(n, v); - headersBuilder.addHeader(n, v); + addHeader(n, v); + } + + protected void addHeader(String name, String value) { + headersBuilder.addHeader(name, value); } public HttpHeaders headers() { 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 b2eb570db0c..49e669e5add 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 @@ -616,6 +616,19 @@ public static int getIntegerProperty(String name, int defaultValue) { Integer.parseInt(System.getProperty(name, String.valueOf(defaultValue)))); } + public static int getIntegerNetProperty(String property, int min, int max, int defaultValue, boolean log) { + int value = Utils.getIntegerNetProperty(property, defaultValue); + // use default value if misconfigured + if (value < min || value > max) { + if (log && Log.errors()) { + Log.logError("Property value for {0}={1} not in [{2}..{3}]: " + + "using default={4}", property, value, min, max, defaultValue); + } + value = defaultValue; + } + return value; + } + public static SSLParameters copySSLParameters(SSLParameters p) { SSLParameters p1 = new SSLParameters(); p1.setAlgorithmConstraints(p.getAlgorithmConstraints()); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Decoder.java b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Decoder.java index 9d57a734ac3..881be12c67c 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Decoder.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Decoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * 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.internal.net.http.hpack.HPACK.Logger; import java.io.IOException; +import java.net.ProtocolException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicLong; @@ -107,12 +108,16 @@ public final class Decoder { private final StringReader stringReader; private final StringBuilder name; private final StringBuilder value; + private final int maxHeaderListSize; + private final int maxIndexed; private int intValue; private boolean firstValueRead; private boolean firstValueIndex; private boolean nameHuffmanEncoded; private boolean valueHuffmanEncoded; private int capacity; + private long size; + private int indexed; /** * Constructs a {@code Decoder} with the specified initial capacity of the @@ -129,6 +134,31 @@ public final class Decoder { * if capacity is negative */ public Decoder(int capacity) { + this(capacity, 0, 0); + } + + /** + * Constructs a {@code Decoder} with the specified initial capacity of the + * header table, a max header list size, and a maximum number of literals + * with indexing per header section. + * + *

    The value of the capacity has to be agreed between decoder and encoder out-of-band, + * e.g. by a protocol that uses HPACK + * (see 4.2. Maximum Table Size). + * + * @param capacity + * a non-negative integer + * @param maxHeaderListSize + * a maximum value for the header list size. This is the uncompressed + * names size + uncompressed values size + 32 bytes per field line + * @param maxIndexed + * the maximum number of literal with indexing we're prepared to handle + * for a header field section + * + * @throws IllegalArgumentException + * if capacity is negative + */ + public Decoder(int capacity, int maxHeaderListSize, int maxIndexed) { id = DECODERS_IDS.incrementAndGet(); logger = HPACK.getLogger().subLogger("Decoder#" + id); if (logger.isLoggable(NORMAL)) { @@ -145,6 +175,8 @@ public Decoder(int capacity) { toString(), hashCode); }); } + this.maxHeaderListSize = maxHeaderListSize; + this.maxIndexed = maxIndexed; setMaxCapacity0(capacity); table = new SimpleHeaderTable(capacity, logger.subLogger("HeaderTable")); integerReader = new IntegerReader(); @@ -242,22 +274,25 @@ public void decode(ByteBuffer headerBlock, requireNonNull(consumer, "consumer"); if (logger.isLoggable(NORMAL)) { logger.log(NORMAL, () -> format("reading %s, end of header block? %s", - headerBlock, endOfHeaderBlock)); + headerBlock, endOfHeaderBlock)); } while (headerBlock.hasRemaining()) { proceed(headerBlock, consumer); } if (endOfHeaderBlock && state != State.READY) { logger.log(NORMAL, () -> format("unexpected end of %s representation", - state)); + state)); throw new IOException("Unexpected end of header block"); } + if (endOfHeaderBlock) { + size = indexed = 0; + } } private void proceed(ByteBuffer input, DecodingCallback action) throws IOException { switch (state) { - case READY -> resumeReady(input); + case READY -> resumeReady(input, action); case INDEXED -> resumeIndexed(input, action); case LITERAL -> resumeLiteral(input, action); case LITERAL_WITH_INDEXING -> resumeLiteralWithIndexing(input, action); @@ -268,7 +303,7 @@ private void proceed(ByteBuffer input, DecodingCallback action) } } - private void resumeReady(ByteBuffer input) { + private void resumeReady(ByteBuffer input, DecodingCallback action) throws IOException { int b = input.get(input.position()) & 0xff; // absolute read State s = states.get(b); if (logger.isLoggable(EXTRA)) { @@ -289,6 +324,9 @@ private void resumeReady(ByteBuffer input) { } break; case LITERAL_WITH_INDEXING: + if (maxIndexed > 0 && ++indexed > maxIndexed) { + action.onMaxLiteralWithIndexingReached(indexed, maxIndexed); + } state = State.LITERAL_WITH_INDEXING; firstValueIndex = (b & 0b0011_1111) != 0; if (firstValueIndex) { @@ -315,6 +353,12 @@ private void resumeReady(ByteBuffer input) { } } + private void checkMaxHeaderListSize(long sz, DecodingCallback consumer) throws ProtocolException { + if (maxHeaderListSize > 0 && sz > maxHeaderListSize) { + consumer.onMaxHeaderListSizeReached(sz, maxHeaderListSize); + } + } + // 0 1 2 3 4 5 6 7 // +---+---+---+---+---+---+---+---+ // | 1 | Index (7+) | @@ -332,6 +376,8 @@ private void resumeIndexed(ByteBuffer input, DecodingCallback action) } try { SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue); + size = size + 32 + f.name.length() + f.value.length(); + checkMaxHeaderListSize(size, action); action.onIndexed(intValue, f.name, f.value); } finally { state = State.READY; @@ -374,7 +420,7 @@ private SimpleHeaderTable.HeaderField getHeaderFieldAt(int index) // private void resumeLiteral(ByteBuffer input, DecodingCallback action) throws IOException { - if (!completeReading(input)) { + if (!completeReading(input, action)) { return; } try { @@ -385,6 +431,8 @@ private void resumeLiteral(ByteBuffer input, DecodingCallback action) intValue, value, valueHuffmanEncoded)); } SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue); + size = size + 32 + f.name.length() + value.length(); + checkMaxHeaderListSize(size, action); action.onLiteral(intValue, f.name, value, valueHuffmanEncoded); } else { if (logger.isLoggable(NORMAL)) { @@ -392,6 +440,8 @@ private void resumeLiteral(ByteBuffer input, DecodingCallback action) "literal without indexing ('%s', huffman=%b, '%s', huffman=%b)", name, nameHuffmanEncoded, value, valueHuffmanEncoded)); } + size = size + 32 + name.length() + value.length(); + checkMaxHeaderListSize(size, action); action.onLiteral(name, nameHuffmanEncoded, value, valueHuffmanEncoded); } } finally { @@ -425,7 +475,7 @@ private void resumeLiteral(ByteBuffer input, DecodingCallback action) private void resumeLiteralWithIndexing(ByteBuffer input, DecodingCallback action) throws IOException { - if (!completeReading(input)) { + if (!completeReading(input, action)) { return; } try { @@ -445,6 +495,8 @@ private void resumeLiteralWithIndexing(ByteBuffer input, } SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue); n = f.name; + size = size + 32 + n.length() + v.length(); + checkMaxHeaderListSize(size, action); action.onLiteralWithIndexing(intValue, n, v, valueHuffmanEncoded); } else { n = name.toString(); @@ -453,6 +505,8 @@ private void resumeLiteralWithIndexing(ByteBuffer input, "literal with incremental indexing ('%s', huffman=%b, '%s', huffman=%b)", n, nameHuffmanEncoded, value, valueHuffmanEncoded)); } + size = size + 32 + n.length() + v.length(); + checkMaxHeaderListSize(size, action); action.onLiteralWithIndexing(n, nameHuffmanEncoded, v, valueHuffmanEncoded); } table.put(n, v); @@ -486,7 +540,7 @@ private void resumeLiteralWithIndexing(ByteBuffer input, private void resumeLiteralNeverIndexed(ByteBuffer input, DecodingCallback action) throws IOException { - if (!completeReading(input)) { + if (!completeReading(input, action)) { return; } try { @@ -497,6 +551,8 @@ private void resumeLiteralNeverIndexed(ByteBuffer input, intValue, value, valueHuffmanEncoded)); } SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue); + size = size + 32 + f.name.length() + value.length(); + checkMaxHeaderListSize(size, action); action.onLiteralNeverIndexed(intValue, f.name, value, valueHuffmanEncoded); } else { if (logger.isLoggable(NORMAL)) { @@ -504,6 +560,8 @@ private void resumeLiteralNeverIndexed(ByteBuffer input, "literal never indexed ('%s', huffman=%b, '%s', huffman=%b)", name, nameHuffmanEncoded, value, valueHuffmanEncoded)); } + size = size + 32 + name.length() + value.length(); + checkMaxHeaderListSize(size, action); action.onLiteralNeverIndexed(name, nameHuffmanEncoded, value, valueHuffmanEncoded); } } finally { @@ -541,7 +599,7 @@ private void resumeSizeUpdate(ByteBuffer input, } } - private boolean completeReading(ByteBuffer input) throws IOException { + private boolean completeReading(ByteBuffer input, DecodingCallback action) throws IOException { if (!firstValueRead) { if (firstValueIndex) { if (!integerReader.read(input)) { @@ -551,6 +609,8 @@ private boolean completeReading(ByteBuffer input) throws IOException { integerReader.reset(); } else { if (!stringReader.read(input, name)) { + long sz = size + 32 + name.length(); + checkMaxHeaderListSize(sz, action); return false; } nameHuffmanEncoded = stringReader.isHuffmanEncoded(); @@ -560,6 +620,8 @@ private boolean completeReading(ByteBuffer input) throws IOException { return false; } else { if (!stringReader.read(input, value)) { + long sz = size + 32 + name.length() + value.length(); + checkMaxHeaderListSize(sz, action); return false; } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/DecodingCallback.java b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/DecodingCallback.java index 5e9df860feb..228f9bf0206 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/DecodingCallback.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/DecodingCallback.java @@ -24,6 +24,7 @@ */ package jdk.internal.net.http.hpack; +import java.net.ProtocolException; import java.nio.ByteBuffer; /** @@ -292,4 +293,17 @@ default void onLiteralWithIndexing(CharSequence name, * new capacity of the header table */ default void onSizeUpdate(int capacity) { } + + default void onMaxHeaderListSizeReached(long size, int maxHeaderListSize) + throws ProtocolException { + throw new ProtocolException(String + .format("Size exceeds MAX_HEADERS_LIST_SIZE: %s > %s", + size, maxHeaderListSize)); + } + + default void onMaxLiteralWithIndexingReached(long indexed, int maxIndexed) + throws ProtocolException { + throw new ProtocolException(String.format("Too many literal with indexing: %s > %s", + indexed, maxIndexed)); + } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Encoder.java b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Encoder.java index 4188937b1ad..c603e917ca4 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Encoder.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Encoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -258,9 +258,10 @@ public void header(CharSequence name, } } } + assert encoding : "encoding is false"; } - private boolean isHuffmanBetterFor(CharSequence value) { + protected final boolean isHuffmanBetterFor(CharSequence value) { // prefer Huffman encoding only if it is strictly smaller than Latin-1 return huffmanWriter.lengthOf(value) < value.length(); } @@ -340,6 +341,10 @@ protected int calculateCapacity(int maxCapacity) { return 0; } + protected final int tableIndexOf(CharSequence name, CharSequence value) { + return getHeaderTable().indexOf(name, value); + } + /** * Encodes the {@linkplain #header(CharSequence, CharSequence) set up} * header into the given buffer. @@ -380,6 +385,7 @@ public final boolean encode(ByteBuffer headerBlock) { writer.reset(); // FIXME: WHY? encoding = false; } + assert done || encoding : "done: " + done + ", encoding: " + encoding; return done; } @@ -542,4 +548,8 @@ protected final void checkEncoding() { // TODO: better name e.g. checkIfEncoding "Previous encoding operation hasn't finished yet"); } } + + protected final Logger logger() { + return logger; + } } diff --git a/src/java.net.http/share/classes/module-info.java b/src/java.net.http/share/classes/module-info.java index cf9d07bdf32..5303e818866 100644 --- a/src/java.net.http/share/classes/module-info.java +++ b/src/java.net.http/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -115,6 +115,25 @@ * The HTTP/2 client maximum frame size in bytes. The server is not permitted to send a frame * larger than this. *

  • + *
  • {@systemProperty jdk.httpclient.maxLiteralWithIndexing} (default: 512)
    + * The maximum number of header field lines (header name and value pairs) that a + * client is willing to add to the HPack Decoder dynamic table during the decoding + * of an entire header field section. + * This is purely an implementation limit. + * If a peer sends a field section with encoding that + * exceeds this limit a {@link java.net.ProtocolException ProtocolException} will be raised. + * A value of zero or a negative value means no limit. + *

  • + *
  • {@systemProperty jdk.httpclient.maxNonFinalResponses} (default: 8)
    + * The maximum number of interim (non-final) responses that a client is prepared + * to accept on a request-response stream before the final response is received. + * Interim responses are responses with a status in the range [100, 199] inclusive. + * This is purely an implementation limit. + * If a peer sends a number of interim response that exceeds this limit before + * sending the final response, a {@link java.net.ProtocolException ProtocolException} + * will be raised. + * A value of zero or a negative value means no limit. + *

  • *
  • {@systemProperty jdk.httpclient.maxstreams} (default: 100)
    * The maximum number of HTTP/2 push streams that the client will permit servers to open * simultaneously. @@ -155,6 +174,15 @@ * conf/net.properties)
    A comma separated list of HTTP authentication scheme names, that * are disallowed for use by the HTTP client implementation, for HTTP CONNECT tunneling. *

  • + *
  • {@systemProperty jdk.http.maxHeaderSize} (default: 393216 or 384kB) + *
    The maximum header field section size that the client is prepared to accept. + * This is computed as the sum of the size of the uncompressed header name, plus + * the size of the uncompressed header value, plus an overhead of 32 bytes for + * each field section line. If a peer sends a field section that exceeds this + * size a {@link java.net.ProtocolException ProtocolException} will be raised. + * This applies to all versions of the protocol. A value of zero or a negative + * value means no limit. + *

  • * * @moduleGraph * @since 11 diff --git a/src/jdk.httpserver/share/classes/module-info.java b/src/jdk.httpserver/share/classes/module-info.java index 46dbb6ea64f..23e04405ee8 100644 --- a/src/jdk.httpserver/share/classes/module-info.java +++ b/src/jdk.httpserver/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -70,6 +70,16 @@ * while the headers are being read, then the connection is terminated and the request ignored. * If the value is less than or equal to zero, then the default value is used. * + *
  • {@systemProperty sun.net.httpserver.maxReqHeaderSize} (default: 393216 or 384kB)
    + * The maximum header field section size that the server is prepared to accept. + * This is computed as the sum of the size of the header name, plus + * the size of the header value, plus an overhead of 32 bytes for + * each field section line. The request line counts as a first field section line, + * where the name is empty and the value is the whole line. + * If this limit is exceeded while the headers are being read, then the connection + * is terminated and the request ignored. + * If the value is less than or equal to zero, there is no limit. + *

  • *
  • {@systemProperty sun.net.httpserver.maxReqTime} (default: -1)
    * The maximum time in milliseconds allowed to receive a request headers and body. * In practice, the actual time is a function of request size, network speed, and handler diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java index 99dac0547ce..6c0c2c271ed 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * 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,8 +44,10 @@ class Request { private SocketChannel chan; private InputStream is; private OutputStream os; + private final int maxReqHeaderSize; Request (InputStream rawInputStream, OutputStream rawout) throws IOException { + this.maxReqHeaderSize = ServerConfig.getMaxReqHeaderSize(); is = rawInputStream; os = rawout; do { @@ -75,6 +77,7 @@ public OutputStream outputStream () { public String readLine () throws IOException { boolean gotCR = false, gotLF = false; pos = 0; lineBuf = new StringBuffer(); + long lsize = 32; while (!gotLF) { int c = is.read(); if (c == -1) { @@ -87,20 +90,27 @@ public String readLine () throws IOException { gotCR = false; consume (CR); consume (c); + lsize = lsize + 2; } } else { if (c == CR) { gotCR = true; } else { consume (c); + lsize = lsize + 1; } } + if (maxReqHeaderSize > 0 && lsize > maxReqHeaderSize) { + throw new IOException("Maximum header (" + + "sun.net.httpserver.maxReqHeaderSize) exceeded, " + + ServerConfig.getMaxReqHeaderSize() + "."); + } } lineBuf.append (buf, 0, pos); return new String (lineBuf); } - private void consume (int c) { + private void consume (int c) throws IOException { if (pos == BUF_LEN) { lineBuf.append (buf); pos = 0; @@ -138,13 +148,22 @@ Headers headers () throws IOException { len = 1; firstc = c; } + long hsize = startLine.length() + 32L; while (firstc != LF && firstc != CR && firstc >= 0) { int keyend = -1; int c; boolean inKey = firstc > ' '; s[len++] = (char) firstc; + hsize = hsize + 1; parseloop:{ + // We start parsing for a new name value pair here. + // The max header size includes an overhead of 32 bytes per + // name value pair. + // See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2. + long maxRemaining = maxReqHeaderSize > 0 + ? maxReqHeaderSize - hsize - 32 + : Long.MAX_VALUE; while ((c = is.read()) >= 0) { switch (c) { /*fallthrough*/ @@ -178,6 +197,11 @@ Headers headers () throws IOException { s = ns; } s[len++] = (char) c; + if (maxReqHeaderSize > 0 && len > maxRemaining) { + throw new IOException("Maximum header (" + + "sun.net.httpserver.maxReqHeaderSize) exceeded, " + + ServerConfig.getMaxReqHeaderSize() + "."); + } } firstc = -1; } @@ -205,6 +229,13 @@ Headers headers () throws IOException { "sun.net.httpserver.maxReqHeaders) exceeded, " + ServerConfig.getMaxReqHeaders() + "."); } + hsize = hsize + len + 32; + if (maxReqHeaderSize > 0 && hsize > maxReqHeaderSize) { + throw new IOException("Maximum header (" + + "sun.net.httpserver.maxReqHeaderSize) exceeded, " + + ServerConfig.getMaxReqHeaderSize() + "."); + } + if (k == null) { // Headers disallows null keys, use empty string k = ""; // instead to represent invalid key } diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java index 21f6165b05e..9186dd4c168 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,6 +49,7 @@ class ServerConfig { // timing out request/response if max request/response time is configured private static final long DEFAULT_REQ_RSP_TIMER_TASK_SCHEDULE_MILLIS = 1000; private static final int DEFAULT_MAX_REQ_HEADERS = 200; + private static final int DEFAULT_MAX_REQ_HEADER_SIZE = 380 * 1024; private static final long DEFAULT_DRAIN_AMOUNT = 64 * 1024; private static long idleTimerScheduleMillis; @@ -62,6 +63,9 @@ class ServerConfig { private static int maxIdleConnections; // The maximum number of request headers allowable private static int maxReqHeaders; + // a maximum value for the header list size. This is the + // names size + values size + 32 bytes per field line + private static int maxReqHeadersSize; // max time a request or response is allowed to take private static long maxReqTime; private static long maxRspTime; @@ -107,6 +111,14 @@ public Void run () { maxReqHeaders = DEFAULT_MAX_REQ_HEADERS; } + // a value <= 0 means unlimited + maxReqHeadersSize = Integer.getInteger( + "sun.net.httpserver.maxReqHeaderSize", + DEFAULT_MAX_REQ_HEADER_SIZE); + if (maxReqHeadersSize <= 0) { + maxReqHeadersSize = 0; + } + maxReqTime = Long.getLong("sun.net.httpserver.maxReqTime", DEFAULT_MAX_REQ_TIME); @@ -215,6 +227,10 @@ static int getMaxReqHeaders() { return maxReqHeaders; } + static int getMaxReqHeaderSize() { + return maxReqHeadersSize; + } + /** * @return Returns the maximum amount of time the server will wait for the request to be read * completely. This method can return a value of 0 or negative to imply no maximum limit has diff --git a/test/jdk/java/net/httpclient/ExpectContinueTest.java b/test/jdk/java/net/httpclient/ExpectContinueTest.java index 3d28ae8c8b4..50e43099255 100644 --- a/test/jdk/java/net/httpclient/ExpectContinueTest.java +++ b/test/jdk/java/net/httpclient/ExpectContinueTest.java @@ -59,6 +59,7 @@ import java.io.Writer; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.ProtocolException; import java.net.ServerSocket; import java.net.Socket; import java.net.URI; @@ -361,7 +362,7 @@ private void verifyRequest(String path, int expectedStatusCode, HttpResponse cf.join()); - assertEquals(t.getCause().getClass(), IOException.class, "Expected an IOException but got " + t.getCause()); + assertEquals(t.getCause().getClass(), ProtocolException.class, + "Expected a ProtocolException but got " + t.getCause()); System.err.println("Client received the following expected exception: " + t.getCause()); faultyServer.stop(); } @@ -222,7 +224,10 @@ private void verify(HttpResponse resp) { static class Http2PushPromiseHeadersExchangeImpl extends Http2TestExchangeImpl { - Http2PushPromiseHeadersExchangeImpl(int streamid, String method, HttpHeaders reqheaders, HttpHeadersBuilder rspheadersBuilder, URI uri, InputStream is, SSLSession sslSession, BodyOutputStream os, Http2TestServerConnection conn, boolean pushAllowed) { + Http2PushPromiseHeadersExchangeImpl(int streamid, String method, HttpHeaders reqheaders, + HttpHeadersBuilder rspheadersBuilder, URI uri, InputStream is, + SSLSession sslSession, BodyOutputStream os, + Http2TestServerConnection conn, boolean pushAllowed) { super(streamid, method, reqheaders, rspheadersBuilder, uri, is, sslSession, os, conn, pushAllowed); } diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.java index 36498684a9a..27dbe637b94 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -241,6 +241,7 @@ public static abstract class HttpTestExchange implements AutoCloseable { public abstract void close(); public abstract InetSocketAddress getRemoteAddress(); public abstract String getConnectionKey(); + public abstract InetSocketAddress getLocalAddress(); public void serverPush(URI uri, HttpHeaders headers, byte[] body) { ByteArrayInputStream bais = new ByteArrayInputStream(body); serverPush(uri, headers, bais); @@ -303,7 +304,10 @@ void doFilter(Filter.Chain chain) throws IOException { public InetSocketAddress getRemoteAddress() { return exchange.getRemoteAddress(); } - + @Override + public InetSocketAddress getLocalAddress() { + return exchange.getLocalAddress(); + } @Override public URI getRequestURI() { return exchange.getRequestURI(); } @Override @@ -370,6 +374,10 @@ void doFilter(Filter.Chain filter) throws IOException { public InetSocketAddress getRemoteAddress() { return exchange.getRemoteAddress(); } + @Override + public InetSocketAddress getLocalAddress() { + return exchange.getLocalAddress(); + } @Override public String getConnectionKey() { diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/HpackTestEncoder.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/HpackTestEncoder.java new file mode 100644 index 00000000000..f54a4a766b8 --- /dev/null +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/HpackTestEncoder.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * 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.httpclient.test.lib.http2; + +import java.util.function.*; + +import jdk.internal.net.http.hpack.Encoder; + +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; +import static jdk.internal.net.http.hpack.HPACK.Logger.Level.EXTRA; +import static jdk.internal.net.http.hpack.HPACK.Logger.Level.NORMAL; + +public class HpackTestEncoder extends Encoder { + + public HpackTestEncoder(int maxCapacity) { + super(maxCapacity); + } + + /** + * Sets up the given header {@code (name, value)} with possibly sensitive + * value. + * + *

    If the {@code value} is sensitive (think security, secrecy, etc.) + * this encoder will compress it using a special representation + * (see 6.2.3. Literal Header Field Never Indexed). + * + *

    Fixates {@code name} and {@code value} for the duration of encoding. + * + * @param name + * the name + * @param value + * the value + * @param sensitive + * whether or not the value is sensitive + * + * @throws NullPointerException + * if any of the arguments are {@code null} + * @throws IllegalStateException + * if the encoder hasn't fully encoded the previous header, or + * hasn't yet started to encode it + * @see #header(CharSequence, CharSequence) + * @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean) + */ + public void header(CharSequence name, + CharSequence value, + boolean sensitive) throws IllegalStateException { + if (sensitive || getMaxCapacity() == 0) { + super.header(name, value, true); + } else { + header(name, value, false, (n,v) -> false); + } + } + /** + * Sets up the given header {@code (name, value)} with possibly sensitive + * value. + * + *

    If the {@code value} is sensitive (think security, secrecy, etc.) + * this encoder will compress it using a special representation + * (see 6.2.3. Literal Header Field Never Indexed). + * + *

    Fixates {@code name} and {@code value} for the duration of encoding. + * + * @param name + * the name + * @param value + * the value + * @param insertionPolicy + * a bipredicate to indicate whether a name value pair + * should be added to the dynamic table + * + * @throws NullPointerException + * if any of the arguments are {@code null} + * @throws IllegalStateException + * if the encoder hasn't fully encoded the previous header, or + * hasn't yet started to encode it + * @see #header(CharSequence, CharSequence) + * @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean) + */ + public void header(CharSequence name, + CharSequence value, + BiPredicate insertionPolicy) + throws IllegalStateException { + header(name, value, false, insertionPolicy); + } + + /** + * Sets up the given header {@code (name, value)} with possibly sensitive + * value. + * + *

    If the {@code value} is sensitive (think security, secrecy, etc.) + * this encoder will compress it using a special representation + * (see + * 6.2.3. Literal Header Field Never Indexed). + * + *

    Fixates {@code name} and {@code value} for the duration of encoding. + * + * @param name + * the name + * @param value + * the value + * @param sensitive + * whether or not the value is sensitive + * @param insertionPolicy + * a bipredicate to indicate whether a name value pair + * should be added to the dynamic table + * + * @throws NullPointerException + * if any of the arguments are {@code null} + * @throws IllegalStateException + * if the encoder hasn't fully encoded the previous header, or + * hasn't yet started to encode it + * @see #header(CharSequence, CharSequence) + * @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean) + */ + public void header(CharSequence name, + CharSequence value, + boolean sensitive, + BiPredicate insertionPolicy) + throws IllegalStateException { + if (sensitive == true || getMaxCapacity() == 0 || !insertionPolicy.test(name, value)) { + super.header(name, value, sensitive); + return; + } + var logger = logger(); + // Arguably a good balance between complexity of implementation and + // efficiency of encoding + requireNonNull(name, "name"); + requireNonNull(value, "value"); + var t = getHeaderTable(); + int index = tableIndexOf(name, value); + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("encoding with indexing ('%s', '%s'): index:%s", + name, value, index)); + } + if (index > 0) { + indexed(index); + } else { + boolean huffmanValue = isHuffmanBetterFor(value); + if (index < 0) { + literalWithIndexing(-index, value, huffmanValue); + } else { + boolean huffmanName = isHuffmanBetterFor(name); + literalWithIndexing(name, huffmanName, value, huffmanValue); + } + } + } + + protected int calculateCapacity(int maxCapacity) { + return maxCapacity; + } + +} diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java index d982349dac5..828c939f53f 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java @@ -29,9 +29,12 @@ import java.net.URI; import java.net.InetSocketAddress; import java.net.http.HttpHeaders; +import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.function.BiPredicate; import javax.net.ssl.SSLSession; import jdk.internal.net.http.common.HttpHeadersBuilder; +import jdk.internal.net.http.frame.Http2Frame; public interface Http2TestExchange { @@ -53,6 +56,12 @@ public interface Http2TestExchange { void sendResponseHeaders(int rCode, long responseLength) throws IOException; + default void sendResponseHeaders(int rCode, long responseLength, + BiPredicate insertionPolicy) + throws IOException { + sendResponseHeaders(rCode, responseLength); + } + InetSocketAddress getRemoteAddress(); int getResponseCode(); @@ -65,6 +74,10 @@ public interface Http2TestExchange { void serverPush(URI uri, HttpHeaders headers, InputStream content); + default void sendFrames(List frames) throws IOException { + throw new UnsupportedOperationException("not implemented"); + } + /** * Send a PING on this exchanges connection, and completes the returned CF * with the number of milliseconds it took to get a valid response. diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java index d25019f9094..fa7589c0232 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java @@ -27,6 +27,7 @@ import jdk.internal.net.http.common.HttpHeadersBuilder; import jdk.internal.net.http.frame.HeaderFrame; import jdk.internal.net.http.frame.HeadersFrame; +import jdk.internal.net.http.frame.Http2Frame; import jdk.internal.net.http.frame.ResetFrame; import javax.net.ssl.SSLSession; @@ -39,6 +40,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.function.BiPredicate; public class Http2TestExchangeImpl implements Http2TestExchange { @@ -132,8 +134,13 @@ public OutputStream getResponseBody() { return os; } - @Override public void sendResponseHeaders(int rCode, long responseLength) throws IOException { + sendResponseHeaders(rCode, responseLength, (n,v) -> false); + } + @Override + public void sendResponseHeaders(int rCode, long responseLength, + BiPredicate insertionPolicy) + throws IOException { // Do not set Content-Length for 100, and do not set END_STREAM if (rCode == 100) responseLength = 0; @@ -147,7 +154,7 @@ public void sendResponseHeaders(int rCode, long responseLength) throws IOExcepti HttpHeaders headers = rspheadersBuilder.build(); ResponseHeaders response - = new ResponseHeaders(headers); + = new ResponseHeaders(headers, insertionPolicy); response.streamid(streamid); response.setFlag(HeaderFrame.END_HEADERS); @@ -172,6 +179,11 @@ public void sendResponseHeaders(ResponseHeaders response) throws IOException { conn.outputQ.put(response); } + @Override + public void sendFrames(List frames) throws IOException { + conn.sendFrames(frames); + } + @Override public InetSocketAddress getRemoteAddress() { return (InetSocketAddress) conn.socket.getRemoteSocketAddress(); diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java index 1aeeee60b19..c98e986ca85 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java @@ -24,6 +24,8 @@ package jdk.httpclient.test.lib.http2; import jdk.internal.net.http.common.HttpHeadersBuilder; +import jdk.internal.net.http.common.Log; +import jdk.internal.net.http.frame.ContinuationFrame; import jdk.internal.net.http.frame.DataFrame; import jdk.internal.net.http.frame.ErrorFrame; import jdk.internal.net.http.frame.FramesDecoder; @@ -80,6 +82,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.Predicate; @@ -87,6 +90,7 @@ import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.UTF_8; import static jdk.internal.net.http.frame.ErrorFrame.REFUSED_STREAM; +import static jdk.internal.net.http.frame.SettingsFrame.DEFAULT_MAX_FRAME_SIZE; import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE; /** @@ -105,7 +109,7 @@ public class Http2TestServerConnection { final Http2TestExchangeSupplier exchangeSupplier; final InputStream is; final OutputStream os; - volatile Encoder hpackOut; + volatile HpackTestEncoder hpackOut; volatile Decoder hpackIn; volatile SettingsFrame clientSettings; final SettingsFrame serverSettings; @@ -421,7 +425,9 @@ private SettingsFrame getSettingsFromString(String s) throws IOException { } public int getMaxFrameSize() { - return clientSettings.getParameter(SettingsFrame.MAX_FRAME_SIZE); + var max = clientSettings.getParameter(SettingsFrame.MAX_FRAME_SIZE); + if (max <= 0) max = DEFAULT_MAX_FRAME_SIZE; + return max; } /** Sends a pre-canned HTTP/1.1 response. */ @@ -482,7 +488,7 @@ void run() throws Exception { //System.out.println("ServerSettings: " + serverSettings); //System.out.println("ClientSettings: " + clientSettings); - hpackOut = new Encoder(serverSettings.getParameter(HEADER_TABLE_SIZE)); + hpackOut = new HpackTestEncoder(serverSettings.getParameter(HEADER_TABLE_SIZE)); hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE)); if (!secure) { @@ -812,6 +818,14 @@ headers, rspheadersBuilder, uri, bis, getSSLSession(), } } + public void sendFrames(List frames) throws IOException { + synchronized (outputQ) { + for (var frame : frames) { + outputQ.put(frame); + } + } + } + protected HttpHeadersBuilder createNewHeadersBuilder() { return new HttpHeadersBuilder(); } @@ -938,26 +952,38 @@ static boolean isServerStreamId(int streamid) { return (streamid & 0x01) == 0x00; } + final ReentrantLock headersLock = new ReentrantLock(); + /** Encodes an group of headers, without any ordering guarantees. */ public List encodeHeaders(HttpHeaders headers) { + return encodeHeaders(headers, (n,v) -> false); + } + + public List encodeHeaders(HttpHeaders headers, + BiPredicate insertionPolicy) { List buffers = new LinkedList<>(); ByteBuffer buf = getBuffer(); boolean encoded; - for (Map.Entry> entry : headers.map().entrySet()) { - List values = entry.getValue(); - String key = entry.getKey().toLowerCase(); - for (String value : values) { - do { - hpackOut.header(key, value); - encoded = hpackOut.encode(buf); - if (!encoded) { - buf.flip(); - buffers.add(buf); - buf = getBuffer(); - } - } while (!encoded); + headersLock.lock(); + try { + for (Map.Entry> entry : headers.map().entrySet()) { + List values = entry.getValue(); + String key = entry.getKey().toLowerCase(); + for (String value : values) { + hpackOut.header(key, value, insertionPolicy); + do { + encoded = hpackOut.encode(buf); + if (!encoded && !buf.hasRemaining()) { + buf.flip(); + buffers.add(buf); + buf = getBuffer(); + } + } while (!encoded); + } } + } finally { + headersLock.unlock(); } buf.flip(); buffers.add(buf); @@ -970,18 +996,23 @@ public List encodeHeadersOrdered(List> head ByteBuffer buf = getBuffer(); boolean encoded; - for (Map.Entry entry : headers) { - String value = entry.getValue(); - String key = entry.getKey().toLowerCase(); - do { + headersLock.lock(); + try { + for (Map.Entry entry : headers) { + String value = entry.getValue(); + String key = entry.getKey().toLowerCase(); hpackOut.header(key, value); - encoded = hpackOut.encode(buf); - if (!encoded) { - buf.flip(); - buffers.add(buf); - buf = getBuffer(); - } - } while (!encoded); + do { + encoded = hpackOut.encode(buf); + if (!encoded && !buf.hasRemaining()) { + buf.flip(); + buffers.add(buf); + buf = getBuffer(); + } + } while (!encoded); + } + } finally { + headersLock.unlock(); } buf.flip(); buffers.add(buf); @@ -1008,10 +1039,50 @@ void writeLoop() { break; } else throw x; } - if (frame instanceof ResponseHeaders) { - ResponseHeaders rh = (ResponseHeaders)frame; - HeadersFrame hf = new HeadersFrame(rh.streamid(), rh.getFlags(), encodeHeaders(rh.headers)); - writeFrame(hf); + if (frame instanceof ResponseHeaders rh) { + var buffers = encodeHeaders(rh.headers, rh.insertionPolicy); + int maxFrameSize = Math.min(rh.getMaxFrameSize(), getMaxFrameSize() - 64); + int next = 0; + int cont = 0; + do { + // If the total size of headers exceeds the max frame + // size we need to split the headers into one + // HeadersFrame + N x ContinuationFrames + int remaining = maxFrameSize; + var list = new ArrayList(buffers.size()); + for (; next < buffers.size(); next++) { + var b = buffers.get(next); + var len = b.remaining(); + if (!b.hasRemaining()) continue; + if (len <= remaining) { + remaining -= len; + list.add(b); + } else { + if (next == 0) { + list.add(b.slice(b.position(), remaining)); + b.position(b.position() + remaining); + remaining = 0; + } + break; + } + } + int flags = rh.getFlags(); + if (next != buffers.size()) { + flags = flags & ~HeadersFrame.END_HEADERS; + } + if (cont > 0) { + flags = flags & ~HeadersFrame.END_STREAM; + } + HeaderFrame hf = cont == 0 + ? new HeadersFrame(rh.streamid(), flags, list) + : new ContinuationFrame(rh.streamid(), flags, list); + if (Log.headers()) { + // avoid too much chatter: log only if Log.headers() is enabled + System.err.println("TestServer writing " + hf); + } + writeFrame(hf); + cont++; + } while (next < buffers.size()); } else if (frame instanceof OutgoingPushPromise) { handlePush((OutgoingPushPromise)frame); } else @@ -1322,11 +1393,29 @@ synchronized void updateConnectionWindow(int amount) { // for the hashmap. public static class ResponseHeaders extends Http2Frame { - HttpHeaders headers; + final HttpHeaders headers; + final BiPredicate insertionPolicy; + + final int maxFrameSize; public ResponseHeaders(HttpHeaders headers) { + this(headers, (n,v) -> false); + } + public ResponseHeaders(HttpHeaders headers, BiPredicate insertionPolicy) { + this(headers, insertionPolicy, Integer.MAX_VALUE); + } + + public ResponseHeaders(HttpHeaders headers, + BiPredicate insertionPolicy, + int maxFrameSize) { super(0, 0); this.headers = headers; + this.insertionPolicy = insertionPolicy; + this.maxFrameSize = maxFrameSize; + } + + public int getMaxFrameSize() { + return maxFrameSize; } } From cfa25b71a65bfff1b31efe0d37ded37c50a98247 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 21 May 2024 10:31:14 +0000 Subject: [PATCH 56/95] 8328544: Improve handling of vectorization Co-authored-by: Christian Hagedorn Reviewed-by: mschoene, kvn, chagedorn, rhalade --- src/hotspot/share/opto/vectorization.cpp | 472 +++++++++++++++++- src/hotspot/share/opto/vectorization.hpp | 89 +++- .../TestVectorizationMismatchedAccess.java | 335 +++++++++++-- .../loopopts/superword/TestAlignVector.java | 8 +- .../superword/TestAlignVectorFuzzer.java | 20 +- ...tIndependentPacksWithCyclicDependency.java | 140 +++--- ...IndependentPacksWithCyclicDependency2.java | 28 +- .../TestScheduleReordersScalarMemops.java | 8 +- 8 files changed, 935 insertions(+), 165 deletions(-) diff --git a/src/hotspot/share/opto/vectorization.cpp b/src/hotspot/share/opto/vectorization.cpp index 8d2d3868fe6..f013abb38fb 100644 --- a/src/hotspot/share/opto/vectorization.cpp +++ b/src/hotspot/share/opto/vectorization.cpp @@ -416,6 +416,10 @@ VPointer::VPointer(MemNode* const mem, const VLoop& vloop, #ifdef ASSERT _debug_invar(nullptr), _debug_negate_invar(false), _debug_invar_scale(nullptr), #endif + _has_int_index_after_convI2L(false), + _int_index_after_convI2L_offset(0), + _int_index_after_convI2L_invar(nullptr), + _int_index_after_convI2L_scale(0), _nstack(nstack), _analyze_only(analyze_only), _stack_idx(0) #ifndef PRODUCT , _tracer(vloop.is_trace_pointer_analysis()) @@ -495,6 +499,11 @@ VPointer::VPointer(MemNode* const mem, const VLoop& vloop, return; } + if (!is_safe_to_use_as_simple_form(base, adr)) { + assert(!valid(), "does not have simple form"); + return; + } + _base = base; _adr = adr; assert(valid(), "Usable"); @@ -508,6 +517,10 @@ VPointer::VPointer(VPointer* p) : #ifdef ASSERT _debug_invar(nullptr), _debug_negate_invar(false), _debug_invar_scale(nullptr), #endif + _has_int_index_after_convI2L(false), + _int_index_after_convI2L_offset(0), + _int_index_after_convI2L_invar(nullptr), + _int_index_after_convI2L_scale(0), _nstack(p->_nstack), _analyze_only(p->_analyze_only), _stack_idx(p->_stack_idx) #ifndef PRODUCT , _tracer(p->_tracer._is_trace_alignment) @@ -530,6 +543,354 @@ int VPointer::invar_factor() const { return 1; } +// We would like to make decisions about aliasing (i.e. removing memory edges) and adjacency +// (i.e. which loads/stores can be packed) based on the simple form: +// +// s_pointer = adr + offset + invar + scale * ConvI2L(iv) +// +// However, we parse the compound-long-int form: +// +// c_pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_index) +// int_index = int_offset + int_invar + int_scale * iv +// +// In general, the simple and the compound-long-int form do not always compute the same pointer +// at runtime. For example, the simple form would give a different result due to an overflow +// in the int_index. +// +// Example: +// For both forms, we have: +// iv = 0 +// scale = 1 +// +// We now account the offset and invar once to the long part and once to the int part: +// Pointer 1 (long offset and long invar): +// long_offset = min_int +// long_invar = min_int +// int_offset = 0 +// int_invar = 0 +// +// Pointer 2 (int offset and int invar): +// long_offset = 0 +// long_invar = 0 +// int_offset = min_int +// int_invar = min_int +// +// This gives us the following pointers: +// Compound-long-int form pointers: +// Form: +// c_pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_offset + int_invar + int_scale * iv) +// +// Pointers: +// c_pointer1 = adr + min_int + min_int + 1 * ConvI2L(0 + 0 + 1 * 0) +// = adr + min_int + min_int +// = adr - 2^32 +// +// c_pointer2 = adr + 0 + 0 + 1 * ConvI2L(min_int + min_int + 1 * 0) +// = adr + ConvI2L(min_int + min_int) +// = adr + 0 +// = adr +// +// Simple form pointers: +// Form: +// s_pointer = adr + offset + invar + scale * ConvI2L(iv) +// s_pointer = adr + (long_offset + int_offset) + (long_invar + int_invar) + (long_scale * int_scale) * ConvI2L(iv) +// +// Pointers: +// s_pointer1 = adr + (min_int + 0 ) + (min_int + 0 ) + 1 * 0 +// = adr + min_int + min_int +// = adr - 2^32 +// s_pointer2 = adr + (0 + min_int ) + (0 + min_int ) + 1 * 0 +// = adr + min_int + min_int +// = adr - 2^32 +// +// We see that the two addresses are actually 2^32 bytes apart (derived from the c_pointers), but their simple form look identical. +// +// Hence, we need to determine in which cases it is safe to make decisions based on the simple +// form, rather than the compound-long-int form. If we cannot prove that using the simple form +// is safe (i.e. equivalent to the compound-long-int form), then we do not get a valid VPointer, +// and the associated memop cannot be vectorized. +bool VPointer::is_safe_to_use_as_simple_form(Node* base, Node* adr) const { +#ifndef _LP64 + // On 32-bit platforms, there is never an explicit int_index with ConvI2L for the iv. Thus, the + // parsed pointer form is always the simple form, with int operations: + // + // pointer = adr + offset + invar + scale * iv + // + assert(!_has_int_index_after_convI2L, "32-bit never has an int_index with ConvI2L for the iv"); + return true; +#else + + // Array accesses that are not Unsafe always have a RangeCheck which ensures that there is no + // int_index overflow. This implies that the conversion to long can be done separately: + // + // ConvI2L(int_index) = ConvI2L(int_offset) + ConvI2L(int_invar) + ConvI2L(scale) * ConvI2L(iv) + // + // And hence, the simple form is guaranteed to be identical to the compound-long-int form at + // runtime and the VPointer is safe/valid to be used. + const TypeAryPtr* ary_ptr_t = _mem->adr_type()->isa_aryptr(); + if (ary_ptr_t != nullptr) { + if (!_mem->is_unsafe_access()) { + return true; + } + } + + // We did not find the int_index. Just to be safe, reject this VPointer. + if (!_has_int_index_after_convI2L) { + return false; + } + + int int_offset = _int_index_after_convI2L_offset; + Node* int_invar = _int_index_after_convI2L_invar; + int int_scale = _int_index_after_convI2L_scale; + int long_scale = _scale / int_scale; + + // If "int_index = iv", then the simple form is identical to the compound-long-int form. + // + // int_index = int_offset + int_invar + int_scale * iv + // = 0 0 1 * iv + // = iv + if (int_offset == 0 && int_invar == nullptr && int_scale == 1) { + return true; + } + + // Intuition: What happens if the int_index overflows? Let us look at two pointers on the "overflow edge": + // + // pointer1 = adr + ConvI2L(int_index1) + // pointer2 = adr + ConvI2L(int_index2) + // + // int_index1 = max_int + 0 = max_int -> very close to but before the overflow + // int_index2 = max_int + 1 = min_int -> just enough to get the overflow + // + // When looking at the difference of pointer1 and pointer2, we notice that it is very large + // (almost 2^32). Since arrays have at most 2^31 elements, chances are high that pointer2 is + // an actual out-of-bounds access at runtime. These would normally be prevented by range checks + // at runtime. However, if the access was done by using Unsafe, where range checks are omitted, + // then an out-of-bounds access constitutes undefined behavior. This means that we are allowed to + // do anything, including changing the behavior. + // + // If we can set the right conditions, we have a guarantee that an overflow is either impossible + // (no overflow or range checks preventing that) or undefined behavior. In both cases, we are + // safe to do a vectorization. + // + // Approach: We want to prove a lower bound for the distance between these two pointers, and an + // upper bound for the size of a memory object. We can derive such an upper bound for + // arrays. We know they have at most 2^31 elements. If we know the size of the elements + // in bytes, we have: + // + // array_element_size_in_bytes * 2^31 >= max_possible_array_size_in_bytes + // >= array_size_in_bytes (ARR) + // + // If some small difference "delta" leads to an int_index overflow, we know that the + // int_index1 before overflow must have been close to max_int, and the int_index2 after + // the overflow must be close to min_int: + // + // pointer1 = adr + long_offset + long_invar + long_scale * ConvI2L(int_index1) + // =approx adr + long_offset + long_invar + long_scale * max_int + // + // pointer2 = adr + long_offset + long_invar + long_scale * ConvI2L(int_index2) + // =approx adr + long_offset + long_invar + long_scale * min_int + // + // We realize that the pointer difference is very large: + // + // difference =approx long_scale * 2^32 + // + // Hence, if we set the right condition for long_scale and array_element_size_in_bytes, + // we can prove that an overflow is impossible (or would imply undefined behaviour). + // + // We must now take this intuition, and develop a rigorous proof. We start by stating the problem + // more precisely, with the help of some definitions and the Statement we are going to prove. + // + // Definition: + // Two VPointers are "comparable" (i.e. VPointer::comparable is true, set with VPointer::cmp()), + // iff all of these conditions apply for the simple form: + // 1) Both VPointers are valid. + // 2) The adr are identical, or both are array bases of different arrays. + // 3) They have identical scale. + // 4) They have identical invar. + // 5) The difference in offsets is limited: abs(offset1 - offset2) < 2^31. (DIFF) + // + // For the Vectorization Optimization, we pair-wise compare VPointers and determine if they are: + // 1) "not comparable": + // We do not optimize them (assume they alias, not assume adjacency). + // + // Whenever we chose this option based on the simple form, it is also correct based on the + // compound-long-int form, since we make no optimizations based on it. + // + // 2) "comparable" with different array bases at runtime: + // We assume they do not alias (remove memory edges), but not assume adjacency. + // + // Whenever we have two different array bases for the simple form, we also have different + // array bases for the compound-long-form. Since VPointers provably point to different + // memory objects, they can never alias. + // + // 3) "comparable" with the same base address: + // We compute the relative pointer difference, and based on the load/store size we can + // compute aliasing and adjacency. + // + // We must find a condition under which the pointer difference of the simple form is + // identical to the pointer difference of the compound-long-form. We do this with the + // Statement below, which we then proceed to prove. + // + // Statement: + // If two VPointers satisfy these 3 conditions: + // 1) They are "comparable". + // 2) They have the same base address. + // 3) Their long_scale is a multiple of the array element size in bytes: + // + // abs(long_scale) % array_element_size_in_bytes = 0 (A) + // + // Then their pointer difference of the simple form is identical to the pointer difference + // of the compound-long-int form. + // + // More precisely: + // Such two VPointers by definition have identical adr, invar, and scale. + // Their simple form is: + // + // s_pointer1 = adr + offset1 + invar + scale * ConvI2L(iv) (B1) + // s_pointer2 = adr + offset2 + invar + scale * ConvI2L(iv) (B2) + // + // Thus, the pointer difference of the simple forms collapses to the difference in offsets: + // + // s_difference = s_pointer1 - s_pointer2 = offset1 - offset2 (C) + // + // Their compound-long-int form for these VPointer is: + // + // c_pointer1 = adr + long_offset1 + long_invar1 + long_scale1 * ConvI2L(int_index1) (D1) + // int_index1 = int_offset1 + int_invar1 + int_scale1 * iv (D2) + // + // c_pointer2 = adr + long_offset2 + long_invar2 + long_scale2 * ConvI2L(int_index2) (D3) + // int_index2 = int_offset2 + int_invar2 + int_scale2 * iv (D4) + // + // And these are the offset1, offset2, invar and scale from the simple form (B1) and (B2): + // + // offset1 = long_offset1 + long_scale1 * ConvI2L(int_offset1) (D5) + // offset2 = long_offset2 + long_scale2 * ConvI2L(int_offset2) (D6) + // + // invar = long_invar1 + long_scale1 * ConvI2L(int_invar1) + // = long_invar2 + long_scale2 * ConvI2L(int_invar2) (D7) + // + // scale = long_scale1 * ConvI2L(int_scale1) + // = long_scale2 * ConvI2L(int_scale2) (D8) + // + // The pointer difference of the compound-long-int form is defined as: + // + // c_difference = c_pointer1 - c_pointer2 + // + // Thus, the statement claims that for the two VPointer we have: + // + // s_difference = c_difference (Statement) + // + // We prove the Statement with the help of a Lemma: + // + // Lemma: + // There is some integer x, such that: + // + // c_difference = s_difference + array_element_size_in_bytes * x * 2^32 (Lemma) + // + // From condition (DIFF), we can derive: + // + // abs(s_difference) < 2^31 (E) + // + // Assuming the Lemma, we prove the Statement: + // If "x = 0" (intuitively: the int_index does not overflow), then: + // c_difference = s_difference + // and hence the simple form computes the same pointer difference as the compound-long-int form. + // If "x != 0" (intuitively: the int_index overflows), then: + // abs(c_difference) >= abs(s_difference + array_element_size_in_bytes * x * 2^32) + // >= array_element_size_in_bytes * 2^32 - abs(s_difference) + // -- apply (E) -- + // > array_element_size_in_bytes * 2^32 - 2^31 + // >= array_element_size_in_bytes * 2^31 + // -- apply (ARR) -- + // >= max_possible_array_size_in_bytes + // >= array_size_in_bytes + // + // This shows that c_pointer1 and c_pointer2 have a distance that exceeds the maximum array size. + // Thus, at least one of the two pointers must be outside of the array bounds. But we can assume + // that out-of-bounds accesses do not happen. If they still do, it is undefined behavior. Hence, + // we are allowed to do anything. We can also "safely" use the simple form in this case even though + // it might not match the compound-long-int form at runtime. + // QED Statement. + // + // We must now prove the Lemma. + // + // ConvI2L always truncates by some power of 2^32, i.e. there is some integer y such that: + // + // ConvI2L(y1 + y2) = ConvI2L(y1) + ConvI2L(y2) + 2^32 * y (F) + // + // It follows, that there is an integer y1 such that: + // + // ConvI2L(int_index1) = ConvI2L(int_offset1 + int_invar1 + int_scale1 * iv) + // -- apply (F) -- + // = ConvI2L(int_offset1) + // + ConvI2L(int_invar1) + // + ConvI2L(int_scale1) * ConvI2L(iv) + // + y1 * 2^32 (G) + // + // Thus, we can write the compound-long-int form (D1) as: + // + // c_pointer1 = adr + long_offset1 + long_invar1 + long_scale1 * ConvI2L(int_index1) + // -- apply (G) -- + // = adr + // + long_offset1 + // + long_invar1 + // + long_scale1 * ConvI2L(int_offset1) + // + long_scale1 * ConvI2L(int_invar1) + // + long_scale1 * ConvI2L(int_scale1) * ConvI2L(iv) + // + long_scale1 * y1 * 2^32 (H) + // + // And we can write the simple form as: + // + // s_pointer1 = adr + offset1 + invar + scale * ConvI2L(iv) + // -- apply (D5, D7, D8) -- + // = adr + // + long_offset1 + // + long_scale1 * ConvI2L(int_offset1) + // + long_invar1 + // + long_scale1 * ConvI2L(int_invar1) + // + long_scale1 * ConvI2L(int_scale1) * ConvI2L(iv) (K) + // + // We now compute the pointer difference between the simple (K) and compound-long-int form (H). + // Most terms cancel out immediately: + // + // sc_difference1 = c_pointer1 - s_pointer1 = long_scale1 * y1 * 2^32 (L) + // + // Rearranging the equation (L), we get: + // + // c_pointer1 = s_pointer1 + long_scale1 * y1 * 2^32 (M) + // + // And since long_scale1 is a multiple of array_element_size_in_bytes, there is some integer + // x1, such that (M) implies: + // + // c_pointer1 = s_pointer1 + array_element_size_in_bytes * x1 * 2^32 (N) + // + // With an analogue equation for c_pointer2, we can now compute the pointer difference for + // the compound-long-int form: + // + // c_difference = c_pointer1 - c_pointer2 + // -- apply (N) -- + // = s_pointer1 + array_element_size_in_bytes * x1 * 2^32 + // -(s_pointer2 + array_element_size_in_bytes * x2 * 2^32) + // -- where "x = x1 - x2" -- + // = s_pointer1 - s_pointer2 + array_element_size_in_bytes * x * 2^32 + // -- apply (C) -- + // = s_difference + array_element_size_in_bytes * x * 2^32 + // QED Lemma. + if (ary_ptr_t != nullptr) { + BasicType array_element_bt = ary_ptr_t->elem()->array_element_basic_type(); + if (is_java_primitive(array_element_bt)) { + int array_element_size_in_bytes = type2aelembytes(array_element_bt); + if (abs(long_scale) % array_element_size_in_bytes == 0) { + return true; + } + } + } + + // General case: we do not know if it is safe to use the simple form. + return false; +#endif +} + bool VPointer::is_loop_member(Node* n) const { Node* n_c = phase()->get_ctrl(n); return lpt()->is_member(phase()->get_loop(n_c)); @@ -632,6 +993,37 @@ bool VPointer::scaled_iv(Node* n) { NOT_PRODUCT(_tracer.scaled_iv_6(n, _scale);) return true; } + } else if (opc == Op_ConvI2L && !has_iv()) { + // So far we have not found the iv yet, and are about to enter a ConvI2L subgraph, + // which may be the int index (that might overflow) for the memory access, of the form: + // + // int_index = int_offset + int_invar + int_scale * iv + // + // If we simply continue parsing with the current VPointer, then the int_offset and + // int_invar simply get added to the long offset and invar. But for the checks in + // VPointer::is_safe_to_use_as_simple_form() we need to have explicit access to the + // int_index. Thus, we must parse it explicitly here. For this, we use a temporary + // VPointer, to pattern match the int_index sub-expression of the address. + + NOT_PRODUCT(Tracer::Depth dddd;) + VPointer tmp(this); + NOT_PRODUCT(_tracer.scaled_iv_8(n, &tmp);) + + if (tmp.scaled_iv_plus_offset(n->in(1)) && tmp.has_iv()) { + // We successfully matched an integer index, of the form: + // int_index = int_offset + int_invar + int_scale * iv + _has_int_index_after_convI2L = true; + _int_index_after_convI2L_offset = tmp._offset; + _int_index_after_convI2L_invar = tmp._invar; + _int_index_after_convI2L_scale = tmp._scale; + } + + // Now parse it again for the real VPointer. This makes sure that the int_offset, int_invar, + // and int_scale are properly added to the final VPointer's offset, invar, and scale. + if (scaled_iv_plus_offset(n->in(1))) { + NOT_PRODUCT(_tracer.scaled_iv_7(n);) + return true; + } } else if (opc == Op_ConvI2L || opc == Op_CastII) { if (scaled_iv_plus_offset(n->in(1))) { NOT_PRODUCT(_tracer.scaled_iv_7(n);) @@ -648,8 +1040,17 @@ bool VPointer::scaled_iv(Node* n) { if (tmp.scaled_iv_plus_offset(n->in(1))) { int scale = n->in(2)->get_int(); + // Accumulate scale. _scale = tmp._scale << scale; - _offset += tmp._offset << scale; + // Accumulate offset. + int shifted_offset = 0; + if (!try_LShiftI_no_overflow(tmp._offset, scale, shifted_offset)) { + return false; // shift overflow. + } + if (!try_AddI_no_overflow(_offset, shifted_offset, _offset)) { + return false; // add overflow. + } + // Accumulate invar. if (tmp._invar != nullptr) { BasicType bt = tmp._invar->bottom_type()->basic_type(); assert(bt == T_INT || bt == T_LONG, ""); @@ -658,6 +1059,13 @@ bool VPointer::scaled_iv(Node* n) { _debug_invar_scale = n->in(2); #endif } + + // Forward info about the int_index: + _has_int_index_after_convI2L = tmp._has_int_index_after_convI2L; + _int_index_after_convI2L_offset = tmp._int_index_after_convI2L_offset; + _int_index_after_convI2L_invar = tmp._int_index_after_convI2L_invar; + _int_index_after_convI2L_scale = tmp._int_index_after_convI2L_scale; + NOT_PRODUCT(_tracer.scaled_iv_9(n, _scale, _offset, _invar);) return true; } @@ -675,7 +1083,9 @@ bool VPointer::offset_plus_k(Node* n, bool negate) { int opc = n->Opcode(); if (opc == Op_ConI) { - _offset += negate ? -(n->get_int()) : n->get_int(); + if (!try_AddSubI_no_overflow(_offset, n->get_int(), negate, _offset)) { + return false; // add/sub overflow. + } NOT_PRODUCT(_tracer.offset_plus_k_2(n, _offset);) return true; } else if (opc == Op_ConL) { @@ -684,7 +1094,9 @@ bool VPointer::offset_plus_k(Node* n, bool negate) { if (t->higher_equal(TypeLong::INT)) { jlong loff = n->get_long(); jint off = (jint)loff; - _offset += negate ? -off : loff; + if (!try_AddSubI_no_overflow(_offset, off, negate, _offset)) { + return false; // add/sub overflow. + } NOT_PRODUCT(_tracer.offset_plus_k_3(n, _offset);) return true; } @@ -699,11 +1111,15 @@ bool VPointer::offset_plus_k(Node* n, bool negate) { if (opc == Op_AddI) { if (n->in(2)->is_Con() && invariant(n->in(1))) { maybe_add_to_invar(n->in(1), negate); - _offset += negate ? -(n->in(2)->get_int()) : n->in(2)->get_int(); + if (!try_AddSubI_no_overflow(_offset, n->in(2)->get_int(), negate, _offset)) { + return false; // add/sub overflow. + } NOT_PRODUCT(_tracer.offset_plus_k_6(n, _invar, negate, _offset);) return true; } else if (n->in(1)->is_Con() && invariant(n->in(2))) { - _offset += negate ? -(n->in(1)->get_int()) : n->in(1)->get_int(); + if (!try_AddSubI_no_overflow(_offset, n->in(1)->get_int(), negate, _offset)) { + return false; // add/sub overflow. + } maybe_add_to_invar(n->in(2), negate); NOT_PRODUCT(_tracer.offset_plus_k_7(n, _invar, negate, _offset);) return true; @@ -712,11 +1128,15 @@ bool VPointer::offset_plus_k(Node* n, bool negate) { if (opc == Op_SubI) { if (n->in(2)->is_Con() && invariant(n->in(1))) { maybe_add_to_invar(n->in(1), negate); - _offset += !negate ? -(n->in(2)->get_int()) : n->in(2)->get_int(); + if (!try_AddSubI_no_overflow(_offset, n->in(2)->get_int(), !negate, _offset)) { + return false; // add/sub overflow. + } NOT_PRODUCT(_tracer.offset_plus_k_8(n, _invar, negate, _offset);) return true; } else if (n->in(1)->is_Con() && invariant(n->in(2))) { - _offset += negate ? -(n->in(1)->get_int()) : n->in(1)->get_int(); + if (!try_AddSubI_no_overflow(_offset, n->in(1)->get_int(), negate, _offset)) { + return false; // add/sub overflow. + } maybe_add_to_invar(n->in(2), !negate); NOT_PRODUCT(_tracer.offset_plus_k_9(n, _invar, !negate, _offset);) return true; @@ -806,6 +1226,44 @@ void VPointer::maybe_add_to_invar(Node* new_invar, bool negate) { _invar = register_if_new(add); } +bool VPointer::try_AddI_no_overflow(int offset1, int offset2, int& result) { + jlong long_offset = java_add((jlong)(offset1), (jlong)(offset2)); + jint int_offset = java_add( offset1, offset2); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + +bool VPointer::try_SubI_no_overflow(int offset1, int offset2, int& result) { + jlong long_offset = java_subtract((jlong)(offset1), (jlong)(offset2)); + jint int_offset = java_subtract( offset1, offset2); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + +bool VPointer::try_AddSubI_no_overflow(int offset1, int offset2, bool is_sub, int& result) { + if (is_sub) { + return try_SubI_no_overflow(offset1, offset2, result); + } else { + return try_AddI_no_overflow(offset1, offset2, result); + } +} + +bool VPointer::try_LShiftI_no_overflow(int offset, int shift, int& result) { + jlong long_offset = java_shift_left((jlong)(offset), shift); + jint int_offset = java_shift_left( offset, shift); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + // We use two comparisons, because a subtraction could underflow. #define RETURN_CMP_VALUE_IF_NOT_EQUAL(a, b) \ if (a < b) { return -1; } \ diff --git a/src/hotspot/share/opto/vectorization.hpp b/src/hotspot/share/opto/vectorization.hpp index 3984407c565..7de29444e5f 100644 --- a/src/hotspot/share/opto/vectorization.hpp +++ b/src/hotspot/share/opto/vectorization.hpp @@ -670,13 +670,51 @@ class VLoopAnalyzer : StackObj { // A vectorization pointer (VPointer) has information about an address for // dependence checking and vector alignment. It's usually bound to a memory // operation in a counted loop for vectorizable analysis. +// +// We parse and represent pointers of the simple form: +// +// pointer = adr + offset + invar + scale * ConvI2L(iv) +// +// Where: +// +// adr: the base address of an array (base = adr) +// OR +// some address to off-heap memory (base = TOP) +// +// offset: a constant offset +// invar: a runtime variable, which is invariant during the loop +// scale: scaling factor +// iv: loop induction variable +// +// But more precisely, we parse the composite-long-int form: +// +// pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_offset + inv_invar + int_scale * iv) +// +// pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_index) +// int_index = int_offset + int_invar + int_scale * iv +// +// However, for aliasing and adjacency checks (e.g. VPointer::cmp()) we always use the simple form to make +// decisions. Hence, we must make sure to only create a "valid" VPointer if the optimisations based on the +// simple form produce the same result as the compound-long-int form would. Intuitively, this depends on +// if the int_index overflows, but the precise conditions are given in VPointer::is_safe_to_use_as_simple_form(). +// +// ConvI2L(int_index) = ConvI2L(int_offset + int_invar + int_scale * iv) +// = Convi2L(int_offset) + ConvI2L(int_invar) + ConvI2L(int_scale) * ConvI2L(iv) +// +// scale = long_scale * ConvI2L(int_scale) +// offset = long_offset + long_scale * ConvI2L(int_offset) +// invar = long_invar + long_scale * ConvI2L(int_invar) +// +// pointer = adr + offset + invar + scale * ConvI2L(iv) +// class VPointer : public ArenaObj { protected: MemNode* const _mem; // My memory reference node const VLoop& _vloop; - Node* _base; // null if unsafe nonheap reference - Node* _adr; // address pointer + // Components of the simple form: + Node* _base; // Base address of an array OR null if some off-heap memory. + Node* _adr; // Same as _base if an array pointer OR some off-heap memory pointer. int _scale; // multiplier for iv (in bytes), 0 if no loop iv int _offset; // constant offset (in bytes) @@ -687,6 +725,13 @@ class VPointer : public ArenaObj { Node* _debug_invar_scale; // multiplier for invariant #endif + // The int_index components of the compound-long-int form. Used to decide if it is safe to use the + // simple form rather than the compound-long-int form that was parsed. + bool _has_int_index_after_convI2L; + int _int_index_after_convI2L_offset; + Node* _int_index_after_convI2L_invar; + int _int_index_after_convI2L_scale; + Node_Stack* _nstack; // stack used to record a vpointer trace of variants bool _analyze_only; // Used in loop unrolling only for vpointer trace uint _stack_idx; // Used in loop unrolling only for vpointer trace @@ -726,6 +771,8 @@ class VPointer : public ArenaObj { VPointer(VPointer* p); NONCOPYABLE(VPointer); + bool is_safe_to_use_as_simple_form(Node* base, Node* adr) const; + public: bool valid() const { return _adr != nullptr; } bool has_iv() const { return _scale != 0; } @@ -751,10 +798,43 @@ class VPointer : public ArenaObj { return _invar == q._invar; } + // We compute if and how two VPointers can alias at runtime, i.e. if the two addressed regions of memory can + // ever overlap. There are essentially 3 relevant return states: + // - NotComparable: Synonymous to "unknown aliasing". + // We have no information about how the two VPointers can alias. They could overlap, refer + // to another location in the same memory object, or point to a completely different object. + // -> Memory edge required. Aliasing unlikely but possible. + // + // - Less / Greater: Synonymous to "never aliasing". + // The two VPointers may point into the same memory object, but be non-aliasing (i.e. we + // know both address regions inside the same memory object, but these regions are non- + // overlapping), or the VPointers point to entirely different objects. + // -> No memory edge required. Aliasing impossible. + // + // - Equal: Synonymous to "overlap, or point to different memory objects". + // The two VPointers either overlap on the same memory object, or point to two different + // memory objects. + // -> Memory edge required. Aliasing likely. + // + // In a future refactoring, we can simplify to two states: + // - NeverAlias: instead of Less / Greater + // - MayAlias: instead of Equal / NotComparable + // + // Two VPointer are "comparable" (Less / Greater / Equal), iff all of these conditions apply: + // 1) Both are valid, i.e. expressible in the compound-long-int or simple form. + // 2) The adr are identical, or both are array bases of different arrays. + // 3) They have identical scale. + // 4) They have identical invar. + // 5) The difference in offsets is limited: abs(offset0 - offset1) < 2^31. int cmp(const VPointer& q) const { if (valid() && q.valid() && (_adr == q._adr || (_base == _adr && q._base == q._adr)) && _scale == q._scale && invar_equals(q)) { + jlong difference = abs(java_subtract((jlong)_offset, (jlong)q._offset)); + jlong max_diff = (jlong)1 << 31; + if (difference >= max_diff) { + return NotComparable; + } bool overlap = q._offset < _offset + memory_size() && _offset < q._offset + q.memory_size(); return overlap ? Equal : (_offset < q._offset ? Less : Greater); @@ -859,6 +939,11 @@ class VPointer : public ArenaObj { void maybe_add_to_invar(Node* new_invar, bool negate); + static bool try_AddI_no_overflow(int offset1, int offset2, int& result); + static bool try_SubI_no_overflow(int offset1, int offset2, int& result); + static bool try_AddSubI_no_overflow(int offset1, int offset2, bool is_sub, int& result); + static bool try_LShiftI_no_overflow(int offset1, int offset2, int& result); + Node* register_if_new(Node* n) const; }; diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java index b68ddfe2799..2fdbb0816ad 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2023, Red Hat, Inc. All rights reserved. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * 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 +36,6 @@ * @test * @bug 8300258 * @key randomness - * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") * @summary C2: vectorization fails on simple ByteBuffer loop * @modules java.base/jdk.internal.misc * @library /test/lib / @@ -147,193 +147,420 @@ static private void runAndVerify3(Runnable test, int offset) { } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testByteLong1(byte[] dest, long[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned). + // might get fixed with JDK-8325155. + public static void testByteLong1a(byte[] dest, long[] src) { for (int i = 0; i < src.length; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i, src[i]); } } - @Run(test = "testByteLong1") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + public static void testByteLong1b(byte[] dest, long[] src) { + for (int i = 0; i < src.length; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i, src[i]); + } + } + + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}) + public static void testByteLong1c(byte[] dest, long[] src) { + long base = 64; // make sure it is big enough and 8 byte aligned (required for 32-bit) + for (int i = 0; i < src.length - 8; i++) { + UNSAFE.putLongUnaligned(dest, base + 8 * i, src[i]); + } + } + + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + public static void testByteLong1d(byte[] dest, long[] src) { + long base = 64; // make sure it is big enough and 8 byte aligned (required for 32-bit) + for (int i = 0; i < src.length - 8; i++) { + UNSAFE.putLongUnaligned(dest, base + 8L * i, src[i]); + } + } + + @Run(test = {"testByteLong1a", "testByteLong1b", "testByteLong1c", "testByteLong1d"}) public static void testByteLong1_runner() { - runAndVerify(() -> testByteLong1(byteArray, longArray), 0); + runAndVerify(() -> testByteLong1a(byteArray, longArray), 0); + runAndVerify(() -> testByteLong1b(byteArray, longArray), 0); + testByteLong1c(byteArray, longArray); + testByteLong1d(byteArray, longArray); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testByteLong2(byte[] dest, long[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned). + // might get fixed with JDK-8325155. + public static void testByteLong2a(byte[] dest, long[] src) { for (int i = 1; i < src.length; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i - 1), src[i]); } } - @Run(test = "testByteLong2") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + public static void testByteLong2b(byte[] dest, long[] src) { + for (int i = 1; i < src.length; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i - 1), src[i]); + } + } + + @Run(test = {"testByteLong2a", "testByteLong2b"}) public static void testByteLong2_runner() { - runAndVerify(() -> testByteLong2(byteArray, longArray), -8); + runAndVerify(() -> testByteLong2a(byteArray, longArray), -8); + runAndVerify(() -> testByteLong2b(byteArray, longArray), -8); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testByteLong3(byte[] dest, long[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned). + // might get fixed with JDK-8325155. + public static void testByteLong3a(byte[] dest, long[] src) { for (int i = 0; i < src.length - 1; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + 1), src[i]); } } - @Run(test = "testByteLong3") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + public static void testByteLong3b(byte[] dest, long[] src) { + for (int i = 0; i < src.length - 1; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + 1), src[i]); + } + } + + @Run(test = {"testByteLong3a", "testByteLong3b"}) public static void testByteLong3_runner() { - runAndVerify(() -> testByteLong3(byteArray, longArray), 8); + runAndVerify(() -> testByteLong3a(byteArray, longArray), 8); + runAndVerify(() -> testByteLong3b(byteArray, longArray), 8); } @Test @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}, applyIf = {"AlignVector", "false"}) + // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned). + // might get fixed with JDK-8325155. // AlignVector cannot guarantee that invar is aligned. - public static void testByteLong4(byte[] dest, long[] src, int start, int stop) { + public static void testByteLong4a(byte[] dest, long[] src, int start, int stop) { for (int i = start; i < stop; i++) { UNSAFE.putLongUnaligned(dest, 8 * i + baseOffset, src[i]); } } - @Run(test = "testByteLong4") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}, + applyIf = {"AlignVector", "false"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + // AlignVector cannot guarantee that invar is aligned. + public static void testByteLong4b(byte[] dest, long[] src, int start, int stop) { + for (int i = start; i < stop; i++) { + UNSAFE.putLongUnaligned(dest, 8L * i + baseOffset, src[i]); + } + } + + @Run(test = {"testByteLong4a", "testByteLong4b"}) public static void testByteLong4_runner() { baseOffset = UNSAFE.ARRAY_BYTE_BASE_OFFSET; - runAndVerify(() -> testByteLong4(byteArray, longArray, 0, size), 0); + runAndVerify(() -> testByteLong4a(byteArray, longArray, 0, size), 0); + runAndVerify(() -> testByteLong4b(byteArray, longArray, 0, size), 0); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testByteLong5(byte[] dest, long[] src, int start, int stop) { + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned). + // might get fixed with JDK-8325155. + public static void testByteLong5a(byte[] dest, long[] src, int start, int stop) { for (int i = start; i < stop; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + baseOffset), src[i]); } } - @Run(test = "testByteLong5") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + public static void testByteLong5b(byte[] dest, long[] src, int start, int stop) { + for (int i = start; i < stop; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + baseOffset), src[i]); + } + } + + @Run(test = {"testByteLong5a", "testByteLong5b"}) public static void testByteLong5_runner() { baseOffset = 1; - runAndVerify(() -> testByteLong5(byteArray, longArray, 0, size-1), 8); + runAndVerify(() -> testByteLong5a(byteArray, longArray, 0, size-1), 8); + runAndVerify(() -> testByteLong5b(byteArray, longArray, 0, size-1), 8); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testByteByte1(byte[] dest, byte[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned). + // might get fixed with JDK-8325155. + public static void testByteByte1a(byte[] dest, byte[] src) { for (int i = 0; i < src.length / 8; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i)); } } - @Run(test = "testByteByte1") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + public static void testByteByte1b(byte[] dest, byte[] src) { + for (int i = 0; i < src.length / 8; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i)); + } + } + + @Run(test = {"testByteByte1a", "testByteByte1b"}) public static void testByteByte1_runner() { - runAndVerify2(() -> testByteByte1(byteArray, byteArray), 0); + runAndVerify2(() -> testByteByte1a(byteArray, byteArray), 0); + runAndVerify2(() -> testByteByte1b(byteArray, byteArray), 0); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testByteByte2(byte[] dest, byte[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned). + // might get fixed with JDK-8325155. + public static void testByteByte2a(byte[] dest, byte[] src) { for (int i = 1; i < src.length / 8; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i - 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i)); } } - @Run(test = "testByteByte2") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + public static void testByteByte2b(byte[] dest, byte[] src) { + for (int i = 1; i < src.length / 8; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i - 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i)); + } + } + + @Run(test = {"testByteByte2a", "testByteByte2b"}) public static void testByteByte2_runner() { - runAndVerify2(() -> testByteByte2(byteArray, byteArray), -8); + runAndVerify2(() -> testByteByte2a(byteArray, byteArray), -8); + runAndVerify2(() -> testByteByte2b(byteArray, byteArray), -8); } @Test @IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR }) - public static void testByteByte3(byte[] dest, byte[] src) { + public static void testByteByte3a(byte[] dest, byte[] src) { for (int i = 0; i < src.length / 8 - 1; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i)); } } - @Run(test = "testByteByte3") + @Test + @IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR }) + public static void testByteByte3b(byte[] dest, byte[] src) { + for (int i = 0; i < src.length / 8 - 1; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i)); + } + } + + @Run(test = {"testByteByte3a", "testByteByte3b"}) public static void testByteByte3_runner() { - runAndVerify2(() -> testByteByte3(byteArray, byteArray), 8); + runAndVerify2(() -> testByteByte3a(byteArray, byteArray), 8); + runAndVerify2(() -> testByteByte3b(byteArray, byteArray), 8); } @Test @IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR }) - public static void testByteByte4(byte[] dest, byte[] src, int start, int stop) { + public static void testByteByte4a(byte[] dest, byte[] src, int start, int stop) { for (int i = start; i < stop; i++) { UNSAFE.putLongUnaligned(dest, 8 * i + baseOffset, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i)); } } - @Run(test = "testByteByte4") + @Test + @IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR }) + public static void testByteByte4b(byte[] dest, byte[] src, int start, int stop) { + for (int i = start; i < stop; i++) { + UNSAFE.putLongUnaligned(dest, 8L * i + baseOffset, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i)); + } + } + + @Run(test = {"testByteByte4a", "testByteByte4b"}) public static void testByteByte4_runner() { baseOffset = UNSAFE.ARRAY_BYTE_BASE_OFFSET; - runAndVerify2(() -> testByteByte4(byteArray, byteArray, 0, size), 0); + runAndVerify2(() -> testByteByte4a(byteArray, byteArray, 0, size), 0); + runAndVerify2(() -> testByteByte4b(byteArray, byteArray, 0, size), 0); } @Test @IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR }) - public static void testByteByte5(byte[] dest, byte[] src, int start, int stop) { + public static void testByteByte5a(byte[] dest, byte[] src, int start, int stop) { for (int i = start; i < stop; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + baseOffset), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i)); } } - @Run(test = "testByteByte5") + @Test + @IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR }) + public static void testByteByte5b(byte[] dest, byte[] src, int start, int stop) { + for (int i = start; i < stop; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + baseOffset), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i)); + } + } + + @Run(test = {"testByteByte5a", "testByteByte5b"}) public static void testByteByte5_runner() { baseOffset = 1; - runAndVerify2(() -> testByteByte5(byteArray, byteArray, 0, size-1), 8); + runAndVerify2(() -> testByteByte5a(byteArray, byteArray, 0, size-1), 8); + runAndVerify2(() -> testByteByte5b(byteArray, byteArray, 0, size-1), 8); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testOffHeapLong1(long dest, long[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) + // FAILS: adr is CastX2P(dest + 8 * (i + int_con)) + // See: JDK-8331576 + public static void testOffHeapLong1a(long dest, long[] src) { for (int i = 0; i < src.length; i++) { UNSAFE.putLongUnaligned(null, dest + 8 * i, src[i]); } } - @Run(test = "testOffHeapLong1") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) + // FAILS: adr is CastX2P(dest + 8L * (i + int_con)) + // See: JDK-8331576 + public static void testOffHeapLong1b(long dest, long[] src) { + for (int i = 0; i < src.length; i++) { + UNSAFE.putLongUnaligned(null, dest + 8L * i, src[i]); + } + } + + @Run(test = {"testOffHeapLong1a", "testOffHeapLong1b"}) public static void testOffHeapLong1_runner() { - runAndVerify3(() -> testOffHeapLong1(baseOffHeap, longArray), 0); + runAndVerify3(() -> testOffHeapLong1a(baseOffHeap, longArray), 0); + runAndVerify3(() -> testOffHeapLong1b(baseOffHeap, longArray), 0); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testOffHeapLong2(long dest, long[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) + // FAILS: adr is CastX2P + // See: JDK-8331576 + public static void testOffHeapLong2a(long dest, long[] src) { for (int i = 1; i < src.length; i++) { UNSAFE.putLongUnaligned(null, dest + 8 * (i - 1), src[i]); } } - @Run(test = "testOffHeapLong2") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) + // FAILS: adr is CastX2P + // See: JDK-8331576 + public static void testOffHeapLong2b(long dest, long[] src) { + for (int i = 1; i < src.length; i++) { + UNSAFE.putLongUnaligned(null, dest + 8L * (i - 1), src[i]); + } + } + + @Run(test = {"testOffHeapLong2a", "testOffHeapLong2b"}) public static void testOffHeapLong2_runner() { - runAndVerify3(() -> testOffHeapLong2(baseOffHeap, longArray), -8); + runAndVerify3(() -> testOffHeapLong2a(baseOffHeap, longArray), -8); + runAndVerify3(() -> testOffHeapLong2b(baseOffHeap, longArray), -8); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testOffHeapLong3(long dest, long[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) + // FAILS: adr is CastX2P + // See: JDK-8331576 + public static void testOffHeapLong3a(long dest, long[] src) { for (int i = 0; i < src.length - 1; i++) { UNSAFE.putLongUnaligned(null, dest + 8 * (i + 1), src[i]); } } - @Run(test = "testOffHeapLong3") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) + // FAILS: adr is CastX2P + // See: JDK-8331576 + public static void testOffHeapLong3b(long dest, long[] src) { + for (int i = 0; i < src.length - 1; i++) { + UNSAFE.putLongUnaligned(null, dest + 8L * (i + 1), src[i]); + } + } + + @Run(test = {"testOffHeapLong3a", "testOffHeapLong3b"}) public static void testOffHeapLong3_runner() { - runAndVerify3(() -> testOffHeapLong3(baseOffHeap, longArray), 8); + runAndVerify3(() -> testOffHeapLong3a(baseOffHeap, longArray), 8); + runAndVerify3(() -> testOffHeapLong3b(baseOffHeap, longArray), 8); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, - applyIf = {"AlignVector", "false"}) + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + // applyIf = {"AlignVector", "false"}) + // FAILS: adr is CastX2P + // See: JDK-8331576 // AlignVector cannot guarantee that invar is aligned. - public static void testOffHeapLong4(long dest, long[] src, int start, int stop) { + public static void testOffHeapLong4a(long dest, long[] src, int start, int stop) { for (int i = start; i < stop; i++) { UNSAFE.putLongUnaligned(null, dest + 8 * i + baseOffset, src[i]); } } - @Run(test = "testOffHeapLong4") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + // applyIf = {"AlignVector", "false"}) + // FAILS: adr is CastX2P + // See: JDK-8331576 + // AlignVector cannot guarantee that invar is aligned. + public static void testOffHeapLong4b(long dest, long[] src, int start, int stop) { + for (int i = start; i < stop; i++) { + UNSAFE.putLongUnaligned(null, dest + 8L * i + baseOffset, src[i]); + } + } + + @Run(test = {"testOffHeapLong4a", "testOffHeapLong4b"}) public static void testOffHeapLong4_runner() { baseOffset = 8; - runAndVerify3(() -> testOffHeapLong4(baseOffHeap, longArray, 0, size-1), 8); + runAndVerify3(() -> testOffHeapLong4a(baseOffHeap, longArray, 0, size-1), 8); + runAndVerify3(() -> testOffHeapLong4b(baseOffHeap, longArray, 0, size-1), 8); } } diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java index fd5c2969074..c77f4f6fa2e 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java @@ -1363,7 +1363,7 @@ static Object[] test16b(byte[] a) { static Object[] test17a(long[] a) { // Unsafe: vectorizes with profiling (not xcomp) for (int i = 0; i < RANGE; i++) { - int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i; + long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i; long v = UNSAFE.getLongUnaligned(a, adr); UNSAFE.putLongUnaligned(a, adr, v + 1); } @@ -1375,7 +1375,7 @@ static Object[] test17a(long[] a) { static Object[] test17b(long[] a) { // Not alignable for (int i = 0; i < RANGE-1; i++) { - int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i + 1; + long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i + 1; long v = UNSAFE.getLongUnaligned(a, adr); UNSAFE.putLongUnaligned(a, adr, v + 1); } @@ -1392,7 +1392,7 @@ static Object[] test17b(long[] a) { static Object[] test17c(long[] a) { // Unsafe: aligned vectorizes for (int i = 0; i < RANGE-1; i+=4) { - int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i; + long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i; long v0 = UNSAFE.getLongUnaligned(a, adr + 0); long v1 = UNSAFE.getLongUnaligned(a, adr + 8); UNSAFE.putLongUnaligned(a, adr + 0, v0 + 1); @@ -1422,7 +1422,7 @@ static Object[] test17c(long[] a) { static Object[] test17d(long[] a) { // Not alignable for (int i = 0; i < RANGE-1; i+=4) { - int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i + 1; + long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i + 1; long v0 = UNSAFE.getLongUnaligned(a, adr + 0); long v1 = UNSAFE.getLongUnaligned(a, adr + 8); UNSAFE.putLongUnaligned(a, adr + 0, v0 + 1); diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVectorFuzzer.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVectorFuzzer.java index 7b95781905e..d75db965ea3 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVectorFuzzer.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVectorFuzzer.java @@ -1090,11 +1090,11 @@ static Object[] testUU_unsafe_BasI(byte[] a) { int init = init_con_or_var(); int limit = limit_con_or_var(); int stride = stride_con(); - int scale = scale_con(); - int offset = offset1_con_or_var(); + long scale = scale_con(); + long offset = offset1_con_or_var(); for (int i = init; i < limit; i += stride) { - int adr = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset + i * scale; + long adr = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset + i * scale; int v = UNSAFE.getIntUnaligned(a, adr); UNSAFE.putIntUnaligned(a, adr, v + 1); } @@ -1105,19 +1105,19 @@ static Object[] testUU_unsafe_BasIH(byte[] a, byte[] b, byte[] c) { int init = init_con_or_var(); int limit = limit_con_or_var(); int stride = stride_con(); - int scale = scale_con(); - int offset1 = offset1_con_or_var(); - int offset2 = offset2_con_or_var(); - int offset3 = offset3_con_or_var(); + long scale = scale_con(); + long offset1 = offset1_con_or_var(); + long offset2 = offset2_con_or_var(); + long offset3 = offset3_con_or_var(); int h1 = hand_unrolling1_con(); int h2 = hand_unrolling2_con(); int h3 = hand_unrolling3_con(); for (int i = init; i < limit; i += stride) { - int adr1 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset1 + i * scale; - int adr2 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset2 + i * scale; - int adr3 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset3 + i * scale; + long adr1 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset1 + i * scale; + long adr2 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset2 + i * scale; + long adr3 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset3 + i * scale; if (h1 >= 1) { UNSAFE.putIntUnaligned(a, adr1 + 0*4, UNSAFE.getIntUnaligned(a, adr1 + 0*4) + 1); } if (h1 >= 2) { UNSAFE.putIntUnaligned(a, adr1 + 1*4, UNSAFE.getIntUnaligned(a, adr1 + 1*4) + 1); } diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency.java index 65398e8adfd..197ae08b6d8 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency.java @@ -172,10 +172,10 @@ public void runTest2() { static void test2(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb) { for (int i = 0; i < RANGE; i+=2) { // int and float arrays are two slices. But we pretend both are of type int. - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, dataIa[i+0] + 1); - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, dataIa[i+1] + 1); - dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0); - dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4); + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, dataIa[i+0] + 1); + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, dataIa[i+1] + 1); + dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0); + dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4); } } @@ -248,10 +248,10 @@ static void test5(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb) { for (int i = 0; i < RANGE; i+=2) { // same as test2, except that reordering leads to different semantics // explanation analogue to test4 - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, dataIa[i+0] + 1); // A - dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0); // X - dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4); // Y - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, dataIa[i+1] + 1); // B + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, dataIa[i+0] + 1); // A + dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0); // X + dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4); // Y + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, dataIa[i+1] + 1); // B } } @@ -275,18 +275,18 @@ static void test6(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb, long[] dataLa, long[] dataLb) { for (int i = 0; i < RANGE; i+=2) { // Chain of parallelizable op and conversion - int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3; - int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00); - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01); - int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45; - int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45; - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10); - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11); - float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f; - float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f; - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20); - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21); + int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3; + int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00); + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01); + int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45; + int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45; + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10); + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11); + float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f; + float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f; + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20); + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21); } } @@ -307,18 +307,18 @@ static void test7(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb, long[] dataLa, long[] dataLb) { for (int i = 0; i < RANGE; i+=2) { // Cycle involving 3 memory slices - int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3; - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00); - int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45; - int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45; - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10); - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11); - float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f; - float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f; - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20); - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21); - int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; // moved down - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01); + int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3; + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00); + int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45; + int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45; + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10); + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11); + float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f; + float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f; + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20); + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21); + int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; // moved down + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01); } } @@ -340,19 +340,19 @@ static void test8(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb, long[] dataLa, long[] dataLb) { for (int i = 0; i < RANGE; i+=2) { // 2-cycle, with more ops after - int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3; - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00); - int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45; - int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45; - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10); - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11); - int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01); + int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3; + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00); + int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45; + int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45; + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10); + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11); + int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01); // more stuff after - float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f; - float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f; - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20); - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21); + float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f; + float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f; + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20); + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21); } } @@ -373,19 +373,19 @@ static void test9(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb, long[] dataLa, long[] dataLb) { for (int i = 0; i < RANGE; i+=2) { // 2-cycle, with more stuff before - float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f; - float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f; - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20); - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21); + float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f; + float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f; + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20); + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21); // 2-cycle - int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3; - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00); - int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45; - int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45; - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10); - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11); - int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01); + int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3; + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00); + int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45; + int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45; + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10); + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11); + int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01); } } @@ -423,18 +423,18 @@ static void test10(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb, // // The cycle thus does not only go via packs, but also scalar ops. // - int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3; // A - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00); - int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45; // R: constant mismatch - int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) + 43; // S - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10); - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11); - float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f; // U - float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f; // V - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20); - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21); - int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; // B: moved down - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01); + int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3; // A + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00); + int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45; // R: constant mismatch + int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) + 43; // S + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10); + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11); + float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f; // U + float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f; // V + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20); + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21); + int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; // B: moved down + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01); } } @@ -463,8 +463,8 @@ static void verify(String name, int[] data, int[] gold) { static void verify(String name, float[] data, float[] gold) { for (int i = 0; i < RANGE; i++) { - int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i); - int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i); + int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i); + int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i); if (datav != goldv) { throw new RuntimeException(" Invalid " + name + " result: dataF[" + i + "]: " + datav + " != " + goldv); } diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency2.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency2.java index 32d69689a42..2be7d52c780 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency2.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency2.java @@ -58,18 +58,18 @@ static void test(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb, long[] dataLa, long[] dataLb) { for (int i = 0; i < RANGE; i+=2) { // For explanation, see test 10 in TestIndependentPacksWithCyclicDependency.java - int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3; - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00); - int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45; - int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) + 43; - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10); - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11); - float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f; - float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f; - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20); - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21); - int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; // moved down - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01); + int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3; + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00); + int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45; + int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) + 43; + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10); + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11); + float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f; + float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f; + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20); + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21); + int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; // moved down + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01); } } @@ -83,8 +83,8 @@ static void verify(String name, int[] data, int[] gold) { static void verify(String name, float[] data, float[] gold) { for (int i = 0; i < RANGE; i++) { - int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i); - int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i); + int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i); + int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i); if (datav != goldv) { throw new RuntimeException(" Invalid " + name + " result: dataF[" + i + "]: " + datav + " != " + goldv); } diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestScheduleReordersScalarMemops.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestScheduleReordersScalarMemops.java index f2ed8b6aec2..c54a684c691 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestScheduleReordersScalarMemops.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestScheduleReordersScalarMemops.java @@ -124,10 +124,10 @@ static void test1(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb) { for (int i = 0; i < RANGE; i+=2) { // Do the same as test0, but without int-float conversion. // This should reproduce on machines where conversion is not implemented. - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, dataIa[i+0] + 1); // A +1 - dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0); // X - dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4); // Y - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, dataIa[i+1] * 11); // B *11 + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, dataIa[i+0] + 1); // A +1 + dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0); // X + dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4); // Y + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, dataIa[i+1] * 11); // B *11 } } From 498a58244d79b6aaffd17300844f9908991d463c Mon Sep 17 00:00:00 2001 From: Matias Saavedra Silva Date: Fri, 21 Jun 2024 16:48:04 +0000 Subject: [PATCH 57/95] 8311208: Improve CDS Support Reviewed-by: rhalade, mschoene, ccheung, iklam --- src/hotspot/share/classfile/verifier.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index a66fbf645f5..375570cf196 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -32,6 +32,7 @@ #include "classfile/stackMapTableFormat.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "classfile/verifier.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" @@ -212,6 +213,11 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) { exception_name == vmSymbols::java_lang_ClassFormatError())) { log_info(verification)("Fail over class verification to old verifier for: %s", klass->external_name()); log_info(class, init)("Fail over class verification to old verifier for: %s", klass->external_name()); + // Exclude any classes that fail over during dynamic dumping + if (CDSConfig::is_dumping_dynamic_archive()) { + SystemDictionaryShared::warn_excluded(klass, "Failed over class verification while dynamic dumping"); + SystemDictionaryShared::set_excluded(klass); + } message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len); exception_message = message_buffer; exception_name = inference_verify( From dadcee1b89515a651d0532d9803c22dfeeb64f9e Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Mon, 1 Jul 2024 19:43:34 +0000 Subject: [PATCH 58/95] 8307383: Enhance DTLS connections Co-authored-by: Jamil Nimeh Reviewed-by: rhalade, mschoene, ascarpino --- .../classes/sun/security/ssl/ClientHello.java | 5 +- .../sun/security/ssl/DTLSInputRecord.java | 137 +++++++++++++++++- .../security/ssl/ServerHandshakeContext.java | 3 +- .../sun/security/ssl/TransportContext.java | 7 +- .../javax/net/ssl/DTLS/InvalidRecords.java | 32 +++- .../jdk/javax/net/ssl/TLSCommon/MFLNTest.java | 12 +- 6 files changed, 184 insertions(+), 12 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ssl/ClientHello.java b/src/java.base/share/classes/sun/security/ssl/ClientHello.java index babf2bb452d..e75076b11d6 100644 --- a/src/java.base/share/classes/sun/security/ssl/ClientHello.java +++ b/src/java.base/share/classes/sun/security/ssl/ClientHello.java @@ -213,8 +213,6 @@ byte[] getHelloCookieBytes() { // ignore cookie hos.putBytes16(getEncodedCipherSuites()); hos.putBytes8(compressionMethod); - extensions.send(hos); // In TLS 1.3, use of certain - // extensions is mandatory. } catch (IOException ioe) { // unlikely } @@ -1426,6 +1424,9 @@ public void consume(ConnectionContext context, shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO.id, SSLHandshake.SERVER_HELLO); + // Reset the ClientHello non-zero offset fragment allowance + shc.acceptCliHelloFragments = false; + // // produce // 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 337cf76f2c2..e0196f3009c 100644 --- a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * 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,12 +40,23 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { private DTLSReassembler reassembler = null; private int readEpoch; + private SSLContextImpl sslContext; DTLSInputRecord(HandshakeHash handshakeHash) { super(handshakeHash, SSLReadCipher.nullDTlsReadCipher()); this.readEpoch = 0; } + // Method to set TransportContext + public void setTransportContext(TransportContext tc) { + this.tc = tc; + } + + // Method to set SSLContext + public void setSSLContext(SSLContextImpl sslContext) { + this.sslContext = sslContext; + } + @Override void changeReadCiphers(SSLReadCipher readCipher) { this.readCipher = readCipher; @@ -537,6 +548,27 @@ public int compareTo(RecordFragment o) { } } + /** + * Turn a sufficiently-large initial ClientHello fragment into one that + * stops immediately after the compression methods. This is only used + * for the initial CH message fragment at offset 0. + * + * @param srcFrag the fragment actually received by the DTLSReassembler + * @param limit the size of the new, cloned/truncated handshake fragment + * + * @return a truncated handshake fragment that is sized to look like a + * complete message, but actually contains only up to the compression + * methods (no extensions) + */ + private static HandshakeFragment truncateChFragment(HandshakeFragment srcFrag, + int limit) { + return new HandshakeFragment(Arrays.copyOf(srcFrag.fragment, limit), + srcFrag.contentType, srcFrag.majorVersion, + srcFrag.minorVersion, srcFrag.recordEnS, srcFrag.recordEpoch, + srcFrag.recordSeq, srcFrag.handshakeType, limit, + srcFrag.messageSeq, srcFrag.fragmentOffset, limit); + } + private static final class HoleDescriptor { int offset; // fragment_offset int limit; // fragment_offset + fragment_length @@ -640,10 +672,17 @@ void expectingFinishFlight() { // Queue up a handshake message. void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException { if (!isDesirable(hsf)) { - // Not a dedired record, discard it. + // Not a desired record, discard it. return; } + if (hsf.handshakeType == SSLHandshake.CLIENT_HELLO.id) { + // validate the first or subsequent ClientHello message + if ((hsf = valHello(hsf, hsf.messageSeq == 0)) == null) { + return; + } + } + // Clean up the retransmission messages if necessary. cleanUpRetransmit(hsf); @@ -769,6 +808,100 @@ void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException { } } + private HandshakeFragment valHello(HandshakeFragment hsf, + boolean firstHello) { + ServerHandshakeContext shc = + (ServerHandshakeContext) tc.handshakeContext; + // Drop any fragment that is not a zero offset until we've received + // a second (or possibly later) CH message that passes the cookie + // check. + if (shc == null || !shc.acceptCliHelloFragments) { + if (hsf.fragmentOffset != 0) { + return null; + } + } else { + // Let this fragment through to the DTLSReassembler as-is + return hsf; + } + + try { + ByteBuffer fragmentData = ByteBuffer.wrap(hsf.fragment); + + ProtocolVersion pv = ProtocolVersion.valueOf( + Record.getInt16(fragmentData)); + if (!pv.isDTLS) { + return null; + } + // Read the random (32 bytes) + if (fragmentData.remaining() < 32) { + if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + SSLLogger.fine("Rejected client hello fragment (bad random len) " + + "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength); + } + return null; + } + fragmentData.position(fragmentData.position() + 32); + + // SessionID + byte[] sessId = Record.getBytes8(fragmentData); + if (sessId.length > 0 && + !SSLConfiguration.enableDtlsResumeCookie) { + // If we are in a resumption it is possible that the cookie + // exchange will be skipped. This is a server-side setting + // and it is NOT the default. If enableDtlsResumeCookie is + // false though, then we will buffer fragments since there + // is no cookie exchange to execute prior to performing + // reassembly. + return hsf; + } + + // Cookie + byte[] cookie = Record.getBytes8(fragmentData); + if (firstHello && cookie.length != 0) { + if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + SSLLogger.fine("Rejected initial client hello fragment (bad cookie len) " + + "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength); + } + return null; + } + // CipherSuites + Record.getBytes16(fragmentData); + // Compression methods + Record.getBytes8(fragmentData); + + // If it's the first fragment, we'll truncate it and push it + // through the reassembler. + if (firstHello) { + return truncateChFragment(hsf, fragmentData.position()); + } else { + HelloCookieManager hcMgr = sslContext. + getHelloCookieManager(ProtocolVersion.DTLS10); + ByteBuffer msgFragBuf = ByteBuffer.wrap(hsf.fragment, 0, + fragmentData.position()); + ClientHello.ClientHelloMessage chMsg = + new ClientHello.ClientHelloMessage(shc, msgFragBuf, null); + if (!hcMgr.isCookieValid(shc, chMsg, cookie)) { + // Bad cookie check, truncate it and let the ClientHello + // consumer recheck, fail and take the appropriate action. + return truncateChFragment(hsf, fragmentData.position()); + } else { + // It's a good cookie, return the original handshake + // fragment and let it go into the DTLSReassembler like + // any other fragment so we can wait for the rest of + // the CH message. + shc.acceptCliHelloFragments = true; + return hsf; + } + } + } catch (IOException ioe) { + if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + SSLLogger.fine("Rejected client hello fragment " + + "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength); + } + return null; + } + } + // Queue up a ChangeCipherSpec message void queueUpChangeCipherSpec(RecordFragment rf) throws SSLProtocolException { diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java index 829fa2af96c..11b625e5791 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * 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,7 @@ class ServerHandshakeContext extends HandshakeContext { CertificateMessage.CertificateEntry currentCertEntry; private static final long DEFAULT_STATUS_RESP_DELAY = 5000L; final long statusRespTimeout; + boolean acceptCliHelloFragments = false; ServerHandshakeContext(SSLContextImpl sslContext, diff --git a/src/java.base/share/classes/sun/security/ssl/TransportContext.java b/src/java.base/share/classes/sun/security/ssl/TransportContext.java index c235da3068c..f65a08dfcfe 100644 --- a/src/java.base/share/classes/sun/security/ssl/TransportContext.java +++ b/src/java.base/share/classes/sun/security/ssl/TransportContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -156,6 +156,11 @@ private TransportContext(SSLContextImpl sslContext, SSLTransport transport, this.acc = AccessController.getContext(); this.consumers = new HashMap<>(); + + if (inputRecord instanceof DTLSInputRecord dtlsInputRecord) { + dtlsInputRecord.setTransportContext(this); + dtlsInputRecord.setSSLContext(this.sslContext); + } } // Dispatch plaintext to a specific consumer. diff --git a/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java b/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java index 304cb0695d6..120e6b258e6 100644 --- a/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java +++ b/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ /* * @test - * @bug 8043758 + * @bug 8043758 8307383 * @summary Datagram Transport Layer Security (DTLS) * @modules java.base/sun.security.util * @library /test/lib @@ -36,6 +36,7 @@ import java.net.DatagramPacket; import java.net.SocketAddress; +import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -73,11 +74,34 @@ DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) { // ClientHello with cookie needInvalidRecords.set(false); System.out.println("invalidate ClientHello message"); - if (ba[ba.length - 1] == (byte)0xFF) { - ba[ba.length - 1] = (byte)0xFE; + // We will alter the compression method field in order to make the cookie + // check fail. + ByteBuffer chRec = ByteBuffer.wrap(ba); + // Skip 59 bytes past the record header (13), the handshake header (12), + // the protocol version (2), and client random (32) + chRec.position(59); + // Jump past the session ID + int len = Byte.toUnsignedInt(chRec.get()); + chRec.position(chRec.position() + len); + // Skip the cookie + len = Byte.toUnsignedInt(chRec.get()); + chRec.position(chRec.position() + len); + // Skip past cipher suites + len = Short.toUnsignedInt(chRec.getShort()); + chRec.position(chRec.position() + len); + // Read the data on the compression methods, should be at least 1 + len = Byte.toUnsignedInt(chRec.get()); + if (len >= 1) { + System.out.println("Detected compression methods (count = " + len + ")"); } else { ba[ba.length - 1] = (byte)0xFF; + throw new RuntimeException("Got zero length comp methods"); } + // alter the first comp method. + int compMethodVal = Byte.toUnsignedInt(chRec.get(chRec.position())); + System.out.println("Changing value at position " + chRec.position() + + " from " + compMethodVal + " to " + ++compMethodVal); + chRec.put(chRec.position(), (byte)compMethodVal); } return super.createHandshakePacket(ba, socketAddr); diff --git a/test/jdk/javax/net/ssl/TLSCommon/MFLNTest.java b/test/jdk/javax/net/ssl/TLSCommon/MFLNTest.java index ee50f21ae27..0867925f135 100644 --- a/test/jdk/javax/net/ssl/TLSCommon/MFLNTest.java +++ b/test/jdk/javax/net/ssl/TLSCommon/MFLNTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * 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,7 +34,15 @@ public class MFLNTest extends SSLEngineTestCase { public static void main(String[] args) { setUpAndStartKDCIfNeeded(); System.setProperty("jsse.enableMFLNExtension", "true"); - for (int mfl = 4096; mfl >= 256; mfl /= 2) { + String testMode = System.getProperty("test.mode", "norm"); + int mflLen; + if (testMode.equals("norm_sni")) { + mflLen = 512; + } else { + mflLen = 256; + } + + for (int mfl = 4096; mfl >= mflLen; mfl /= 2) { System.out.println("==============================================" + "=============="); System.out.printf("Testsing DTLS handshake with MFL = %d%n", mfl); From d9f638303737c4e5174e5cf21ad2f634cc0b1494 Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Thu, 4 Jul 2024 23:09:40 +0000 Subject: [PATCH 59/95] 8331446: Improve deserialization support Reviewed-by: rhalade, skoivu, rriggs, naoto --- .../classes/java/text/MessageFormat.java | 60 ++++++++++-- .../MessageFormat/MaxArgumentIndexTest.java | 97 +++++++++++++++++++ .../MessageFormat/SerializationTest.java | 96 ++++++++++++++++++ 3 files changed, 243 insertions(+), 10 deletions(-) create mode 100644 test/jdk/java/text/Format/MessageFormat/MaxArgumentIndexTest.java create mode 100644 test/jdk/java/text/Format/MessageFormat/SerializationTest.java diff --git a/src/java.base/share/classes/java/text/MessageFormat.java b/src/java.base/share/classes/java/text/MessageFormat.java index 58872834567..83cc3ec4647 100644 --- a/src/java.base/share/classes/java/text/MessageFormat.java +++ b/src/java.base/share/classes/java/text/MessageFormat.java @@ -41,6 +41,7 @@ import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; +import java.io.ObjectStreamException; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; @@ -1181,6 +1182,8 @@ public Object[] parse(String source, ParsePosition pos) { maximumArgumentNumber = argumentNumbers[i]; } } + + // Constructors/applyPattern ensure that resultArray.length < MAX_ARGUMENT_INDEX Object[] resultArray = new Object[maximumArgumentNumber + 1]; int patternOffset = 0; @@ -1459,6 +1462,9 @@ protected Object readResolve() throws InvalidObjectException { * @serial */ private int[] argumentNumbers = new int[INITIAL_FORMATS]; + // Implementation limit for ArgumentIndex pattern element. Valid indices must + // be less than this value + private static final int MAX_ARGUMENT_INDEX = 10000; /** * One less than the number of entries in {@code offsets}. Can also be thought of @@ -1639,6 +1645,11 @@ private void setFormatFromPattern(int position, int offsetNumber, + argumentNumber); } + if (argumentNumber >= MAX_ARGUMENT_INDEX) { + throw new IllegalArgumentException( + argumentNumber + " exceeds the ArgumentIndex implementation limit"); + } + // resize format information arrays if necessary if (offsetNumber >= formats.length) { int newLength = formats.length * 2; @@ -2006,24 +2017,53 @@ private static FormatStyle fromString(String text) { */ @java.io.Serial private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - boolean isValid = maxOffset >= -1 - && formats.length > maxOffset - && offsets.length > maxOffset - && argumentNumbers.length > maxOffset; + ObjectInputStream.GetField fields = in.readFields(); + if (fields.defaulted("argumentNumbers") || fields.defaulted("offsets") + || fields.defaulted("formats") || fields.defaulted("locale") + || fields.defaulted("pattern") || fields.defaulted("maxOffset")){ + throw new InvalidObjectException("Stream has missing data"); + } + + locale = (Locale) fields.get("locale", null); + String patt = (String) fields.get("pattern", null); + int maxOff = fields.get("maxOffset", -2); + int[] argNums = ((int[]) fields.get("argumentNumbers", null)).clone(); + int[] offs = ((int[]) fields.get("offsets", null)).clone(); + Format[] fmts = ((Format[]) fields.get("formats", null)).clone(); + + // Check arrays/maxOffset have correct value/length + boolean isValid = maxOff >= -1 && argNums.length > maxOff + && offs.length > maxOff && fmts.length > maxOff; + + // Check the correctness of arguments and offsets if (isValid) { - int lastOffset = pattern.length() + 1; - for (int i = maxOffset; i >= 0; --i) { - if ((offsets[i] < 0) || (offsets[i] > lastOffset)) { + int lastOffset = patt.length() + 1; + for (int i = maxOff; i >= 0; --i) { + if (argNums[i] < 0 || argNums[i] >= MAX_ARGUMENT_INDEX + || offs[i] < 0 || offs[i] > lastOffset) { isValid = false; break; } else { - lastOffset = offsets[i]; + lastOffset = offs[i]; } } } + if (!isValid) { - throw new InvalidObjectException("Could not reconstruct MessageFormat from corrupt stream."); + throw new InvalidObjectException("Stream has invalid data"); } + maxOffset = maxOff; + pattern = patt; + offsets = offs; + formats = fmts; + argumentNumbers = argNums; + } + + /** + * Serialization without data not supported for this class. + */ + @java.io.Serial + private void readObjectNoData() throws ObjectStreamException { + throw new InvalidObjectException("Deserialized MessageFormat objects need data"); } } diff --git a/test/jdk/java/text/Format/MessageFormat/MaxArgumentIndexTest.java b/test/jdk/java/text/Format/MessageFormat/MaxArgumentIndexTest.java new file mode 100644 index 00000000000..e12dabb6383 --- /dev/null +++ b/test/jdk/java/text/Format/MessageFormat/MaxArgumentIndexTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 8331446 + * @summary Enforce the MAX_ARGUMENT_INDEX(10,000) implementation limit for the + * ArgumentIndex element in the MessageFormat pattern syntax. This + * should be checked during construction/applyPattern/readObject and should effectively + * prevent parse/format from being invoked with values over the limit. + * @run junit MaxArgumentIndexTest + */ + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.text.MessageFormat; +import java.util.Locale; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class MaxArgumentIndexTest { + + // A MessageFormat pattern that contains an ArgumentIndex value + // which violates this implementation's limit: MAX_ARGUMENT_INDEX(10,000) + // As this check is exclusive, 10,000 will violate the limit + private static final String VIOLATES_MAX_ARGUMENT_INDEX = "{10000}"; + + // Check String constructor enforces the limit + @Test + public void constructorTest() { + assertThrows(IllegalArgumentException.class, + () -> new MessageFormat(VIOLATES_MAX_ARGUMENT_INDEX)); + } + + // Check String, Locale constructor enforces the limit + @ParameterizedTest + @MethodSource + public void constructorWithLocaleTest(Locale locale) { + assertThrows(IllegalArgumentException.class, + () -> new MessageFormat(VIOLATES_MAX_ARGUMENT_INDEX, locale)); + } + + // Provide some basic common locale values + private static Stream constructorWithLocaleTest() { + return Stream.of(null, Locale.US, Locale.ROOT); + } + + // Edge case: Test a locale dependent subformat (with null locale) with a + // violating ArgumentIndex. In this instance, the violating ArgumentIndex + // will be caught and IAE thrown instead of the NPE + @Test + public void localeDependentSubFormatTest() { + assertThrows(IllegalArgumentException.class, + () -> new MessageFormat("{10000,number,short}", null)); + // For reference + assertThrows(NullPointerException.class, + () -> new MessageFormat("{999,number,short}", null)); + } + + // Check that the static format method enforces the limit + @Test + public void staticFormatTest() { + assertThrows(IllegalArgumentException.class, + () -> MessageFormat.format(VIOLATES_MAX_ARGUMENT_INDEX, new Object[]{1})); + } + + // Check that applyPattern(String) enforces the limit + @Test + public void applyPatternTest() { + MessageFormat mf = new MessageFormat(""); + assertThrows(IllegalArgumentException.class, + () -> mf.applyPattern(VIOLATES_MAX_ARGUMENT_INDEX)); + } +} diff --git a/test/jdk/java/text/Format/MessageFormat/SerializationTest.java b/test/jdk/java/text/Format/MessageFormat/SerializationTest.java new file mode 100644 index 00000000000..9191c5caef3 --- /dev/null +++ b/test/jdk/java/text/Format/MessageFormat/SerializationTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 8331446 + * @summary Check correctness of deserialization + * @run junit SerializationTest + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.text.MessageFormat; +import java.util.Locale; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SerializationTest { + + // Ensure basic correctness of serialization round trip + @ParameterizedTest + @MethodSource + public void serializationRoundTrip(MessageFormat expectedMf) + throws IOException, ClassNotFoundException { + byte[] bytes = ser(expectedMf); + MessageFormat actualMf = (MessageFormat) deSer(bytes); + assertEquals(expectedMf, actualMf); + } + + // Various valid MessageFormats + private static Stream serializationRoundTrip() { + return Stream.of( + // basic pattern + new MessageFormat("{0} foo"), + // Multiple arguments + new MessageFormat("{0} {1} foo"), + // duplicate arguments + new MessageFormat("{0} {0} {1} foo"), + // Non-ascending arguments + new MessageFormat("{1} {0} foo"), + // With locale + new MessageFormat("{1} {0} foo", Locale.UK), + // With null locale. (NPE not thrown, if no format defined) + new MessageFormat("{1} {0} foo", null), + // With formats + new MessageFormat("{0,number,short} {0} {1,date,long} foo") + ); + } + + // Utility method to serialize + private static byte[] ser(Object obj) throws IOException { + ByteArrayOutputStream byteArrayOutputStream = new + ByteArrayOutputStream(); + ObjectOutputStream oos = new + ObjectOutputStream(byteArrayOutputStream); + oos.writeObject(obj); + return byteArrayOutputStream.toByteArray(); + } + + // Utility method to deserialize + private static Object deSer(byte[] bytes) throws + IOException, ClassNotFoundException { + ByteArrayInputStream byteArrayInputStream = new + ByteArrayInputStream(bytes); + ObjectInputStream ois = new + ObjectInputStream(byteArrayInputStream); + return ois.readObject(); + } +} From c89f76c0b9ca085192775af9bd9368562b582dd6 Mon Sep 17 00:00:00 2001 From: Dean Long Date: Thu, 11 Jul 2024 22:32:41 +0000 Subject: [PATCH 60/95] 8332644: Improve graph optimizations Co-authored-by: Martin Balao Co-authored-by: Francisco Ferrari Bihurriet Reviewed-by: epeter, ahgross, thartmann, rhalade, dlong, roland --- src/hotspot/share/opto/loopnode.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 6cb50b3dee2..c0e929ca1f4 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -2601,7 +2601,7 @@ Node *LoopLimitNode::Ideal(PhaseGVN *phase, bool can_reshape) { const TypeInt* init_t = phase->type(in(Init) )->is_int(); const TypeInt* limit_t = phase->type(in(Limit))->is_int(); - int stride_p; + jlong stride_p; jlong lim, ini; julong max; if (stride_con > 0) { @@ -2610,10 +2610,10 @@ Node *LoopLimitNode::Ideal(PhaseGVN *phase, bool can_reshape) { ini = init_t->_lo; max = (julong)max_jint; } else { - stride_p = -stride_con; + stride_p = -(jlong)stride_con; lim = init_t->_hi; ini = limit_t->_lo; - max = (julong)min_jint; + max = (julong)(juint)min_jint; // double cast to get 0x0000000080000000, not 0xffffffff80000000 } julong range = lim - ini + stride_p; if (range <= max) { From 490d099e234f27adef7d691d3c5a08ebdb550c5d Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 23 Jul 2024 09:28:58 +0000 Subject: [PATCH 61/95] 8335713: Enhance vectorization analysis Co-authored-by: Roland Westrelin Reviewed-by: rhalade, ahgross, thartmann, kvn --- src/hotspot/share/opto/vectorization.cpp | 76 ++++++++++++++++++++---- src/hotspot/share/opto/vectorization.hpp | 1 + 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/hotspot/share/opto/vectorization.cpp b/src/hotspot/share/opto/vectorization.cpp index f013abb38fb..fc4eaccff5c 100644 --- a/src/hotspot/share/opto/vectorization.cpp +++ b/src/hotspot/share/opto/vectorization.cpp @@ -943,11 +943,40 @@ bool VPointer::scaled_iv_plus_offset(Node* n) { } } else if (opc == Op_SubI || opc == Op_SubL) { if (offset_plus_k(n->in(2), true) && scaled_iv_plus_offset(n->in(1))) { + // (offset1 + invar1 + scale * iv) - (offset2 + invar2) + // Subtraction handled via "negate" flag of "offset_plus_k". NOT_PRODUCT(_tracer.scaled_iv_plus_offset_6(n);) return true; } - if (offset_plus_k(n->in(1)) && scaled_iv_plus_offset(n->in(2))) { - _scale *= -1; + VPointer tmp(this); + if (offset_plus_k(n->in(1)) && tmp.scaled_iv_plus_offset(n->in(2))) { + // (offset1 + invar1) - (offset2 + invar2 + scale * iv) + // Subtraction handled explicitly below. + assert(_scale == 0, "shouldn't be set yet"); + // _scale = -tmp._scale + if (!try_MulI_no_overflow(-1, tmp._scale, _scale)) { + return false; // mul overflow. + } + // _offset -= tmp._offset + if (!try_SubI_no_overflow(_offset, tmp._offset, _offset)) { + return false; // sub overflow. + } + // _invar -= tmp._invar + if (tmp._invar != nullptr) { + maybe_add_to_invar(tmp._invar, true); +#ifdef ASSERT + _debug_invar_scale = tmp._debug_invar_scale; + _debug_negate_invar = !tmp._debug_negate_invar; +#endif + } + + // Forward info about the int_index: + assert(!_has_int_index_after_convI2L, "no previous int_index discovered"); + _has_int_index_after_convI2L = tmp._has_int_index_after_convI2L; + _int_index_after_convI2L_offset = tmp._int_index_after_convI2L_offset; + _int_index_after_convI2L_invar = tmp._int_index_after_convI2L_invar; + _int_index_after_convI2L_scale = tmp._int_index_after_convI2L_scale; + NOT_PRODUCT(_tracer.scaled_iv_plus_offset_7(n);) return true; } @@ -989,7 +1018,9 @@ bool VPointer::scaled_iv(Node* n) { } } else if (opc == Op_LShiftI) { if (n->in(1) == iv() && n->in(2)->is_Con()) { - _scale = 1 << n->in(2)->get_int(); + if (!try_LShiftI_no_overflow(1, n->in(2)->get_int(), _scale)) { + return false; // shift overflow. + } NOT_PRODUCT(_tracer.scaled_iv_6(n, _scale);) return true; } @@ -1012,15 +1043,24 @@ bool VPointer::scaled_iv(Node* n) { if (tmp.scaled_iv_plus_offset(n->in(1)) && tmp.has_iv()) { // We successfully matched an integer index, of the form: // int_index = int_offset + int_invar + int_scale * iv + // Forward scale. + assert(_scale == 0 && tmp._scale != 0, "iv only found just now"); + _scale = tmp._scale; + // Accumulate offset. + if (!try_AddI_no_overflow(_offset, tmp._offset, _offset)) { + return false; // add overflow. + } + // Accumulate invar. + if (tmp._invar != nullptr) { + maybe_add_to_invar(tmp._invar, false); + } + // Set info about the int_index: + assert(!_has_int_index_after_convI2L, "no previous int_index discovered"); _has_int_index_after_convI2L = true; _int_index_after_convI2L_offset = tmp._offset; _int_index_after_convI2L_invar = tmp._invar; _int_index_after_convI2L_scale = tmp._scale; - } - // Now parse it again for the real VPointer. This makes sure that the int_offset, int_invar, - // and int_scale are properly added to the final VPointer's offset, invar, and scale. - if (scaled_iv_plus_offset(n->in(1))) { NOT_PRODUCT(_tracer.scaled_iv_7(n);) return true; } @@ -1039,12 +1079,14 @@ bool VPointer::scaled_iv(Node* n) { NOT_PRODUCT(_tracer.scaled_iv_8(n, &tmp);) if (tmp.scaled_iv_plus_offset(n->in(1))) { - int scale = n->in(2)->get_int(); + int shift = n->in(2)->get_int(); // Accumulate scale. - _scale = tmp._scale << scale; + if (!try_LShiftI_no_overflow(tmp._scale, shift, _scale)) { + return false; // shift overflow. + } // Accumulate offset. int shifted_offset = 0; - if (!try_LShiftI_no_overflow(tmp._offset, scale, shifted_offset)) { + if (!try_LShiftI_no_overflow(tmp._offset, shift, shifted_offset)) { return false; // shift overflow. } if (!try_AddI_no_overflow(_offset, shifted_offset, _offset)) { @@ -1061,6 +1103,7 @@ bool VPointer::scaled_iv(Node* n) { } // Forward info about the int_index: + assert(!_has_int_index_after_convI2L, "no previous int_index discovered"); _has_int_index_after_convI2L = tmp._has_int_index_after_convI2L; _int_index_after_convI2L_offset = tmp._int_index_after_convI2L_offset; _int_index_after_convI2L_invar = tmp._int_index_after_convI2L_invar; @@ -1255,6 +1298,9 @@ bool VPointer::try_AddSubI_no_overflow(int offset1, int offset2, bool is_sub, in } bool VPointer::try_LShiftI_no_overflow(int offset, int shift, int& result) { + if (shift < 0 || shift > 31) { + return false; + } jlong long_offset = java_shift_left((jlong)(offset), shift); jint int_offset = java_shift_left( offset, shift); if (long_offset != int_offset) { @@ -1264,6 +1310,16 @@ bool VPointer::try_LShiftI_no_overflow(int offset, int shift, int& result) { return true; } +bool VPointer::try_MulI_no_overflow(int offset1, int offset2, int& result) { + jlong long_offset = java_multiply((jlong)(offset1), (jlong)(offset2)); + jint int_offset = java_multiply( offset1, offset2); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + // We use two comparisons, because a subtraction could underflow. #define RETURN_CMP_VALUE_IF_NOT_EQUAL(a, b) \ if (a < b) { return -1; } \ diff --git a/src/hotspot/share/opto/vectorization.hpp b/src/hotspot/share/opto/vectorization.hpp index 7de29444e5f..b084edd44b3 100644 --- a/src/hotspot/share/opto/vectorization.hpp +++ b/src/hotspot/share/opto/vectorization.hpp @@ -943,6 +943,7 @@ class VPointer : public ArenaObj { static bool try_SubI_no_overflow(int offset1, int offset2, int& result); static bool try_AddSubI_no_overflow(int offset1, int offset2, bool is_sub, int& result); static bool try_LShiftI_no_overflow(int offset1, int offset2, int& result); + static bool try_MulI_no_overflow(int offset1, int offset2, int& result); Node* register_if_new(Node* n) const; }; From 2c0c65353b2f67bdcd954b4d2c2ae3e9b24d1c22 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 15 Oct 2024 11:52:34 +0000 Subject: [PATCH 62/95] 8342044: Increase timeout of gc/shenandoah/oom/TestClassLoaderLeak.java Reviewed-by: shade --- test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java b/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java index 1a3d07bf80d..00e32a4136e 100644 --- a/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java +++ b/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java @@ -27,7 +27,7 @@ * @summary Test OOME in due to classloader leak * @requires vm.gc.Shenandoah * @library /test/lib - * @run driver TestClassLoaderLeak + * @run driver/timeout=600 TestClassLoaderLeak */ import java.util.*; From 54c9348c8c0f5b363d1ef31166179fe9ac61ab9c Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 15 Oct 2024 12:16:15 +0000 Subject: [PATCH 63/95] 8336103: Clean up confusing Method::is_initializer Reviewed-by: dholmes, coleenp --- src/hotspot/share/ci/ciMethod.cpp | 1 - src/hotspot/share/ci/ciMethod.hpp | 1 - src/hotspot/share/jvmci/jvmciCompiler.cpp | 2 +- src/hotspot/share/jvmci/jvmciCompilerToVM.cpp | 13 ++++++------- src/hotspot/share/oops/klassVtable.cpp | 7 ++++--- src/hotspot/share/oops/method.cpp | 4 ---- src/hotspot/share/oops/method.hpp | 3 --- 7 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/hotspot/share/ci/ciMethod.cpp b/src/hotspot/share/ci/ciMethod.cpp index 94b405cdbfa..a74a812c6a2 100644 --- a/src/hotspot/share/ci/ciMethod.cpp +++ b/src/hotspot/share/ci/ciMethod.cpp @@ -1249,7 +1249,6 @@ bool ciMethod::has_jsrs () const { FETCH_FLAG_FROM_VM(has_jsrs); bool ciMethod::is_getter () const { FETCH_FLAG_FROM_VM(is_getter); } bool ciMethod::is_setter () const { FETCH_FLAG_FROM_VM(is_setter); } bool ciMethod::is_accessor () const { FETCH_FLAG_FROM_VM(is_accessor); } -bool ciMethod::is_initializer () const { FETCH_FLAG_FROM_VM(is_initializer); } bool ciMethod::is_empty () const { FETCH_FLAG_FROM_VM(is_empty_method); } bool ciMethod::is_boxing_method() const { diff --git a/src/hotspot/share/ci/ciMethod.hpp b/src/hotspot/share/ci/ciMethod.hpp index 5cb63204d0b..cc524930192 100644 --- a/src/hotspot/share/ci/ciMethod.hpp +++ b/src/hotspot/share/ci/ciMethod.hpp @@ -352,7 +352,6 @@ class ciMethod : public ciMetadata { bool is_getter () const; bool is_setter () const; bool is_accessor () const; - bool is_initializer () const; bool is_empty () const; bool can_be_statically_bound() const { return _can_be_statically_bound; } bool has_reserved_stack_access() const { return _has_reserved_stack_access; } diff --git a/src/hotspot/share/jvmci/jvmciCompiler.cpp b/src/hotspot/share/jvmci/jvmciCompiler.cpp index 2b8684f7ab8..04cdb4b4b4f 100644 --- a/src/hotspot/share/jvmci/jvmciCompiler.cpp +++ b/src/hotspot/share/jvmci/jvmciCompiler.cpp @@ -87,7 +87,7 @@ void JVMCICompiler::bootstrap(TRAPS) { int len = objectMethods->length(); for (int i = 0; i < len; i++) { methodHandle mh(THREAD, objectMethods->at(i)); - if (!mh->is_native() && !mh->is_static() && !mh->is_initializer()) { + if (!mh->is_native() && !mh->is_static() && !mh->is_object_initializer() && !mh->is_static_initializer()) { ResourceMark rm; int hot_count = 10; // TODO: what's the appropriate value? CompileBroker::compile_method(mh, InvocationEntryBci, CompLevel_full_optimization, mh, hot_count, CompileTask::Reason_Bootstrap, CHECK); diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index d231fbe8a6a..ba1155e6694 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -2178,7 +2178,7 @@ C2V_VMENTRY_NULL(jobjectArray, getDeclaredConstructors, (JNIEnv* env, jobject, A GrowableArray constructors_array; for (int i = 0; i < iklass->methods()->length(); i++) { Method* m = iklass->methods()->at(i); - if (m->is_initializer() && !m->is_static()) { + if (m->is_object_initializer()) { constructors_array.append(m); } } @@ -2205,7 +2205,7 @@ C2V_VMENTRY_NULL(jobjectArray, getDeclaredMethods, (JNIEnv* env, jobject, ARGUME GrowableArray methods_array; for (int i = 0; i < iklass->methods()->length(); i++) { Method* m = iklass->methods()->at(i); - if (!m->is_initializer() && !m->is_overpass()) { + if (!m->is_object_initializer() && !m->is_static_initializer() && !m->is_overpass()) { methods_array.append(m); } } @@ -2921,12 +2921,11 @@ C2V_VMENTRY_NULL(jobject, asReflectionExecutable, (JNIEnv* env, jobject, ARGUMEN requireInHotSpot("asReflectionExecutable", JVMCI_CHECK_NULL); methodHandle m(THREAD, UNPACK_PAIR(Method, method)); oop executable; - if (m->is_initializer()) { - if (m->is_static_initializer()) { - JVMCI_THROW_MSG_NULL(IllegalArgumentException, - "Cannot create java.lang.reflect.Method for class initializer"); - } + if (m->is_object_initializer()) { executable = Reflection::new_constructor(m, CHECK_NULL); + } else if (m->is_static_initializer()) { + JVMCI_THROW_MSG_NULL(IllegalArgumentException, + "Cannot create java.lang.reflect.Method for class initializer"); } else { executable = Reflection::new_method(m, false, CHECK_NULL); } diff --git a/src/hotspot/share/oops/klassVtable.cpp b/src/hotspot/share/oops/klassVtable.cpp index cbc379709f1..e1bffc90d51 100644 --- a/src/hotspot/share/oops/klassVtable.cpp +++ b/src/hotspot/share/oops/klassVtable.cpp @@ -1230,9 +1230,10 @@ void klassItable::initialize_itable_and_check_constraints(TRAPS) { } inline bool interface_method_needs_itable_index(Method* m) { - if (m->is_static()) return false; // e.g., Stream.empty - if (m->is_initializer()) return false; // or - if (m->is_private()) return false; // uses direct call + if (m->is_static()) return false; // e.g., Stream.empty + if (m->is_object_initializer()) return false; // + if (m->is_static_initializer()) return false; // + if (m->is_private()) return false; // uses direct call // If an interface redeclares a method from java.lang.Object, // it should already have a vtable index, don't touch it. // e.g., CharSequence.toString (from initialize_vtable) diff --git a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp index f4dcd4f1493..a1b380d3646 100644 --- a/src/hotspot/share/oops/method.cpp +++ b/src/hotspot/share/oops/method.cpp @@ -846,10 +846,6 @@ bool Method::is_constant_getter() const { Bytecodes::is_return(java_code_at(last_index))); } -bool Method::is_initializer() const { - return is_object_initializer() || is_static_initializer(); -} - bool Method::has_valid_initializer_flags() const { return (is_static() || method_holder()->major_version() < 51); diff --git a/src/hotspot/share/oops/method.hpp b/src/hotspot/share/oops/method.hpp index 905c53a4ea3..6ffaebcdfda 100644 --- a/src/hotspot/share/oops/method.hpp +++ b/src/hotspot/share/oops/method.hpp @@ -576,9 +576,6 @@ class Method : public Metadata { // returns true if the method does nothing but return a constant of primitive type bool is_constant_getter() const; - // returns true if the method is an initializer ( or ). - bool is_initializer() const; - // returns true if the method is static OR if the classfile version < 51 bool has_valid_initializer_flags() const; From 6ed6dff22208e7e6c24c98d3a9ff895a6c6c0ae0 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 15 Oct 2024 12:55:49 +0000 Subject: [PATCH 64/95] 8341871: Disable G1 for unsupported platforms after JDK-8334060 Reviewed-by: mdoerr, erikj --- make/autoconf/jvm-features.m4 | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/make/autoconf/jvm-features.m4 b/make/autoconf/jvm-features.m4 index bd33315090f..9695644bafe 100644 --- a/make/autoconf/jvm-features.m4 +++ b/make/autoconf/jvm-features.m4 @@ -479,6 +479,22 @@ AC_DEFUN([JVM_FEATURES_CALCULATE_ACTIVE], $JVM_FEATURES_ENABLED, $JVM_FEATURES_DISABLED) ]) +################################################################################ +# Filter the unsupported feature combinations. +# This is called after JVM_FEATURES_ACTIVE are fully populated. +# +AC_DEFUN([JVM_FEATURES_FILTER_UNSUPPORTED], +[ + # G1 late barrier expansion in C2 is not implemented for some platforms. + # Choose not to support G1 in this configuration. + if JVM_FEATURES_IS_ACTIVE(compiler2); then + if test "x$OPENJDK_TARGET_CPU" = "xx86"; then + AC_MSG_NOTICE([G1 cannot be used with C2 on this platform, disabling G1]) + UTIL_GET_NON_MATCHING_VALUES(JVM_FEATURES_ACTIVE, $JVM_FEATURES_ACTIVE, "g1gc") + fi + fi +]) + ################################################################################ # Helper function for JVM_FEATURES_VERIFY. Check if the specified JVM # feature is active. To be used in shell if constructs, like this: @@ -554,6 +570,9 @@ AC_DEFUN_ONCE([JVM_FEATURES_SETUP], # The result is stored in JVM_FEATURES_ACTIVE. JVM_FEATURES_CALCULATE_ACTIVE($variant) + # Filter unsupported feature combinations from JVM_FEATURES_ACTIVE. + JVM_FEATURES_FILTER_UNSUPPORTED + # Verify consistency for JVM_FEATURES_ACTIVE. JVM_FEATURES_VERIFY($variant) From 5eae20f73b9e8578d58c7e49d2da79cf1b0b229c Mon Sep 17 00:00:00 2001 From: Julian Waters Date: Tue, 15 Oct 2024 14:10:53 +0000 Subject: [PATCH 65/95] 8323672: Suppress unwanted autoconf added flags in CC and CXX Reviewed-by: ihse --- make/autoconf/toolchain.m4 | 6 +--- make/autoconf/util.m4 | 64 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/make/autoconf/toolchain.m4 b/make/autoconf/toolchain.m4 index 5b3ef98c736..d84ae447e54 100644 --- a/make/autoconf/toolchain.m4 +++ b/make/autoconf/toolchain.m4 @@ -307,7 +307,7 @@ AC_DEFUN_ONCE([TOOLCHAIN_POST_DETECTION], [ # Restore old path, except for the microsoft toolchain, which requires the # toolchain path to remain in place. Otherwise the compiler will not work in - # some siutations in later configure checks. + # some situations in later configure checks. if test "x$TOOLCHAIN_TYPE" != "xmicrosoft"; then PATH="$OLD_PATH" fi @@ -316,10 +316,6 @@ AC_DEFUN_ONCE([TOOLCHAIN_POST_DETECTION], # This is necessary since AC_PROG_CC defaults CFLAGS to "-g -O2" CFLAGS="$ORG_CFLAGS" CXXFLAGS="$ORG_CXXFLAGS" - - # filter out some unwanted additions autoconf may add to CXX; we saw this on macOS with autoconf 2.72 - UTIL_GET_NON_MATCHING_VALUES(cxx_filtered, $CXX, -std=c++11 -std=gnu++11) - CXX="$cxx_filtered" ]) # Check if a compiler is of the toolchain type we expect, and save the version diff --git a/make/autoconf/util.m4 b/make/autoconf/util.m4 index 6beadb4c942..5a6142d5092 100644 --- a/make/autoconf/util.m4 +++ b/make/autoconf/util.m4 @@ -25,6 +25,70 @@ m4_include([util_paths.m4]) +############################################################################### +# Overwrite the existing version of AC_PROG_CC with our own custom variant. +# Unlike the regular AC_PROG_CC, the compiler list must always be passed. +AC_DEFUN([AC_PROG_CC], +[ + AC_LANG_PUSH(C) + AC_ARG_VAR([CC], [C compiler command]) + AC_ARG_VAR([CFLAGS], [C compiler flags]) + + _AC_ARG_VAR_LDFLAGS() + _AC_ARG_VAR_LIBS() + _AC_ARG_VAR_CPPFLAGS() + + AC_CHECK_TOOLS(CC, [$1]) + + test -z "$CC" && AC_MSG_FAILURE([no acceptable C compiler found in \$PATH]) + + # Provide some information about the compiler. + _AS_ECHO_LOG([checking for _AC_LANG compiler version]) + set X $ac_compile + ac_compiler=$[2] + for ac_option in --version -v -V -qversion -version; do + _AC_DO_LIMIT([$ac_compiler $ac_option >&AS_MESSAGE_LOG_FD]) + done + + m4_expand_once([_AC_COMPILER_EXEEXT]) + m4_expand_once([_AC_COMPILER_OBJEXT]) + + _AC_PROG_CC_G + + AC_LANG_POP(C) +]) + +############################################################################### +# Overwrite the existing version of AC_PROG_CXX with our own custom variant. +# Unlike the regular AC_PROG_CXX, the compiler list must always be passed. +AC_DEFUN([AC_PROG_CXX], +[ + AC_LANG_PUSH(C++) + AC_ARG_VAR([CXX], [C++ compiler command]) + AC_ARG_VAR([CXXFLAGS], [C++ compiler flags]) + + _AC_ARG_VAR_LDFLAGS() + _AC_ARG_VAR_LIBS() + _AC_ARG_VAR_CPPFLAGS() + + AC_CHECK_TOOLS(CXX, [$1]) + + # Provide some information about the compiler. + _AS_ECHO_LOG([checking for _AC_LANG compiler version]) + set X $ac_compile + ac_compiler=$[2] + for ac_option in --version -v -V -qversion; do + _AC_DO_LIMIT([$ac_compiler $ac_option >&AS_MESSAGE_LOG_FD]) + done + + m4_expand_once([_AC_COMPILER_EXEEXT]) + m4_expand_once([_AC_COMPILER_OBJEXT]) + + _AC_PROG_CXX_G + + AC_LANG_POP(C++) +]) + ################################################################################ # Create a function/macro that takes a series of named arguments. The call is # similar to AC_DEFUN, but the setup of the function looks like this: From b9cabbecdac27ae8b93df88660a4a0f3f60e6828 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Tue, 15 Oct 2024 17:44:49 +0000 Subject: [PATCH 66/95] 8341997: Tests create files in src tree instead of scratch dir Reviewed-by: erikj, jpai --- test/jdk/java/io/FileInputStream/ReadXBytes.java | 4 ++-- test/jdk/java/nio/MappedByteBuffer/ForceException.java | 4 ++-- test/jdk/java/nio/MappedByteBuffer/ForceViews.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/jdk/java/io/FileInputStream/ReadXBytes.java b/test/jdk/java/io/FileInputStream/ReadXBytes.java index 3b0fe7d0590..9a38205cb13 100644 --- a/test/jdk/java/io/FileInputStream/ReadXBytes.java +++ b/test/jdk/java/io/FileInputStream/ReadXBytes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * 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 ReadXBytes { private static final Random RND = RandomFactory.getRandom(); public static void main(String args[]) throws IOException { - File dir = new File(System.getProperty("test.src", ".")); + File dir = new File("."); dir.deleteOnExit(); File empty = File.createTempFile("foo", "bar", dir); diff --git a/test/jdk/java/nio/MappedByteBuffer/ForceException.java b/test/jdk/java/nio/MappedByteBuffer/ForceException.java index dea63db42bf..c8bd06967d0 100644 --- a/test/jdk/java/nio/MappedByteBuffer/ForceException.java +++ b/test/jdk/java/nio/MappedByteBuffer/ForceException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * 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 @@ public static void main(String[] args) throws IOException { int numberOfBlocks = 200; int fileLength = numberOfBlocks * blockSize; - File file = new File(System.getProperty("test.src", "."), "test.dat"); + File file = new File(".", "test.dat"); file.deleteOnExit(); try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) { raf.setLength(fileLength); diff --git a/test/jdk/java/nio/MappedByteBuffer/ForceViews.java b/test/jdk/java/nio/MappedByteBuffer/ForceViews.java index 83727f80968..57ddac28cdf 100644 --- a/test/jdk/java/nio/MappedByteBuffer/ForceViews.java +++ b/test/jdk/java/nio/MappedByteBuffer/ForceViews.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * 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 @@ static record Segment(int position, int length) {} @BeforeTest(alwaysRun=true) public void openChannel() throws IOException { - Path file = Path.of(System.getProperty("test.src", "."), "junk"); + Path file = Path.of(".", "junk"); fc = FileChannel.open(file, CREATE_NEW, READ, WRITE, DELETE_ON_CLOSE); ByteBuffer buf = ByteBuffer.wrap(new byte[1024]); fc.write(buf); From 86ce19e8db6950fc529b8c510137e91e97cae0c4 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Wed, 16 Oct 2024 01:51:09 +0000 Subject: [PATCH 67/95] 8341142: Maintain a single source file for sun.net.www.protocol.jar.JarFileFactory Reviewed-by: dfuchs --- .../net/www/protocol/jar/JarFileFactory.java | 9 +- .../net/www/protocol/jar/JarFileFactory.java | 239 ------------------ 2 files changed, 8 insertions(+), 240 deletions(-) rename src/java.base/{windows => share}/classes/sun/net/www/protocol/jar/JarFileFactory.java (97%) delete mode 100644 src/java.base/unix/classes/sun/net/www/protocol/jar/JarFileFactory.java diff --git a/src/java.base/windows/classes/sun/net/www/protocol/jar/JarFileFactory.java b/src/java.base/share/classes/sun/net/www/protocol/jar/JarFileFactory.java similarity index 97% rename from src/java.base/windows/classes/sun/net/www/protocol/jar/JarFileFactory.java rename to src/java.base/share/classes/sun/net/www/protocol/jar/JarFileFactory.java index 178ffe84a06..17ca43d4ace 100644 --- a/src/java.base/windows/classes/sun/net/www/protocol/jar/JarFileFactory.java +++ b/src/java.base/share/classes/sun/net/www/protocol/jar/JarFileFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,8 @@ import java.util.HashMap; import java.util.jar.JarFile; import java.security.Permission; + +import jdk.internal.util.OperatingSystem; import sun.net.util.URLUtil; /* A factory for cached JAR file. This class is used to both retrieve @@ -148,6 +150,11 @@ boolean cacheIfAbsent(URL url, JarFile jarFile) { } private URL urlFor(URL url) throws IOException { + // for systems other than Windows we don't + // do any special conversion + if (!OperatingSystem.isWindows()) { + return url; + } if (url.getProtocol().equalsIgnoreCase("file")) { // Deal with UNC pathnames specially. See 4180841 diff --git a/src/java.base/unix/classes/sun/net/www/protocol/jar/JarFileFactory.java b/src/java.base/unix/classes/sun/net/www/protocol/jar/JarFileFactory.java deleted file mode 100644 index 291b6d06d8e..00000000000 --- a/src/java.base/unix/classes/sun/net/www/protocol/jar/JarFileFactory.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 1997, 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 sun.net.www.protocol.jar; - -import java.io.IOException; -import java.io.FileNotFoundException; -import java.net.URL; -import java.net.URLConnection; -import java.util.HashMap; -import java.util.jar.JarFile; -import java.security.Permission; -import sun.net.util.URLUtil; - -/* A factory for cached JAR file. This class is used to both retrieve - * and cache Jar files. - * - * @author Benjamin Renaud - * @since 1.2 - */ -class JarFileFactory implements URLJarFile.URLJarFileCloseController { - - /* the url to file cache */ - private static final HashMap fileCache = new HashMap<>(); - - /* the file to url cache */ - private static final HashMap urlCache = new HashMap<>(); - - private static final JarFileFactory instance = new JarFileFactory(); - - private JarFileFactory() { } - - public static JarFileFactory getInstance() { - return instance; - } - - URLConnection getConnection(JarFile jarFile) throws IOException { - URL u; - synchronized (instance) { - u = urlCache.get(jarFile); - } - if (u != null) - return u.openConnection(); - - return null; - } - - public JarFile get(URL url) throws IOException { - return get(url, true); - } - - /** - * Get or create a {@code JarFile} for the given {@code url}. - * If {@code useCaches} is true, this method attempts to find - * a jar file in the cache, and if so, returns it. - * If no jar file is found in the cache, or {@code useCaches} - * is false, the method creates a new jar file. - * If the URL points to a local file, the returned jar file - * will not be put in the cache yet. - * The caller should then call {@link #cacheIfAbsent(URL, JarFile)} - * with the returned jar file, if updating the cache is desired. - * @param url the jar file url - * @param useCaches whether the cache should be used - * @return a new or cached jar file. - * @throws IOException if the jar file couldn't be created - */ - JarFile getOrCreate(URL url, boolean useCaches) throws IOException { - if (useCaches == false) { - return get(url, false); - } - - if (!URLJarFile.isFileURL(url)) { - // A temporary file will be created, we can prepopulate - // the cache in this case. - return get(url, useCaches); - } - - // We have a local file. Do not prepopulate the cache. - JarFile result; - synchronized (instance) { - result = getCachedJarFile(url); - } - if (result == null) { - result = URLJarFile.getJarFile(url, this); - } - if (result == null) - throw new FileNotFoundException(url.toString()); - return result; - } - - /** - * Close the given jar file if it isn't present in the cache. - * Otherwise, does nothing. - * @param url the jar file URL - * @param jarFile the jar file to close - * @return true if the jar file has been closed, false otherwise. - * @throws IOException if an error occurs while closing the jar file. - */ - boolean closeIfNotCached(URL url, JarFile jarFile) throws IOException { - JarFile result; - synchronized (instance) { - result = getCachedJarFile(url); - } - if (result != jarFile) jarFile.close(); - return result != jarFile; - } - - boolean cacheIfAbsent(URL url, JarFile jarFile) { - JarFile cached; - synchronized (instance) { - String key = urlKey(url); - cached = fileCache.get(key); - if (cached == null) { - fileCache.put(key, jarFile); - urlCache.put(jarFile, url); - } - } - return cached == null || cached == jarFile; - } - - JarFile get(URL url, boolean useCaches) throws IOException { - - JarFile result; - JarFile local_result; - - if (useCaches) { - synchronized (instance) { - result = getCachedJarFile(url); - } - if (result == null) { - local_result = URLJarFile.getJarFile(url, this); - synchronized (instance) { - result = getCachedJarFile(url); - if (result == null) { - fileCache.put(urlKey(url), local_result); - urlCache.put(local_result, url); - result = local_result; - } else { - if (local_result != null) { - local_result.close(); - } - } - } - } - } else { - result = URLJarFile.getJarFile(url, this); - } - if (result == null) - throw new FileNotFoundException(url.toString()); - - return result; - } - - /** - * Callback method of the URLJarFileCloseController to - * indicate that the JarFile is closed. This way we can - * remove the JarFile from the cache - */ - public void close(JarFile jarFile) { - synchronized (instance) { - URL urlRemoved = urlCache.remove(jarFile); - if (urlRemoved != null) - fileCache.remove(urlKey(urlRemoved)); - } - } - - private JarFile getCachedJarFile(URL url) { - assert Thread.holdsLock(instance); - JarFile result = fileCache.get(urlKey(url)); - - /* if the JAR file is cached, the permission will always be there */ - if (result != null) { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - Permission perm = getPermission(result); - if (perm != null) { - try { - sm.checkPermission(perm); - } catch (SecurityException se) { - // fallback to checkRead/checkConnect for pre 1.2 - // security managers - if ((perm instanceof java.io.FilePermission) && - perm.getActions().contains("read")) { - sm.checkRead(perm.getName()); - } else if ((perm instanceof - java.net.SocketPermission) && - perm.getActions().contains("connect")) { - sm.checkConnect(url.getHost(), url.getPort()); - } else { - throw se; - } - } - } - } - } - return result; - } - - private String urlKey(URL url) { - String urlstr = URLUtil.urlNoFragString(url); - if ("runtime".equals(url.getRef())) urlstr += "#runtime"; - return urlstr; - } - - private Permission getPermission(JarFile jarFile) { - try { - URLConnection uc = getConnection(jarFile); - if (uc != null) - return uc.getPermission(); - } catch (IOException ioe) { - // gulp - } - - return null; - } -} From 6d7e67956b1722b4e3d33253d68c095058f39f02 Mon Sep 17 00:00:00 2001 From: Tejesh R Date: Wed, 16 Oct 2024 05:14:59 +0000 Subject: [PATCH 68/95] 8340790: Open source several AWT Dialog tests - Batch 4 Reviewed-by: honkar, prr --- test/jdk/ProblemList.txt | 1 + .../awt/Dialog/ChoiceModalDialogTest.java | 140 ++++++++++++++++ .../java/awt/Dialog/DialogBackgroundTest.java | 153 ++++++++++++++++++ .../jdk/java/awt/Dialog/EnabledResetTest.java | 145 +++++++++++++++++ .../awt/Dialog/FileDialogGetFileTest.java | 78 +++++++++ 5 files changed, 517 insertions(+) create mode 100644 test/jdk/java/awt/Dialog/ChoiceModalDialogTest.java create mode 100644 test/jdk/java/awt/Dialog/DialogBackgroundTest.java create mode 100644 test/jdk/java/awt/Dialog/EnabledResetTest.java create mode 100644 test/jdk/java/awt/Dialog/FileDialogGetFileTest.java diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 034830a2575..942e15002bd 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -478,6 +478,7 @@ java/awt/KeyboardFocusmanager/ConsumeNextMnemonicKeyTypedTest/ConsumeNextMnemoni java/awt/Window/GetScreenLocation/GetScreenLocationTest.java 8225787 linux-x64 java/awt/Dialog/MakeWindowAlwaysOnTop/MakeWindowAlwaysOnTop.java 8266243 macosx-aarch64 +java/awt/Dialog/ChoiceModalDialogTest.java 8161475 macosx-all java/awt/Dialog/FileDialogUserFilterTest.java 8001142 generic-all java/awt/dnd/BadSerializationTest/BadSerializationTest.java 8277817 linux-x64,windows-x64 diff --git a/test/jdk/java/awt/Dialog/ChoiceModalDialogTest.java b/test/jdk/java/awt/Dialog/ChoiceModalDialogTest.java new file mode 100644 index 00000000000..97ce5a83a96 --- /dev/null +++ b/test/jdk/java/awt/Dialog/ChoiceModalDialogTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 6213128 + * @key headful + * @summary Tests that choice is releasing input capture when a modal + * dialog is shown + * @run main ChoiceModalDialogTest + */ + +import java.awt.Choice; +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class ChoiceModalDialogTest { + static Frame f; + static Dialog d; + static volatile boolean keyOK; + static volatile boolean mouseOK; + static TextField tf; + static Choice c; + + public static void main(String[] args) throws Exception { + Robot r; + try { + r = new Robot(); + r.setAutoDelay(100); + EventQueue.invokeAndWait(() -> { + f = new Frame("Frame"); + c = new Choice(); + f.setBounds(100, 300, 300, 200); + f.setLayout(new FlowLayout()); + tf = new TextField(3); + f.add(tf); + + c.add("1"); + c.add("2"); + c.add("3"); + c.add("4"); + f.add(c); + + tf.addFocusListener(new FocusAdapter() { + public void focusLost(FocusEvent ev) { + d = new Dialog(f, "Dialog", true); + d.setBounds(300, 300, 200, 150); + d.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent ev) { + keyOK = true; + } + }); + d.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent ev) { + mouseOK = true; + } + }); + d.setVisible(true); + } + }); + + f.setVisible(true); + f.toFront(); + }); + r.waitForIdle(); + r.delay(1000); + EventQueue.invokeAndWait(() -> { + r.mouseMove(tf.getLocationOnScreen().x + tf.getSize().width / 2, + tf.getLocationOnScreen().y + tf.getSize().height / 2); + }); + r.waitForIdle(); + r.delay(500); + EventQueue.invokeAndWait(() -> { + r.mouseMove(c.getLocationOnScreen().x + c.getSize().width - 4, + c.getLocationOnScreen().y + c.getSize().height / 2); + r.mousePress(InputEvent.BUTTON1_DOWN_MASK); + r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + }); + r.waitForIdle(); + r.delay(500); + EventQueue.invokeAndWait(() -> { + r.mouseMove(d.getLocationOnScreen().x + d.getSize().width / 2, + d.getLocationOnScreen().y + d.getSize().height / 2); + r.mousePress(InputEvent.BUTTON1_DOWN_MASK); + r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + r.keyPress(KeyEvent.VK_A); + r.keyRelease(KeyEvent.VK_A); + }); + r.waitForIdle(); + r.delay(500); + if (!mouseOK) { + throw new RuntimeException("Test Failed due to Mouse release failure!"); + } + if (!keyOK) { + throw new RuntimeException("Test Failed due to Key release failure!"); + } + System.out.println("Test Passed!"); + } finally { + EventQueue.invokeAndWait(() -> { + if (d != null) { + d.dispose(); + } + if (f != null) { + f.dispose(); + } + }); + } + } +} diff --git a/test/jdk/java/awt/Dialog/DialogBackgroundTest.java b/test/jdk/java/awt/Dialog/DialogBackgroundTest.java new file mode 100644 index 00000000000..793782fc43b --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogBackgroundTest.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 4255230 4191946 + * @summary Tests to verify Dialog inherits background from its owner + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogBackgroundTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.TextField; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +public class DialogBackgroundTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Perform the following steps: + 1) Select "New Frame" from the "File" menu of the + "TreeCopy Frame #1" frame. + 2) Select "Configure" from the "File" menu in the + *new* frame. + If label text "This is a label:" in the appeared + "Configuration Dialog" dialog has a grey background + test PASSES, otherwise it FAILS + """; + TreeCopy treeCopy = new TreeCopy(++TreeCopy.windowCount, null); + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(treeCopy) + .logArea(8) + .build() + .awaitAndCheck(); + } +} + +class TreeCopy extends Frame implements ActionListener { + TextField tfRoot; + ConfigDialog configDlg; + MenuItem miConfigure = new MenuItem("Configure..."); + MenuItem miNewWindow = new MenuItem("New Frame"); + static int windowCount = 0; + Window parent; + + public TreeCopy(int windowNum, Window myParent) { + super(); + setTitle("TreeCopy Frame #" + windowNum); + MenuBar mb = new MenuBar(); + Menu m = new Menu("File"); + configDlg = new ConfigDialog(this); + parent = myParent; + + m.add(miConfigure); + m.add(miNewWindow); + miConfigure.addActionListener(this); + miNewWindow.addActionListener(this); + mb.add(m); + setMenuBar(mb); + m.addActionListener(this); + + tfRoot = new TextField(); + tfRoot.setEditable(false); + add(tfRoot); + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent we) { + dispose(); + } + }); + + setSize(200, 100); + setLocationRelativeTo(parent); + } + + public void actionPerformed(ActionEvent ae) { + Object source = ae.getSource(); + + if (source == miConfigure) { + configDlg.setVisible(true); + if (configDlg.getBackground() != configDlg.labelColor) + PassFailJFrame.log("FAIL: Test failed!!!"); + } else if (source == miNewWindow) { + new TreeCopy(++windowCount, this).setVisible(true); + } + } +} + +class ConfigDialog extends Dialog implements ActionListener { + public Button okButton; + public Button cancelButton; + public Label l2; + public Color labelColor; + + public ConfigDialog(Frame parent) { + super(parent, "Configuration Dialog"); + okButton = new Button("OK"); + cancelButton = new Button("Cancel"); + l2 = new Label("This is a label:"); + + setLayout(new FlowLayout()); + add(l2); + add(okButton); + add(cancelButton); + + okButton.addActionListener(this); + cancelButton.addActionListener(this); + + pack(); + labelColor = l2.getBackground(); + } + + public void actionPerformed(ActionEvent ae) { + dispose(); + } +} diff --git a/test/jdk/java/awt/Dialog/EnabledResetTest.java b/test/jdk/java/awt/Dialog/EnabledResetTest.java new file mode 100644 index 00000000000..d71c9b1801b --- /dev/null +++ b/test/jdk/java/awt/Dialog/EnabledResetTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 4232374 + * @summary Tests that dismissing a modal dialog does not enable + * disabled components + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual EnabledResetTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class EnabledResetTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Press "Create Child" twice to create three windows + Verify that the parent windows are disabled + 2. Press "Create Modal Dialog" + Verify that the parent windows are disabled + 3. Press "enable" + Verify that no windows accept mouse events + 4. Press "ok" + Verify that the first window is still disabled + If all the verifications are done, then test is + PASSED, else test fails. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(new ChildDialog(1, null)) + .build() + .awaitAndCheck(); + } +} + +class ChildDialog extends Frame implements ActionListener { + Window parent; + int id; + Button b, c, d; + + public ChildDialog(int frameNumber, Window myParent) { + super(); + id = frameNumber; + parent = myParent; + + setTitle("Frame Number " + id); + + b = new Button("Dismiss me"); + c = new Button("Create Child"); + d = new Button("Create Modal Dialog"); + + setLayout(new BorderLayout()); + add("North", c); + add("Center", d); + add("South", b); + pack(); + + b.addActionListener(this); + c.addActionListener(this); + d.addActionListener(this); + } + + public void setVisible(boolean b) { + if (parent != null) { + if (b) { + parent.setEnabled(false); + } else { + parent.setEnabled(true); + parent.requestFocus(); + } + } + + super.setVisible(b); + } + + public void dispose() { + if (parent != null) { + parent.setEnabled(true); + parent.requestFocus(); + } + super.dispose(); + } + + + public void actionPerformed(ActionEvent evt) { + if (evt.getSource() == c) { + (new ChildDialog(id + 1, this)).setVisible(true); + } else if (evt.getSource() == d) { + Dialog D = new Dialog(this, "Modal Dialog "); + D.setLayout(new FlowLayout()); + Button b = new Button("ok"); + Button e = new Button("enable"); + D.add(b); + D.add(e); + D.setModal(true); + D.pack(); + b.addActionListener(this); + e.addActionListener(this); + D.setVisible(true); + } else if (evt.getSource() == b) { + dispose(); + } else if (evt.getSource() instanceof Button) { + if ("ok".equals(evt.getActionCommand())) { + Button target = (Button) evt.getSource(); + Window w = (Window) target.getParent(); + w.dispose(); + } + if ("enable".equals(evt.getActionCommand())) { + parent.setEnabled(true); + } + } + } +} diff --git a/test/jdk/java/awt/Dialog/FileDialogGetFileTest.java b/test/jdk/java/awt/Dialog/FileDialogGetFileTest.java new file mode 100644 index 00000000000..d4670cceb60 --- /dev/null +++ b/test/jdk/java/awt/Dialog/FileDialogGetFileTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 4414105 + * @summary Tests that FileDialog returns null when cancelled + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FileDialogGetFileTest + */ + +import java.awt.Button; +import java.awt.FileDialog; +import java.awt.Frame; + +public class FileDialogGetFileTest { + static FileDialog fd; + static Frame frame; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Open FileDialog from "Show File Dialog" button. + 2. Click cancel button without selecting any file/folder. + 3. If FileDialog.getFile return null then test PASSES, + else test FAILS automatically. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .logArea(4) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + frame = new Frame("FileDialog GetFile test"); + fd = new FileDialog(frame); + fd.setFile("FileDialogGetFileTest.html"); + fd.setBounds(100, 100, 400, 400); + Button showBtn = new Button("Show File Dialog"); + frame.add(showBtn); + frame.pack(); + showBtn.addActionListener(e -> { + fd.setVisible(true); + if (fd.getFile() != null) { + PassFailJFrame.forceFail("Test failed: FileDialog returned non-null value"); + } else { + PassFailJFrame.log("Test Passed!"); + } + }); + return frame; + } +} From e94e3bba3932f3d92c0a135d333d1ccd6e72b964 Mon Sep 17 00:00:00 2001 From: Ramkumar Sunderbabu Date: Wed, 16 Oct 2024 06:52:42 +0000 Subject: [PATCH 69/95] 8324672: Update jdk/java/time/tck/java/time/TCKInstant.java now() to be more robust Reviewed-by: rriggs, dfuchs --- .../java/time/tck/java/time/TCKInstant.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/test/jdk/java/time/tck/java/time/TCKInstant.java b/test/jdk/java/time/tck/java/time/TCKInstant.java index d7a63415279..c666a1340d3 100644 --- a/test/jdk/java/time/tck/java/time/TCKInstant.java +++ b/test/jdk/java/time/tck/java/time/TCKInstant.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -187,10 +187,21 @@ public void constant_MAX() { //----------------------------------------------------------------------- @Test public void now() { - Instant expected = Instant.now(Clock.systemUTC()); - Instant test = Instant.now(); - long diff = Math.abs(test.toEpochMilli() - expected.toEpochMilli()); - assertTrue(diff < 100); // less than 0.1 secs + long beforeMillis, instantMillis, afterMillis, diff; + int retryRemaining = 5; // MAX_RETRY_COUNT + do { + beforeMillis = Instant.now(Clock.systemUTC()).toEpochMilli(); + instantMillis = Instant.now().toEpochMilli(); + afterMillis = Instant.now(Clock.systemUTC()).toEpochMilli(); + diff = instantMillis - beforeMillis; + if (instantMillis < beforeMillis || instantMillis > afterMillis) { + throw new RuntimeException(": Invalid instant: (~" + instantMillis + "ms)" + + " when systemUTC in millis is in [" + + beforeMillis + ", " + + afterMillis + "]"); + } + } while (diff > 100 && --retryRemaining > 0); // retry if diff more than 0.1 sec + assertTrue(retryRemaining > 0); } //----------------------------------------------------------------------- From e4ff553c121e29c497336fdde705e70d0abdc826 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Wed, 16 Oct 2024 07:05:17 +0000 Subject: [PATCH 70/95] 8341931: os_linux gtest uses lambdas with explicit capture lists Reviewed-by: jwaters, jsjolen --- test/hotspot/gtest/runtime/test_os_linux.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/gtest/runtime/test_os_linux.cpp b/test/hotspot/gtest/runtime/test_os_linux.cpp index 387940afdf2..77b83ac1bd7 100644 --- a/test/hotspot/gtest/runtime/test_os_linux.cpp +++ b/test/hotspot/gtest/runtime/test_os_linux.cpp @@ -360,10 +360,10 @@ TEST_VM(os_linux, pretouch_thp_and_use_concurrent) { EXPECT_TRUE(os::commit_memory(heap, size, false)); { - auto pretouch = [heap](Thread*, int) { + auto pretouch = [&](Thread*, int) { os::pretouch_memory(heap, heap + size, os::vm_page_size()); }; - auto useMemory = [heap](Thread*, int) { + auto useMemory = [&](Thread*, int) { int* iptr = reinterpret_cast(heap); for (int i = 0; i < 1000; i++) *iptr++ = i; }; From 724de682091623cd9877ee4e5f13123ef1d92ddf Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 16 Oct 2024 07:13:59 +0000 Subject: [PATCH 71/95] 8342081: Shenandoah: Remove extra ShenandoahMarkUpdateRefsSuperClosure Reviewed-by: ysr --- .../gc/shenandoah/shenandoahOopClosures.hpp | 26 ++++++------------- .../shenandoahOopClosures.inline.hpp | 5 ++-- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.hpp index e433c2910c8..e6baa4096f0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.hpp @@ -61,33 +61,23 @@ class ShenandoahMarkRefsSuperClosure : public MetadataVisitingOopIterateClosure } }; -class ShenandoahMarkUpdateRefsSuperClosure : public ShenandoahMarkRefsSuperClosure { -protected: +template +class ShenandoahMarkUpdateRefsClosure : public ShenandoahMarkRefsSuperClosure { +private: ShenandoahHeap* const _heap; - template + template inline void work(T* p); public: - ShenandoahMarkUpdateRefsSuperClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : + ShenandoahMarkUpdateRefsClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : ShenandoahMarkRefsSuperClosure(q, rp), _heap(ShenandoahHeap::heap()) { assert(_heap->is_stw_gc_in_progress(), "Can only be used for STW GC"); - }; -}; - -template -class ShenandoahMarkUpdateRefsClosure : public ShenandoahMarkUpdateRefsSuperClosure { -private: - template - inline void do_oop_work(T* p) { work(p); } - -public: - ShenandoahMarkUpdateRefsClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : - ShenandoahMarkUpdateRefsSuperClosure(q, rp) {} + } - virtual void do_oop(narrowOop* p) { do_oop_work(p); } - virtual void do_oop(oop* p) { do_oop_work(p); } + virtual void do_oop(narrowOop* p) { work(p); } + virtual void do_oop(oop* p) { work(p); } }; template diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.inline.hpp index 70d7e94fb50..e0662c24462 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.inline.hpp @@ -35,8 +35,9 @@ inline void ShenandoahMarkRefsSuperClosure::work(T* p) { ShenandoahMark::mark_through_ref(p, _queue, _mark_context, _weak); } -template -inline void ShenandoahMarkUpdateRefsSuperClosure::work(T* p) { +template +template +inline void ShenandoahMarkUpdateRefsClosure::work(T* p) { // Update the location _heap->update_with_forwarded(p); From b9b0bd0871886eb65f87864f262424b119f2c748 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 16 Oct 2024 07:23:12 +0000 Subject: [PATCH 72/95] 8337221: CompileFramework: test library to conveniently compile java and jasm sources for fuzzing Reviewed-by: chagedorn, tholenstein --- .../compile_framework/ClassLoaderBuilder.java | 64 ++++++ .../lib/compile_framework/Compile.java | 202 ++++++++++++++++++ .../compile_framework/CompileFramework.java | 169 +++++++++++++++ .../CompileFrameworkException.java | 37 ++++ .../InternalCompileFrameworkException.java | 37 ++++ .../compiler/lib/compile_framework/README.md | 57 +++++ .../lib/compile_framework/SourceCode.java | 35 +++ .../compiler/lib/compile_framework/Utils.java | 81 +++++++ .../examples/CombinedJavaJasmExample.java | 114 ++++++++++ .../examples/IRFrameworkJavaExample.java | 156 ++++++++++++++ .../examples/MultiFileJasmExample.java | 88 ++++++++ .../examples/MultiFileJavaExample.java | 81 +++++++ .../examples/RunWithFlagsExample.java | 99 +++++++++ .../examples/SimpleJasmExample.java | 79 +++++++ .../examples/SimpleJavaExample.java | 73 +++++++ .../tests/TestBadJasmCompilation.java | 62 ++++++ .../tests/TestBadJavaCompilation.java | 62 ++++++ .../tests/TestConcurrentCompilation.java | 108 ++++++++++ 18 files changed, 1604 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/lib/compile_framework/ClassLoaderBuilder.java create mode 100644 test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java create mode 100644 test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java create mode 100644 test/hotspot/jtreg/compiler/lib/compile_framework/CompileFrameworkException.java create mode 100644 test/hotspot/jtreg/compiler/lib/compile_framework/InternalCompileFrameworkException.java create mode 100644 test/hotspot/jtreg/compiler/lib/compile_framework/README.md create mode 100644 test/hotspot/jtreg/compiler/lib/compile_framework/SourceCode.java create mode 100644 test/hotspot/jtreg/compiler/lib/compile_framework/Utils.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/CombinedJavaJasmExample.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/IRFrameworkJavaExample.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/MultiFileJasmExample.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/MultiFileJavaExample.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/RunWithFlagsExample.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/SimpleJasmExample.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/SimpleJavaExample.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestBadJasmCompilation.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestBadJavaCompilation.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestConcurrentCompilation.java diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/ClassLoaderBuilder.java b/test/hotspot/jtreg/compiler/lib/compile_framework/ClassLoaderBuilder.java new file mode 100644 index 00000000000..2f14cfb0f04 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/ClassLoaderBuilder.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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.lib.compile_framework; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * Build a ClassLoader that loads from classpath and {@code classesDir}. + * Helper class that generates a ClassLoader which allows loading classes + * from the classpath (see {@link Utils#getClassPaths()}) and {@code classesDir}. + *

    + * The CompileFramework compiles all its classes to a specific {@code classesDir}, + * and this generated ClassLoader thus can be used to load those classes. + */ +class ClassLoaderBuilder { + + /** + * Build a ClassLoader that loads from classpath and {@code classesDir}. + */ + public static ClassLoader build(Path classesDir) { + ClassLoader sysLoader = ClassLoader.getSystemClassLoader(); + + try { + // Classpath for all included classes (e.g. IR Framework). + // Get all class paths, convert to URLs. + List urls = new ArrayList<>(); + for (String path : Utils.getClassPaths()) { + urls.add(new File(path).toURI().toURL()); + } + // And add in the compiled classes from this instance of CompileFramework. + urls.add(new File(classesDir.toString()).toURI().toURL()); + return URLClassLoader.newInstance(urls.toArray(URL[]::new), sysLoader); + } catch (IOException e) { + throw new CompileFrameworkException("IOException while creating ClassLoader", e); + } + } +} diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java b/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java new file mode 100644 index 00000000000..0f45d982af6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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.lib.compile_framework; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; +import java.util.List; +import jdk.test.lib.JDKToolFinder; + +/** + * Helper class for compilation of Java and Jasm {@link SourceCode}. + */ +class Compile { + private static final int COMPILE_TIMEOUT = 60; + + private static final String JAVA_PATH = JDKToolFinder.getJDKTool("java"); + private static final String JAVAC_PATH = JDKToolFinder.getJDKTool("javac"); + + /** + * Compile all sources in {@code javaSources}. First write them to the {@code sourceDir}, + * then compile them to class-files which are stored in {@code classesDir}. + */ + public static void compileJavaSources(List javaSources, Path sourceDir, Path classesDir) { + if (javaSources.isEmpty()) { + Utils.printlnVerbose("No java sources to compile."); + return; + } + Utils.printlnVerbose("Compiling Java sources: " + javaSources.size()); + + List javaFilePaths = writeSourcesToFiles(javaSources, sourceDir); + compileJavaFiles(javaFilePaths, classesDir); + Utils.printlnVerbose("Java sources compiled."); + } + + /** + * Compile a list of files (i.e. {@code paths}) using javac and store + * them in {@code classesDir}. + */ + private static void compileJavaFiles(List paths, Path classesDir) { + List command = new ArrayList<>(); + + command.add(JAVAC_PATH); + command.add("-classpath"); + // Note: the backslashes from windows paths must be escaped! + command.add(Utils.getEscapedClassPathAndClassesDir(classesDir)); + command.add("-d"); + command.add(classesDir.toString()); + for (Path path : paths) { + command.add(path.toAbsolutePath().toString()); + } + + executeCompileCommand(command); + } + + /** + * Compile all sources in {@code jasmSources}. First write them to the {@code sourceDir}, + * then compile them to class-files which are stored in {@code classesDir}. + */ + public static void compileJasmSources(List jasmSources, Path sourceDir, Path classesDir) { + if (jasmSources.isEmpty()) { + Utils.printlnVerbose("No jasm sources to compile."); + return; + } + Utils.printlnVerbose("Compiling jasm sources: " + jasmSources.size()); + + List jasmFilePaths = writeSourcesToFiles(jasmSources, sourceDir); + compileJasmFiles(jasmFilePaths, classesDir); + Utils.printlnVerbose("Jasm sources compiled."); + } + + /** + * Compile a list of files (i.e. {@code paths}) using asmtools jasm and store + * them in {@code classesDir}. + */ + private static void compileJasmFiles(List paths, Path classesDir) { + List command = new ArrayList<>(); + + command.add(JAVA_PATH); + command.add("-classpath"); + command.add(getAsmToolsPath()); + command.add("org.openjdk.asmtools.jasm.Main"); + command.add("-d"); + command.add(classesDir.toString()); + for (Path path : paths) { + command.add(path.toAbsolutePath().toString()); + } + + executeCompileCommand(command); + } + + /** + * Get the path of asmtools, which is shipped with JTREG. + */ + private static String getAsmToolsPath() { + for (String path : Utils.getClassPaths()) { + if (path.endsWith("jtreg.jar")) { + File jtreg = new File(path); + File dir = jtreg.getAbsoluteFile().getParentFile(); + File asmtools = new File(dir, "asmtools.jar"); + if (!asmtools.exists()) { + throw new InternalCompileFrameworkException("Found jtreg.jar in classpath, but could not find asmtools.jar"); + } + return asmtools.getAbsolutePath(); + } + } + throw new InternalCompileFrameworkException("Could not find asmtools because could not find jtreg.jar in classpath"); + } + + private static void writeCodeToFile(String code, Path path) { + Utils.printlnVerbose("File: " + path); + + // Ensure directory of the file exists. + Path dir = path.getParent(); + try { + Files.createDirectories(dir); + } catch (Exception e) { + throw new CompileFrameworkException("Could not create directory: " + dir, e); + } + + // Write to file. + try (BufferedWriter writer = Files.newBufferedWriter(path)) { + writer.write(code); + } catch (Exception e) { + throw new CompileFrameworkException("Could not write file: " + path, e); + } + } + + /** + * Write each source in {@code sources} to a file inside {@code sourceDir}. + */ + private static List writeSourcesToFiles(List sources, Path sourceDir) { + List storedFiles = new ArrayList<>(); + for (SourceCode sourceCode : sources) { + Path path = sourceDir.resolve(sourceCode.filePathName()); + writeCodeToFile(sourceCode.code(), path); + storedFiles.add(path); + } + return storedFiles; + } + + /** + * Execute a given compilation, given as a {@code command}. + */ + private static void executeCompileCommand(List command) { + Utils.printlnVerbose("Compile command: " + String.join(" ", command)); + + ProcessBuilder builder = new ProcessBuilder(command); + builder.redirectErrorStream(true); + + String output; + int exitCode; + try { + Process process = builder.start(); + boolean exited = process.waitFor(COMPILE_TIMEOUT, TimeUnit.SECONDS); + if (!exited) { + process.destroyForcibly(); + System.out.println("Timeout: compile command: " + String.join(" ", command)); + throw new InternalCompileFrameworkException("Process timeout: compilation took too long."); + } + output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8); + exitCode = process.exitValue(); + } catch (IOException e) { + throw new InternalCompileFrameworkException("IOException during compilation", e); + } catch (InterruptedException e) { + throw new CompileFrameworkException("InterruptedException during compilation", e); + } + + if (exitCode != 0 || !output.isEmpty()) { + System.err.println("Compilation failed."); + System.err.println("Exit code: " + exitCode); + System.err.println("Output: '" + output + "'"); + throw new CompileFrameworkException("Compilation failed."); + } + } +} diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java b/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java new file mode 100644 index 00000000000..fe23d596f3c --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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.lib.compile_framework; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * This is the entry-point for the Compile Framework. Its purpose it to allow + * compilation and execution of Java and Jasm sources generated at runtime. + * + *

    Please reference the README.md for more details and examples. + */ +public class CompileFramework { + private final List javaSources = new ArrayList<>(); + private final List jasmSources = new ArrayList<>(); + private final Path sourceDir = Utils.makeUniqueDir("compile-framework-sources-"); + private final Path classesDir = Utils.makeUniqueDir("compile-framework-classes-"); + private ClassLoader classLoader; + + /** + * Set up a new Compile Framework instance, for a new compilation unit. + */ + public CompileFramework() {} + + /** + * Add a Java source to the compilation. + * + * @param className Class name of the class (e.g. "{@code p.xyz.YXZ}"). + * @param code Java code for the class, in the form of a {@link String}. + */ + public void addJavaSourceCode(String className, String code) { + javaSources.add(new SourceCode(className, "java", code)); + } + + /** + * Add a Jasm source to the compilation. + * + * @param className Class name of the class (e.g. "{@code p.xyz.YXZ}"). + * @param code Jasm code for the class, in the form of a {@link String}. + */ + public void addJasmSourceCode(String className, String code) { + jasmSources.add(new SourceCode(className, "jasm", code)); + } + + /** + * Compile all sources: store the sources to the {@link sourceDir} directory, compile + * Java and Jasm sources and store the generated class-files in the {@link classesDir} + * directory. + */ + public void compile() { + if (classLoader != null) { + throw new CompileFrameworkException("Cannot compile twice!"); + } + + Utils.printlnVerbose("------------------ CompileFramework: SourceCode -------------------"); + Utils.printlnVerbose(sourceCodesAsString(jasmSources)); + Utils.printlnVerbose(sourceCodesAsString(javaSources)); + + System.out.println("------------------ CompileFramework: Compilation ------------------"); + System.out.println("Source directory: " + sourceDir); + System.out.println("Classes directory: " + classesDir); + + Compile.compileJasmSources(jasmSources, sourceDir, classesDir); + Compile.compileJavaSources(javaSources, sourceDir, classesDir); + classLoader = ClassLoaderBuilder.build(classesDir); + } + + private static String sourceCodesAsString(List sourceCodes) { + StringBuilder builder = new StringBuilder(); + for (SourceCode sourceCode : sourceCodes) { + builder.append("SourceCode: ").append(sourceCode.filePathName()).append(System.lineSeparator()); + builder.append(sourceCode.code()).append(System.lineSeparator()); + } + return builder.toString(); + } + + /** + * Access a class from the compiled code. + * + * @param name Name of the class to be retrieved. + * @return The class corresponding to the {@code name}. + */ + public Class getClass(String name) { + try { + return Class.forName(name, true, classLoader); + } catch (ClassNotFoundException e) { + throw new CompileFrameworkException("Class not found:", e); + } + } + + /** + * Invoke a static method from the compiled code. + * + * @param className Class name of a compiled class. + * @param methodName Method name of the class. + * @param args List of arguments for the method invocation. + * @return Return value from the invocation. + */ + public Object invoke(String className, String methodName, Object[] args) { + Method method = findMethod(className, methodName); + + try { + return method.invoke(null, args); + } catch (IllegalAccessException e) { + throw new CompileFrameworkException("Illegal access:", e); + } catch (InvocationTargetException e) { + throw new CompileFrameworkException("Invocation target:", e); + } + } + + private Method findMethod(String className, String methodName) { + Class c = getClass(className); + Method[] methods = c.getDeclaredMethods(); + Method method = null; + + for (Method m : methods) { + if (m.getName().equals(methodName)) { + if (method != null) { + throw new CompileFrameworkException("Method name \"" + methodName + "\" not unique in class \n" + className + "\"."); + } + method = m; + } + } + + if (method == null) { + throw new CompileFrameworkException("Method \"" + methodName + "\" not found in class \n" + className + "\"."); + } + + return method; + } + + /** + * Returns the classpath appended with the {@link classesDir}, where + * the compiled classes are stored. This enables another VM to load + * the compiled classes. Note, the string is already backslash escaped, + * so that Windows paths which use backslashes can be used directly + * as strings. + * + * @return Classpath appended with the path to the compiled classes. + */ + public String getEscapedClassPathOfCompiledClasses() { + return Utils.getEscapedClassPathAndClassesDir(classesDir); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFrameworkException.java b/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFrameworkException.java new file mode 100644 index 00000000000..5c71a33a533 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFrameworkException.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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.lib.compile_framework; + +/** + * Exception thrown in the Compilation Framework. Most likely, the user is responsible for the failure. + */ +public class CompileFrameworkException extends RuntimeException { + public CompileFrameworkException(String message) { + super("Exception in Compile Framework:" + System.lineSeparator() + message); + } + + public CompileFrameworkException(String message, Throwable e) { + super("Exception in Compile Framework:" + System.lineSeparator() + message, e); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/InternalCompileFrameworkException.java b/test/hotspot/jtreg/compiler/lib/compile_framework/InternalCompileFrameworkException.java new file mode 100644 index 00000000000..0cfed80ce1c --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/InternalCompileFrameworkException.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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.lib.compile_framework; + +/** + * Internal exception thrown in Compilation Framework. Most likely, this is due to a bug in the CompileFramework. + */ +public class InternalCompileFrameworkException extends RuntimeException { + public InternalCompileFrameworkException(String message) { + super("Internal exception in Compile Framework, please file a bug:" + System.lineSeparator() + message); + } + + public InternalCompileFrameworkException(String message, Throwable e) { + super("Internal exception in Compile Framework, please file a bug:" + System.lineSeparator() + message, e); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/README.md b/test/hotspot/jtreg/compiler/lib/compile_framework/README.md new file mode 100644 index 00000000000..76ddf677811 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/README.md @@ -0,0 +1,57 @@ +# Compile Framework +The Compile Framework allows the compilation and execution of Java and Jasm sources, which are generated at runtime. + +## Motivation +We want to be able to generate Java and Jasm source code in the form of Strings at runtime, then compile them, load the classes and invoke some methods. This allows us to write more elaborate tests. For example small dedicated fuzzers that are targetted at some specific compiler optimization. + +This is more powerful than hand-written tests, as we can generalize tests and cover more examples. It can also be better than a script-generated test: those are static and often the script is not integrated with the generated test. Another limitation of a generator script is that it is only run once, creating fixed static tests. Compilation at runtime allows us to randomly generate tests each time. + +Of course we could compile at runtime without this framework, but it abstracts away the complexity of compilation, and allows the test-writer to focus on the generation of the source code. + +## How to Use the Compile Framework + +Please reference the examples found in [examples](../../../testlibrary_tests/compile_framework/examples/). Some basic tests can be found in [tests](../../../testlibrary_tests/compile_framework/tests/). + +Here a very simple example: + + // Create a new CompileFramework instance. + CompileFramework compileFramework = new CompileFramework(); + + // Add a java source file. + compileFramework.addJavaSourceCode("XYZ", ""); + + // Compile the source file. + compileFramework.compile(); + + // Object returnValue = XYZ.test(5); + Object returnValue = compileFramework.invoke("XYZ", "test", new Object[] {5}); + +### Creating a new Compile Framework Instance + +First, one must create a `new CompileFramework()`, which creates two directories: a sources and a classes directory (see `sourcesDir` and `classesDir` in [CompileFramework](./CompileFramework.java)). The sources directory is where all the sources are placed by the Compile Framework, and the classes directory is where all the compiled classes are placed by the Compile Framework. + +The Compile Framework prints the names of the directories, they are subdirectories of the JTREG scratch directory `JTWork/scratch`. + +### Adding Sources to the Compilation + +Java and Jasm sources can be added to the compilation using `compileFramework.addJavaSourceCode()` and `compileFramework.addJasmSourceCode()`. The source classes can depend on each other, and they can also use the IR Framework ([IRFrameworkJavaExample](../../../testlibrary_tests/compile_framework/examples/IRFrameworkJavaExample.java)). + +When using the IR Framework, or any other library that needs to be compiled, it can be necessary to explicitly let JTREG compile that library. For example with `@compile ../../../compiler/lib/ir_framework/TestFramework.java`. Otherwise, the corresponding class files may not be available, and a corresponding failure will be encounter at class loading. + +### Compiling + +All sources are compiled with `compileFramework.compile()`. First, the sources are stored to the sources directory, then compiled, and then the class-files stored in the classes directory. The respective directory names are printed, so that the user can easily access the generated files for debugging. + +### Interacting with the Compiled Code + +The compiled code is then loaded with a `ClassLoader`. The classes can be accessed directly with `compileFramework.getClass(name)`. Specific methods can also directly be invoked with `compileFramework.invoke()`. + +Should one require the modified classpath that includes the compiled classes, this is available with `compileFramework.getEscapedClassPathOfCompiledClasses()`. This can be necessary if the test launches any other VMs that also access the compiled classes. This is for example necessary when using the IR Framework. + +### Running the Compiled Code in a New VM + +One can also run the compiled code in a new VM. For this, one has to set the classpath with `compileFramework.getEscapedClassPathOfCompiledClasses()` ([RunWithFlagsExample](../../../testlibrary_tests/compile_framework/examples/RunWithFlagsExample.java)) + +### Verbose Printing + +For debugging purposes, one can enable verbose printing, with `-DCompileFrameworkVerbose=true`. diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/SourceCode.java b/test/hotspot/jtreg/compiler/lib/compile_framework/SourceCode.java new file mode 100644 index 00000000000..df38e420758 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/SourceCode.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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.lib.compile_framework; + +/** + * This class represents the source code of a specific class. + */ +record SourceCode(String className, String extension, String code) { + public String filePathName() { + StringBuilder builder = new StringBuilder(); + builder.append(className.replace('.','/')).append(".").append(extension); + return builder.toString(); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/Utils.java b/test/hotspot/jtreg/compiler/lib/compile_framework/Utils.java new file mode 100644 index 00000000000..0ac2720f33b --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/Utils.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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.lib.compile_framework; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.Path; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * Utility class, with many helper methods for the Compile Framework. + */ +class Utils { + private static final boolean VERBOSE = Boolean.getBoolean("CompileFrameworkVerbose"); + + /** + * Verbose printing, enabled with {@code -DCompileFrameworkVerbose=true}. + */ + public static void printlnVerbose(String s) { + if (VERBOSE) { + System.out.println(s); + } + } + + /** + * Create a temporary directory with a unique name to avoid collisions + * with multi-threading. Used to create the sources and classes directories. Since they + * are unique even across threads, the Compile Framework is multi-threading safe, i.e. + * it does not have collisions if two instances generate classes with the same name. + */ + public static Path makeUniqueDir(String prefix) { + try { + return Files.createTempDirectory(Paths.get("."), prefix); + } catch (Exception e) { + throw new InternalCompileFrameworkException("Could not set up temporary directory", e); + } + } + + /** + * Get all paths in the classpath. + */ + public static String[] getClassPaths() { + String separator = File.pathSeparator; + return System.getProperty("java.class.path").split(separator); + } + + /** + * Return the classpath, appended with the {@code classesDir}. + */ + public static String getEscapedClassPathAndClassesDir(Path classesDir) { + String cp = System.getProperty("java.class.path") + + File.pathSeparator + + classesDir.toAbsolutePath(); + // Escape the backslash for Windows paths. We are using the path in the + // command-line and Java code, so we always want it to be escaped. + return cp.replace("\\", "\\\\"); + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/CombinedJavaJasmExample.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/CombinedJavaJasmExample.java new file mode 100644 index 00000000000..565c92c1b70 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/CombinedJavaJasmExample.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 Example test to use the Compile Framework. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.examples.CombinedJavaJasmExample + */ + +package compile_framework.examples; + +import compiler.lib.compile_framework.*; + +/** + * This test shows a compilation of multiple Java and Jasm source code files. + * In this example, the classes even reference each other. + */ +public class CombinedJavaJasmExample { + + // Generate a source jasm file as String + public static String generateJasm() { + return """ + package p/xyz; + + super public class XYZJasm { + public static Method test:"(I)I" + stack 20 locals 20 + { + iload_0; + iconst_2; + imul; + invokestatic Method p/xyz/XYZJava."mul3":"(I)I"; + ireturn; + } + + public static Method mul5:"(I)I" + stack 20 locals 20 + { + iload_0; + ldc 5; + imul; + ireturn; + } + } + """; + } + + // Generate a source java file as String + public static String generateJava() { + return """ + package p.xyz; + + public class XYZJava { + public static int test(int i) { + return p.xyz.XYZJasm.mul5(i * 7); + } + + public static int mul3(int i) { + return i * 3; + } + } + """; + } + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Generate files. + comp.addJasmSourceCode("p.xyz.XYZJasm", generateJasm()); + comp.addJavaSourceCode("p.xyz.XYZJava", generateJava()); + + // Compile the source files. + comp.compile(); + + test(comp, "p.xyz.XYZJasm", "test", 11, 11 * 2 * 3); + test(comp, "p.xyz.XYZJava", "test", 13, 13 * 7 * 5); + + System.out.println("Success."); + } + + public static void test(CompileFramework comp, String className, String methodName, int input, int expected) { + Object ret = comp.invoke(className, methodName, new Object[] {input}); + + // Extract return value of invocation, verify its value. + int i = (int)ret; + System.out.println("Result of call: " + i + " vs expected: " + expected); + if (i != expected) { + throw new RuntimeException("wrong value: " + i); + } + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/IRFrameworkJavaExample.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/IRFrameworkJavaExample.java new file mode 100644 index 00000000000..11b8828d753 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/IRFrameworkJavaExample.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 Example test to use the Compile Framework together with the IR Framework (i.e. TestFramework). + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @compile ../../../compiler/lib/ir_framework/TestFramework.java + * @run driver compile_framework.examples.IRFrameworkJavaExample + */ + +package compile_framework.examples; + +import compiler.lib.compile_framework.*; +import jdk.test.lib.Utils; +import jdk.test.lib.Platform; +import java.lang.reflect.InvocationTargetException; + +/** + * This test shows that the IR verification can be done on code compiled by the Compile Framework. + * The "@compile" command for JTREG is required so that the IRFramework is compiled, other javac + * might not compile it because it is not present in the class, only in the dynamically compiled + * code. + *

    + * Additionally, we must set the classpath for the Test-VM, so that it has access to all compiled + * classes (see {@link CompileFramework#getEscapedClassPathOfCompiledClasses}). + */ +public class IRFrameworkJavaExample { + + public static void main(String[] args) { + testX1(); + testX2(); + } + + // Generate a source java file as String + public static String generateX1(CompileFramework comp) { + return String.format(""" + import compiler.lib.ir_framework.*; + + public class X1 { + public static void main(String args[]) { + TestFramework framework = new TestFramework(X1.class); + framework.addFlags("-classpath", "%s"); + framework.start(); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0"}, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}) + static float[] test() { + float[] a = new float[1024*8]; + for (int i = 0; i < a.length; i++) { + a[i]++; + } + return a; + } + } + """, comp.getEscapedClassPathOfCompiledClasses()); + } + + static void testX1() { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("X1", generateX1(comp)); + + // Compile the source file. + comp.compile(); + + // X1.main(); + comp.invoke("X1", "main", new Object[] {null}); + } + + // Generate a source java file as String + public static String generateX2(CompileFramework comp) { + // Example with conflicting "@IR" rules -> expect a IRViolationException. + return String.format(""" + import compiler.lib.ir_framework.*; + + public class X2 { + public static void main(String args[]) { + TestFramework framework = new TestFramework(X2.class); + framework.addFlags("-classpath", "%s"); + framework.start(); + } + + @Test + @IR(counts = {IRNode.LOAD, "> 0"}) + @IR(failOn = IRNode.LOAD) + static void test() { + } + } + """, comp.getEscapedClassPathOfCompiledClasses()); + } + + static void testX2() { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("X2", generateX2(comp)); + + // Compile the source file. + comp.compile(); + + // Load the compiled class. + Class c = comp.getClass("X2"); + + // Invoke the "X2.main" method from the compiled and loaded class. + try { + c.getDeclaredMethod("main", new Class[] { String[].class }).invoke(null, new Object[] { null }); + + // Check if IR framework is expected to execute the IR rules. + if (Utils.getTestJavaOpts().length == 0 && Platform.isDebugBuild() && !Platform.isInt() && !Platform.isComp()) { + throw new RuntimeException("IRViolationException expected."); + } else { + System.out.println("Got no IRViolationException, but was also not expected."); + } + } catch (NoSuchMethodException e) { + throw new RuntimeException("No such method:", e); + } catch (IllegalAccessException e) { + throw new RuntimeException("Illegal access:", e); + } catch (InvocationTargetException e) { + Throwable t = e.getCause(); + if (t == null) { + throw new RuntimeException("IRViolationException expected:", e); + } + if (!t.getClass().getSimpleName().equals("IRViolationException")) { + throw new RuntimeException("IRViolationException expected:", e); + } + System.out.println("Success, we got a IRViolationException."); + } + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/MultiFileJasmExample.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/MultiFileJasmExample.java new file mode 100644 index 00000000000..33fe07a5397 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/MultiFileJasmExample.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 Example test to use the Compile Framework. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver comile_framework.examples.MultiFileJasmExample + */ + +package comile_framework.examples; + +import compiler.lib.compile_framework.*; +import java.io.StringWriter; +import java.io.PrintWriter; + +/** + * This test shows a compilation of multiple jasm source code files. + */ +public class MultiFileJasmExample { + + // Generate a source jasm file as String + public static String generate(int i) { + StringWriter writer = new StringWriter(); + PrintWriter out = new PrintWriter(writer); + out.println("package p/xyz;"); + out.println(""); + out.println("super public class XYZ" + i + " {"); + out.println(" public static Method test:\"(I)I\""); + out.println(" stack 20 locals 20"); + out.println(" {"); + out.println(" iload_0;"); + out.println(" iconst_2;"); // every call multiplies by 2, in total 2^10 = 1024 + out.println(" imul;"); + if (i != 0) { + out.println(" invokestatic Method p/xyz/XYZ" + (i-1) + ".\"test\":\"(I)I\";"); + } + out.println(" ireturn;"); + out.println(" }"); + out.println("}"); + return writer.toString(); + } + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Generate 10 files. + for (int i = 0; i < 10; i++) { + comp.addJasmSourceCode("p.xyz.XYZ" + i, generate(i)); + } + + // Compile the source files. + comp.compile(); + + // Object ret = XYZ9.test(5); + Object ret = comp.invoke("p.xyz.XYZ9", "test", new Object[] { 5 }); + + // Extract return value of invocation, verify its value. + int i = (int)ret; + System.out.println("Result of call: " + i); + if (i != 5 * 1024) { + throw new RuntimeException("wrong value: " + i); + } + System.out.println("Success."); + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/MultiFileJavaExample.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/MultiFileJavaExample.java new file mode 100644 index 00000000000..e493ebab4e8 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/MultiFileJavaExample.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 Example test to use the Compile Framework. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.examples.MultiFileJavaExample + */ + +package compile_framework.examples; + +import compiler.lib.compile_framework.*; +import java.io.StringWriter; +import java.io.PrintWriter; + +/** + * This test shows a compilation of multiple java source code files. + */ +public class MultiFileJavaExample { + + // Generate a source java file as String + public static String generate(int i) { + StringWriter writer = new StringWriter(); + PrintWriter out = new PrintWriter(writer); + out.println("package p.xyz;"); + out.println(""); + out.println("public class XYZ" + i + " {"); + if (i > 0) { + out.println(" public XYZ" + (i - 1) + " xyz = new XYZ" + (i - 1) + "();"); + } + out.println(""); + out.println(" public static Object test() {"); + out.println(" return new XYZ" + i + "();"); + out.println(" }"); + out.println("}"); + return writer.toString(); + } + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Generate 10 files. + for (int i = 0; i < 10; i++) { + comp.addJavaSourceCode("p.xyz.XYZ" + i, generate(i)); + } + + // Compile the source files. + comp.compile(); + + // Object ret = XYZ9.test(); + Object ret = comp.invoke("p.xyz.XYZ9", "test", new Object[] {}); + + if (!ret.getClass().getSimpleName().equals("XYZ9")) { + throw new RuntimeException("wrong result:" + ret); + } + System.out.println("Success."); + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/RunWithFlagsExample.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/RunWithFlagsExample.java new file mode 100644 index 00000000000..a67e6e0eb49 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/RunWithFlagsExample.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 Example test to use the Compile Framework and run the compiled code with additional flags + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.examples.RunWithFlagsExample + */ + +package compile_framework.examples; + +import compiler.lib.compile_framework.*; + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * This test shows how the generated code can be compiled and invoked in a new VM. This allows + * the execution of the code with additional VM flags and options. + *

    + * The new VM must be able to locate the class files of the newly compiled code. For this we + * set the class path using {@link CompileFramework#getEscapedClassPathOfCompiledClasses}. + */ +public class RunWithFlagsExample { + + private static String generate() { + return """ + package p.xyz; + + public class X { + public static void main(String args[]) { + System.out.println("Hello world!"); + System.out.println(System.getProperty("MyMessage", "fail")); + System.err.println(args[0]); + } + } + """; + } + + public static void main(String[] args) throws Exception { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a Java source file. + comp.addJavaSourceCode("p.xyz.X", generate()); + + // Compile the source file. + comp.compile(); + + // Build command line. + String[] command = { + // Set the classpath to include our newly compiled class. + "-classpath", + comp.getEscapedClassPathOfCompiledClasses(), + // Pass additional flags here. + // "-Xbatch" is a harmless VM flag, so this example runs everywhere without issues. + "-Xbatch", + // We can also pass properties like "MyMessage". + "-DMyMessage=hello_world", + "p.xyz.X", + "hello_arg" + }; + + // Execute the command, and capture the output. + // The JTREG Java and VM options are automatically passed to the test VM. + OutputAnalyzer analyzer = ProcessTools.executeTestJava(command); + + // Verify output. + analyzer.shouldHaveExitValue(0); + analyzer.stdoutContains("Hello world!"); + analyzer.stdoutContains("hello_world"); + analyzer.stdoutContains("hello_arg"); + + // Print output to stderr. + analyzer.reportDiagnosticSummary(); + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/SimpleJasmExample.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/SimpleJasmExample.java new file mode 100644 index 00000000000..e01b45e7441 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/SimpleJasmExample.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 Example test to use the Compile Framework. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.examples.SimpleJasmExample + */ + +package compile_framework.examples; + +import compiler.lib.compile_framework.*; + +/** + * This test shows a simple compilation of java source code, and its invocation. + */ +public class SimpleJasmExample { + + // Generate a source jasm file as String + public static String generate() { + return """ + super public class XYZ { + public static Method test:"(I)I" + stack 20 locals 20 + { + iload_0; + iconst_2; + imul; + ireturn; + } + } + """; + } + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + String src = generate(); + comp.addJasmSourceCode("XYZ", src); + + // Compile the source file. + comp.compile(); + + // Object ret = XYZ.test(5); + Object ret = comp.invoke("XYZ", "test", new Object[] {5}); + + // Extract return value of invocation, verify its value. + int i = (int)ret; + System.out.println("Result of call: " + i); + if (i != 10) { + throw new RuntimeException("wrong value: " + i); + } + System.out.println("Success."); + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/SimpleJavaExample.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/SimpleJavaExample.java new file mode 100644 index 00000000000..5e54a6e8a08 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/SimpleJavaExample.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 Example test to use the Compile Framework. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.examples.SimpleJavaExample + */ + +package compile_framework.examples; + +import compiler.lib.compile_framework.*; + +/** + * This test shows a simple compilation of java source code, and its invocation. + */ +public class SimpleJavaExample { + + // Generate a source java file as String + public static String generate() { + return """ + public class XYZ { + public static int test(int i) { + System.out.println("Hello from XYZ.test: " + i); + return i * 2; + } + } + """; + } + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("XYZ", generate()); + + // Compile the source file. + comp.compile(); + + // Object ret = XYZ.test(5); + Object ret = comp.invoke("XYZ", "test", new Object[] {5}); + + // Extract return value of invocation, verify its value. + int i = (int)ret; + System.out.println("Result of call: " + i); + if (i != 10) { + throw new RuntimeException("wrong value: " + i); + } + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestBadJasmCompilation.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestBadJasmCompilation.java new file mode 100644 index 00000000000..b5b6f3e1040 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestBadJasmCompilation.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 Example test with failing jasm compilation. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.tests.TestBadJasmCompilation + */ + +package compile_framework.tests; + +import compiler.lib.compile_framework.*; + +public class TestBadJasmCompilation { + + // Generate a source jasm file as String + public static String generate() { + return """ + super public class XYZ { + some bad code + } + """; + } + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJasmSourceCode("XYZ", generate()); + + try { + // Compile the source file. + comp.compile(); + throw new RuntimeException("Expected compilation to fail."); + } catch (CompileFrameworkException e) { + System.out.println("Success, expected compilation to fail."); + } + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestBadJavaCompilation.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestBadJavaCompilation.java new file mode 100644 index 00000000000..1cb1d79afbc --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestBadJavaCompilation.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 Example test with failing java compilation. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.tests.TestBadJavaCompilation + */ + +package compile_framework.tests; + +import compiler.lib.compile_framework.*; + +public class TestBadJavaCompilation { + + // Generate a source java file as String + public static String generate() { + return """ + public class XYZ { + some bad code + } + """; + } + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("XYZ", generate()); + + try { + // Compile the source file. + comp.compile(); + throw new RuntimeException("Expected compilation to fail."); + } catch (CompileFrameworkException e) { + System.out.println("Success, expected compilation to fail."); + } + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestConcurrentCompilation.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestConcurrentCompilation.java new file mode 100644 index 00000000000..1cb902d34e4 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestConcurrentCompilation.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 Example test with multi-threaded use of the CompileFramework. + * Tests that the source and class directories are set up correctly. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.tests.TestConcurrentCompilation + */ + +package compile_framework.tests; + +import compiler.lib.compile_framework.*; + +import java.util.ArrayList; +import java.util.List; + +public class TestConcurrentCompilation { + + // Generate a source java file as String + public static String generate(int i) { + return String.format(""" + public class XYZ { + public static int test() { + return %d; + } + } + """, i); + } + + public static void test(int i) { + System.out.println("Generate and compile XYZ for " + i); + CompileFramework comp = new CompileFramework(); + comp.addJavaSourceCode("XYZ", generate(i)); + comp.compile(); + + // Now, sleep to give the other threads time to compile and store their class-files. + System.out.println("Sleep for " + i); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + System.out.println("Sleep interrupted for " + i); + } + + // Now, hopefully all threads have compiled and stored their class-files. + // We can check if we get the expected result, i.e. the class-file from the current thread. + System.out.println("Run XYZ.test for " + i); + int j = (int)comp.invoke("XYZ", "test", new Object[] {}); + if (i != j) { + System.out.println("Wrong value: " + i + " vs " + j); + throw new RuntimeException("Wrong value: " + i + " vs " + j); + } + System.out.println("Success for " + i); + } + + public static class MyRunnable implements Runnable { + private int i; + + public MyRunnable(int i) { + this.i = i; + } + + public void run() { + TestConcurrentCompilation.test(i); + } + } + + public static void main(String[] args) { + System.out.println("Generating threads:"); + List threads = new ArrayList(); + for (int i = 0; i < 3; i++) { + Thread thread = new Thread(new MyRunnable(i)); + thread.start(); + threads.add(thread); + } + System.out.println("Waiting to join threads:"); + try { + for (Thread thread : threads) { + thread.join(); + } + } catch (InterruptedException e) { + throw new RuntimeException("interrupted", e); + } + System.out.println("Success."); + } +} From 577babf1968700e4b648305cd5a5c2ddf712e2dc Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Wed, 16 Oct 2024 07:27:28 +0000 Subject: [PATCH 73/95] 8334010: VM crashes with ObjectAlignmentInBytes > GCCardSizeInBytes Reviewed-by: shade, iwalulya --- src/hotspot/share/gc/shared/gcArguments.cpp | 8 +++ .../jtreg/gc/TestObjectAlignmentCardSize.java | 63 +++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 test/hotspot/jtreg/gc/TestObjectAlignmentCardSize.java diff --git a/src/hotspot/share/gc/shared/gcArguments.cpp b/src/hotspot/share/gc/shared/gcArguments.cpp index 9736c0f7fdc..2522925746b 100644 --- a/src/hotspot/share/gc/shared/gcArguments.cpp +++ b/src/hotspot/share/gc/shared/gcArguments.cpp @@ -30,6 +30,7 @@ #include "runtime/arguments.hpp" #include "runtime/globals.hpp" #include "runtime/globals_extension.hpp" +#include "utilities/formatBuffer.hpp" #include "utilities/macros.hpp" size_t HeapAlignment = 0; @@ -166,6 +167,13 @@ void GCArguments::initialize_heap_flags_and_sizes() { FLAG_SET_ERGO(MinHeapDeltaBytes, align_up(MinHeapDeltaBytes, SpaceAlignment)); + if (checked_cast(ObjectAlignmentInBytes) > GCCardSizeInBytes) { + err_msg message("ObjectAlignmentInBytes %u is larger than GCCardSizeInBytes %u", + ObjectAlignmentInBytes, GCCardSizeInBytes); + vm_exit_during_initialization("Invalid combination of GCCardSizeInBytes and ObjectAlignmentInBytes", + message); + } + DEBUG_ONLY(assert_flags();) } diff --git a/test/hotspot/jtreg/gc/TestObjectAlignmentCardSize.java b/test/hotspot/jtreg/gc/TestObjectAlignmentCardSize.java new file mode 100644 index 00000000000..5fb46a87b51 --- /dev/null +++ b/test/hotspot/jtreg/gc/TestObjectAlignmentCardSize.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 TestObjectAlignmentCardSize.java + * @summary Test to check correct handling of ObjectAlignmentInBytes and GCCardSizeInBytes combinations + * @requires vm.gc != "Z" + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @run driver gc.TestObjectAlignmentCardSize + */ + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +public class TestObjectAlignmentCardSize { + private static void runTest(int objectAlignment, int cardSize, boolean shouldSucceed) throws Exception { + OutputAnalyzer output = ProcessTools.executeTestJava( + "-XX:ObjectAlignmentInBytes=" + objectAlignment, + "-XX:GCCardSizeInBytes=" + cardSize, + "-Xmx32m", + "-Xms32m", + "-version"); + + System.out.println("Output:\n" + output.getOutput()); + + if (shouldSucceed) { + output.shouldHaveExitValue(0); + } else { + output.shouldContain("Invalid combination of GCCardSizeInBytes and ObjectAlignmentInBytes"); + output.shouldNotHaveExitValue(0); + } + } + + public static void main(String[] args) throws Exception { + runTest(8, 512, true); + runTest(128, 128, true); + runTest(256, 128, false); + runTest(256, 256, true); + runTest(256, 512, true); + } +} From e7cf25ce0efdf568fc8eaff249d49e46d5a6eda1 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Wed, 16 Oct 2024 07:28:36 +0000 Subject: [PATCH 74/95] 8340801: Disable ubsan checks in some awt/2d coding Reviewed-by: ihse, lucy, goetz, jwaters --- src/java.base/share/native/libjava/ub.h | 42 +++++++++++++++++++ .../share/native/libawt/java2d/loops/IntRgb.c | 3 ++ 2 files changed, 45 insertions(+) create mode 100644 src/java.base/share/native/libjava/ub.h diff --git a/src/java.base/share/native/libjava/ub.h b/src/java.base/share/native/libjava/ub.h new file mode 100644 index 00000000000..cf7f491ca45 --- /dev/null +++ b/src/java.base/share/native/libjava/ub.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef _UB_H_ +#define _UB_H_ + +/* ATTRIBUTE_NO_UBSAN - Function attribute which informs the compiler to disable UBSan checks in the + * following function or method. + */ +#ifdef UNDEFINED_BEHAVIOR_SANITIZER +#if defined(__clang__) || defined(__GNUC__) +#define ATTRIBUTE_NO_UBSAN __attribute__((no_sanitize("undefined"))) +#endif +#endif + +#ifndef ATTRIBUTE_NO_UBSAN +#define ATTRIBUTE_NO_UBSAN +#endif + +#endif diff --git a/src/java.desktop/share/native/libawt/java2d/loops/IntRgb.c b/src/java.desktop/share/native/libawt/java2d/loops/IntRgb.c index 7155f70b472..aec11d17c89 100644 --- a/src/java.desktop/share/native/libawt/java2d/loops/IntRgb.c +++ b/src/java.desktop/share/native/libawt/java2d/loops/IntRgb.c @@ -34,6 +34,8 @@ #include "ByteGray.h" #include "Index12Gray.h" +#include "ub.h" + /* * This file declares, registers, and defines the various graphics * primitive loops to manipulate surfaces of type "IntRgb". @@ -166,6 +168,7 @@ DEFINE_ALPHA_MASKBLIT(IntArgbPre, IntRgb, 4ByteArgb) DEFINE_ALPHA_MASKBLIT(IntRgb, IntRgb, 4ByteArgb) +ATTRIBUTE_NO_UBSAN DEFINE_SOLID_DRAWGLYPHLISTAA(IntRgb, 3ByteRgb) DEFINE_SOLID_DRAWGLYPHLISTLCD(IntRgb, 3ByteRgb) From ebc17c7c8d6febd5a887309d1b7a466bcd2cc0a9 Mon Sep 17 00:00:00 2001 From: Johny Jose Date: Wed, 16 Oct 2024 07:36:07 +0000 Subject: [PATCH 75/95] 8339637: (tz) Update Timezone Data to 2024b Reviewed-by: naoto, coffeys, jlu --- .../share/classes/java/time/ZoneId.java | 18 +- .../sun/util/calendar/ZoneInfoFile.java | 7 +- src/java.base/share/data/tzdata/VERSION | 2 +- src/java.base/share/data/tzdata/africa | 73 +- src/java.base/share/data/tzdata/antarctica | 58 +- src/java.base/share/data/tzdata/asia | 442 ++++++----- src/java.base/share/data/tzdata/australasia | 130 ++-- src/java.base/share/data/tzdata/backward | 15 +- src/java.base/share/data/tzdata/etcetera | 54 +- src/java.base/share/data/tzdata/europe | 713 +++++++++++------- src/java.base/share/data/tzdata/leapseconds | 8 +- src/java.base/share/data/tzdata/northamerica | 145 +++- src/java.base/share/data/tzdata/southamerica | 486 ++++++------ src/java.base/share/data/tzdata/zone.tab | 3 +- .../Format/DateFormat/TimeZoneNameTest.java | 2 +- .../java/time/tck/java/time/TCKZoneId.java | 12 +- .../java/util/TimeZone/OldIDMappingTest.java | 3 +- .../java/util/TimeZone/TimeZoneData/VERSION | 2 +- .../util/TimeZone/TimeZoneData/aliases.txt | 12 + .../TimeZone/TimeZoneData/displaynames.txt | 11 - .../sun/util/calendar/zi/TestZoneInfo310.java | 1 - .../jdk/sun/util/calendar/zi/ZoneInfoOld.java | 12 +- .../util/resources/TimeZone/Bug4848242.java | 12 +- 23 files changed, 1258 insertions(+), 963 deletions(-) diff --git a/src/java.base/share/classes/java/time/ZoneId.java b/src/java.base/share/classes/java/time/ZoneId.java index 21c9054761c..47758b64e54 100644 --- a/src/java.base/share/classes/java/time/ZoneId.java +++ b/src/java.base/share/classes/java/time/ZoneId.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -186,15 +186,12 @@ public abstract sealed class ZoneId implements Serializable permits ZoneOffset, * This map allows the IDs to continue to be used via the * {@link #of(String, Map)} factory method. *

    - * This map contains a mapping of the IDs that is in line with TZDB 2005r and + * This map contains a mapping of the IDs that is in line with TZDB 2024b and * later, where 'EST', 'MST' and 'HST' map to IDs which do not include daylight - * savings. + * savings since 1970. This mapping may change in update releases in support of new versions of TZDB. *

    * This maps as follows: *

      - *
    • EST - -05:00
    • - *
    • HST - -10:00
    • - *
    • MST - -07:00
    • *
    • ACT - Australia/Darwin
    • *
    • AET - Australia/Sydney
    • *
    • AGT - America/Argentina/Buenos_Aires
    • @@ -208,10 +205,13 @@ public abstract sealed class ZoneId implements Serializable permits ZoneOffset, *
    • CTT - Asia/Shanghai
    • *
    • EAT - Africa/Addis_Ababa
    • *
    • ECT - Europe/Paris
    • + *
    • EST - America/Panama
    • + *
    • HST - Pacific/Honolulu
    • *
    • IET - America/Indiana/Indianapolis
    • *
    • IST - Asia/Kolkata
    • *
    • JST - Asia/Tokyo
    • *
    • MIT - Pacific/Apia
    • + *
    • MST - America/Phoenix
    • *
    • NET - Asia/Yerevan
    • *
    • NST - Pacific/Auckland
    • *
    • PLT - Asia/Karachi
    • @@ -249,9 +249,9 @@ public abstract sealed class ZoneId implements Serializable permits ZoneOffset, entry("PST", "America/Los_Angeles"), entry("SST", "Pacific/Guadalcanal"), entry("VST", "Asia/Ho_Chi_Minh"), - entry("EST", "-05:00"), - entry("MST", "-07:00"), - entry("HST", "-10:00") + entry("EST", "America/Panama"), + entry("MST", "America/Phoenix"), + entry("HST", "Pacific/Honolulu") ); /** * Serialization version. diff --git a/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java b/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java index 4e30e403bb2..1550f7da3de 100644 --- a/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java +++ b/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java @@ -281,12 +281,11 @@ private static void addOldMapping() { if (USE_OLDMAPPING) { aliases.put("EST", "America/New_York"); aliases.put("MST", "America/Denver"); - aliases.put("HST", "Pacific/Honolulu"); } else { - zones.put("EST", new ZoneInfo("EST", -18000000)); - zones.put("MST", new ZoneInfo("MST", -25200000)); - zones.put("HST", new ZoneInfo("HST", -36000000)); + aliases.put("EST", "America/Panama"); + aliases.put("MST", "America/Phoenix"); } + aliases.put("HST", "Pacific/Honolulu"); } public static boolean useOldMapping() { diff --git a/src/java.base/share/data/tzdata/VERSION b/src/java.base/share/data/tzdata/VERSION index b138ed7fa78..740427424a6 100644 --- a/src/java.base/share/data/tzdata/VERSION +++ b/src/java.base/share/data/tzdata/VERSION @@ -21,4 +21,4 @@ # or visit www.oracle.com if you need additional information or have any # questions. # -tzdata2024a +tzdata2024b diff --git a/src/java.base/share/data/tzdata/africa b/src/java.base/share/data/tzdata/africa index 72b188f074d..8098f4bea9d 100644 --- a/src/java.base/share/data/tzdata/africa +++ b/src/java.base/share/data/tzdata/africa @@ -126,17 +126,16 @@ Zone Africa/Algiers 0:12:12 - LMT 1891 Mar 16 # Cape Verde / Cabo Verde # -# From Paul Eggert (2018-02-16): -# Shanks gives 1907 for the transition to +02. -# For now, ignore that and follow the 1911-05-26 Portuguese decree -# (see Europe/Lisbon). +# From Tim Parenti (2024-07-01), per Paul Eggert (2018-02-16): +# For timestamps before independence, see commentary for Europe/Lisbon. +# Shanks gives 1907 instead for the transition to -02. # # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Atlantic/Cape_Verde -1:34:04 - LMT 1912 Jan 01 2:00u # Praia - -2:00 - -02 1942 Sep - -2:00 1:00 -01 1945 Oct 15 - -2:00 - -02 1975 Nov 25 2:00 - -1:00 - -01 + -2:00 - %z 1942 Sep + -2:00 1:00 %z 1945 Oct 15 + -2:00 - %z 1975 Nov 25 2:00 + -1:00 - %z # Chad # Zone NAME STDOFF RULES FORMAT [UNTIL] @@ -368,14 +367,12 @@ Zone Africa/Cairo 2:05:09 - LMT 1900 Oct # Guinea-Bissau # -# From Paul Eggert (2018-02-16): -# Shanks gives 1911-05-26 for the transition to WAT, -# evidently confusing the date of the Portuguese decree -# (see Europe/Lisbon) with the date that it took effect. +# From Tim Parenti (2024-07-01), per Paul Eggert (2018-02-16): +# For timestamps before independence, see commentary for Europe/Lisbon. # # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Africa/Bissau -1:02:20 - LMT 1912 Jan 1 1:00u - -1:00 - -01 1975 + -1:00 - %z 1975 0:00 - GMT # Comoros @@ -440,10 +437,10 @@ Zone Africa/Bissau -1:02:20 - LMT 1912 Jan 1 1:00u # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Africa/Nairobi 2:27:16 - LMT 1908 May - 2:30 - +0230 1928 Jun 30 24:00 + 2:30 - %z 1928 Jun 30 24:00 3:00 - EAT 1930 Jan 4 24:00 - 2:30 - +0230 1936 Dec 31 24:00 - 2:45 - +0245 1942 Jul 31 24:00 + 2:30 - %z 1936 Dec 31 24:00 + 2:45 - %z 1942 Jul 31 24:00 3:00 - EAT # Liberia @@ -614,7 +611,7 @@ Rule Mauritius 2008 only - Oct lastSun 2:00 1:00 - Rule Mauritius 2009 only - Mar lastSun 2:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis - 4:00 Mauritius +04/+05 + 4:00 Mauritius %z # Agalega Is, Rodriguez # no information; probably like Indian/Mauritius @@ -1094,10 +1091,10 @@ Rule Morocco 2087 only - May 11 2:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Africa/Casablanca -0:30:20 - LMT 1913 Oct 26 - 0:00 Morocco +00/+01 1984 Mar 16 - 1:00 - +01 1986 - 0:00 Morocco +00/+01 2018 Oct 28 3:00 - 1:00 Morocco +01/+00 + 0:00 Morocco %z 1984 Mar 16 + 1:00 - %z 1986 + 0:00 Morocco %z 2018 Oct 28 3:00 + 1:00 Morocco %z # Western Sahara # @@ -1111,9 +1108,9 @@ Zone Africa/Casablanca -0:30:20 - LMT 1913 Oct 26 # since most of it was then controlled by Morocco. Zone Africa/El_Aaiun -0:52:48 - LMT 1934 Jan # El Aaiún - -1:00 - -01 1976 Apr 14 - 0:00 Morocco +00/+01 2018 Oct 28 3:00 - 1:00 Morocco +01/+00 + -1:00 - %z 1976 Apr 14 + 0:00 Morocco %z 2018 Oct 28 3:00 + 1:00 Morocco %z # Botswana # Burundi @@ -1124,13 +1121,27 @@ Zone Africa/El_Aaiun -0:52:48 - LMT 1934 Jan # El Aaiún # Zambia # Zimbabwe # -# Shanks gives 1903-03-01 for the transition to CAT. -# Perhaps the 1911-05-26 Portuguese decree -# https://dre.pt/pdf1sdip/1911/05/12500/23132313.pdf -# merely made it official? +# From Tim Parenti (2024-07-01): +# For timestamps before Mozambique's independence, see commentary for +# Europe/Lisbon. +# +# From Paul Eggert (2024-05-24): +# The London Gazette, 1903-04-03, page 2245, says that +# as of 1903-03-03 a time ball at the port of Lourenço Marques +# (as Maputo was then called) was dropped daily at 13:00:00 LMT, +# corresponding to 22:49:41.7 GMT, so local time was +02:10:18.3. +# Conversely, the newspaper South Africa, 1909-02-09, page 321, +# says the port had just installed an apparatus that communicated +# "from the controlling clock in the new Observatory at Reuben Point ... +# exact mean South African time, i.e., 30 deg., or 2 hours East of Greenwich". +# Although Shanks gives 1903-03-01 for the transition to CAT, +# evidently the port transitioned to CAT after 1903-03-03 but before +# the Portuguese legal transition of 1912-01-01 (see Europe/Lisbon commentary). +# For lack of better info, list 1909 as the transition date. # # Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Africa/Maputo 2:10:20 - LMT 1903 Mar + #STDOFF 2:10:18.3 +Zone Africa/Maputo 2:10:18 - LMT 1909 2:00 - CAT # Namibia @@ -1195,7 +1206,7 @@ Rule Namibia 1995 2017 - Apr Sun>=1 2:00 -1:00 WAT # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Africa/Windhoek 1:08:24 - LMT 1892 Feb 8 - 1:30 - +0130 1903 Mar + 1:30 - %z 1903 Mar 2:00 - SAST 1942 Sep 20 2:00 2:00 1:00 SAST 1943 Mar 21 2:00 2:00 - SAST 1990 Mar 21 # independence @@ -1283,7 +1294,7 @@ Zone Africa/Windhoek 1:08:24 - LMT 1892 Feb 8 Zone Africa/Lagos 0:13:35 - LMT 1905 Jul 1 0:00 - GMT 1908 Jul 1 0:13:35 - LMT 1914 Jan 1 - 0:30 - +0030 1919 Sep 1 + 0:30 - %z 1919 Sep 1 1:00 - WAT # São Tomé and Príncipe diff --git a/src/java.base/share/data/tzdata/antarctica b/src/java.base/share/data/tzdata/antarctica index fc7176cd0d5..058d8d6a7a2 100644 --- a/src/java.base/share/data/tzdata/antarctica +++ b/src/java.base/share/data/tzdata/antarctica @@ -110,34 +110,34 @@ # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Antarctica/Casey 0 - -00 1969 - 8:00 - +08 2009 Oct 18 2:00 - 11:00 - +11 2010 Mar 5 2:00 - 8:00 - +08 2011 Oct 28 2:00 - 11:00 - +11 2012 Feb 21 17:00u - 8:00 - +08 2016 Oct 22 - 11:00 - +11 2018 Mar 11 4:00 - 8:00 - +08 2018 Oct 7 4:00 - 11:00 - +11 2019 Mar 17 3:00 - 8:00 - +08 2019 Oct 4 3:00 - 11:00 - +11 2020 Mar 8 3:00 - 8:00 - +08 2020 Oct 4 0:01 - 11:00 - +11 2021 Mar 14 0:00 - 8:00 - +08 2021 Oct 3 0:01 - 11:00 - +11 2022 Mar 13 0:00 - 8:00 - +08 2022 Oct 2 0:01 - 11:00 - +11 2023 Mar 9 3:00 - 8:00 - +08 + 8:00 - %z 2009 Oct 18 2:00 + 11:00 - %z 2010 Mar 5 2:00 + 8:00 - %z 2011 Oct 28 2:00 + 11:00 - %z 2012 Feb 21 17:00u + 8:00 - %z 2016 Oct 22 + 11:00 - %z 2018 Mar 11 4:00 + 8:00 - %z 2018 Oct 7 4:00 + 11:00 - %z 2019 Mar 17 3:00 + 8:00 - %z 2019 Oct 4 3:00 + 11:00 - %z 2020 Mar 8 3:00 + 8:00 - %z 2020 Oct 4 0:01 + 11:00 - %z 2021 Mar 14 0:00 + 8:00 - %z 2021 Oct 3 0:01 + 11:00 - %z 2022 Mar 13 0:00 + 8:00 - %z 2022 Oct 2 0:01 + 11:00 - %z 2023 Mar 9 3:00 + 8:00 - %z Zone Antarctica/Davis 0 - -00 1957 Jan 13 - 7:00 - +07 1964 Nov + 7:00 - %z 1964 Nov 0 - -00 1969 Feb - 7:00 - +07 2009 Oct 18 2:00 - 5:00 - +05 2010 Mar 10 20:00u - 7:00 - +07 2011 Oct 28 2:00 - 5:00 - +05 2012 Feb 21 20:00u - 7:00 - +07 + 7:00 - %z 2009 Oct 18 2:00 + 5:00 - %z 2010 Mar 10 20:00u + 7:00 - %z 2011 Oct 28 2:00 + 5:00 - %z 2012 Feb 21 20:00u + 7:00 - %z Zone Antarctica/Mawson 0 - -00 1954 Feb 13 - 6:00 - +06 2009 Oct 18 2:00 - 5:00 - +05 + 6:00 - %z 2009 Oct 18 2:00 + 5:00 - %z # References: # Casey Weather (1998-02-26) # http://www.antdiv.gov.au/aad/exop/sfo/casey/casey_aws.html @@ -313,10 +313,10 @@ Zone Antarctica/Troll 0 - -00 2005 Feb 12 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Antarctica/Vostok 0 - -00 1957 Dec 16 - 7:00 - +07 1994 Feb + 7:00 - %z 1994 Feb 0 - -00 1994 Nov - 7:00 - +07 2023 Dec 18 2:00 - 5:00 - +05 + 7:00 - %z 2023 Dec 18 2:00 + 5:00 - %z # S Africa - year-round bases # Marion Island, -4653+03752 @@ -349,7 +349,7 @@ Zone Antarctica/Vostok 0 - -00 1957 Dec 16 # # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Antarctica/Rothera 0 - -00 1976 Dec 1 - -3:00 - -03 + -3:00 - %z # Uruguay - year round base # Artigas, King George Island, -621104-0585107 diff --git a/src/java.base/share/data/tzdata/asia b/src/java.base/share/data/tzdata/asia index 3a54291919d..5c8568f334a 100644 --- a/src/java.base/share/data/tzdata/asia +++ b/src/java.base/share/data/tzdata/asia @@ -106,8 +106,8 @@ Rule RussiaAsia 1996 2010 - Oct lastSun 2:00s 0 - # Afghanistan # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Kabul 4:36:48 - LMT 1890 - 4:00 - +04 1945 - 4:30 - +0430 + 4:00 - %z 1945 + 4:30 - %z # Armenia # From Paul Eggert (2006-03-22): @@ -139,12 +139,12 @@ Rule Armenia 2011 only - Mar lastSun 2:00s 1:00 - Rule Armenia 2011 only - Oct lastSun 2:00s 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Yerevan 2:58:00 - LMT 1924 May 2 - 3:00 - +03 1957 Mar - 4:00 RussiaAsia +04/+05 1991 Mar 31 2:00s - 3:00 RussiaAsia +03/+04 1995 Sep 24 2:00s - 4:00 - +04 1997 - 4:00 RussiaAsia +04/+05 2011 - 4:00 Armenia +04/+05 + 3:00 - %z 1957 Mar + 4:00 RussiaAsia %z 1991 Mar 31 2:00s + 3:00 RussiaAsia %z 1995 Sep 24 2:00s + 4:00 - %z 1997 + 4:00 RussiaAsia %z 2011 + 4:00 Armenia %z # Azerbaijan @@ -165,12 +165,12 @@ Rule Azer 1997 2015 - Mar lastSun 4:00 1:00 - Rule Azer 1997 2015 - Oct lastSun 5:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Baku 3:19:24 - LMT 1924 May 2 - 3:00 - +03 1957 Mar - 4:00 RussiaAsia +04/+05 1991 Mar 31 2:00s - 3:00 RussiaAsia +03/+04 1992 Sep lastSun 2:00s - 4:00 - +04 1996 - 4:00 EUAsia +04/+05 1997 - 4:00 Azer +04/+05 + 3:00 - %z 1957 Mar + 4:00 RussiaAsia %z 1991 Mar 31 2:00s + 3:00 RussiaAsia %z 1992 Sep lastSun 2:00s + 4:00 - %z 1996 + 4:00 EUAsia %z 1997 + 4:00 Azer %z # Bangladesh # From Alexander Krivenyshev (2009-05-13): @@ -251,17 +251,17 @@ Rule Dhaka 2009 only - Dec 31 24:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Dhaka 6:01:40 - LMT 1890 5:53:20 - HMT 1941 Oct # Howrah Mean Time? - 6:30 - +0630 1942 May 15 - 5:30 - +0530 1942 Sep - 6:30 - +0630 1951 Sep 30 - 6:00 - +06 2009 - 6:00 Dhaka +06/+07 + 6:30 - %z 1942 May 15 + 5:30 - %z 1942 Sep + 6:30 - %z 1951 Sep 30 + 6:00 - %z 2009 + 6:00 Dhaka %z # Bhutan # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Thimphu 5:58:36 - LMT 1947 Aug 15 # or Thimbu - 5:30 - +0530 1987 Oct - 6:00 - +06 + 5:30 - %z 1987 Oct + 6:00 - %z # British Indian Ocean Territory # Whitman and the 1995 CIA time zone map say 5:00, but the @@ -271,8 +271,8 @@ Zone Asia/Thimphu 5:58:36 - LMT 1947 Aug 15 # or Thimbu # then contained the Chagos Archipelago). # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Indian/Chagos 4:49:40 - LMT 1907 - 5:00 - +05 1996 - 6:00 - +06 + 5:00 - %z 1996 + 6:00 - %z # Cocos (Keeling) Islands # Myanmar (Burma) @@ -288,9 +288,9 @@ Zone Indian/Chagos 4:49:40 - LMT 1907 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Yangon 6:24:47 - LMT 1880 # or Rangoon 6:24:47 - RMT 1920 # Rangoon local time - 6:30 - +0630 1942 May - 9:00 - +09 1945 May 3 - 6:30 - +0630 + 6:30 - %z 1942 May + 9:00 - %z 1945 May 3 + 6:30 - %z # China @@ -679,7 +679,7 @@ Zone Asia/Shanghai 8:05:43 - LMT 1901 # Xinjiang time, used by many in western China; represented by Ürümqi / Ürümchi # / Wulumuqi. (Please use Asia/Shanghai if you prefer Beijing time.) Zone Asia/Urumqi 5:50:20 - LMT 1928 - 6:00 - +06 + 6:00 - %z # Hong Kong @@ -1137,7 +1137,7 @@ Rule Macau 1979 only - Oct Sun>=16 03:30 0 S # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Macau 7:34:10 - LMT 1904 Oct 30 8:00 - CST 1941 Dec 21 23:00 - 9:00 Macau +09/+10 1945 Sep 30 24:00 + 9:00 Macau %z 1945 Sep 30 24:00 8:00 Macau C%sT @@ -1180,7 +1180,7 @@ Zone Asia/Nicosia 2:13:28 - LMT 1921 Nov 14 Zone Asia/Famagusta 2:15:48 - LMT 1921 Nov 14 2:00 Cyprus EE%sT 1998 Sep 2:00 EUAsia EE%sT 2016 Sep 8 - 3:00 - +03 2017 Oct 29 1:00u + 3:00 - %z 2017 Oct 29 1:00u 2:00 EUAsia EE%sT # Georgia @@ -1221,18 +1221,25 @@ Zone Asia/Famagusta 2:15:48 - LMT 1921 Nov 14 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Tbilisi 2:59:11 - LMT 1880 2:59:11 - TBMT 1924 May 2 # Tbilisi Mean Time - 3:00 - +03 1957 Mar - 4:00 RussiaAsia +04/+05 1991 Mar 31 2:00s - 3:00 RussiaAsia +03/+04 1992 - 3:00 E-EurAsia +03/+04 1994 Sep lastSun - 4:00 E-EurAsia +04/+05 1996 Oct lastSun - 4:00 1:00 +05 1997 Mar lastSun - 4:00 E-EurAsia +04/+05 2004 Jun 27 - 3:00 RussiaAsia +03/+04 2005 Mar lastSun 2:00 - 4:00 - +04 + 3:00 - %z 1957 Mar + 4:00 RussiaAsia %z 1991 Mar 31 2:00s + 3:00 RussiaAsia %z 1992 + 3:00 E-EurAsia %z 1994 Sep lastSun + 4:00 E-EurAsia %z 1996 Oct lastSun + 4:00 1:00 %z 1997 Mar lastSun + 4:00 E-EurAsia %z 2004 Jun 27 + 3:00 RussiaAsia %z 2005 Mar lastSun 2:00 + 4:00 - %z # East Timor +# From Tim Parenti (2024-07-01): +# The 1912-01-01 transition occurred at 00:00 new time, per the 1911-05-24 +# Portuguese decree (see Europe/Lisbon). A provision in article 5(c) of the +# decree prescribed that Timor "will keep counting time in harmony with +# neighboring foreign colonies, [for] as long as they do not adopt the time +# that belongs to them in [the Washington Convention] system." + # See Indonesia for the 1945 transition. # From João Carrascalão, brother of the former governor of East Timor, in @@ -1256,11 +1263,11 @@ Zone Asia/Tbilisi 2:59:11 - LMT 1880 # midnight on Saturday, September 16. # Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Asia/Dili 8:22:20 - LMT 1912 Jan 1 - 8:00 - +08 1942 Feb 21 23:00 - 9:00 - +09 1976 May 3 - 8:00 - +08 2000 Sep 17 0:00 - 9:00 - +09 +Zone Asia/Dili 8:22:20 - LMT 1911 Dec 31 16:00u + 8:00 - %z 1942 Feb 21 23:00 + 9:00 - %z 1976 May 3 + 8:00 - %z 2000 Sep 17 0:00 + 9:00 - %z # India @@ -1326,9 +1333,9 @@ Zone Asia/Kolkata 5:53:28 - LMT 1854 Jun 28 # Kolkata 5:53:20 - HMT 1870 # Howrah Mean Time? 5:21:10 - MMT 1906 Jan 1 # Madras local time 5:30 - IST 1941 Oct - 5:30 1:00 +0630 1942 May 15 + 5:30 1:00 %z 1942 May 15 5:30 - IST 1942 Sep - 5:30 1:00 +0630 1945 Oct 15 + 5:30 1:00 %z 1945 Oct 15 5:30 - IST # Since 1970 the following are like Asia/Kolkata: # Andaman Is @@ -1380,33 +1387,33 @@ Zone Asia/Jakarta 7:07:12 - LMT 1867 Aug 10 # Shanks & Pottenger say the next transition was at 1924 Jan 1 0:13, # but this must be a typo. 7:07:12 - BMT 1923 Dec 31 16:40u # Batavia - 7:20 - +0720 1932 Nov - 7:30 - +0730 1942 Mar 23 - 9:00 - +09 1945 Sep 23 - 7:30 - +0730 1948 May - 8:00 - +08 1950 May - 7:30 - +0730 1964 + 7:20 - %z 1932 Nov + 7:30 - %z 1942 Mar 23 + 9:00 - %z 1945 Sep 23 + 7:30 - %z 1948 May + 8:00 - %z 1950 May + 7:30 - %z 1964 7:00 - WIB # west and central Borneo Zone Asia/Pontianak 7:17:20 - LMT 1908 May 7:17:20 - PMT 1932 Nov # Pontianak MT - 7:30 - +0730 1942 Jan 29 - 9:00 - +09 1945 Sep 23 - 7:30 - +0730 1948 May - 8:00 - +08 1950 May - 7:30 - +0730 1964 + 7:30 - %z 1942 Jan 29 + 9:00 - %z 1945 Sep 23 + 7:30 - %z 1948 May + 8:00 - %z 1950 May + 7:30 - %z 1964 8:00 - WITA 1988 Jan 1 7:00 - WIB # Sulawesi, Lesser Sundas, east and south Borneo Zone Asia/Makassar 7:57:36 - LMT 1920 7:57:36 - MMT 1932 Nov # Macassar MT - 8:00 - +08 1942 Feb 9 - 9:00 - +09 1945 Sep 23 + 8:00 - %z 1942 Feb 9 + 9:00 - %z 1945 Sep 23 8:00 - WITA # Maluku Islands, West Papua, Papua Zone Asia/Jayapura 9:22:48 - LMT 1932 Nov - 9:00 - +09 1944 Sep 1 - 9:30 - +0930 1964 + 9:00 - %z 1944 Sep 1 + 9:30 - %z 1964 9:00 - WIT # Iran @@ -1642,9 +1649,9 @@ Rule Iran 2021 2022 - Sep 21 24:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Tehran 3:25:44 - LMT 1916 3:25:44 - TMT 1935 Jun 13 # Tehran Mean Time - 3:30 Iran +0330/+0430 1977 Oct 20 24:00 - 4:00 Iran +04/+05 1979 - 3:30 Iran +0330/+0430 + 3:30 Iran %z 1977 Oct 20 24:00 + 4:00 Iran %z 1979 + 3:30 Iran %z # Iraq @@ -1687,8 +1694,8 @@ Rule Iraq 1991 2007 - Oct 1 3:00s 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Baghdad 2:57:40 - LMT 1890 2:57:36 - BMT 1918 # Baghdad Mean Time? - 3:00 - +03 1982 May - 3:00 Iraq +03/+04 + 3:00 - %z 1982 May + 3:00 Iraq %z ############################################################################### @@ -2285,7 +2292,7 @@ Rule Jordan 2022 only - Feb lastThu 24:00 1:00 S # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Amman 2:23:44 - LMT 1931 2:00 Jordan EE%sT 2022 Oct 28 0:00s - 3:00 - +03 + 3:00 - %z # Kazakhstan @@ -2496,88 +2503,88 @@ Zone Asia/Amman 2:23:44 - LMT 1931 # Almaty (formerly Alma-Ata), representing most locations in Kazakhstan # This includes Abai/Abay (ISO 3166-2 code KZ-10), Aqmola/Akmola (KZ-11), # Almaty (KZ-19), Almaty city (KZ-75), Astana city (KZ-71), -# East Kazkhstan (KZ-63), Jambyl/Zhambyl (KZ-31), Jetisu/Zhetysu (KZ-33), +# East Kazakhstan (KZ-63), Jambyl/Zhambyl (KZ-31), Jetisu/Zhetysu (KZ-33), # Karaganda (KZ-35), North Kazakhstan (KZ-59), Pavlodar (KZ-55), -# Shyumkent city (KZ-79), Turkistan (KZ-61), and Ulytau (KZ-62). +# Shymkent city (KZ-79), Turkistan (KZ-61), and Ulytau (KZ-62). Zone Asia/Almaty 5:07:48 - LMT 1924 May 2 # or Alma-Ata - 5:00 - +05 1930 Jun 21 - 6:00 RussiaAsia +06/+07 1991 Mar 31 2:00s - 5:00 RussiaAsia +05/+06 1992 Jan 19 2:00s - 6:00 RussiaAsia +06/+07 2004 Oct 31 2:00s - 6:00 - +06 2024 Mar 1 0:00 - 5:00 - +05 + 5:00 - %z 1930 Jun 21 + 6:00 RussiaAsia %z 1991 Mar 31 2:00s + 5:00 RussiaAsia %z 1992 Jan 19 2:00s + 6:00 RussiaAsia %z 2004 Oct 31 2:00s + 6:00 - %z 2024 Mar 1 0:00 + 5:00 - %z # Qyzylorda (aka Kyzylorda, Kizilorda, Kzyl-Orda, etc.) (KZ-43) Zone Asia/Qyzylorda 4:21:52 - LMT 1924 May 2 - 4:00 - +04 1930 Jun 21 - 5:00 - +05 1981 Apr 1 - 5:00 1:00 +06 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00s - 4:00 RussiaAsia +04/+05 1991 Sep 29 2:00s - 5:00 RussiaAsia +05/+06 1992 Jan 19 2:00s - 6:00 RussiaAsia +06/+07 1992 Mar 29 2:00s - 5:00 RussiaAsia +05/+06 2004 Oct 31 2:00s - 6:00 - +06 2018 Dec 21 0:00 - 5:00 - +05 + 4:00 - %z 1930 Jun 21 + 5:00 - %z 1981 Apr 1 + 5:00 1:00 %z 1981 Oct 1 + 6:00 - %z 1982 Apr 1 + 5:00 RussiaAsia %z 1991 Mar 31 2:00s + 4:00 RussiaAsia %z 1991 Sep 29 2:00s + 5:00 RussiaAsia %z 1992 Jan 19 2:00s + 6:00 RussiaAsia %z 1992 Mar 29 2:00s + 5:00 RussiaAsia %z 2004 Oct 31 2:00s + 6:00 - %z 2018 Dec 21 0:00 + 5:00 - %z # Qostanay (aka Kostanay, Kustanay) (KZ-39) # The 1991/2 rules are unclear partly because of the 1997 Turgai # reorganization. Zone Asia/Qostanay 4:14:28 - LMT 1924 May 2 - 4:00 - +04 1930 Jun 21 - 5:00 - +05 1981 Apr 1 - 5:00 1:00 +06 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00s - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00s - 5:00 RussiaAsia +05/+06 2004 Oct 31 2:00s - 6:00 - +06 2024 Mar 1 0:00 - 5:00 - +05 + 4:00 - %z 1930 Jun 21 + 5:00 - %z 1981 Apr 1 + 5:00 1:00 %z 1981 Oct 1 + 6:00 - %z 1982 Apr 1 + 5:00 RussiaAsia %z 1991 Mar 31 2:00s + 4:00 RussiaAsia %z 1992 Jan 19 2:00s + 5:00 RussiaAsia %z 2004 Oct 31 2:00s + 6:00 - %z 2024 Mar 1 0:00 + 5:00 - %z # Aqtöbe (aka Aktobe, formerly Aktyubinsk) (KZ-15) Zone Asia/Aqtobe 3:48:40 - LMT 1924 May 2 - 4:00 - +04 1930 Jun 21 - 5:00 - +05 1981 Apr 1 - 5:00 1:00 +06 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00s - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00s - 5:00 RussiaAsia +05/+06 2004 Oct 31 2:00s - 5:00 - +05 + 4:00 - %z 1930 Jun 21 + 5:00 - %z 1981 Apr 1 + 5:00 1:00 %z 1981 Oct 1 + 6:00 - %z 1982 Apr 1 + 5:00 RussiaAsia %z 1991 Mar 31 2:00s + 4:00 RussiaAsia %z 1992 Jan 19 2:00s + 5:00 RussiaAsia %z 2004 Oct 31 2:00s + 5:00 - %z # Mangghystaū (KZ-47) # Aqtau was not founded until 1963, but it represents an inhabited region, # so include timestamps before 1963. Zone Asia/Aqtau 3:21:04 - LMT 1924 May 2 - 4:00 - +04 1930 Jun 21 - 5:00 - +05 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00s - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00s - 5:00 RussiaAsia +05/+06 1994 Sep 25 2:00s - 4:00 RussiaAsia +04/+05 2004 Oct 31 2:00s - 5:00 - +05 + 4:00 - %z 1930 Jun 21 + 5:00 - %z 1981 Oct 1 + 6:00 - %z 1982 Apr 1 + 5:00 RussiaAsia %z 1991 Mar 31 2:00s + 4:00 RussiaAsia %z 1992 Jan 19 2:00s + 5:00 RussiaAsia %z 1994 Sep 25 2:00s + 4:00 RussiaAsia %z 2004 Oct 31 2:00s + 5:00 - %z # Atyraū (KZ-23) is like Mangghystaū except it switched from # +04/+05 to +05/+06 in spring 1999, not fall 1994. Zone Asia/Atyrau 3:27:44 - LMT 1924 May 2 - 3:00 - +03 1930 Jun 21 - 5:00 - +05 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00s - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00s - 5:00 RussiaAsia +05/+06 1999 Mar 28 2:00s - 4:00 RussiaAsia +04/+05 2004 Oct 31 2:00s - 5:00 - +05 + 3:00 - %z 1930 Jun 21 + 5:00 - %z 1981 Oct 1 + 6:00 - %z 1982 Apr 1 + 5:00 RussiaAsia %z 1991 Mar 31 2:00s + 4:00 RussiaAsia %z 1992 Jan 19 2:00s + 5:00 RussiaAsia %z 1999 Mar 28 2:00s + 4:00 RussiaAsia %z 2004 Oct 31 2:00s + 5:00 - %z # West Kazakhstan (KZ-27) # From Paul Eggert (2016-03-18): # The 1989 transition is from USSR act No. 227 (1989-03-14). Zone Asia/Oral 3:25:24 - LMT 1924 May 2 # or Ural'sk - 3:00 - +03 1930 Jun 21 - 5:00 - +05 1981 Apr 1 - 5:00 1:00 +06 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1989 Mar 26 2:00s - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00s - 5:00 RussiaAsia +05/+06 1992 Mar 29 2:00s - 4:00 RussiaAsia +04/+05 2004 Oct 31 2:00s - 5:00 - +05 + 3:00 - %z 1930 Jun 21 + 5:00 - %z 1981 Apr 1 + 5:00 1:00 %z 1981 Oct 1 + 6:00 - %z 1982 Apr 1 + 5:00 RussiaAsia %z 1989 Mar 26 2:00s + 4:00 RussiaAsia %z 1992 Jan 19 2:00s + 5:00 RussiaAsia %z 1992 Mar 29 2:00s + 4:00 RussiaAsia %z 2004 Oct 31 2:00s + 5:00 - %z # Kyrgyzstan (Kirgizstan) # Transitions through 1991 are from Shanks & Pottenger. @@ -2598,11 +2605,11 @@ Rule Kyrgyz 1997 2005 - Mar lastSun 2:30 1:00 - Rule Kyrgyz 1997 2004 - Oct lastSun 2:30 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Bishkek 4:58:24 - LMT 1924 May 2 - 5:00 - +05 1930 Jun 21 - 6:00 RussiaAsia +06/+07 1991 Mar 31 2:00s - 5:00 RussiaAsia +05/+06 1991 Aug 31 2:00 - 5:00 Kyrgyz +05/+06 2005 Aug 12 - 6:00 - +06 + 5:00 - %z 1930 Jun 21 + 6:00 RussiaAsia %z 1991 Mar 31 2:00s + 5:00 RussiaAsia %z 1991 Aug 31 2:00 + 5:00 Kyrgyz %z 2005 Aug 12 + 6:00 - %z ############################################################################### @@ -2809,16 +2816,16 @@ Rule NBorneo 1935 1941 - Dec 14 0:00 0 - # and 1982 transition dates are from Mok Ly Yng. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Kuching 7:21:20 - LMT 1926 Mar - 7:30 - +0730 1933 - 8:00 NBorneo +08/+0820 1942 Feb 16 - 9:00 - +09 1945 Sep 12 - 8:00 - +08 + 7:30 - %z 1933 + 8:00 NBorneo %z 1942 Feb 16 + 9:00 - %z 1945 Sep 12 + 8:00 - %z # Maldives # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Indian/Maldives 4:54:00 - LMT 1880 # Malé 4:54:00 - MMT 1960 # Malé Mean Time - 5:00 - +05 + 5:00 - %z # Mongolia @@ -2920,9 +2927,37 @@ Zone Indian/Maldives 4:54:00 - LMT 1880 # Malé # From Arthur David Olson (2008-05-19): # Assume that Choibalsan is indeed offset by 8:00. -# XXX--in the absence of better information, assume that transition -# was at the start of 2008-03-31 (the day of Steffen Thorsen's report); -# this is almost surely wrong. + +# From Heitor David Pinto (2024-06-23): +# Sources about time zones in Mongolia seem to list one of two conflicting +# configurations. The first configuration, mentioned in a comment to the TZ +# database in 1999, citing a Mongolian government website, lists the provinces +# of Bayan-Ölgii, Khovd and Uvs in UTC+7, and the rest of the country in +# UTC+8. The second configuration, mentioned in a comment to the database in +# 2001, lists Bayan-Ölgii, Khovd, Uvs, Govi-Altai and Zavkhan in UTC+7, Dornod +# and Sükhbaatar in UTC+9, and the rest of the country in UTC+8. +# +# The first configuration is still mentioned by several Mongolian travel +# agencies: +# https://www.adventurerider.mn/en/page/about_mongolia +# http://www.naturetours.mn/nt/mongolia.php +# https://www.newjuulchin.mn/web/content/7506?unique=fa24a0f6e96e022a3578ee5195ac879638c734ce +# +# It also matches these flight schedules in 2013: +# http://web.archive.org/web/20130722023600/https://www.hunnuair.com/en/timetabled +# The flight times imply that the airports of Uliastai (Zavkhan), Choibalsan +# (Dornod) and Altai (Govi-Altai) are in the same time zone as Ulaanbaatar, +# and Khovd is one hour behind.... +# +# The second configuration was mentioned by an official of the Mongolian +# standards agency in an interview in 2014: https://ikon.mn/n/9v6 +# And it's still listed by the Mongolian aviation agency: +# https://ais.mn/files/aip/eAIP/2023-12-25/html/eSUP/ZM-eSUP-23-04-en-MN.html +# +# ... I believe that the first configuration is what is actually observed in +# Mongolia and has been so all along, at least since 1999. The second +# configuration closely matches the ideal time zone boundaries at 97.5° E and +# 112.5° E but it doesn't seem to be used in practice. # From Ganbold Tsagaankhuu (2015-03-10): # It seems like yesterday Mongolian Government meeting has concluded to use @@ -2961,25 +2996,18 @@ Rule Mongol 2015 2016 - Sep lastSat 0:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] # Hovd, a.k.a. Chovd, Dund-Us, Dzhargalant, Khovd, Jirgalanta Zone Asia/Hovd 6:06:36 - LMT 1905 Aug - 6:00 - +06 1978 - 7:00 Mongol +07/+08 + 6:00 - %z 1978 + 7:00 Mongol %z # Ulaanbaatar, a.k.a. Ulan Bataar, Ulan Bator, Urga Zone Asia/Ulaanbaatar 7:07:32 - LMT 1905 Aug - 7:00 - +07 1978 - 8:00 Mongol +08/+09 -# Choibalsan, a.k.a. Bajan Tümen, Bajan Tumen, Chojbalsan, -# Choybalsan, Sanbejse, Tchoibalsan -Zone Asia/Choibalsan 7:38:00 - LMT 1905 Aug - 7:00 - +07 1978 - 8:00 - +08 1983 Apr - 9:00 Mongol +09/+10 2008 Mar 31 - 8:00 Mongol +08/+09 + 7:00 - %z 1978 + 8:00 Mongol %z # Nepal # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Kathmandu 5:41:16 - LMT 1920 - 5:30 - +0530 1986 - 5:45 - +0545 + 5:30 - %z 1986 + 5:45 - %z # Pakistan @@ -3125,10 +3153,10 @@ Rule Pakistan 2009 only - Apr 15 0:00 1:00 S # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Karachi 4:28:12 - LMT 1907 - 5:30 - +0530 1942 Sep - 5:30 1:00 +0630 1945 Oct 15 - 5:30 - +0530 1951 Sep 30 - 5:00 - +05 1971 Mar 26 + 5:30 - %z 1942 Sep + 5:30 1:00 %z 1945 Oct 15 + 5:30 - %z 1951 Sep 30 + 5:00 - %z 1971 Mar 26 5:00 Pakistan PK%sT # Pakistan Time # Palestine @@ -3676,14 +3704,14 @@ Zone Asia/Hebron 2:20:23 - LMT 1900 Oct # Philippine Star 2014-08-05 # http://www.philstar.com/headlines/2014/08/05/1354152/pnoy-urged-declare-use-daylight-saving-time -# From Paul Goyette (2018-06-15): +# From Paul Goyette (2018-06-15) with URLs updated by Guy Harris (2024-02-15): # In the Philippines, there is a national law, Republic Act No. 10535 # which declares the official time here as "Philippine Standard Time". # The act [1] even specifies use of PST as the abbreviation, although # the FAQ provided by PAGASA [2] uses the "acronym PhST to distinguish # it from the Pacific Standard Time (PST)." -# [1] http://www.officialgazette.gov.ph/2013/05/15/republic-act-no-10535/ -# [2] https://www1.pagasa.dost.gov.ph/index.php/astronomy/philippine-standard-time#republic-act-10535 +# [1] https://www.officialgazette.gov.ph/2013/05/15/republic-act-no-10535/ +# [2] https://prsd.pagasa.dost.gov.ph/index.php/28-astronomy/302-philippine-standard-time # # From Paul Eggert (2018-06-19): # I surveyed recent news reports, and my impression is that "PST" is @@ -3716,8 +3744,8 @@ Zone Asia/Manila -15:56:00 - LMT 1844 Dec 31 # Qatar # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Qatar 3:26:08 - LMT 1920 # Al Dawhah / Doha - 4:00 - +04 1972 Jun - 3:00 - +03 + 4:00 - %z 1972 Jun + 3:00 - %z # Kuwait # Saudi Arabia @@ -3767,7 +3795,7 @@ Zone Asia/Qatar 3:26:08 - LMT 1920 # Al Dawhah / Doha # # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Riyadh 3:06:52 - LMT 1947 Mar 14 - 3:00 - +03 + 3:00 - %z # Singapore # taken from Mok Ly Yng (2003-10-30) @@ -3775,13 +3803,13 @@ Zone Asia/Riyadh 3:06:52 - LMT 1947 Mar 14 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Singapore 6:55:25 - LMT 1901 Jan 1 6:55:25 - SMT 1905 Jun 1 # Singapore M.T. - 7:00 - +07 1933 Jan 1 - 7:00 0:20 +0720 1936 Jan 1 - 7:20 - +0720 1941 Sep 1 - 7:30 - +0730 1942 Feb 16 - 9:00 - +09 1945 Sep 12 - 7:30 - +0730 1981 Dec 31 16:00u - 8:00 - +08 + 7:00 - %z 1933 Jan 1 + 7:00 0:20 %z 1936 Jan 1 + 7:20 - %z 1941 Sep 1 + 7:30 - %z 1942 Feb 16 + 9:00 - %z 1945 Sep 12 + 7:30 - %z 1981 Dec 31 16:00u + 8:00 - %z # Spratly Is # no information @@ -3839,13 +3867,13 @@ Zone Asia/Singapore 6:55:25 - LMT 1901 Jan 1 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Colombo 5:19:24 - LMT 1880 5:19:32 - MMT 1906 # Moratuwa Mean Time - 5:30 - +0530 1942 Jan 5 - 5:30 0:30 +06 1942 Sep - 5:30 1:00 +0630 1945 Oct 16 2:00 - 5:30 - +0530 1996 May 25 0:00 - 6:30 - +0630 1996 Oct 26 0:30 - 6:00 - +06 2006 Apr 15 0:30 - 5:30 - +0530 + 5:30 - %z 1942 Jan 5 + 5:30 0:30 %z 1942 Sep + 5:30 1:00 %z 1945 Oct 16 2:00 + 5:30 - %z 1996 May 25 0:00 + 6:30 - %z 1996 Oct 26 0:30 + 6:00 - %z 2006 Apr 15 0:30 + 5:30 - %z # Syria # Rule NAME FROM TO - IN ON AT SAVE LETTER/S @@ -4016,16 +4044,16 @@ Rule Syria 2009 2022 - Oct lastFri 0:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Damascus 2:25:12 - LMT 1920 # Dimashq 2:00 Syria EE%sT 2022 Oct 28 0:00 - 3:00 - +03 + 3:00 - %z # Tajikistan # From Shanks & Pottenger. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Dushanbe 4:35:12 - LMT 1924 May 2 - 5:00 - +05 1930 Jun 21 - 6:00 RussiaAsia +06/+07 1991 Mar 31 2:00s - 5:00 1:00 +06 1991 Sep 9 2:00s - 5:00 - +05 + 5:00 - %z 1930 Jun 21 + 6:00 RussiaAsia %z 1991 Mar 31 2:00s + 5:00 1:00 %z 1991 Sep 9 2:00s + 5:00 - %z # Cambodia # Christmas I @@ -4035,16 +4063,16 @@ Zone Asia/Dushanbe 4:35:12 - LMT 1924 May 2 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Bangkok 6:42:04 - LMT 1880 6:42:04 - BMT 1920 Apr # Bangkok Mean Time - 7:00 - +07 + 7:00 - %z # Turkmenistan # From Shanks & Pottenger. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Ashgabat 3:53:32 - LMT 1924 May 2 # or Ashkhabad - 4:00 - +04 1930 Jun 21 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00 - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00 - 5:00 - +05 + 4:00 - %z 1930 Jun 21 + 5:00 RussiaAsia %z 1991 Mar 31 2:00 + 4:00 RussiaAsia %z 1992 Jan 19 2:00 + 5:00 - %z # Oman # Réunion @@ -4054,25 +4082,25 @@ Zone Asia/Ashgabat 3:53:32 - LMT 1924 May 2 # or Ashkhabad # The Crozet Is also observe Réunion time; see the 'antarctica' file. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Dubai 3:41:12 - LMT 1920 - 4:00 - +04 + 4:00 - %z # Uzbekistan # Byalokoz 1919 says Uzbekistan was 4:27:53. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Samarkand 4:27:53 - LMT 1924 May 2 - 4:00 - +04 1930 Jun 21 - 5:00 - +05 1981 Apr 1 - 5:00 1:00 +06 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1992 - 5:00 - +05 + 4:00 - %z 1930 Jun 21 + 5:00 - %z 1981 Apr 1 + 5:00 1:00 %z 1981 Oct 1 + 6:00 - %z 1982 Apr 1 + 5:00 RussiaAsia %z 1992 + 5:00 - %z # Milne says Tashkent was 4:37:10.8. #STDOFF 4:37:10.8 Zone Asia/Tashkent 4:37:11 - LMT 1924 May 2 - 5:00 - +05 1930 Jun 21 - 6:00 RussiaAsia +06/+07 1991 Mar 31 2:00 - 5:00 RussiaAsia +05/+06 1992 - 5:00 - +05 + 5:00 - %z 1930 Jun 21 + 6:00 RussiaAsia %z 1991 Mar 31 2:00 + 5:00 RussiaAsia %z 1992 + 5:00 - %z # Vietnam (southern) @@ -4130,7 +4158,7 @@ Zone Asia/Tashkent 4:37:11 - LMT 1924 May 2 # Võ Nguyên Giáp, Việt Nam Dân Quốc Công Báo, No. 1 (1945-09-29), page 13 # http://baochi.nlv.gov.vn/baochi/cgi-bin/baochi?a=d&d=JwvzO19450929.2.5&dliv=none # It says that on 1945-09-01 at 24:00, Vietnam moved back two hours, to +07. -# It also mentions a 1945-03-29 decree (by a Japanese Goveror-General) +# It also mentions a 1945-03-29 decree (by a Japanese Governor-General) # to set the time zone to +09, but does not say whether that decree # merely legalized an earlier change to +09. # @@ -4151,14 +4179,14 @@ Zone Asia/Tashkent 4:37:11 - LMT 1924 May 2 #STDOFF 7:06:30.13 Zone Asia/Ho_Chi_Minh 7:06:30 - LMT 1906 Jul 1 7:06:30 - PLMT 1911 May 1 # Phù Liễn MT - 7:00 - +07 1942 Dec 31 23:00 - 8:00 - +08 1945 Mar 14 23:00 - 9:00 - +09 1945 Sep 1 24:00 - 7:00 - +07 1947 Apr 1 - 8:00 - +08 1955 Jul 1 01:00 - 7:00 - +07 1959 Dec 31 23:00 - 8:00 - +08 1975 Jun 13 - 7:00 - +07 + 7:00 - %z 1942 Dec 31 23:00 + 8:00 - %z 1945 Mar 14 23:00 + 9:00 - %z 1945 Sep 1 24:00 + 7:00 - %z 1947 Apr 1 + 8:00 - %z 1955 Jul 1 01:00 + 7:00 - %z 1959 Dec 31 23:00 + 8:00 - %z 1975 Jun 13 + 7:00 - %z # From Paul Eggert (2019-02-19): # diff --git a/src/java.base/share/data/tzdata/australasia b/src/java.base/share/data/tzdata/australasia index 624735be652..09698826a49 100644 --- a/src/java.base/share/data/tzdata/australasia +++ b/src/java.base/share/data/tzdata/australasia @@ -66,8 +66,8 @@ Zone Australia/Perth 7:43:24 - LMT 1895 Dec 8:00 Aus AW%sT 1943 Jul 8:00 AW AW%sT Zone Australia/Eucla 8:35:28 - LMT 1895 Dec - 8:45 Aus +0845/+0945 1943 Jul - 8:45 AW +0845/+0945 + 8:45 Aus %z 1943 Jul + 8:45 AW %z # Queensland # @@ -232,8 +232,8 @@ Rule LH 2008 max - Apr Sun>=1 2:00 0 - Rule LH 2008 max - Oct Sun>=1 2:00 0:30 - Zone Australia/Lord_Howe 10:36:20 - LMT 1895 Feb 10:00 - AEST 1981 Mar - 10:30 LH +1030/+1130 1985 Jul - 10:30 LH +1030/+11 + 10:30 LH %z 1985 Jul + 10:30 LH %z # Australian miscellany # @@ -439,16 +439,16 @@ Rule Fiji 2019 only - Nov Sun>=8 2:00 1:00 - Rule Fiji 2020 only - Dec 20 2:00 1:00 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Fiji 11:55:44 - LMT 1915 Oct 26 # Suva - 12:00 Fiji +12/+13 + 12:00 Fiji %z # French Polynesia # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Gambier -8:59:48 - LMT 1912 Oct 1 # Rikitea - -9:00 - -09 + -9:00 - %z Zone Pacific/Marquesas -9:18:00 - LMT 1912 Oct 1 - -9:30 - -0930 + -9:30 - %z Zone Pacific/Tahiti -9:58:16 - LMT 1912 Oct 1 # Papeete - -10:00 - -10 + -10:00 - %z # Clipperton (near North America) is administered from French Polynesia; # it is uninhabited. @@ -491,7 +491,7 @@ Rule Guam 1977 only - Aug 28 2:00 0 S Zone Pacific/Guam -14:21:00 - LMT 1844 Dec 31 9:39:00 - LMT 1901 # Agana 10:00 - GST 1941 Dec 10 # Guam - 9:00 - +09 1944 Jul 31 + 9:00 - %z 1944 Jul 31 10:00 Guam G%sT 2000 Dec 23 10:00 - ChST # Chamorro Standard Time @@ -503,30 +503,30 @@ Zone Pacific/Guam -14:21:00 - LMT 1844 Dec 31 # Wallis & Futuna # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Tarawa 11:32:04 - LMT 1901 # Bairiki - 12:00 - +12 + 12:00 - %z # Kiribati (except Gilbert Is) # See Pacific/Tarawa for the Gilbert Is. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Kanton 0 - -00 1937 Aug 31 - -12:00 - -12 1979 Oct - -11:00 - -11 1994 Dec 31 - 13:00 - +13 + -12:00 - %z 1979 Oct + -11:00 - %z 1994 Dec 31 + 13:00 - %z Zone Pacific/Kiritimati -10:29:20 - LMT 1901 - -10:40 - -1040 1979 Oct - -10:00 - -10 1994 Dec 31 - 14:00 - +14 + -10:40 - %z 1979 Oct + -10:00 - %z 1994 Dec 31 + 14:00 - %z # Marshall Is # See Pacific/Tarawa for most locations. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Kwajalein 11:09:20 - LMT 1901 - 11:00 - +11 1937 - 10:00 - +10 1941 Apr 1 - 9:00 - +09 1944 Feb 6 - 11:00 - +11 1969 Oct - -12:00 - -12 1993 Aug 20 24:00 - 12:00 - +12 + 11:00 - %z 1937 + 10:00 - %z 1941 Apr 1 + 9:00 - %z 1944 Feb 6 + 11:00 - %z 1969 Oct + -12:00 - %z 1993 Aug 20 24:00 + 12:00 - %z # Micronesia # For Chuuk and Yap see Pacific/Port_Moresby. @@ -534,22 +534,22 @@ Zone Pacific/Kwajalein 11:09:20 - LMT 1901 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Kosrae -13:08:04 - LMT 1844 Dec 31 10:51:56 - LMT 1901 - 11:00 - +11 1914 Oct - 9:00 - +09 1919 Feb 1 - 11:00 - +11 1937 - 10:00 - +10 1941 Apr 1 - 9:00 - +09 1945 Aug - 11:00 - +11 1969 Oct - 12:00 - +12 1999 - 11:00 - +11 + 11:00 - %z 1914 Oct + 9:00 - %z 1919 Feb 1 + 11:00 - %z 1937 + 10:00 - %z 1941 Apr 1 + 9:00 - %z 1945 Aug + 11:00 - %z 1969 Oct + 12:00 - %z 1999 + 11:00 - %z # Nauru # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Nauru 11:07:40 - LMT 1921 Jan 15 # Uaobe - 11:30 - +1130 1942 Aug 29 - 9:00 - +09 1945 Sep 8 - 11:30 - +1130 1979 Feb 10 2:00 - 12:00 - +12 + 11:30 - %z 1942 Aug 29 + 9:00 - %z 1945 Sep 8 + 11:30 - %z 1979 Feb 10 2:00 + 12:00 - %z # New Caledonia # Rule NAME FROM TO - IN ON AT SAVE LETTER/S @@ -560,7 +560,7 @@ Rule NC 1996 only - Dec 1 2:00s 1:00 - Rule NC 1997 only - Mar 2 2:00s 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Noumea 11:05:48 - LMT 1912 Jan 13 # Nouméa - 11:00 NC +11/+12 + 11:00 NC %z ############################################################################### @@ -604,8 +604,8 @@ Zone Pacific/Auckland 11:39:04 - LMT 1868 Nov 2 12:00 NZ NZ%sT Zone Pacific/Chatham 12:13:48 - LMT 1868 Nov 2 - 12:15 - +1215 1946 Jan 1 - 12:45 Chatham +1245/+1345 + 12:15 - %z 1946 Jan 1 + 12:45 Chatham %z # Auckland Is # uninhabited; Māori and Moriori, colonial settlers, pastoralists, sealers, @@ -658,8 +658,8 @@ Rule Cook 1979 1990 - Oct lastSun 0:00 0:30 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Rarotonga 13:20:56 - LMT 1899 Dec 26 # Avarua -10:39:04 - LMT 1952 Oct 16 - -10:30 - -1030 1978 Nov 12 - -10:00 Cook -10/-0930 + -10:30 - %z 1978 Nov 12 + -10:00 Cook %z ############################################################################### @@ -676,30 +676,30 @@ Zone Pacific/Rarotonga 13:20:56 - LMT 1899 Dec 26 # Avarua # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Niue -11:19:40 - LMT 1952 Oct 16 # Alofi - -11:20 - -1120 1964 Jul - -11:00 - -11 + -11:20 - %z 1964 Jul + -11:00 - %z # Norfolk # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Norfolk 11:11:52 - LMT 1901 # Kingston - 11:12 - +1112 1951 - 11:30 - +1130 1974 Oct 27 02:00s - 11:30 1:00 +1230 1975 Mar 2 02:00s - 11:30 - +1130 2015 Oct 4 02:00s - 11:00 - +11 2019 Jul - 11:00 AN +11/+12 + 11:12 - %z 1951 + 11:30 - %z 1974 Oct 27 02:00s + 11:30 1:00 %z 1975 Mar 2 02:00s + 11:30 - %z 2015 Oct 4 02:00s + 11:00 - %z 2019 Jul + 11:00 AN %z # Palau (Belau) # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Palau -15:02:04 - LMT 1844 Dec 31 # Koror 8:57:56 - LMT 1901 - 9:00 - +09 + 9:00 - %z # Papua New Guinea # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Port_Moresby 9:48:40 - LMT 1880 9:48:32 - PMMT 1895 # Port Moresby Mean Time - 10:00 - +10 + 10:00 - %z # # From Paul Eggert (2014-10-13): # Base the Bougainville entry on the Arawa-Kieta region, which appears to have @@ -720,16 +720,16 @@ Zone Pacific/Port_Moresby 9:48:40 - LMT 1880 # Zone Pacific/Bougainville 10:22:16 - LMT 1880 9:48:32 - PMMT 1895 - 10:00 - +10 1942 Jul - 9:00 - +09 1945 Aug 21 - 10:00 - +10 2014 Dec 28 2:00 - 11:00 - +11 + 10:00 - %z 1942 Jul + 9:00 - %z 1945 Aug 21 + 10:00 - %z 2014 Dec 28 2:00 + 11:00 - %z # Pitcairn # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Pitcairn -8:40:20 - LMT 1901 # Adamstown - -8:30 - -0830 1998 Apr 27 0:00 - -8:00 - -08 + -8:30 - %z 1998 Apr 27 0:00 + -8:00 - %z # American Samoa # Midway @@ -818,15 +818,15 @@ Rule WS 2012 2020 - Sep lastSun 3:00 1 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Apia 12:33:04 - LMT 1892 Jul 5 -11:26:56 - LMT 1911 - -11:30 - -1130 1950 - -11:00 WS -11/-10 2011 Dec 29 24:00 - 13:00 WS +13/+14 + -11:30 - %z 1950 + -11:00 WS %z 2011 Dec 29 24:00 + 13:00 WS %z # Solomon Is # excludes Bougainville, for which see Papua New Guinea # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Guadalcanal 10:39:48 - LMT 1912 Oct 1 # Honiara - 11:00 - +11 + 11:00 - %z # Tokelau # @@ -849,8 +849,8 @@ Zone Pacific/Guadalcanal 10:39:48 - LMT 1912 Oct 1 # Honiara # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Fakaofo -11:24:56 - LMT 1901 - -11:00 - -11 2011 Dec 30 - 13:00 - +13 + -11:00 - %z 2011 Dec 30 + 13:00 - %z # Tonga # Rule NAME FROM TO - IN ON AT SAVE LETTER/S @@ -862,9 +862,9 @@ Rule Tonga 2016 only - Nov Sun>=1 2:00 1:00 - Rule Tonga 2017 only - Jan Sun>=15 3:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Tongatapu 12:19:12 - LMT 1945 Sep 10 - 12:20 - +1220 1961 - 13:00 - +13 1999 - 13:00 Tonga +13/+14 + 12:20 - %z 1961 + 13:00 - %z 1999 + 13:00 Tonga %z # US minor outlying islands @@ -953,7 +953,7 @@ Rule Vanuatu 1992 1993 - Jan Sat>=22 24:00 0 - Rule Vanuatu 1992 only - Oct Sat>=22 24:00 1:00 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Efate 11:13:16 - LMT 1912 Jan 13 # Vila - 11:00 Vanuatu +11/+12 + 11:00 Vanuatu %z ############################################################################### diff --git a/src/java.base/share/data/tzdata/backward b/src/java.base/share/data/tzdata/backward index 7ddc6cc3d93..cda2ccc0c66 100644 --- a/src/java.base/share/data/tzdata/backward +++ b/src/java.base/share/data/tzdata/backward @@ -21,12 +21,13 @@ # or visit www.oracle.com if you need additional information or have any # questions. # -# tzdb links for backward compatibility +# Links and zones for backward compatibility # This file is in the public domain, so clarified as of # 2009-05-17 by Arthur David Olson. # This file provides links from old or merged timezone names to current ones. +# It also provides a few zone entries for old naming conventions. # Many names changed in 1993 and in 1995, and many merged names moved here # in the period from 2013 through 2022. Several of these names are # also present in the file 'backzone', which has data important only @@ -67,6 +68,8 @@ Link America/Rio_Branco Brazil/Acre #= America/Porto_Acre Link America/Noronha Brazil/DeNoronha Link America/Sao_Paulo Brazil/East Link America/Manaus Brazil/West +Link Europe/Brussels CET +Link America/Chicago CST6CDT Link America/Halifax Canada/Atlantic Link America/Winnipeg Canada/Central # This line is commented out, as the name exceeded the 14-character limit @@ -81,6 +84,9 @@ Link America/Whitehorse Canada/Yukon Link America/Santiago Chile/Continental Link Pacific/Easter Chile/EasterIsland Link America/Havana Cuba +Link Europe/Athens EET +Link America/Panama EST +Link America/New_York EST5EDT Link Africa/Cairo Egypt Link Europe/Dublin Eire # Vanguard section, for most .zi parsers. @@ -119,6 +125,9 @@ Link America/Jamaica Jamaica Link Asia/Tokyo Japan Link Pacific/Kwajalein Kwajalein Link Africa/Tripoli Libya +Link Europe/Brussels MET +Link America/Phoenix MST +Link America/Denver MST7MDT Link America/Tijuana Mexico/BajaNorte Link America/Mazatlan Mexico/BajaSur Link America/Mexico_City Mexico/General @@ -298,6 +307,7 @@ Link America/Denver America/Shiprock Link America/Toronto America/Thunder_Bay Link America/Edmonton America/Yellowknife Link Pacific/Auckland Antarctica/South_Pole +Link Asia/Ulaanbaatar Asia/Choibalsan Link Asia/Shanghai Asia/Chongqing Link Asia/Shanghai Asia/Harbin Link Asia/Urumqi Asia/Kashgar @@ -312,6 +322,7 @@ Link Europe/Kyiv Europe/Zaporozhye Link Pacific/Kanton Pacific/Enderbury Link Pacific/Honolulu Pacific/Johnston Link Pacific/Port_Moresby Pacific/Yap +Link Europe/Lisbon WET # Alternate names for the same location @@ -337,5 +348,7 @@ Link Europe/Kyiv Europe/Kiev # Classically, Cyprus is in Asia; e.g. see Herodotus, Histories, I.72. # However, for various reasons many users expect to find it under Europe. Link Asia/Nicosia Europe/Nicosia +Link Pacific/Honolulu HST +Link America/Los_Angeles PST8PDT Link Pacific/Guadalcanal Pacific/Ponape #= Pacific/Pohnpei Link Pacific/Port_Moresby Pacific/Truk #= Pacific/Chuuk diff --git a/src/java.base/share/data/tzdata/etcetera b/src/java.base/share/data/tzdata/etcetera index 27147715ef6..780c835819d 100644 --- a/src/java.base/share/data/tzdata/etcetera +++ b/src/java.base/share/data/tzdata/etcetera @@ -28,7 +28,7 @@ # These entries are for uses not otherwise covered by the tz database. # Their main practical use is for platforms like Android that lack -# support for POSIX.1-2017-style TZ strings. On such platforms these entries +# support for POSIX proleptic TZ strings. On such platforms these entries # can be useful if the timezone database is wrong or if a ship or # aircraft at sea is not in a timezone. @@ -74,29 +74,29 @@ Link Etc/GMT GMT # so we moved the names into the Etc subdirectory. # Also, the time zone abbreviations are now compatible with %z. -Zone Etc/GMT-14 14 - +14 -Zone Etc/GMT-13 13 - +13 -Zone Etc/GMT-12 12 - +12 -Zone Etc/GMT-11 11 - +11 -Zone Etc/GMT-10 10 - +10 -Zone Etc/GMT-9 9 - +09 -Zone Etc/GMT-8 8 - +08 -Zone Etc/GMT-7 7 - +07 -Zone Etc/GMT-6 6 - +06 -Zone Etc/GMT-5 5 - +05 -Zone Etc/GMT-4 4 - +04 -Zone Etc/GMT-3 3 - +03 -Zone Etc/GMT-2 2 - +02 -Zone Etc/GMT-1 1 - +01 -Zone Etc/GMT+1 -1 - -01 -Zone Etc/GMT+2 -2 - -02 -Zone Etc/GMT+3 -3 - -03 -Zone Etc/GMT+4 -4 - -04 -Zone Etc/GMT+5 -5 - -05 -Zone Etc/GMT+6 -6 - -06 -Zone Etc/GMT+7 -7 - -07 -Zone Etc/GMT+8 -8 - -08 -Zone Etc/GMT+9 -9 - -09 -Zone Etc/GMT+10 -10 - -10 -Zone Etc/GMT+11 -11 - -11 -Zone Etc/GMT+12 -12 - -12 +Zone Etc/GMT-14 14 - %z +Zone Etc/GMT-13 13 - %z +Zone Etc/GMT-12 12 - %z +Zone Etc/GMT-11 11 - %z +Zone Etc/GMT-10 10 - %z +Zone Etc/GMT-9 9 - %z +Zone Etc/GMT-8 8 - %z +Zone Etc/GMT-7 7 - %z +Zone Etc/GMT-6 6 - %z +Zone Etc/GMT-5 5 - %z +Zone Etc/GMT-4 4 - %z +Zone Etc/GMT-3 3 - %z +Zone Etc/GMT-2 2 - %z +Zone Etc/GMT-1 1 - %z +Zone Etc/GMT+1 -1 - %z +Zone Etc/GMT+2 -2 - %z +Zone Etc/GMT+3 -3 - %z +Zone Etc/GMT+4 -4 - %z +Zone Etc/GMT+5 -5 - %z +Zone Etc/GMT+6 -6 - %z +Zone Etc/GMT+7 -7 - %z +Zone Etc/GMT+8 -8 - %z +Zone Etc/GMT+9 -9 - %z +Zone Etc/GMT+10 -10 - %z +Zone Etc/GMT+11 -11 - %z +Zone Etc/GMT+12 -12 - %z diff --git a/src/java.base/share/data/tzdata/europe b/src/java.base/share/data/tzdata/europe index 18865f33b6c..df203f218d1 100644 --- a/src/java.base/share/data/tzdata/europe +++ b/src/java.base/share/data/tzdata/europe @@ -753,14 +753,6 @@ Rule Russia 1996 2010 - Oct lastSun 2:00s 0 - # Take "abolishing daylight saving time" to mean that time is now considered # to be standard. -# These are for backward compatibility with older versions. - -# Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone WET 0:00 EU WE%sT -Zone CET 1:00 C-Eur CE%sT -Zone MET 1:00 C-Eur ME%sT -Zone EET 2:00 EU EE%sT - # Previous editions of this database used abbreviations like MET DST # for Central European Summer Time, but this didn't agree with common usage. @@ -894,7 +886,7 @@ Zone Europe/Minsk 1:50:16 - LMT 1880 3:00 Russia MSK/MSD 1990 3:00 - MSK 1991 Mar 31 2:00s 2:00 Russia EE%sT 2011 Mar 27 2:00s - 3:00 - +03 + 3:00 - %z # Belgium # Luxembourg @@ -1199,22 +1191,22 @@ Rule Thule 2007 max - Nov Sun>=1 2:00 0 S # # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Danmarkshavn -1:14:40 - LMT 1916 Jul 28 - -3:00 - -03 1980 Apr 6 2:00 - -3:00 EU -03/-02 1996 + -3:00 - %z 1980 Apr 6 2:00 + -3:00 EU %z 1996 0:00 - GMT # # Use the old name Scoresbysund, as the current name Ittoqqortoormiit # exceeds tzdb's 14-letter limit and has no common English abbreviation. Zone America/Scoresbysund -1:27:52 - LMT 1916 Jul 28 # Ittoqqortoormiit - -2:00 - -02 1980 Apr 6 2:00 - -2:00 C-Eur -02/-01 1981 Mar 29 - -1:00 EU -01/+00 2024 Mar 31 - -2:00 EU -02/-01 + -2:00 - %z 1980 Apr 6 2:00 + -2:00 C-Eur %z 1981 Mar 29 + -1:00 EU %z 2024 Mar 31 + -2:00 EU %z Zone America/Nuuk -3:26:56 - LMT 1916 Jul 28 # Godthåb - -3:00 - -03 1980 Apr 6 2:00 - -3:00 EU -03/-02 2023 Mar 26 1:00u - -2:00 - -02 2023 Oct 29 1:00u - -2:00 EU -02/-01 + -3:00 - %z 1980 Apr 6 2:00 + -3:00 EU %z 2023 Mar 26 1:00u + -2:00 - %z 2023 Oct 29 1:00u + -2:00 EU %z Zone America/Thule -4:35:08 - LMT 1916 Jul 28 # Pituffik -4:00 Thule A%sT @@ -2086,10 +2078,39 @@ Zone Europe/Warsaw 1:24:00 - LMT 1880 # Portugal -# From Paul Eggert (2014-08-11), after a heads-up from Stephen Colebourne: -# According to a Portuguese decree (1911-05-26) -# https://dre.pt/application/dir/pdf1sdip/1911/05/12500/23132313.pdf -# Lisbon was at -0:36:44.68, but switched to GMT on 1912-01-01 at 00:00. +# From Tim Parenti (2024-07-01), per Alois Treindl (2021-02-07) and Michael +# Deckers (2021-02-10): +# http://oal.ul.pt/documentos/2018/01/hl1911a2018.pdf/ +# The Astronomical Observatory of Lisbon has published a list detailing the +# historical transitions in legal time within continental Portugal. It +# directly references many decrees and ordinances which are, in turn, +# referenced below. They can be viewed in the public archives of the Diário da +# República (until 1976-04-09 known as the Diário do Govêrno) at +# https://dre.pt/ (in Portuguese). +# +# Most of the Rules below have been updated simply to match the Observatory's +# listing for continental (mainland) Portugal. Although there are over 50 +# referenced decrees and ordinances, only the handful with comments below have +# been verified against the text, typically to provide additional confidence +# wherever dates provided by Whitman and Shanks & Pottenger had disagreed. +# See further below for the Azores and Madeira. + +# From Tim Parenti (2024-07-01), per Paul Eggert (2014-08-11), after a +# heads-up from Stephen Colebourne: +# According to a 1911-05-24 Portuguese decree, Lisbon was at -0:36:44.68, but +# switched to GMT on 1912-01-01 at 00:00. +# https://dre.pt/dr/detalhe/decreto/593090 +# https://dre.pt/application/conteudo/593090 +# The decree made legal time throughout Portugal and her possessions +# "subordinate to the Greenwich meridian, according to the principle adopted at +# the Washington Convention in 1884" and eliminated the "difference of five +# minutes between the internal and external clocks of railway stations". +# +# The decree was gazetted in the 1911-05-30 issue of Diário do Govêrno, and is +# considered to be dated 1911-05-24 by that issue's summary; however, the text +# of the decree itself is dated 1911-05-26. The Diário da República website +# notes the discrepancy, but later laws and the Observatory all seem to refer +# to this decree by the 1911-05-24 date. # # From Michael Deckers (2018-02-15): # article 5 [of the 1911 decree; Deckers's translation] ...: @@ -2097,37 +2118,62 @@ Zone Europe/Warsaw 1:24:00 - LMT 1880 # according to the 2nd article, the civil day January 1, 1912 begins, # all clocks therefore having to be advanced or set back correspondingly ... -# From Rui Pedro Salgueiro (1992-11-12): -# Portugal has recently (September, 27) changed timezone -# (from WET to MET or CET) to harmonize with EEC. -# -# Martin Bruckmann (1996-02-29) reports via Peter Ilieve -# that Portugal is reverting to 0:00 by not moving its clocks this spring. -# The new Prime Minister was fed up with getting up in the dark in the winter. -# -# From Paul Eggert (1996-11-12): -# IATA SSIM (1991-09) reports several 1991-09 and 1992-09 transitions -# at 02:00u, not 01:00u. Assume that these are typos. -# IATA SSIM (1991/1992) reports that the Azores were at -1:00. -# IATA SSIM (1993-02) says +0:00; later issues (through 1996-09) say -1:00. -# Guess that the Azores changed to EU rules in 1992 (since that's when Portugal -# harmonized with EU rules), and that they stayed +0:00 that winter. -# # Rule NAME FROM TO - IN ON AT SAVE LETTER/S -# DSH writes that despite Decree 1,469 (1915), the change to the clocks was not -# done every year, depending on what Spain did, because of railroad schedules. -# Go with Shanks & Pottenger. +# From Tim Parenti (2024-07-01), per Paul Eggert (1999-01-30): +# DSH writes in their history that Decreto 1469 of 1915-03-30 established +# summer time and that, "despite" this, the change to the clocks was not done +# every year, depending on what Spain did, because of railroad schedules. +# In fact, that decree had nothing to do with DST; rather, it regulated the +# sending of time signals. But we do see linkage to Spain in the 1920s below. +# https://dre.pt/dr/detalhe/decreto/1469-1915-285721 +# https://dre.pt/application/conteudo/285721 +# +# According to the Observatory, standard time was first advanced by Decreto +# 2433 of 1916-06-09 and restored by Decreto 2712 of 1916-10-28. While Whitman +# gives 1916-10-31 for the latter transition, Shanks & Pottenger agrees more +# closely with the decree, which stated that its provision "will start sixty +# minutes after the end of 31 October, according to the current time," i.e., +# 01:00 on 1 November. +# https://dre.pt/dr/detalhe/decreto/2433-1916-267192 +# https://dre.pt/application/conteudo/267192 +# https://dre.pt/dr/detalhe/decreto/2712-1916-590937 +# https://dre.pt/application/conteudo/590937 Rule Port 1916 only - Jun 17 23:00 1:00 S -# Whitman gives 1916 Oct 31; go with Shanks & Pottenger. Rule Port 1916 only - Nov 1 1:00 0 - -Rule Port 1917 only - Feb 28 23:00s 1:00 S -Rule Port 1917 1921 - Oct 14 23:00s 0 - -Rule Port 1918 only - Mar 1 23:00s 1:00 S -Rule Port 1919 only - Feb 28 23:00s 1:00 S -Rule Port 1920 only - Feb 29 23:00s 1:00 S -Rule Port 1921 only - Feb 28 23:00s 1:00 S +# From Tim Parenti (2024-07-01): +# Article 7 of Decreto 2922 of 1916-12-30 stated that "the legal time will be +# advanced by sixty minutes from 1 March to 31 October." Per Article 15, this +# came into force from 1917-01-01. Just before the first fall back, Decreto +# 3446 of 1917-10-11 changed the annual end date to 14 October. +# https://dre.pt/dr/detalhe/decreto/2922-1916-261894 +# https://dre.pt/application/conteudo/261894 +# https://dre.pt/dr/detalhe/decreto/3446-1917-495161 +# https://dre.pt/application/conteudo/495161 +# This annual change was revoked by Decreto 8038 of 1922-02-18. +# https://dre.pt/dr/detalhe/decreto/8038-1922-569751 +# https://dre.pt/application/conteudo/569751 +Rule Port 1917 1921 - Mar 1 0:00 1:00 S +Rule Port 1917 1921 - Oct 14 24:00 0 - +# From Tim Parenti (2024-07-01): +# Decreto 9592 of 1924-04-14 noted that "France maintains the advance of legal +# time in the summer and Spain has now adopted it for the first time" and +# considered "that the absence of similar measures would cause serious +# difficulties for international rail connections with consequent repercussions +# on domestic service hours..." along with "inconvenient analogues...for postal +# and telegraph services." Summer time would be in effect from 17 April to 4 +# October, with the spring change explicitly specified by bringing clocks +# forward from 16 April 23:00. +# https://dre.pt/dr/detalhe/decreto/9592-1924-652133 +# https://dre.pt/application/conteudo/652133 +# +# Decreto 10700, issued 1925-04-16, noted that Spain had not continued summer +# time, declared that "the current legal hour prior to 17 April remains +# unchanged from that day forward", and revoked legislation to the contrary, +# just a day before summer time would have otherwise resumed. +# https://dre.pt/dr/detalhe/decreto/10700-1925-437826 +# https://dre.pt/application/conteudo/437826 Rule Port 1924 only - Apr 16 23:00s 1:00 S -Rule Port 1924 only - Oct 14 23:00s 0 - +Rule Port 1924 only - Oct 4 23:00s 0 - Rule Port 1926 only - Apr 17 23:00s 1:00 S Rule Port 1926 1929 - Oct Sat>=1 23:00s 0 - Rule Port 1927 only - Apr 9 23:00s 1:00 S @@ -2139,6 +2185,8 @@ Rule Port 1931 1932 - Oct Sat>=1 23:00s 0 - Rule Port 1932 only - Apr 2 23:00s 1:00 S Rule Port 1934 only - Apr 7 23:00s 1:00 S # Whitman gives 1934 Oct 5; go with Shanks & Pottenger. +# Note: The 1935 law specified 10-06 00:00, not 10-05 24:00, but the following +# is equivalent and more succinct. Rule Port 1934 1938 - Oct Sat>=1 23:00s 0 - # Shanks & Pottenger give 1935 Apr 30; go with Whitman. Rule Port 1935 only - Mar 30 23:00s 1:00 S @@ -2149,10 +2197,19 @@ Rule Port 1938 only - Mar 26 23:00s 1:00 S Rule Port 1939 only - Apr 15 23:00s 1:00 S # Whitman gives 1939 Oct 7; go with Shanks & Pottenger. Rule Port 1939 only - Nov 18 23:00s 0 - +# From Tim Parenti (2024-07-01): +# Portaria 9465 of 1940-02-17 advanced clocks from Saturday 1940-02-24 23:00. +# The clocks were restored by Portaria 9658, issued Monday 1940-10-07, +# effective from 24:00 that very night, which agrees with Shanks & Pottenger; +# Whitman gives Saturday 1940-10-05 instead. +# https://dre.pt/dr/detalhe/portaria/9465-1940-189096 +# https://dre.pt/application/conteudo/189096 +# https://dre.pt/dr/detalhe/portaria/9658-1940-196729 +# https://dre.pt/application/conteudo/196729 Rule Port 1940 only - Feb 24 23:00s 1:00 S -# Shanks & Pottenger give 1940 Oct 7; go with Whitman. -Rule Port 1940 1941 - Oct 5 23:00s 0 - +Rule Port 1940 only - Oct 7 23:00s 0 - Rule Port 1941 only - Apr 5 23:00s 1:00 S +Rule Port 1941 only - Oct 5 23:00s 0 - Rule Port 1942 1945 - Mar Sat>=8 23:00s 1:00 S Rule Port 1942 only - Apr 25 22:00s 2:00 M # Midsummer Rule Port 1942 only - Aug 15 22:00s 1:00 S @@ -2162,66 +2219,195 @@ Rule Port 1943 1945 - Aug Sat>=25 22:00s 1:00 S Rule Port 1944 1945 - Apr Sat>=21 22:00s 2:00 M Rule Port 1946 only - Apr Sat>=1 23:00s 1:00 S Rule Port 1946 only - Oct Sat>=1 23:00s 0 - -# Whitman says DST was not observed in 1950; go with Shanks & Pottenger. -# Whitman gives Oct lastSun for 1952 on; go with Shanks & Pottenger. -Rule Port 1947 1965 - Apr Sun>=1 2:00s 1:00 S +# From Tim Parenti (2024-07-01), per Alois Treindl (2021-02-07): +# The Astronomical Observatory of Lisbon cites Portaria 11767 of 1947-03-28 for +# 1947 and Portaria 12286 of 1948-02-19 for 1948. +# https://dre.pt/dr/detalhe/portaria/11767-1947-414787 +# https://dre.pt/application/conteudo/414787 +# https://dre.pt/dr/detalhe/portaria/12286-1948-152953 +# https://dre.pt/application/conteudo/152953 +# +# Although the latter ordinance explicitly had the 1948-10-03 transition +# scheduled for 02:00 rather than 03:00 as had been used in 1947, Decreto-Lei +# 37048 of 1948-09-07 recognized "that it is advisable to definitely set...the +# 'summer time' regime", and fixed the fall transition at 03:00 moving forward. +# https://dre.pt/dr/detalhe/decreto-lei/37048-1948-373810 +# https://dre.pt/application/conteudo/373810 +# While the Observatory only cites this act for 1949-1965 and not for 1948, it +# does not appear to have had any provision delaying its effect, so assume that +# it overrode the prior ordinance for 1948-10-03. +# +# Whitman says DST was not observed in 1950 and gives Oct lastSun for 1952 on. +# The Observatory, however, agrees with Shanks & Pottenger that 1950 was not an +# exception and that Oct Sun>=1 was maintained through 1965. +Rule Port 1947 1966 - Apr Sun>=1 2:00s 1:00 S Rule Port 1947 1965 - Oct Sun>=1 2:00s 0 - -Rule Port 1977 only - Mar 27 0:00s 1:00 S -Rule Port 1977 only - Sep 25 0:00s 0 - -Rule Port 1978 1979 - Apr Sun>=1 0:00s 1:00 S -Rule Port 1978 only - Oct 1 0:00s 0 - -Rule Port 1979 1982 - Sep lastSun 1:00s 0 - -Rule Port 1980 only - Mar lastSun 0:00s 1:00 S -Rule Port 1981 1982 - Mar lastSun 1:00s 1:00 S -Rule Port 1983 only - Mar lastSun 2:00s 1:00 S +# From Tim Parenti (2024-07-01): +# Decreto-Lei 47233 of 1966-10-01 considered that the "duality" in time was +# "the cause of serious disturbances" and noted that "the countries with which +# we have the most frequent contacts...have already adopted" a solution +# coinciding with the extant "summer time". It established that the former +# "summer time" would apply year-round on the mainland and adjacent islands +# with immediate effect, as the fall back would have otherwise occurred later +# that evening. +# https://dre.pt/dr/detalhe/decreto-lei/47233-1966-293729 +# Model this by changing zones without changing clocks at the +# previously-appointed fall back time. +# +# Decreto-Lei 309/76 of 1976-04-27 acknowledged that those international +# contacts had returned to adopting seasonal times, and considered that the +# year-round advancement "entails considerable sacrifices for the vast majority +# of the working population during the winter months", including morning +# visibility concerns for schoolchildren. It specified, beginning 1976-09-26 +# 01:00, an annual return to UT+00 on the mainland from 00:00 UT on Sep lastSun +# to 00:00 UT on Mar lastSun (unless the latter date fell on Easter, in which +# case it was to be brought forward to the preceding Sunday). It also assigned +# the Permanent Time Commission to study and propose revisions for the Azores +# and Madeira, neither of which resumed DST until 1982 (as described further +# below). +# https://dre.pt/dr/detalhe/decreto-lei/309-1976-502063 +Rule Port 1976 only - Sep lastSun 1:00 0 - +Rule Port 1977 only - Mar lastSun 0:00s 1:00 S +Rule Port 1977 only - Sep lastSun 0:00s 0 - +# From Tim Parenti (2024-07-01): +# Beginning in 1978, rather than triggering the Easter rule of the 1976 decree +# (Easter fell on 1978-03-26), Article 5 was used instead, which allowed DST +# dates to be changed by order of the Minister of Education and Scientific +# Research, upon consultation with the Permanent Time Commission, "whenever +# considered convenient." As such, a series of one-off ordinances were +# promulgated for the mainland in 1978 through 1980, after which the 1976 +# decree naturally came back into force from 1981. +Rule Port 1978 1980 - Apr Sun>=1 1:00s 1:00 S +Rule Port 1978 only - Oct 1 1:00s 0 - +Rule Port 1979 1980 - Sep lastSun 1:00s 0 - +Rule Port 1981 1986 - Mar lastSun 0:00s 1:00 S +Rule Port 1981 1985 - Sep lastSun 0:00s 0 - +# From Tim Parenti (2024-07-01): +# Decreto-Lei 44-B/86 of 1986-03-07 switched mainland Portugal's transition +# times from 0:00s to 1:00u to harmonize with the EEC from 1986-03-30. +# https://dre.pt/dr/detalhe/decreto-lei/44-b-1986-628280 +# (Transitions of 1:00s as previously reported and used by the W-Eur rules, +# though equivalent, appear to have been fiction here.) Madeira continued to +# use 0:00s for spring 1986 before joining with the mainland using 1:00u in the +# fall; meanwhile, in the Azores the two were equivalent, so the law specifying +# 0:00s wasn't touched until 1992. (See below for more on the islands.) +# +# From Rui Pedro Salgueiro (1992-11-12): +# Portugal has recently (September, 27) changed timezone +# (from WET to MET or CET) to harmonize with EEC. +# +# Martin Bruckmann (1996-02-29) reports via Peter Ilieve +# that Portugal is reverting to 0:00 by not moving its clocks this spring. +# The new Prime Minister was fed up with getting up in the dark in the winter. +# +# From Paul Eggert (1996-11-12): +# IATA SSIM (1991-09) reports several 1991-09 and 1992-09 transitions +# at 02:00u, not 01:00u. Assume that these are typos. # # Zone NAME STDOFF RULES FORMAT [UNTIL] #STDOFF -0:36:44.68 Zone Europe/Lisbon -0:36:45 - LMT 1884 -0:36:45 - LMT 1912 Jan 1 0:00u # Lisbon MT - 0:00 Port WE%sT 1966 Apr 3 2:00 + 0:00 Port WE%sT 1966 Oct 2 2:00s 1:00 - CET 1976 Sep 26 1:00 - 0:00 Port WE%sT 1983 Sep 25 1:00s - 0:00 W-Eur WE%sT 1992 Sep 27 1:00s + 0:00 Port WE%sT 1986 + 0:00 EU WE%sT 1992 Sep 27 1:00u 1:00 EU CE%sT 1996 Mar 31 1:00u 0:00 EU WE%sT + +# From Tim Parenti (2024-07-01): +# For the Azores and Madeira, legislation was followed from the laws currently +# in force as listed at: +# https://oal.ul.pt/hora-legal/legislacao/ +# working backward through references of revocation and abrogation to +# Decreto-Lei 47233 of 1966-10-01, the last time DST was abolished across the +# mainland and its adjacent islands. Because of that reference, it is +# therefore assumed that DST rules in the islands prior to 1966 were like that +# of the mainland, though most legislation of the time didn't explicitly +# specify DST practices for the islands. Zone Atlantic/Azores -1:42:40 - LMT 1884 # Ponta Delgada -1:54:32 - HMT 1912 Jan 1 2:00u # Horta MT # Vanguard section, for zic and other parsers that support %z. -# -2:00 Port %z 1966 Apr 3 2:00 -# -1:00 Port %z 1983 Sep 25 1:00s -# -1:00 W-Eur %z 1992 Sep 27 1:00s + -2:00 Port %z 1966 Oct 2 2:00s +# From Tim Parenti (2024-07-01): +# While Decreto-Lei 309/76 of 1976-04-27 reintroduced DST on the mainland by +# falling back on 1976-09-26, it assigned the Permanent Time Commission to +# study and propose revisions for the Azores and Madeira. Decreto Regional +# 9/77/A of 1977-05-17 affirmed that "the legal time remained unchanged in the +# Azores" at UT-1, and would remain there year-round. +# https://dre.pt/dr/detalhe/decreto-regional/9-1977-252066 +# +# Decreto Regional 2/82/A, published 1982-03-02, adopted DST in the same +# fashion as the mainland used at the time. +# https://dre.pt/dr/detalhe/decreto-regional/2-1982-599965 +# Though transitions in the Azores officially remained at 0:00s through 1992, +# this was equivalent to the EU-style 1:00u adopted by the mainland in 1986, so +# model it as such. + -1:00 - %z 1982 Mar 28 0:00s + -1:00 Port %z 1986 # Rearguard section, for parsers lacking %z; see ziguard.awk. - -2:00 Port -02/-01 1942 Apr 25 22:00s - -2:00 Port +00 1942 Aug 15 22:00s - -2:00 Port -02/-01 1943 Apr 17 22:00s - -2:00 Port +00 1943 Aug 28 22:00s - -2:00 Port -02/-01 1944 Apr 22 22:00s - -2:00 Port +00 1944 Aug 26 22:00s - -2:00 Port -02/-01 1945 Apr 21 22:00s - -2:00 Port +00 1945 Aug 25 22:00s - -2:00 Port -02/-01 1966 Apr 3 2:00 - -1:00 Port -01/+00 1983 Sep 25 1:00s - -1:00 W-Eur -01/+00 1992 Sep 27 1:00s +# -2:00 Port -02/-01 1942 Apr 25 22:00s +# -2:00 Port +00 1942 Aug 15 22:00s +# -2:00 Port -02/-01 1943 Apr 17 22:00s +# -2:00 Port +00 1943 Aug 28 22:00s +# -2:00 Port -02/-01 1944 Apr 22 22:00s +# -2:00 Port +00 1944 Aug 26 22:00s +# -2:00 Port -02/-01 1945 Apr 21 22:00s +# -2:00 Port +00 1945 Aug 25 22:00s +# -2:00 Port -02/-01 1966 Oct 2 2:00s +# -1:00 - -01 1982 Mar 28 0:00s +# -1:00 Port -01/+00 1986 # End of rearguard section. - 0:00 EU WE%sT 1993 Mar 28 1:00u - -1:00 EU -01/+00 +# +# From Paul Eggert (1996-11-12): +# IATA SSIM (1991/1992) reports that the Azores were at -1:00. +# IATA SSIM (1993-02) says +0:00; later issues (through 1996-09) say -1:00. +# +# From Tim Parenti (2024-07-01): +# After mainland Portugal had shifted forward an hour from 1992-09-27, Decreto +# Legislativo Regional 29/92/A of 1992-12-23 sought to "reduce the time +# difference" by shifting the Azores forward as well from 1992-12-27. Just six +# months later, this was revoked by Decreto Legislativo Regional 9/93/A, citing +# "major changes in work habits and way of life." Though the revocation didn't +# give a transition time, it was signed Wednesday 1993-06-16; assume it took +# effect later that evening, and that an EU-style spring forward (to +01) was +# still observed in the interim on 1993-03-28. +# https://dre.pt/dr/detalhe/decreto-legislativo-regional/29-1992-621553 +# https://dre.pt/dr/detalhe/decreto-legislativo-regional/9-1993-389633 + -1:00 EU %z 1992 Dec 27 1:00s + 0:00 EU WE%sT 1993 Jun 17 1:00u + -1:00 EU %z + Zone Atlantic/Madeira -1:07:36 - LMT 1884 # Funchal -1:07:36 - FMT 1912 Jan 1 1:00u # Funchal MT # Vanguard section, for zic and other parsers that support %z. -# -1:00 Port %z 1966 Apr 3 2:00 + -1:00 Port %z 1966 Oct 2 2:00s # Rearguard section, for parsers lacking %z; see ziguard.awk. - -1:00 Port -01/+00 1942 Apr 25 22:00s - -1:00 Port +01 1942 Aug 15 22:00s - -1:00 Port -01/+00 1943 Apr 17 22:00s - -1:00 Port +01 1943 Aug 28 22:00s - -1:00 Port -01/+00 1944 Apr 22 22:00s - -1:00 Port +01 1944 Aug 26 22:00s - -1:00 Port -01/+00 1945 Apr 21 22:00s - -1:00 Port +01 1945 Aug 25 22:00s - -1:00 Port -01/+00 1966 Apr 3 2:00 +# -1:00 Port -01/+00 1942 Apr 25 22:00s +# -1:00 Port +01 1942 Aug 15 22:00s +# -1:00 Port -01/+00 1943 Apr 17 22:00s +# -1:00 Port +01 1943 Aug 28 22:00s +# -1:00 Port -01/+00 1944 Apr 22 22:00s +# -1:00 Port +01 1944 Aug 26 22:00s +# -1:00 Port -01/+00 1945 Apr 21 22:00s +# -1:00 Port +01 1945 Aug 25 22:00s +# -1:00 Port -01/+00 1966 Oct 2 2:00s # End of rearguard section. - 0:00 Port WE%sT 1983 Sep 25 1:00s +# +# From Tim Parenti (2024-07-01): +# Decreto Regional 5/82/M, published 1982-04-03, established DST transitions at +# 0:00u, which for Madeira is equivalent to the mainland's rules (0:00s) at the +# time. It came into effect the day following its publication, Sunday +# 1982-04-04, thus resuming Madeira's DST practice about a week later than the +# mainland and the Azores. +# https://dre.pt/dr/detalhe/decreto-regional/5-1982-608273 +# +# Decreto Legislativo Regional 18/86/M, published 1986-10-01, adopted EU-style +# rules (1:00u) and entered into immediate force after being signed on +# 1986-07-31. +# https://dre.pt/dr/detalhe/decreto-legislativo-regional/18-1986-221705 + 0:00 - WET 1982 Apr 4 + 0:00 Port WE%sT 1986 Jul 31 0:00 EU WE%sT # Romania @@ -2433,7 +2619,7 @@ Zone Europe/Kaliningrad 1:22:00 - LMT 1893 Apr 2:00 Poland EE%sT 1946 Apr 7 3:00 Russia MSK/MSD 1989 Mar 26 2:00s 2:00 Russia EE%sT 2011 Mar 27 2:00s - 3:00 - +03 2014 Oct 26 2:00s + 3:00 - %z 2014 Oct 26 2:00s 2:00 - EET @@ -2683,14 +2869,14 @@ Zone Europe/Simferopol 2:16:24 - LMT 1880 # http://publication.pravo.gov.ru/Document/View/0001201602150056 Zone Europe/Astrakhan 3:12:12 - LMT 1924 May - 3:00 - +03 1930 Jun 21 - 4:00 Russia +04/+05 1989 Mar 26 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s - 4:00 - +04 1992 Mar 29 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 2014 Oct 26 2:00s - 3:00 - +03 2016 Mar 27 2:00s - 4:00 - +04 + 3:00 - %z 1930 Jun 21 + 4:00 Russia %z 1989 Mar 26 2:00s + 3:00 Russia %z 1991 Mar 31 2:00s + 4:00 - %z 1992 Mar 29 2:00s + 3:00 Russia %z 2011 Mar 27 2:00s + 4:00 - %z 2014 Oct 26 2:00s + 3:00 - %z 2016 Mar 27 2:00s + 4:00 - %z # From Paul Eggert (2016-11-11): # Europe/Volgograd covers: @@ -2720,15 +2906,15 @@ Zone Europe/Astrakhan 3:12:12 - LMT 1924 May # http://publication.pravo.gov.ru/Document/View/0001202012220002 Zone Europe/Volgograd 2:57:40 - LMT 1920 Jan 3 - 3:00 - +03 1930 Jun 21 - 4:00 - +04 1961 Nov 11 - 4:00 Russia +04/+05 1988 Mar 27 2:00s + 3:00 - %z 1930 Jun 21 + 4:00 - %z 1961 Nov 11 + 4:00 Russia %z 1988 Mar 27 2:00s 3:00 Russia MSK/MSD 1991 Mar 31 2:00s - 4:00 - +04 1992 Mar 29 2:00s + 4:00 - %z 1992 Mar 29 2:00s 3:00 Russia MSK/MSD 2011 Mar 27 2:00s 4:00 - MSK 2014 Oct 26 2:00s 3:00 - MSK 2018 Oct 28 2:00s - 4:00 - +04 2020 Dec 27 2:00s + 4:00 - %z 2020 Dec 27 2:00s 3:00 - MSK # From Paul Eggert (2016-11-11): @@ -2743,14 +2929,14 @@ Zone Europe/Volgograd 2:57:40 - LMT 1920 Jan 3 # http://publication.pravo.gov.ru/Document/View/0001201611220031 Zone Europe/Saratov 3:04:18 - LMT 1919 Jul 1 0:00u - 3:00 - +03 1930 Jun 21 - 4:00 Russia +04/+05 1988 Mar 27 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s - 4:00 - +04 1992 Mar 29 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 2014 Oct 26 2:00s - 3:00 - +03 2016 Dec 4 2:00s - 4:00 - +04 + 3:00 - %z 1930 Jun 21 + 4:00 Russia %z 1988 Mar 27 2:00s + 3:00 Russia %z 1991 Mar 31 2:00s + 4:00 - %z 1992 Mar 29 2:00s + 3:00 Russia %z 2011 Mar 27 2:00s + 4:00 - %z 2014 Oct 26 2:00s + 3:00 - %z 2016 Dec 4 2:00s + 4:00 - %z # From Paul Eggert (2016-03-18): # Europe/Kirov covers: @@ -2758,10 +2944,10 @@ Zone Europe/Saratov 3:04:18 - LMT 1919 Jul 1 0:00u # The 1989 transition is from USSR act No. 227 (1989-03-14). # Zone Europe/Kirov 3:18:48 - LMT 1919 Jul 1 0:00u - 3:00 - +03 1930 Jun 21 - 4:00 Russia +04/+05 1989 Mar 26 2:00s + 3:00 - %z 1930 Jun 21 + 4:00 Russia %z 1989 Mar 26 2:00s 3:00 Russia MSK/MSD 1991 Mar 31 2:00s - 4:00 - +04 1992 Mar 29 2:00s + 4:00 - %z 1992 Mar 29 2:00s 3:00 Russia MSK/MSD 2011 Mar 27 2:00s 4:00 - MSK 2014 Oct 26 2:00s 3:00 - MSK @@ -2776,15 +2962,15 @@ Zone Europe/Kirov 3:18:48 - LMT 1919 Jul 1 0:00u # The 1989 transition is from USSR act No. 227 (1989-03-14). Zone Europe/Samara 3:20:20 - LMT 1919 Jul 1 0:00u - 3:00 - +03 1930 Jun 21 - 4:00 - +04 1935 Jan 27 - 4:00 Russia +04/+05 1989 Mar 26 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s - 2:00 Russia +02/+03 1991 Sep 29 2:00s - 3:00 - +03 1991 Oct 20 3:00 - 4:00 Russia +04/+05 2010 Mar 28 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 + 3:00 - %z 1930 Jun 21 + 4:00 - %z 1935 Jan 27 + 4:00 Russia %z 1989 Mar 26 2:00s + 3:00 Russia %z 1991 Mar 31 2:00s + 2:00 Russia %z 1991 Sep 29 2:00s + 3:00 - %z 1991 Oct 20 3:00 + 4:00 Russia %z 2010 Mar 28 2:00s + 3:00 Russia %z 2011 Mar 27 2:00s + 4:00 - %z # From Paul Eggert (2016-03-18): # Europe/Ulyanovsk covers: @@ -2800,14 +2986,14 @@ Zone Europe/Samara 3:20:20 - LMT 1919 Jul 1 0:00u # http://publication.pravo.gov.ru/Document/View/0001201603090051 Zone Europe/Ulyanovsk 3:13:36 - LMT 1919 Jul 1 0:00u - 3:00 - +03 1930 Jun 21 - 4:00 Russia +04/+05 1989 Mar 26 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s - 2:00 Russia +02/+03 1992 Jan 19 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 2014 Oct 26 2:00s - 3:00 - +03 2016 Mar 27 2:00s - 4:00 - +04 + 3:00 - %z 1930 Jun 21 + 4:00 Russia %z 1989 Mar 26 2:00s + 3:00 Russia %z 1991 Mar 31 2:00s + 2:00 Russia %z 1992 Jan 19 2:00s + 3:00 Russia %z 2011 Mar 27 2:00s + 4:00 - %z 2014 Oct 26 2:00s + 3:00 - %z 2016 Mar 27 2:00s + 4:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): # Asia/Yekaterinburg covers... @@ -2832,12 +3018,12 @@ Zone Europe/Ulyanovsk 3:13:36 - LMT 1919 Jul 1 0:00u #STDOFF 4:02:32.9 Zone Asia/Yekaterinburg 4:02:33 - LMT 1916 Jul 3 3:45:05 - PMT 1919 Jul 15 4:00 - 4:00 - +04 1930 Jun 21 - 5:00 Russia +05/+06 1991 Mar 31 2:00s - 4:00 Russia +04/+05 1992 Jan 19 2:00s - 5:00 Russia +05/+06 2011 Mar 27 2:00s - 6:00 - +06 2014 Oct 26 2:00s - 5:00 - +05 + 4:00 - %z 1930 Jun 21 + 5:00 Russia %z 1991 Mar 31 2:00s + 4:00 Russia %z 1992 Jan 19 2:00s + 5:00 Russia %z 2011 Mar 27 2:00s + 6:00 - %z 2014 Oct 26 2:00s + 5:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): @@ -2847,12 +3033,12 @@ Zone Asia/Yekaterinburg 4:02:33 - LMT 1916 Jul 3 # Byalokoz 1919 says Omsk was 4:53:30. Zone Asia/Omsk 4:53:30 - LMT 1919 Nov 14 - 5:00 - +05 1930 Jun 21 - 6:00 Russia +06/+07 1991 Mar 31 2:00s - 5:00 Russia +05/+06 1992 Jan 19 2:00s - 6:00 Russia +06/+07 2011 Mar 27 2:00s - 7:00 - +07 2014 Oct 26 2:00s - 6:00 - +06 + 5:00 - %z 1930 Jun 21 + 6:00 Russia %z 1991 Mar 31 2:00s + 5:00 Russia %z 1992 Jan 19 2:00s + 6:00 Russia %z 2011 Mar 27 2:00s + 7:00 - %z 2014 Oct 26 2:00s + 6:00 - %z # From Paul Eggert (2016-02-22): # Asia/Barnaul covers: @@ -2885,14 +3071,14 @@ Zone Asia/Omsk 4:53:30 - LMT 1919 Nov 14 # http://publication.pravo.gov.ru/Document/View/0001201603090038 Zone Asia/Barnaul 5:35:00 - LMT 1919 Dec 10 - 6:00 - +06 1930 Jun 21 - 7:00 Russia +07/+08 1991 Mar 31 2:00s - 6:00 Russia +06/+07 1992 Jan 19 2:00s - 7:00 Russia +07/+08 1995 May 28 - 6:00 Russia +06/+07 2011 Mar 27 2:00s - 7:00 - +07 2014 Oct 26 2:00s - 6:00 - +06 2016 Mar 27 2:00s - 7:00 - +07 + 6:00 - %z 1930 Jun 21 + 7:00 Russia %z 1991 Mar 31 2:00s + 6:00 Russia %z 1992 Jan 19 2:00s + 7:00 Russia %z 1995 May 28 + 6:00 Russia %z 2011 Mar 27 2:00s + 7:00 - %z 2014 Oct 26 2:00s + 6:00 - %z 2016 Mar 27 2:00s + 7:00 - %z # From Paul Eggert (2016-03-18): # Asia/Novosibirsk covers: @@ -2906,14 +3092,14 @@ Zone Asia/Barnaul 5:35:00 - LMT 1919 Dec 10 # http://publication.pravo.gov.ru/Document/View/0001201607040064 Zone Asia/Novosibirsk 5:31:40 - LMT 1919 Dec 14 6:00 - 6:00 - +06 1930 Jun 21 - 7:00 Russia +07/+08 1991 Mar 31 2:00s - 6:00 Russia +06/+07 1992 Jan 19 2:00s - 7:00 Russia +07/+08 1993 May 23 # say Shanks & P. - 6:00 Russia +06/+07 2011 Mar 27 2:00s - 7:00 - +07 2014 Oct 26 2:00s - 6:00 - +06 2016 Jul 24 2:00s - 7:00 - +07 + 6:00 - %z 1930 Jun 21 + 7:00 Russia %z 1991 Mar 31 2:00s + 6:00 Russia %z 1992 Jan 19 2:00s + 7:00 Russia %z 1993 May 23 # say Shanks & P. + 6:00 Russia %z 2011 Mar 27 2:00s + 7:00 - %z 2014 Oct 26 2:00s + 6:00 - %z 2016 Jul 24 2:00s + 7:00 - %z # From Paul Eggert (2016-03-18): # Asia/Tomsk covers: @@ -2958,14 +3144,14 @@ Zone Asia/Novosibirsk 5:31:40 - LMT 1919 Dec 14 6:00 # http://publication.pravo.gov.ru/Document/View/0001201604260048 Zone Asia/Tomsk 5:39:51 - LMT 1919 Dec 22 - 6:00 - +06 1930 Jun 21 - 7:00 Russia +07/+08 1991 Mar 31 2:00s - 6:00 Russia +06/+07 1992 Jan 19 2:00s - 7:00 Russia +07/+08 2002 May 1 3:00 - 6:00 Russia +06/+07 2011 Mar 27 2:00s - 7:00 - +07 2014 Oct 26 2:00s - 6:00 - +06 2016 May 29 2:00s - 7:00 - +07 + 6:00 - %z 1930 Jun 21 + 7:00 Russia %z 1991 Mar 31 2:00s + 6:00 Russia %z 1992 Jan 19 2:00s + 7:00 Russia %z 2002 May 1 3:00 + 6:00 Russia %z 2011 Mar 27 2:00s + 7:00 - %z 2014 Oct 26 2:00s + 6:00 - %z 2016 May 29 2:00s + 7:00 - %z # From Tim Parenti (2014-07-03): @@ -2996,12 +3182,12 @@ Zone Asia/Tomsk 5:39:51 - LMT 1919 Dec 22 # realigning itself with KRAT. Zone Asia/Novokuznetsk 5:48:48 - LMT 1924 May 1 - 6:00 - +06 1930 Jun 21 - 7:00 Russia +07/+08 1991 Mar 31 2:00s - 6:00 Russia +06/+07 1992 Jan 19 2:00s - 7:00 Russia +07/+08 2010 Mar 28 2:00s - 6:00 Russia +06/+07 2011 Mar 27 2:00s - 7:00 - +07 + 6:00 - %z 1930 Jun 21 + 7:00 Russia %z 1991 Mar 31 2:00s + 6:00 Russia %z 1992 Jan 19 2:00s + 7:00 Russia %z 2010 Mar 28 2:00s + 6:00 Russia %z 2011 Mar 27 2:00s + 7:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): # Asia/Krasnoyarsk covers... @@ -3015,12 +3201,12 @@ Zone Asia/Novokuznetsk 5:48:48 - LMT 1924 May 1 # Byalokoz 1919 says Krasnoyarsk was 6:11:26. Zone Asia/Krasnoyarsk 6:11:26 - LMT 1920 Jan 6 - 6:00 - +06 1930 Jun 21 - 7:00 Russia +07/+08 1991 Mar 31 2:00s - 6:00 Russia +06/+07 1992 Jan 19 2:00s - 7:00 Russia +07/+08 2011 Mar 27 2:00s - 8:00 - +08 2014 Oct 26 2:00s - 7:00 - +07 + 6:00 - %z 1930 Jun 21 + 7:00 Russia %z 1991 Mar 31 2:00s + 6:00 Russia %z 1992 Jan 19 2:00s + 7:00 Russia %z 2011 Mar 27 2:00s + 8:00 - %z 2014 Oct 26 2:00s + 7:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): @@ -3037,12 +3223,12 @@ Zone Asia/Krasnoyarsk 6:11:26 - LMT 1920 Jan 6 Zone Asia/Irkutsk 6:57:05 - LMT 1880 6:57:05 - IMT 1920 Jan 25 # Irkutsk Mean Time - 7:00 - +07 1930 Jun 21 - 8:00 Russia +08/+09 1991 Mar 31 2:00s - 7:00 Russia +07/+08 1992 Jan 19 2:00s - 8:00 Russia +08/+09 2011 Mar 27 2:00s - 9:00 - +09 2014 Oct 26 2:00s - 8:00 - +08 + 7:00 - %z 1930 Jun 21 + 8:00 Russia %z 1991 Mar 31 2:00s + 7:00 Russia %z 1992 Jan 19 2:00s + 8:00 Russia %z 2011 Mar 27 2:00s + 9:00 - %z 2014 Oct 26 2:00s + 8:00 - %z # From Tim Parenti (2014-07-06): @@ -3059,13 +3245,13 @@ Zone Asia/Irkutsk 6:57:05 - LMT 1880 # http://publication.pravo.gov.ru/Document/View/0001201512300107 Zone Asia/Chita 7:33:52 - LMT 1919 Dec 15 - 8:00 - +08 1930 Jun 21 - 9:00 Russia +09/+10 1991 Mar 31 2:00s - 8:00 Russia +08/+09 1992 Jan 19 2:00s - 9:00 Russia +09/+10 2011 Mar 27 2:00s - 10:00 - +10 2014 Oct 26 2:00s - 8:00 - +08 2016 Mar 27 2:00 - 9:00 - +09 + 8:00 - %z 1930 Jun 21 + 9:00 Russia %z 1991 Mar 31 2:00s + 8:00 Russia %z 1992 Jan 19 2:00s + 9:00 Russia %z 2011 Mar 27 2:00s + 10:00 - %z 2014 Oct 26 2:00s + 8:00 - %z 2016 Mar 27 2:00 + 9:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2009-11-29): @@ -3105,12 +3291,12 @@ Zone Asia/Chita 7:33:52 - LMT 1919 Dec 15 # Byalokoz 1919 says Yakutsk was 8:38:58. Zone Asia/Yakutsk 8:38:58 - LMT 1919 Dec 15 - 8:00 - +08 1930 Jun 21 - 9:00 Russia +09/+10 1991 Mar 31 2:00s - 8:00 Russia +08/+09 1992 Jan 19 2:00s - 9:00 Russia +09/+10 2011 Mar 27 2:00s - 10:00 - +10 2014 Oct 26 2:00s - 9:00 - +09 + 8:00 - %z 1930 Jun 21 + 9:00 Russia %z 1991 Mar 31 2:00s + 8:00 Russia %z 1992 Jan 19 2:00s + 9:00 Russia %z 2011 Mar 27 2:00s + 10:00 - %z 2014 Oct 26 2:00s + 9:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2009-11-29): @@ -3128,12 +3314,12 @@ Zone Asia/Yakutsk 8:38:58 - LMT 1919 Dec 15 # Go with Byalokoz. Zone Asia/Vladivostok 8:47:31 - LMT 1922 Nov 15 - 9:00 - +09 1930 Jun 21 - 10:00 Russia +10/+11 1991 Mar 31 2:00s - 9:00 Russia +09/+10 1992 Jan 19 2:00s - 10:00 Russia +10/+11 2011 Mar 27 2:00s - 11:00 - +11 2014 Oct 26 2:00s - 10:00 - +10 + 9:00 - %z 1930 Jun 21 + 10:00 Russia %z 1991 Mar 31 2:00s + 9:00 Russia %z 1992 Jan 19 2:00s + 10:00 Russia %z 2011 Mar 27 2:00s + 11:00 - %z 2014 Oct 26 2:00s + 10:00 - %z # From Tim Parenti (2014-07-03): @@ -3151,14 +3337,14 @@ Zone Asia/Vladivostok 8:47:31 - LMT 1922 Nov 15 # This transition is no doubt wrong, but we have no better info. Zone Asia/Khandyga 9:02:13 - LMT 1919 Dec 15 - 8:00 - +08 1930 Jun 21 - 9:00 Russia +09/+10 1991 Mar 31 2:00s - 8:00 Russia +08/+09 1992 Jan 19 2:00s - 9:00 Russia +09/+10 2004 - 10:00 Russia +10/+11 2011 Mar 27 2:00s - 11:00 - +11 2011 Sep 13 0:00s # Decree 725? - 10:00 - +10 2014 Oct 26 2:00s - 9:00 - +09 + 8:00 - %z 1930 Jun 21 + 9:00 Russia %z 1991 Mar 31 2:00s + 8:00 Russia %z 1992 Jan 19 2:00s + 9:00 Russia %z 2004 + 10:00 Russia %z 2011 Mar 27 2:00s + 11:00 - %z 2011 Sep 13 0:00s # Decree 725? + 10:00 - %z 2014 Oct 26 2:00s + 9:00 - %z # From Tim Parenti (2014-07-03): @@ -3174,14 +3360,14 @@ Zone Asia/Khandyga 9:02:13 - LMT 1919 Dec 15 # The Zone name should be Asia/Yuzhno-Sakhalinsk, but that's too long. Zone Asia/Sakhalin 9:30:48 - LMT 1905 Aug 23 - 9:00 - +09 1945 Aug 25 - 11:00 Russia +11/+12 1991 Mar 31 2:00s # Sakhalin T - 10:00 Russia +10/+11 1992 Jan 19 2:00s - 11:00 Russia +11/+12 1997 Mar lastSun 2:00s - 10:00 Russia +10/+11 2011 Mar 27 2:00s - 11:00 - +11 2014 Oct 26 2:00s - 10:00 - +10 2016 Mar 27 2:00s - 11:00 - +11 + 9:00 - %z 1945 Aug 25 + 11:00 Russia %z 1991 Mar 31 2:00s # Sakhalin T + 10:00 Russia %z 1992 Jan 19 2:00s + 11:00 Russia %z 1997 Mar lastSun 2:00s + 10:00 Russia %z 2011 Mar 27 2:00s + 11:00 - %z 2014 Oct 26 2:00s + 10:00 - %z 2016 Mar 27 2:00s + 11:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2009-11-29): @@ -3204,13 +3390,13 @@ Zone Asia/Sakhalin 9:30:48 - LMT 1905 Aug 23 # http://publication.pravo.gov.ru/Document/View/0001201604050038 Zone Asia/Magadan 10:03:12 - LMT 1924 May 2 - 10:00 - +10 1930 Jun 21 # Magadan Time - 11:00 Russia +11/+12 1991 Mar 31 2:00s - 10:00 Russia +10/+11 1992 Jan 19 2:00s - 11:00 Russia +11/+12 2011 Mar 27 2:00s - 12:00 - +12 2014 Oct 26 2:00s - 10:00 - +10 2016 Apr 24 2:00s - 11:00 - +11 + 10:00 - %z 1930 Jun 21 # Magadan Time + 11:00 Russia %z 1991 Mar 31 2:00s + 10:00 Russia %z 1992 Jan 19 2:00s + 11:00 Russia %z 2011 Mar 27 2:00s + 12:00 - %z 2014 Oct 26 2:00s + 10:00 - %z 2016 Apr 24 2:00s + 11:00 - %z # From Tim Parenti (2014-07-06): @@ -3255,12 +3441,12 @@ Zone Asia/Magadan 10:03:12 - LMT 1924 May 2 # Go with Srednekolymsk. Zone Asia/Srednekolymsk 10:14:52 - LMT 1924 May 2 - 10:00 - +10 1930 Jun 21 - 11:00 Russia +11/+12 1991 Mar 31 2:00s - 10:00 Russia +10/+11 1992 Jan 19 2:00s - 11:00 Russia +11/+12 2011 Mar 27 2:00s - 12:00 - +12 2014 Oct 26 2:00s - 11:00 - +11 + 10:00 - %z 1930 Jun 21 + 11:00 Russia %z 1991 Mar 31 2:00s + 10:00 Russia %z 1992 Jan 19 2:00s + 11:00 Russia %z 2011 Mar 27 2:00s + 12:00 - %z 2014 Oct 26 2:00s + 11:00 - %z # From Tim Parenti (2014-07-03): @@ -3278,14 +3464,14 @@ Zone Asia/Srednekolymsk 10:14:52 - LMT 1924 May 2 # UTC+12 since at least then, too. Zone Asia/Ust-Nera 9:32:54 - LMT 1919 Dec 15 - 8:00 - +08 1930 Jun 21 - 9:00 Russia +09/+10 1981 Apr 1 - 11:00 Russia +11/+12 1991 Mar 31 2:00s - 10:00 Russia +10/+11 1992 Jan 19 2:00s - 11:00 Russia +11/+12 2011 Mar 27 2:00s - 12:00 - +12 2011 Sep 13 0:00s # Decree 725? - 11:00 - +11 2014 Oct 26 2:00s - 10:00 - +10 + 8:00 - %z 1930 Jun 21 + 9:00 Russia %z 1981 Apr 1 + 11:00 Russia %z 1991 Mar 31 2:00s + 10:00 Russia %z 1992 Jan 19 2:00s + 11:00 Russia %z 2011 Mar 27 2:00s + 12:00 - %z 2011 Sep 13 0:00s # Decree 725? + 11:00 - %z 2014 Oct 26 2:00s + 10:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): @@ -3298,12 +3484,12 @@ Zone Asia/Ust-Nera 9:32:54 - LMT 1919 Dec 15 # The Zone name should be Asia/Petropavlovsk-Kamchatski or perhaps # Asia/Petropavlovsk-Kamchatsky, but these are too long. Zone Asia/Kamchatka 10:34:36 - LMT 1922 Nov 10 - 11:00 - +11 1930 Jun 21 - 12:00 Russia +12/+13 1991 Mar 31 2:00s - 11:00 Russia +11/+12 1992 Jan 19 2:00s - 12:00 Russia +12/+13 2010 Mar 28 2:00s - 11:00 Russia +11/+12 2011 Mar 27 2:00s - 12:00 - +12 + 11:00 - %z 1930 Jun 21 + 12:00 Russia %z 1991 Mar 31 2:00s + 11:00 Russia %z 1992 Jan 19 2:00s + 12:00 Russia %z 2010 Mar 28 2:00s + 11:00 Russia %z 2011 Mar 27 2:00s + 12:00 - %z # From Tim Parenti (2014-07-03): @@ -3311,13 +3497,13 @@ Zone Asia/Kamchatka 10:34:36 - LMT 1922 Nov 10 # 87 RU-CHU Chukotka Autonomous Okrug Zone Asia/Anadyr 11:49:56 - LMT 1924 May 2 - 12:00 - +12 1930 Jun 21 - 13:00 Russia +13/+14 1982 Apr 1 0:00s - 12:00 Russia +12/+13 1991 Mar 31 2:00s - 11:00 Russia +11/+12 1992 Jan 19 2:00s - 12:00 Russia +12/+13 2010 Mar 28 2:00s - 11:00 Russia +11/+12 2011 Mar 27 2:00s - 12:00 - +12 + 12:00 - %z 1930 Jun 21 + 13:00 Russia %z 1982 Apr 1 0:00s + 12:00 Russia %z 1991 Mar 31 2:00s + 11:00 Russia %z 1992 Jan 19 2:00s + 12:00 Russia %z 2010 Mar 28 2:00s + 11:00 Russia %z 2011 Mar 27 2:00s + 12:00 - %z # Bosnia & Herzegovina # Croatia @@ -3436,7 +3622,7 @@ Zone Africa/Ceuta -0:21:16 - LMT 1901 Jan 1 0:00u 1:00 - CET 1986 1:00 EU CE%sT Zone Atlantic/Canary -1:01:36 - LMT 1922 Mar # Las Palmas de Gran C. - -1:00 - -01 1946 Sep 30 1:00 + -1:00 - %z 1946 Sep 30 1:00 0:00 - WET 1980 Apr 6 0:00s 0:00 1:00 WEST 1980 Sep 28 1:00u 0:00 EU WE%sT @@ -3517,8 +3703,8 @@ Zone Atlantic/Canary -1:01:36 - LMT 1922 Mar # Las Palmas de Gran C. # but if no one is present after 11 at night, could be postponed until one # hour before the beginning of service. -# From Paul Eggert (2013-09-11): -# Round BMT to the nearest even second, 0:29:46. +# From Paul Eggert (2024-05-24): +# Express BMT as 0:29:45.500, approximately the same precision 7° 26' 22.50". # # We can find no reliable source for Shanks's assertion that all of Switzerland # except Geneva switched to Bern Mean Time at 00:00 on 1848-09-12. This book: @@ -3557,6 +3743,7 @@ Rule Swiss 1941 1942 - May Mon>=1 1:00 1:00 S Rule Swiss 1941 1942 - Oct Mon>=1 2:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Europe/Zurich 0:34:08 - LMT 1853 Jul 16 # See above comment. + #STDOFF 0:29:45.500 0:29:46 - BMT 1894 Jun # Bern Mean Time 1:00 Swiss CE%sT 1981 1:00 EU CE%sT @@ -3754,7 +3941,7 @@ Rule Turkey 1996 2006 - Oct lastSun 1:00s 0 - Zone Europe/Istanbul 1:55:52 - LMT 1880 1:56:56 - IMT 1910 Oct # Istanbul Mean Time? 2:00 Turkey EE%sT 1978 Jun 29 - 3:00 Turkey +03/+04 1984 Nov 1 2:00 + 3:00 Turkey %z 1984 Nov 1 2:00 2:00 Turkey EE%sT 2007 2:00 EU EE%sT 2011 Mar 27 1:00u 2:00 - EET 2011 Mar 28 1:00u @@ -3763,7 +3950,7 @@ Zone Europe/Istanbul 1:55:52 - LMT 1880 2:00 EU EE%sT 2015 Oct 25 1:00u 2:00 1:00 EEST 2015 Nov 8 1:00u 2:00 EU EE%sT 2016 Sep 7 - 3:00 - +03 + 3:00 - %z # Ukraine # diff --git a/src/java.base/share/data/tzdata/leapseconds b/src/java.base/share/data/tzdata/leapseconds index 8e7df3de984..63a76620dbf 100644 --- a/src/java.base/share/data/tzdata/leapseconds +++ b/src/java.base/share/data/tzdata/leapseconds @@ -92,11 +92,11 @@ Leap 2016 Dec 31 23:59:60 + S # Any additional leap seconds will come after this. # This Expires line is commented out for now, # so that pre-2020a zic implementations do not reject this file. -#Expires 2024 Dec 28 00:00:00 +#Expires 2025 Jun 28 00:00:00 # POSIX timestamps for the data in this file: -#updated 1704708379 (2024-01-08 10:06:19 UTC) -#expires 1735344000 (2024-12-28 00:00:00 UTC) +#updated 1720104763 (2024-07-04 14:52:43 UTC) +#expires 1751068800 (2025-06-28 00:00:00 UTC) # Updated through IERS Bulletin C (https://hpiers.obspm.fr/iers/bul/bulc/bulletinc.dat) -# File expires on 28 December 2024 +# File expires on 28 June 2025 diff --git a/src/java.base/share/data/tzdata/northamerica b/src/java.base/share/data/tzdata/northamerica index a8b2ef3f7fa..c95e7d0e643 100644 --- a/src/java.base/share/data/tzdata/northamerica +++ b/src/java.base/share/data/tzdata/northamerica @@ -208,26 +208,6 @@ Rule US 1987 2006 - Apr Sun>=1 2:00 1:00 D Rule US 2007 max - Mar Sun>=8 2:00 1:00 D Rule US 2007 max - Nov Sun>=1 2:00 0 S -# From Arthur David Olson, 2005-12-19 -# We generate the files specified below to guard against old files with -# obsolete information being left in the time zone binary directory. -# We limit the list to names that have appeared in previous versions of -# this time zone package. -# We do these as separate Zones rather than as Links to avoid problems if -# a particular place changes whether it observes DST. -# We put these specifications here in the northamerica file both to -# increase the chances that they'll actually get compiled and to -# avoid the need to duplicate the US rules in another file. - -# Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone EST -5:00 - EST -Zone MST -7:00 - MST -Zone HST -10:00 - HST -Zone EST5EDT -5:00 US E%sT -Zone CST6CDT -6:00 US C%sT -Zone MST7MDT -7:00 US M%sT -Zone PST8PDT -8:00 US P%sT - # From U. S. Naval Observatory (1989-01-19): # USA EASTERN 5 H BEHIND UTC NEW YORK, WASHINGTON # USA EASTERN 4 H BEHIND UTC APR 3 - OCT 30 @@ -2396,6 +2376,81 @@ Zone America/Dawson -9:17:40 - LMT 1900 Aug 20 # the researchers who prepared the Decrees page failed to find some of # the relevant documents. +# From Heitor David Pinto (2024-08-04): +# In 1931, the decree implementing DST specified that it would take +# effect on 30 April.... +# https://www.dof.gob.mx/nota_to_imagen_fs.php?cod_diario=192270&pagina=2&seccion=1 +# +# In 1981, the decree changing Campeche, Yucatán and Quintana Roo to UTC-5 +# specified that it would enter into force on 26 December 1981 at 2:00.... +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4705667&fecha=23/12/1981&cod_diario=202796 +# +# In 1982, the decree returning Campeche and Yucatán to UTC-6 specified that +# it would enter into force on 2 November 1982 at 2:00.... +# https://www.dof.gob.mx/nota_to_imagen_fs.php?cod_diario=205689&pagina=3&seccion=0 +# +# Quintana Roo changed to UTC-6 on 4 January 1983 at 0:00, and again +# to UTC-5 on 26 October 1997 at 2:00.... +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4787355&fecha=28/12/1982&cod_diario=206112 +# https://www.dof.gob.mx/nota_to_imagen_fs.php?cod_diario=209559&pagina=15&seccion=0 +# +# Durango, Coahuila, Nuevo León and Tamaulipas were set to UTC-7 on 1 January +# 1922, and changed to UTC-6 on 10 June 1927. Then Durango, Coahuila and +# Nuevo León (but not Tamaulipas) returned to UTC-7 on 15 November 1930, +# observed DST in 1931, and changed again to UTC-6 on 1 April 1932.... +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4441846&fecha=29/12/1921&cod_diario=187468 +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4541520&fecha=09/06/1927&cod_diario=193920 +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4491963&fecha=15/11/1930&cod_diario=190835 +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4418437&fecha=21/01/1932&cod_diario=185588 +# +# ... the ... 10 June 1927 ... decree only said 10 June 1927, without +# specifying a time, so I suppose that it should be considered at 0:00. +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4541520&fecha=09/06/1927&cod_diario=193920 +# +# In 1942, the decree changing Baja California, Baja California Sur, Sonora, +# Sinaloa and Nayarit to UTC-7 was published on 24 April, but it said that it +# would apply from 1 April, so it's unclear when the change actually +# occurred. The database currently shows 24 April 1942. +# https://www.dof.gob.mx/nota_to_imagen_fs.php?cod_diario=192203&pagina=2&seccion=1 +# +# Baja California Sur, Sonora, Sinaloa and Nayarit never used UTC-8. The ... +# 14 January 1949 ... change [to UTC-8] only occurred in Baja California. +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4515613&fecha=13/01/1949&cod_diario=192309 +# +# In 1945, the decree changing Baja California to UTC-8 specified that it +# would take effect on the third day from its publication. +# It was published on 12 November, so it would take effect on 15 November.... +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4555049&fecha=12/11/1945&cod_diario=194763 +# +# In 1948, the decree changing Baja California to UTC-7 specified that it +# would take effect on "this date". The decree was made on 13 March, +# but published on 5 April, so it's unclear when the change actually occurred. +# The database currently shows 5 April 1948. +# https://www.dof.gob.mx/nota_to_imagen_fs.php?cod_diario=188624&pagina=2&seccion=0 +# +# In 1949, the decree changing Baja California to UTC-8 was published on 13 +# January, but it said that it would apply from 1 January, so it's unclear when +# the change actually occurred. The database currently shows 14 January 1949. +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4515613&fecha=13/01/1949&cod_diario=192309 +# +# Baja California also observed UTC-7 from 1 May to 24 September 1950, +# from 29 April to 30 September 1951 at 2:00, +# and from 27 April to 28 September 1952 at 2:00.... +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4600403&fecha=29/04/1950&cod_diario=197505 +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4623553&fecha=23/09/1950&cod_diario=198805 +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4469444&fecha=27/04/1951&cod_diario=189317 +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4533868&fecha=10/03/1952&cod_diario=193465 +# +# All changes in Baja California from 1948 to 1952 match those in California, +# on the same dates or with a difference of one day. +# So it may be easier to implement these changes as DST with rule CA +# during this whole period. +# +# From Paul Eggert (2024-08-18): +# For now, maintain the slightly-different history for Baja California, +# as we have no information on whether 1948/1952 clocks in Tijuana followed +# the decrees or followed San Diego. + # From Alan Perry (1996-02-15): # A guy from our Mexico subsidiary finally found the Presidential Decree # outlining the timezone changes in Mexico. @@ -2599,7 +2654,7 @@ Zone America/Dawson -9:17:40 - LMT 1900 Aug 20 # http://puentelibre.mx/noticia/ciudad_juarez_cambio_horario_noviembre_2022/ # Rule NAME FROM TO - IN ON AT SAVE LETTER/S -Rule Mexico 1931 only - May 1 23:00 1:00 D +Rule Mexico 1931 only - April 30 0:00 1:00 D Rule Mexico 1931 only - Oct 1 0:00 0 S Rule Mexico 1939 only - Feb 5 0:00 1:00 D Rule Mexico 1939 only - Jun 25 0:00 0 S @@ -2618,14 +2673,16 @@ Rule Mexico 2002 2022 - Oct lastSun 2:00 0 S # Zone NAME STDOFF RULES FORMAT [UNTIL] # Quintana Roo; represented by Cancún Zone America/Cancun -5:47:04 - LMT 1922 Jan 1 6:00u - -6:00 - CST 1981 Dec 23 + -6:00 - CST 1981 Dec 26 2:00 + -5:00 - EST 1983 Jan 4 0:00 + -6:00 Mexico C%sT 1997 Oct 26 2:00 -5:00 Mexico E%sT 1998 Aug 2 2:00 -6:00 Mexico C%sT 2015 Feb 1 2:00 -5:00 - EST # Campeche, Yucatán; represented by Mérida Zone America/Merida -5:58:28 - LMT 1922 Jan 1 6:00u - -6:00 - CST 1981 Dec 23 - -5:00 - EST 1982 Dec 2 + -6:00 - CST 1981 Dec 26 2:00 + -5:00 - EST 1982 Nov 2 2:00 -6:00 Mexico C%sT # Coahuila, Nuevo León, Tamaulipas (near US border) # This includes the following municipios: @@ -2642,12 +2699,15 @@ Zone America/Matamoros -6:30:00 - LMT 1922 Jan 1 6:00u -6:00 US C%sT # Durango; Coahuila, Nuevo León, Tamaulipas (away from US border) Zone America/Monterrey -6:41:16 - LMT 1922 Jan 1 6:00u + -7:00 - MST 1927 Jun 10 + -6:00 - CST 1930 Nov 15 + -7:00 Mexico M%sT 1932 Apr 1 -6:00 - CST 1988 -6:00 US C%sT 1989 -6:00 Mexico C%sT # Central Mexico Zone America/Mexico_City -6:36:36 - LMT 1922 Jan 1 7:00u - -7:00 - MST 1927 Jun 10 23:00 + -7:00 - MST 1927 Jun 10 -6:00 - CST 1930 Nov 15 -7:00 Mexico M%sT 1932 Apr 1 -6:00 Mexico C%sT 2001 Sep 30 2:00 @@ -2658,7 +2718,7 @@ Zone America/Mexico_City -6:36:36 - LMT 1922 Jan 1 7:00u # Práxedis G Guerrero. # http://gaceta.diputados.gob.mx/PDF/65/2a022/nov/20221124-VII.pdf Zone America/Ciudad_Juarez -7:05:56 - LMT 1922 Jan 1 7:00u - -7:00 - MST 1927 Jun 10 23:00 + -7:00 - MST 1927 Jun 10 -6:00 - CST 1930 Nov 15 -7:00 Mexico M%sT 1932 Apr 1 -6:00 - CST 1996 @@ -2673,7 +2733,7 @@ Zone America/Ciudad_Juarez -7:05:56 - LMT 1922 Jan 1 7:00u # Benavides. # http://gaceta.diputados.gob.mx/PDF/65/2a022/nov/20221124-VII.pdf Zone America/Ojinaga -6:57:40 - LMT 1922 Jan 1 7:00u - -7:00 - MST 1927 Jun 10 23:00 + -7:00 - MST 1927 Jun 10 -6:00 - CST 1930 Nov 15 -7:00 Mexico M%sT 1932 Apr 1 -6:00 - CST 1996 @@ -2685,7 +2745,7 @@ Zone America/Ojinaga -6:57:40 - LMT 1922 Jan 1 7:00u -6:00 US C%sT # Chihuahua (away from US border) Zone America/Chihuahua -7:04:20 - LMT 1922 Jan 1 7:00u - -7:00 - MST 1927 Jun 10 23:00 + -7:00 - MST 1927 Jun 10 -6:00 - CST 1930 Nov 15 -7:00 Mexico M%sT 1932 Apr 1 -6:00 - CST 1996 @@ -2695,23 +2755,21 @@ Zone America/Chihuahua -7:04:20 - LMT 1922 Jan 1 7:00u -6:00 - CST # Sonora Zone America/Hermosillo -7:23:52 - LMT 1922 Jan 1 7:00u - -7:00 - MST 1927 Jun 10 23:00 + -7:00 - MST 1927 Jun 10 -6:00 - CST 1930 Nov 15 -7:00 Mexico M%sT 1932 Apr 1 -6:00 - CST 1942 Apr 24 - -7:00 - MST 1949 Jan 14 - -8:00 - PST 1970 + -7:00 - MST 1996 -7:00 Mexico M%sT 1999 -7:00 - MST # Baja California Sur, Nayarit (except Bahía de Banderas), Sinaloa Zone America/Mazatlan -7:05:40 - LMT 1922 Jan 1 7:00u - -7:00 - MST 1927 Jun 10 23:00 + -7:00 - MST 1927 Jun 10 -6:00 - CST 1930 Nov 15 -7:00 Mexico M%sT 1932 Apr 1 -6:00 - CST 1942 Apr 24 - -7:00 - MST 1949 Jan 14 - -8:00 - PST 1970 + -7:00 - MST 1970 -7:00 Mexico M%sT # Bahía de Banderas @@ -2744,27 +2802,32 @@ Zone America/Mazatlan -7:05:40 - LMT 1922 Jan 1 7:00u # Use "Bahia_Banderas" to keep the name to fourteen characters. Zone America/Bahia_Banderas -7:01:00 - LMT 1922 Jan 1 7:00u - -7:00 - MST 1927 Jun 10 23:00 + -7:00 - MST 1927 Jun 10 -6:00 - CST 1930 Nov 15 -7:00 Mexico M%sT 1932 Apr 1 -6:00 - CST 1942 Apr 24 - -7:00 - MST 1949 Jan 14 - -8:00 - PST 1970 + -7:00 - MST 1970 -7:00 Mexico M%sT 2010 Apr 4 2:00 -6:00 Mexico C%sT # Baja California Zone America/Tijuana -7:48:04 - LMT 1922 Jan 1 7:00u -7:00 - MST 1924 - -8:00 - PST 1927 Jun 10 23:00 + -8:00 - PST 1927 Jun 10 -7:00 - MST 1930 Nov 15 -8:00 - PST 1931 Apr 1 -8:00 1:00 PDT 1931 Sep 30 -8:00 - PST 1942 Apr 24 -8:00 1:00 PWT 1945 Aug 14 23:00u - -8:00 1:00 PPT 1945 Nov 12 # Peace + -8:00 1:00 PPT 1945 Nov 15 # Peace -8:00 - PST 1948 Apr 5 -8:00 1:00 PDT 1949 Jan 14 + -8:00 - PST 1950 May 1 + -8:00 1:00 PDT 1950 Sep 24 + -8:00 - PST 1951 Apr 29 2:00 + -8:00 1:00 PDT 1951 Sep 30 2:00 + -8:00 - PST 1952 Apr 27 2:00 + -8:00 1:00 PDT 1952 Sep 28 2:00 -8:00 - PST 1954 -8:00 CA P%sT 1961 -8:00 - PST 1976 @@ -3573,8 +3636,8 @@ Zone America/Puerto_Rico -4:24:25 - LMT 1899 Mar 28 12:00 # San Juan # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Miquelon -3:44:40 - LMT 1911 Jun 15 # St Pierre -4:00 - AST 1980 May - -3:00 - -03 1987 - -3:00 Canada -03/-02 + -3:00 - %z 1987 + -3:00 Canada %z # Turks and Caicos # diff --git a/src/java.base/share/data/tzdata/southamerica b/src/java.base/share/data/tzdata/southamerica index d77acc08857..3824202546a 100644 --- a/src/java.base/share/data/tzdata/southamerica +++ b/src/java.base/share/data/tzdata/southamerica @@ -425,11 +425,11 @@ Rule Arg 2008 only - Oct Sun>=15 0:00 1:00 - Zone America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May # Córdoba Mean Time - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 Arg -03/-02 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 Arg %z # # Córdoba (CB), Santa Fe (SF), Entre Ríos (ER), Corrientes (CN), Misiones (MN), # Chaco (CC), Formosa (FM), Santiago del Estero (SE) @@ -444,120 +444,120 @@ Zone America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 Zone America/Argentina/Cordoba -4:16:48 - LMT 1894 Oct 31 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 3 - -4:00 - -04 1991 Oct 20 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 Arg -03/-02 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1991 Mar 3 + -4:00 - %z 1991 Oct 20 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 Arg %z # # Salta (SA), La Pampa (LP), Neuquén (NQ), Rio Negro (RN) Zone America/Argentina/Salta -4:21:40 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 3 - -4:00 - -04 1991 Oct 20 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1991 Mar 3 + -4:00 - %z 1991 Oct 20 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # # Tucumán (TM) Zone America/Argentina/Tucuman -4:20:52 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 3 - -4:00 - -04 1991 Oct 20 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 Jun 1 - -4:00 - -04 2004 Jun 13 - -3:00 Arg -03/-02 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1991 Mar 3 + -4:00 - %z 1991 Oct 20 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 - %z 2004 Jun 1 + -4:00 - %z 2004 Jun 13 + -3:00 Arg %z # # La Rioja (LR) Zone America/Argentina/La_Rioja -4:27:24 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 1 - -4:00 - -04 1991 May 7 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 Jun 1 - -4:00 - -04 2004 Jun 20 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1991 Mar 1 + -4:00 - %z 1991 May 7 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 - %z 2004 Jun 1 + -4:00 - %z 2004 Jun 20 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # # San Juan (SJ) Zone America/Argentina/San_Juan -4:34:04 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 1 - -4:00 - -04 1991 May 7 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 May 31 - -4:00 - -04 2004 Jul 25 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1991 Mar 1 + -4:00 - %z 1991 May 7 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 - %z 2004 May 31 + -4:00 - %z 2004 Jul 25 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # # Jujuy (JY) Zone America/Argentina/Jujuy -4:21:12 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1990 Mar 4 - -4:00 - -04 1990 Oct 28 - -4:00 1:00 -03 1991 Mar 17 - -4:00 - -04 1991 Oct 6 - -3:00 1:00 -02 1992 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1990 Mar 4 + -4:00 - %z 1990 Oct 28 + -4:00 1:00 %z 1991 Mar 17 + -4:00 - %z 1991 Oct 6 + -3:00 1:00 %z 1992 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # # Catamarca (CT), Chubut (CH) Zone America/Argentina/Catamarca -4:23:08 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 3 - -4:00 - -04 1991 Oct 20 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 Jun 1 - -4:00 - -04 2004 Jun 20 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1991 Mar 3 + -4:00 - %z 1991 Oct 20 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 - %z 2004 Jun 1 + -4:00 - %z 2004 Jun 20 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # # Mendoza (MZ) Zone America/Argentina/Mendoza -4:35:16 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1990 Mar 4 - -4:00 - -04 1990 Oct 15 - -4:00 1:00 -03 1991 Mar 1 - -4:00 - -04 1991 Oct 15 - -4:00 1:00 -03 1992 Mar 1 - -4:00 - -04 1992 Oct 18 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 May 23 - -4:00 - -04 2004 Sep 26 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1990 Mar 4 + -4:00 - %z 1990 Oct 15 + -4:00 1:00 %z 1991 Mar 1 + -4:00 - %z 1991 Oct 15 + -4:00 1:00 %z 1992 Mar 1 + -4:00 - %z 1992 Oct 18 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 - %z 2004 May 23 + -4:00 - %z 2004 Sep 26 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # # San Luis (SL) @@ -567,53 +567,53 @@ Rule SanLuis 2007 2008 - Oct Sun>=8 0:00 1:00 - Zone America/Argentina/San_Luis -4:25:24 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1990 - -3:00 1:00 -02 1990 Mar 14 - -4:00 - -04 1990 Oct 15 - -4:00 1:00 -03 1991 Mar 1 - -4:00 - -04 1991 Jun 1 - -3:00 - -03 1999 Oct 3 - -4:00 1:00 -03 2000 Mar 3 - -3:00 - -03 2004 May 31 - -4:00 - -04 2004 Jul 25 - -3:00 Arg -03/-02 2008 Jan 21 - -4:00 SanLuis -04/-03 2009 Oct 11 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1990 + -3:00 1:00 %z 1990 Mar 14 + -4:00 - %z 1990 Oct 15 + -4:00 1:00 %z 1991 Mar 1 + -4:00 - %z 1991 Jun 1 + -3:00 - %z 1999 Oct 3 + -4:00 1:00 %z 2000 Mar 3 + -3:00 - %z 2004 May 31 + -4:00 - %z 2004 Jul 25 + -3:00 Arg %z 2008 Jan 21 + -4:00 SanLuis %z 2009 Oct 11 + -3:00 - %z # # Santa Cruz (SC) Zone America/Argentina/Rio_Gallegos -4:36:52 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 Jun 1 - -4:00 - -04 2004 Jun 20 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 - %z 2004 Jun 1 + -4:00 - %z 2004 Jun 20 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # # Tierra del Fuego, Antártida e Islas del Atlántico Sur (TF) Zone America/Argentina/Ushuaia -4:33:12 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 May 30 - -4:00 - -04 2004 Jun 20 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 - %z 2004 May 30 + -4:00 - %z 2004 Jun 20 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # Bolivia # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/La_Paz -4:32:36 - LMT 1890 -4:32:36 - CMT 1931 Oct 15 # Calamarca MT -4:32:36 1:00 BST 1932 Mar 21 # Bolivia ST - -4:00 - -04 + -4:00 - %z # Brazil @@ -984,12 +984,12 @@ Rule Brazil 2018 only - Nov Sun>=1 0:00 1:00 - # # Fernando de Noronha (administratively part of PE) Zone America/Noronha -2:09:40 - LMT 1914 - -2:00 Brazil -02/-01 1990 Sep 17 - -2:00 - -02 1999 Sep 30 - -2:00 Brazil -02/-01 2000 Oct 15 - -2:00 - -02 2001 Sep 13 - -2:00 Brazil -02/-01 2002 Oct 1 - -2:00 - -02 + -2:00 Brazil %z 1990 Sep 17 + -2:00 - %z 1999 Sep 30 + -2:00 Brazil %z 2000 Oct 15 + -2:00 - %z 2001 Sep 13 + -2:00 Brazil %z 2002 Oct 1 + -2:00 - %z # Other Atlantic islands have no permanent settlement. # These include Trindade and Martim Vaz (administratively part of ES), # Rocas Atoll (RN), and the St Peter and St Paul Archipelago (PE). @@ -1002,119 +1002,119 @@ Zone America/Noronha -2:09:40 - LMT 1914 # In the north a very small part from the river Javary (now Jari I guess, # the border with Amapá) to the Amazon, then to the Xingu. Zone America/Belem -3:13:56 - LMT 1914 - -3:00 Brazil -03/-02 1988 Sep 12 - -3:00 - -03 + -3:00 Brazil %z 1988 Sep 12 + -3:00 - %z # # west Pará (PA) # West Pará includes Altamira, Óbidos, Prainha, Oriximiná, and Santarém. Zone America/Santarem -3:38:48 - LMT 1914 - -4:00 Brazil -04/-03 1988 Sep 12 - -4:00 - -04 2008 Jun 24 0:00 - -3:00 - -03 + -4:00 Brazil %z 1988 Sep 12 + -4:00 - %z 2008 Jun 24 0:00 + -3:00 - %z # # Maranhão (MA), Piauí (PI), Ceará (CE), Rio Grande do Norte (RN), # Paraíba (PB) Zone America/Fortaleza -2:34:00 - LMT 1914 - -3:00 Brazil -03/-02 1990 Sep 17 - -3:00 - -03 1999 Sep 30 - -3:00 Brazil -03/-02 2000 Oct 22 - -3:00 - -03 2001 Sep 13 - -3:00 Brazil -03/-02 2002 Oct 1 - -3:00 - -03 + -3:00 Brazil %z 1990 Sep 17 + -3:00 - %z 1999 Sep 30 + -3:00 Brazil %z 2000 Oct 22 + -3:00 - %z 2001 Sep 13 + -3:00 Brazil %z 2002 Oct 1 + -3:00 - %z # # Pernambuco (PE) (except Atlantic islands) Zone America/Recife -2:19:36 - LMT 1914 - -3:00 Brazil -03/-02 1990 Sep 17 - -3:00 - -03 1999 Sep 30 - -3:00 Brazil -03/-02 2000 Oct 15 - -3:00 - -03 2001 Sep 13 - -3:00 Brazil -03/-02 2002 Oct 1 - -3:00 - -03 + -3:00 Brazil %z 1990 Sep 17 + -3:00 - %z 1999 Sep 30 + -3:00 Brazil %z 2000 Oct 15 + -3:00 - %z 2001 Sep 13 + -3:00 Brazil %z 2002 Oct 1 + -3:00 - %z # # Tocantins (TO) Zone America/Araguaina -3:12:48 - LMT 1914 - -3:00 Brazil -03/-02 1990 Sep 17 - -3:00 - -03 1995 Sep 14 - -3:00 Brazil -03/-02 2003 Sep 24 - -3:00 - -03 2012 Oct 21 - -3:00 Brazil -03/-02 2013 Sep - -3:00 - -03 + -3:00 Brazil %z 1990 Sep 17 + -3:00 - %z 1995 Sep 14 + -3:00 Brazil %z 2003 Sep 24 + -3:00 - %z 2012 Oct 21 + -3:00 Brazil %z 2013 Sep + -3:00 - %z # # Alagoas (AL), Sergipe (SE) Zone America/Maceio -2:22:52 - LMT 1914 - -3:00 Brazil -03/-02 1990 Sep 17 - -3:00 - -03 1995 Oct 13 - -3:00 Brazil -03/-02 1996 Sep 4 - -3:00 - -03 1999 Sep 30 - -3:00 Brazil -03/-02 2000 Oct 22 - -3:00 - -03 2001 Sep 13 - -3:00 Brazil -03/-02 2002 Oct 1 - -3:00 - -03 + -3:00 Brazil %z 1990 Sep 17 + -3:00 - %z 1995 Oct 13 + -3:00 Brazil %z 1996 Sep 4 + -3:00 - %z 1999 Sep 30 + -3:00 Brazil %z 2000 Oct 22 + -3:00 - %z 2001 Sep 13 + -3:00 Brazil %z 2002 Oct 1 + -3:00 - %z # # Bahia (BA) # There are too many Salvadors elsewhere, so use America/Bahia instead # of America/Salvador. Zone America/Bahia -2:34:04 - LMT 1914 - -3:00 Brazil -03/-02 2003 Sep 24 - -3:00 - -03 2011 Oct 16 - -3:00 Brazil -03/-02 2012 Oct 21 - -3:00 - -03 + -3:00 Brazil %z 2003 Sep 24 + -3:00 - %z 2011 Oct 16 + -3:00 Brazil %z 2012 Oct 21 + -3:00 - %z # # Goiás (GO), Distrito Federal (DF), Minas Gerais (MG), # Espírito Santo (ES), Rio de Janeiro (RJ), São Paulo (SP), Paraná (PR), # Santa Catarina (SC), Rio Grande do Sul (RS) Zone America/Sao_Paulo -3:06:28 - LMT 1914 - -3:00 Brazil -03/-02 1963 Oct 23 0:00 - -3:00 1:00 -02 1964 - -3:00 Brazil -03/-02 + -3:00 Brazil %z 1963 Oct 23 0:00 + -3:00 1:00 %z 1964 + -3:00 Brazil %z # # Mato Grosso do Sul (MS) Zone America/Campo_Grande -3:38:28 - LMT 1914 - -4:00 Brazil -04/-03 + -4:00 Brazil %z # # Mato Grosso (MT) Zone America/Cuiaba -3:44:20 - LMT 1914 - -4:00 Brazil -04/-03 2003 Sep 24 - -4:00 - -04 2004 Oct 1 - -4:00 Brazil -04/-03 + -4:00 Brazil %z 2003 Sep 24 + -4:00 - %z 2004 Oct 1 + -4:00 Brazil %z # # Rondônia (RO) Zone America/Porto_Velho -4:15:36 - LMT 1914 - -4:00 Brazil -04/-03 1988 Sep 12 - -4:00 - -04 + -4:00 Brazil %z 1988 Sep 12 + -4:00 - %z # # Roraima (RR) Zone America/Boa_Vista -4:02:40 - LMT 1914 - -4:00 Brazil -04/-03 1988 Sep 12 - -4:00 - -04 1999 Sep 30 - -4:00 Brazil -04/-03 2000 Oct 15 - -4:00 - -04 + -4:00 Brazil %z 1988 Sep 12 + -4:00 - %z 1999 Sep 30 + -4:00 Brazil %z 2000 Oct 15 + -4:00 - %z # # east Amazonas (AM): Boca do Acre, Jutaí, Manaus, Floriano Peixoto # The great circle line from Tabatinga to Porto Acre divides # east from west Amazonas. Zone America/Manaus -4:00:04 - LMT 1914 - -4:00 Brazil -04/-03 1988 Sep 12 - -4:00 - -04 1993 Sep 28 - -4:00 Brazil -04/-03 1994 Sep 22 - -4:00 - -04 + -4:00 Brazil %z 1988 Sep 12 + -4:00 - %z 1993 Sep 28 + -4:00 Brazil %z 1994 Sep 22 + -4:00 - %z # # west Amazonas (AM): Atalaia do Norte, Boca do Maoco, Benjamin Constant, # Eirunepé, Envira, Ipixuna Zone America/Eirunepe -4:39:28 - LMT 1914 - -5:00 Brazil -05/-04 1988 Sep 12 - -5:00 - -05 1993 Sep 28 - -5:00 Brazil -05/-04 1994 Sep 22 - -5:00 - -05 2008 Jun 24 0:00 - -4:00 - -04 2013 Nov 10 - -5:00 - -05 + -5:00 Brazil %z 1988 Sep 12 + -5:00 - %z 1993 Sep 28 + -5:00 Brazil %z 1994 Sep 22 + -5:00 - %z 2008 Jun 24 0:00 + -4:00 - %z 2013 Nov 10 + -5:00 - %z # # Acre (AC) Zone America/Rio_Branco -4:31:12 - LMT 1914 - -5:00 Brazil -05/-04 1988 Sep 12 - -5:00 - -05 2008 Jun 24 0:00 - -4:00 - -04 2013 Nov 10 - -5:00 - -05 + -5:00 Brazil %z 1988 Sep 12 + -5:00 - %z 2008 Jun 24 0:00 + -4:00 - %z 2013 Nov 10 + -5:00 - %z # Chile @@ -1382,36 +1382,36 @@ Rule Chile 2023 max - Sep Sun>=2 4:00u 1:00 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Santiago -4:42:45 - LMT 1890 -4:42:45 - SMT 1910 Jan 10 # Santiago Mean Time - -5:00 - -05 1916 Jul 1 + -5:00 - %z 1916 Jul 1 -4:42:45 - SMT 1918 Sep 10 - -4:00 - -04 1919 Jul 1 + -4:00 - %z 1919 Jul 1 -4:42:45 - SMT 1927 Sep 1 - -5:00 Chile -05/-04 1932 Sep 1 - -4:00 - -04 1942 Jun 1 - -5:00 - -05 1942 Aug 1 - -4:00 - -04 1946 Jul 14 24:00 - -4:00 1:00 -03 1946 Aug 28 24:00 # central CL - -5:00 1:00 -04 1947 Mar 31 24:00 - -5:00 - -05 1947 May 21 23:00 - -4:00 Chile -04/-03 + -5:00 Chile %z 1932 Sep 1 + -4:00 - %z 1942 Jun 1 + -5:00 - %z 1942 Aug 1 + -4:00 - %z 1946 Jul 14 24:00 + -4:00 1:00 %z 1946 Aug 28 24:00 # central CL + -5:00 1:00 %z 1947 Mar 31 24:00 + -5:00 - %z 1947 May 21 23:00 + -4:00 Chile %z Zone America/Punta_Arenas -4:43:40 - LMT 1890 -4:42:45 - SMT 1910 Jan 10 - -5:00 - -05 1916 Jul 1 + -5:00 - %z 1916 Jul 1 -4:42:45 - SMT 1918 Sep 10 - -4:00 - -04 1919 Jul 1 + -4:00 - %z 1919 Jul 1 -4:42:45 - SMT 1927 Sep 1 - -5:00 Chile -05/-04 1932 Sep 1 - -4:00 - -04 1942 Jun 1 - -5:00 - -05 1942 Aug 1 - -4:00 - -04 1946 Aug 28 24:00 - -5:00 1:00 -04 1947 Mar 31 24:00 - -5:00 - -05 1947 May 21 23:00 - -4:00 Chile -04/-03 2016 Dec 4 - -3:00 - -03 + -5:00 Chile %z 1932 Sep 1 + -4:00 - %z 1942 Jun 1 + -5:00 - %z 1942 Aug 1 + -4:00 - %z 1946 Aug 28 24:00 + -5:00 1:00 %z 1947 Mar 31 24:00 + -5:00 - %z 1947 May 21 23:00 + -4:00 Chile %z 2016 Dec 4 + -3:00 - %z Zone Pacific/Easter -7:17:28 - LMT 1890 -7:17:28 - EMT 1932 Sep # Easter Mean Time - -7:00 Chile -07/-06 1982 Mar 14 3:00u # Easter Time - -6:00 Chile -06/-05 + -7:00 Chile %z 1982 Mar 14 3:00u # Easter Time + -6:00 Chile %z # # Salas y Gómez Island is uninhabited. # Other Chilean locations, including Juan Fernández Is, Desventuradas Is, @@ -1431,10 +1431,10 @@ Zone Pacific/Easter -7:17:28 - LMT 1890 # # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Antarctica/Palmer 0 - -00 1965 - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1982 May - -4:00 Chile -04/-03 2016 Dec 4 - -3:00 - -03 + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1982 May + -4:00 Chile %z 2016 Dec 4 + -3:00 - %z # Colombia @@ -1453,7 +1453,7 @@ Rule CO 1993 only - Feb 6 24:00 0 - #STDOFF -4:56:16.4 Zone America/Bogota -4:56:16 - LMT 1884 Mar 13 -4:56:16 - BMT 1914 Nov 23 # Bogotá Mean Time - -5:00 CO -05/-04 + -5:00 CO %z # Malpelo, Providencia, San Andres # no information; probably like America/Bogota @@ -1484,10 +1484,10 @@ Rule Ecuador 1993 only - Feb 5 0:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Guayaquil -5:19:20 - LMT 1890 -5:14:00 - QMT 1931 # Quito Mean Time - -5:00 Ecuador -05/-04 + -5:00 Ecuador %z Zone Pacific/Galapagos -5:58:24 - LMT 1931 # Puerto Baquerizo Moreno - -5:00 - -05 1986 - -6:00 Ecuador -06/-05 + -5:00 - %z 1986 + -6:00 Ecuador %z # Falklands @@ -1587,10 +1587,10 @@ Rule Falk 2001 2010 - Sep Sun>=1 2:00 1:00 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Atlantic/Stanley -3:51:24 - LMT 1890 -3:51:24 - SMT 1912 Mar 12 # Stanley Mean Time - -4:00 Falk -04/-03 1983 May - -3:00 Falk -03/-02 1985 Sep 15 - -4:00 Falk -04/-03 2010 Sep 5 2:00 - -3:00 - -03 + -4:00 Falk %z 1983 May + -3:00 Falk %z 1985 Sep 15 + -4:00 Falk %z 2010 Sep 5 2:00 + -3:00 - %z # French Guiana # For the 1911/1912 establishment of standard time in French possessions, see: @@ -1598,8 +1598,8 @@ Zone Atlantic/Stanley -3:51:24 - LMT 1890 # page 752, 18b. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Cayenne -3:29:20 - LMT 1911 Jul 1 - -4:00 - -04 1967 Oct - -3:00 - -03 + -4:00 - %z 1967 Oct + -3:00 - %z # Guyana @@ -1633,10 +1633,10 @@ Zone America/Cayenne -3:29:20 - LMT 1911 Jul 1 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Guyana -3:52:39 - LMT 1911 Aug 1 # Georgetown - -4:00 - -04 1915 Mar 1 - -3:45 - -0345 1975 Aug 1 - -3:00 - -03 1992 Mar 29 1:00 - -4:00 - -04 + -4:00 - %z 1915 Mar 1 + -3:45 - %z 1975 Aug 1 + -3:00 - %z 1992 Mar 29 1:00 + -4:00 - %z # Paraguay # @@ -1734,9 +1734,9 @@ Rule Para 2013 max - Mar Sun>=22 0:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Asuncion -3:50:40 - LMT 1890 -3:50:40 - AMT 1931 Oct 10 # Asunción Mean Time - -4:00 - -04 1972 Oct - -3:00 - -03 1974 Apr - -4:00 Para -04/-03 + -4:00 - %z 1972 Oct + -3:00 - %z 1974 Apr + -4:00 Para %z # Peru # @@ -1763,12 +1763,12 @@ Rule Peru 1994 only - Apr 1 0:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Lima -5:08:12 - LMT 1890 -5:08:36 - LMT 1908 Jul 28 # Lima Mean Time? - -5:00 Peru -05/-04 + -5:00 Peru %z # South Georgia # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Atlantic/South_Georgia -2:26:08 - LMT 1890 # Grytviken - -2:00 - -02 + -2:00 - %z # South Sandwich Is # uninhabited; scientific personnel have wintered @@ -1778,8 +1778,8 @@ Zone Atlantic/South_Georgia -2:26:08 - LMT 1890 # Grytviken Zone America/Paramaribo -3:40:40 - LMT 1911 -3:40:52 - PMT 1935 # Paramaribo Mean Time -3:40:36 - PMT 1945 Oct # The capital moved? - -3:30 - -0330 1984 Oct - -3:00 - -03 + -3:30 - %z 1984 Oct + -3:00 - %z # Uruguay # From Paul Eggert (1993-11-18): @@ -1994,15 +1994,15 @@ Rule Uruguay 2006 2014 - Oct Sun>=1 2:00 1:00 - # This Zone can be simplified once we assume zic %z. Zone America/Montevideo -3:44:51 - LMT 1908 Jun 10 -3:44:51 - MMT 1920 May 1 # Montevideo MT - -4:00 - -04 1923 Oct 1 - -3:30 Uruguay -0330/-03 1942 Dec 14 - -3:00 Uruguay -03/-0230 1960 - -3:00 Uruguay -03/-02 1968 - -3:00 Uruguay -03/-0230 1970 - -3:00 Uruguay -03/-02 1974 - -3:00 Uruguay -03/-0130 1974 Mar 10 - -3:00 Uruguay -03/-0230 1974 Dec 22 - -3:00 Uruguay -03/-02 + -4:00 - %z 1923 Oct 1 + -3:30 Uruguay %z 1942 Dec 14 + -3:00 Uruguay %z 1960 + -3:00 Uruguay %z 1968 + -3:00 Uruguay %z 1970 + -3:00 Uruguay %z 1974 + -3:00 Uruguay %z 1974 Mar 10 + -3:00 Uruguay %z 1974 Dec 22 + -3:00 Uruguay %z # Venezuela # @@ -2036,7 +2036,7 @@ Zone America/Montevideo -3:44:51 - LMT 1908 Jun 10 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Caracas -4:27:44 - LMT 1890 -4:27:40 - CMT 1912 Feb 12 # Caracas Mean Time? - -4:30 - -0430 1965 Jan 1 0:00 - -4:00 - -04 2007 Dec 9 3:00 - -4:30 - -0430 2016 May 1 2:30 - -4:00 - -04 + -4:30 - %z 1965 Jan 1 0:00 + -4:00 - %z 2007 Dec 9 3:00 + -4:30 - %z 2016 May 1 2:30 + -4:00 - %z diff --git a/src/java.base/share/data/tzdata/zone.tab b/src/java.base/share/data/tzdata/zone.tab index 0a01e8777dd..b90ab4e4b25 100644 --- a/src/java.base/share/data/tzdata/zone.tab +++ b/src/java.base/share/data/tzdata/zone.tab @@ -287,8 +287,7 @@ MK +4159+02126 Europe/Skopje ML +1239-00800 Africa/Bamako MM +1647+09610 Asia/Yangon MN +4755+10653 Asia/Ulaanbaatar most of Mongolia -MN +4801+09139 Asia/Hovd Bayan-Olgiy, Govi-Altai, Hovd, Uvs, Zavkhan -MN +4804+11430 Asia/Choibalsan Dornod, Sukhbaatar +MN +4801+09139 Asia/Hovd Bayan-Olgii, Hovd, Uvs MO +221150+1133230 Asia/Macau MP +1512+14545 Pacific/Saipan MQ +1436-06105 America/Martinique diff --git a/test/jdk/java/text/Format/DateFormat/TimeZoneNameTest.java b/test/jdk/java/text/Format/DateFormat/TimeZoneNameTest.java index 8ce11445ca0..e74bd41ddf9 100644 --- a/test/jdk/java/text/Format/DateFormat/TimeZoneNameTest.java +++ b/test/jdk/java/text/Format/DateFormat/TimeZoneNameTest.java @@ -75,7 +75,7 @@ static void initAll() { "N", "GMT", "GMT", "Greenwich Mean Time", "GMT", "Greenwich Mean Time", "N", "Europe/London", "GMT", "Greenwich Mean Time", "BST", "British Summer Time", "N", "Europe/Paris", "CET", "Central European Standard Time", "CEST", "Central European Summer Time", - "N", "WET", "WET", "GMT", "WEST", "GMT+01:00", + "N", "WET", "WET", "Western European Standard Time", "WEST", "Western European Summer Time", "N", "Europe/Berlin", "CET", "Central European Standard Time", "CEST", "Central European Summer Time", "N", "Asia/Jerusalem", "IST", "Israel Standard Time", "IDT", "Israel Daylight Time", "N", "Europe/Helsinki", "EET", "Eastern European Standard Time", "EEST", "Eastern European Summer Time", diff --git a/test/jdk/java/time/tck/java/time/TCKZoneId.java b/test/jdk/java/time/tck/java/time/TCKZoneId.java index a90c9abf48e..a1d0065d39e 100644 --- a/test/jdk/java/time/tck/java/time/TCKZoneId.java +++ b/test/jdk/java/time/tck/java/time/TCKZoneId.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -96,11 +96,11 @@ public class TCKZoneId extends AbstractTCKTest { //----------------------------------------------------------------------- // SHORT_IDS //----------------------------------------------------------------------- - public void test_constant_OLD_IDS_POST_2005() { + public void test_constant_OLD_IDS_POST_2024b() { Map ids = ZoneId.SHORT_IDS; - assertEquals(ids.get("EST"), "-05:00"); - assertEquals(ids.get("MST"), "-07:00"); - assertEquals(ids.get("HST"), "-10:00"); + assertEquals(ids.get("EST"), "America/Panama"); + assertEquals(ids.get("MST"), "America/Phoenix"); + assertEquals(ids.get("HST"), "Pacific/Honolulu"); assertEquals(ids.get("ACT"), "Australia/Darwin"); assertEquals(ids.get("AET"), "Australia/Sydney"); assertEquals(ids.get("AGT"), "America/Argentina/Buenos_Aires"); @@ -129,7 +129,7 @@ public void test_constant_OLD_IDS_POST_2005() { } @Test(expectedExceptions=UnsupportedOperationException.class) - public void test_constant_OLD_IDS_POST_2005_immutable() { + public void test_constant_OLD_IDS_POST_2024b_immutable() { Map ids = ZoneId.SHORT_IDS; ids.clear(); } diff --git a/test/jdk/java/util/TimeZone/OldIDMappingTest.java b/test/jdk/java/util/TimeZone/OldIDMappingTest.java index 8436080b041..9cc5acb87ff 100644 --- a/test/jdk/java/util/TimeZone/OldIDMappingTest.java +++ b/test/jdk/java/util/TimeZone/OldIDMappingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. * 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,6 @@ public class OldIDMappingTest { // Add known new mappings newmap.put("EST", "EST"); newmap.put("MST", "MST"); - newmap.put("HST", "HST"); } public static void main(String[] args) { diff --git a/test/jdk/java/util/TimeZone/TimeZoneData/VERSION b/test/jdk/java/util/TimeZone/TimeZoneData/VERSION index bf027918ce7..f40be22e9ab 100644 --- a/test/jdk/java/util/TimeZone/TimeZoneData/VERSION +++ b/test/jdk/java/util/TimeZone/TimeZoneData/VERSION @@ -1 +1 @@ -tzdata2024a +tzdata2024b diff --git a/test/jdk/java/util/TimeZone/TimeZoneData/aliases.txt b/test/jdk/java/util/TimeZone/TimeZoneData/aliases.txt index 82bad17c553..3217f68b825 100644 --- a/test/jdk/java/util/TimeZone/TimeZoneData/aliases.txt +++ b/test/jdk/java/util/TimeZone/TimeZoneData/aliases.txt @@ -15,6 +15,8 @@ Link America/Rio_Branco Brazil/Acre #= America/Porto_Acre Link America/Noronha Brazil/DeNoronha Link America/Sao_Paulo Brazil/East Link America/Manaus Brazil/West +Link Europe/Brussels CET +Link America/Chicago CST6CDT Link America/Halifax Canada/Atlantic Link America/Winnipeg Canada/Central Link America/Toronto Canada/Eastern @@ -26,6 +28,9 @@ Link America/Whitehorse Canada/Yukon Link America/Santiago Chile/Continental Link Pacific/Easter Chile/EasterIsland Link America/Havana Cuba +Link Europe/Athens EET +Link America/Panama EST +Link America/New_York EST5EDT Link Africa/Cairo Egypt Link Europe/Dublin Eire Link Etc/GMT Etc/GMT+0 @@ -49,6 +54,9 @@ Link America/Jamaica Jamaica Link Asia/Tokyo Japan Link Pacific/Kwajalein Kwajalein Link Africa/Tripoli Libya +Link Europe/Brussels MET +Link America/Phoenix MST +Link America/Denver MST7MDT Link America/Tijuana Mexico/BajaNorte Link America/Mazatlan Mexico/BajaSur Link America/Mexico_City Mexico/General @@ -212,6 +220,7 @@ Link America/Denver America/Shiprock Link America/Toronto America/Thunder_Bay Link America/Edmonton America/Yellowknife Link Pacific/Auckland Antarctica/South_Pole +Link Asia/Ulaanbaatar Asia/Choibalsan Link Asia/Shanghai Asia/Chongqing Link Asia/Shanghai Asia/Harbin Link Asia/Urumqi Asia/Kashgar @@ -226,6 +235,7 @@ Link Europe/Kyiv Europe/Zaporozhye Link Pacific/Kanton Pacific/Enderbury Link Pacific/Honolulu Pacific/Johnston Link Pacific/Port_Moresby Pacific/Yap +Link Europe/Lisbon WET Link Africa/Nairobi Africa/Asmera #= Africa/Asmara Link America/Nuuk America/Godthab Link Asia/Ashgabat Asia/Ashkhabad @@ -243,5 +253,7 @@ Link Asia/Ulaanbaatar Asia/Ulan_Bator Link Atlantic/Faroe Atlantic/Faeroe Link Europe/Kyiv Europe/Kiev Link Asia/Nicosia Europe/Nicosia +Link Pacific/Honolulu HST +Link America/Los_Angeles PST8PDT Link Pacific/Guadalcanal Pacific/Ponape #= Pacific/Pohnpei Link Pacific/Port_Moresby Pacific/Truk #= Pacific/Chuuk diff --git a/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt b/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt index 2bcccf8f880..e7b6ea8bf3c 100644 --- a/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt +++ b/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt @@ -122,11 +122,6 @@ Australia/Lindeman AEST AEDT Australia/Melbourne AEST AEDT Australia/Perth AWST AWDT Australia/Sydney AEST AEDT -CET CET CEST -CST6CDT CST CDT -EET EET EEST -EST EST -EST5EDT EST EDT Europe/Andorra CET CEST Europe/Athens EET EEST Europe/Belgrade CET CEST @@ -159,13 +154,7 @@ Europe/Vilnius EET EEST Europe/Volgograd MSK Europe/Warsaw CET CEST Europe/Zurich CET CEST -HST HST -MET MET MEST -MST MST -MST7MDT MST MDT -PST8PDT PST PDT Pacific/Auckland NZST NZDT Pacific/Guam ChST Pacific/Honolulu HST Pacific/Pago_Pago SST -WET WET WEST diff --git a/test/jdk/sun/util/calendar/zi/TestZoneInfo310.java b/test/jdk/sun/util/calendar/zi/TestZoneInfo310.java index 1ac4b01f145..0b6570b74dc 100644 --- a/test/jdk/sun/util/calendar/zi/TestZoneInfo310.java +++ b/test/jdk/sun/util/calendar/zi/TestZoneInfo310.java @@ -280,5 +280,4 @@ private static ZoneInfoOld toZoneInfoOld(TimeZone tz) throws Exception { willGMTOffsetChange.getBoolean(tz)); } - } diff --git a/test/jdk/sun/util/calendar/zi/ZoneInfoOld.java b/test/jdk/sun/util/calendar/zi/ZoneInfoOld.java index 40f9cb9056c..2219814f8ea 100644 --- a/test/jdk/sun/util/calendar/zi/ZoneInfoOld.java +++ b/test/jdk/sun/util/calendar/zi/ZoneInfoOld.java @@ -93,9 +93,9 @@ public class ZoneInfoOld extends TimeZone { } // IDs having conflicting data between Olson and JDK 1.1 - static final String[] conflictingIDs = { - "EST", "MST", "HST" - }; + static final Map conflictingIDs = Map.of( + "EST", "America/Panama", + "MST", "America/Phoenix"); private static final CalendarSystem gcal = CalendarSystem.getGregorianCalendar(); @@ -843,10 +843,8 @@ public synchronized static Map getAliasTable() { aliases = ZoneInfoFile.getZoneAliases(); if (aliases != null) { if (!USE_OLDMAPPING) { - // Remove the conflicting IDs from the alias table. - for (String key : conflictingIDs) { - aliases.remove(key); - } + // Replace old mappings from `jdk11_backward` + aliases.putAll(conflictingIDs); } aliasTable = new SoftReference>(aliases); } diff --git a/test/jdk/sun/util/resources/TimeZone/Bug4848242.java b/test/jdk/sun/util/resources/TimeZone/Bug4848242.java index 99f66340316..68962f135d0 100644 --- a/test/jdk/sun/util/resources/TimeZone/Bug4848242.java +++ b/test/jdk/sun/util/resources/TimeZone/Bug4848242.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * 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,6 @@ public class Bug4848242 { public static void main(String[] args) { - getTzInfo("de", "DE"); getTzInfo("es", "ES"); getTzInfo("fr", "FR"); getTzInfo("it", "IT"); @@ -46,7 +45,6 @@ static void getTzInfo(String langName, String locName) { Locale tzLocale = Locale.of(langName, locName); TimeZone euroTz = TimeZone.getTimeZone("MET"); - System.out.println("Locale is " + langName + "_" + locName); if ( euroTz.getID().equalsIgnoreCase("GMT") ) { @@ -56,13 +54,13 @@ static void getTzInfo(String langName, String locName) // get the timezone info System.out.println(euroTz.getDisplayName(false, TimeZone.SHORT, tzLocale)); - if(!euroTz.getDisplayName(false, TimeZone.SHORT, tzLocale).equals("MET")) - throw new RuntimeException("Timezone name is incorrect (should be MET)\n"); + if (!euroTz.getDisplayName(false, TimeZone.SHORT, tzLocale).equals("CET")) + throw new RuntimeException("Timezone name is incorrect (should be CET)\n"); System.out.println(euroTz.getDisplayName(false, TimeZone.LONG, tzLocale)); System.out.println(euroTz.getDisplayName(true, TimeZone.SHORT, tzLocale)); - if(!euroTz.getDisplayName(true, TimeZone.SHORT, tzLocale).equals("MEST")) - throw new RuntimeException("Summer timezone name is incorrect (should be MEST)\n"); + if (!euroTz.getDisplayName(true, TimeZone.SHORT, tzLocale).equals("CEST")) + throw new RuntimeException("Summer timezone name is incorrect (should be CEST)\n"); System.out.println(euroTz.getDisplayName(true, TimeZone.LONG, tzLocale) + "\n"); } From c81aa7551c37cc025c9054db08472b8abb2cbcb5 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Wed, 16 Oct 2024 10:17:47 +0000 Subject: [PATCH 76/95] 8331051: Add an `@since` checker test for `java.base` module Reviewed-by: jlahoda, jjg --- test/jdk/TEST.groups | 9 +- test/jdk/tools/sincechecker/SinceChecker.java | 948 ++++++++++++++++++ .../java_base/CheckSince_javaBase.java | 34 + 3 files changed, 989 insertions(+), 2 deletions(-) create mode 100644 test/jdk/tools/sincechecker/SinceChecker.java create mode 100644 test/jdk/tools/sincechecker/modules/java_base/CheckSince_javaBase.java diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups index d51fcec733b..e7ee8990f94 100644 --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -1,4 +1,4 @@ -# Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. # 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,8 @@ tier3 = \ :jdk_svc \ -:jdk_svc_sanity \ -:svc_tools \ - :jdk_jpackage + :jdk_jpackage \ + :jdk_since_checks # Everything not in other tiers tier4 = \ @@ -666,3 +667,7 @@ jdk_containers_extended = \ jdk_core_no_security = \ :jdk_core \ -:jdk_security + +# Set of tests for `@since` checks in source code documentation +jdk_since_checks = \ + tools/sincechecker/modules/java_base/CheckSince_javaBase.java diff --git a/test/jdk/tools/sincechecker/SinceChecker.java b/test/jdk/tools/sincechecker/SinceChecker.java new file mode 100644 index 00000000000..860db6a2798 --- /dev/null +++ b/test/jdk/tools/sincechecker/SinceChecker.java @@ -0,0 +1,948 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.Runtime.Version; +import java.net.URI; +import java.nio.file.*; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import javax.lang.model.element.*; +import javax.lang.model.util.ElementFilter; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import javax.tools.*; +import javax.tools.JavaFileManager.Location; +import com.sun.source.tree.*; +import com.sun.source.util.JavacTask; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; +import com.sun.tools.javac.api.JavacTaskImpl; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.util.Pair; +import jtreg.SkippedException; + +/* +This checker checks the values of the `@since` tag found in the documentation comment for an element against +the release in which the element first appeared. +The source code containing the documentation comments is read from `src.zip` in the release of JDK used to run the test. +The releases used to determine the expected value of `@since` tags are taken from the historical data built into `javac`. + +The `@since` checker works as a two-step process: +In the first step, we process JDKs 9-current, only classfiles, + producing a map ` => ``. + - "version(s)", because we handle versioning of Preview API, so there may be two versions + (we use a class with two fields for preview and stable), + one when it was introduced as a preview, and one when it went out of preview. More on that below. + - For each Element, we compute the unique ID, look into the map, and if there's nothing, + record the current version as the originating version. + - At the end of this step we have a map of the Real since values + +In the second step, we look at "effective" `@since` tags in the mainline sources, from `src.zip` + (if the test run doesn't have it, we throw a `jtreg.SkippedException`) + - We only check the specific MODULE whose name was passed as an argument in the test. + In that module, we look for unqualified exports and test those packages. + - The `@since` checker verifies that for every API element, the real since value and + the effective since value are the same, and reports an error if they are not. + +Important note : We only check code written since JDK 9 as the releases used to determine the expected value + of @since tags are taken from the historical data built into javac which only goes back that far + +note on rules for Real and effective `@since: + +Real since value of an API element is computed as the oldest release in which the given API element was introduced. +That is: +- for modules, packages, classes and interfaces, the release in which the element with the given qualified name was introduced +- for constructors, the release in which the constructor with the given VM descriptor was introduced +- for methods and fields, the release in which the given method or field with the given VM descriptor became a member + of its enclosing class or interface, whether direct or inherited + +Effective since value of an API element is computed as follows: +- if the given element has a @since tag in its javadoc, it is used +- in all other cases, return the effective since value of the enclosing element + + +Special Handling for preview method, as per JEP 12: +- When an element is still marked as preview, the `@since` should be the first JDK release where the element was added. +- If the element is no longer marked as preview, the `@since` should be the first JDK release where it was no longer preview. + +note on legacy preview: Until JDK 14, the preview APIs were not marked in any machine-understandable way. + It was deprecated, and had a comment in the javadoc. + and the use of `@PreviewFeature` only became standard in JDK 17. + So the checker has an explicit knowledge of these preview elements. + +note: The `` for methods looks like + `method: .()`. +it is somewhat inspired from the VM Method Descriptors. But we use the erased return so that methods +that were later generified remain the same. + +usage: the checker is run from a module specific test + `@run main SinceChecker [--exclude package1,package2 | --exclude package1 package2]` +*/ + +public class SinceChecker { + private final Map> LEGACY_PREVIEW_METHODS = new HashMap<>(); + private final Map classDictionary = new HashMap<>(); + private final JavaCompiler tool; + private int errorCount = 0; + + // packages to skip during the test + private static final Set EXCLUDE_LIST = new HashSet<>(); + + public static class IntroducedIn { + public String introducedPreview; + public String introducedStable; + } + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + throw new IllegalArgumentException("Test module not specified"); + } + String moduleName = args[0]; + boolean excludeFlag = false; + + for (int i = 1; i < args.length; i++) { + if ("--exclude".equals(args[i])) { + excludeFlag = true; + continue; + } + + if (excludeFlag) { + if (args[i].contains(",")) { + EXCLUDE_LIST.addAll(Arrays.asList(args[i].split(","))); + } else { + EXCLUDE_LIST.add(args[i]); + } + } + } + + SinceChecker sinceCheckerTestHelper = new SinceChecker(moduleName); + sinceCheckerTestHelper.checkModule(moduleName); + } + + private void error(String message) { + System.err.println(message); + errorCount++; + } + + private SinceChecker(String moduleName) throws IOException { + tool = ToolProvider.getSystemJavaCompiler(); + for (int i = 9; i <= Runtime.version().feature(); i++) { + DiagnosticListener noErrors = d -> { + if (!d.getCode().equals("compiler.err.module.not.found")) { + error(d.getMessage(null)); + } + }; + JavacTask ct = (JavacTask) tool.getTask(null, + null, + noErrors, + List.of("--add-modules", moduleName, "--release", String.valueOf(i)), + null, + Collections.singletonList(SimpleJavaFileObject.forSource(URI.create("myfo:/Test.java"), ""))); + ct.analyze(); + + String version = String.valueOf(i); + Elements elements = ct.getElements(); + elements.getModuleElement("java.base"); // forces module graph to be instantiated + elements.getAllModuleElements().forEach(me -> + processModuleElement(me, version, ct)); + } + } + + private void processModuleElement(ModuleElement moduleElement, String releaseVersion, JavacTask ct) { + processElement(moduleElement, moduleElement, ct.getTypes(), releaseVersion); + for (ModuleElement.ExportsDirective ed : ElementFilter.exportsIn(moduleElement.getDirectives())) { + if (ed.getTargetModules() == null) { + processPackageElement(ed.getPackage(), releaseVersion, ct); + } + } + } + + private void processPackageElement(PackageElement pe, String releaseVersion, JavacTask ct) { + processElement(pe, pe, ct.getTypes(), releaseVersion); + List typeElements = ElementFilter.typesIn(pe.getEnclosedElements()); + for (TypeElement te : typeElements) { + processClassElement(te, releaseVersion, ct.getTypes(), ct.getElements()); + } + } + + /// JDK documentation only contains public and protected declarations + private boolean isDocumented(Element te) { + Set mod = te.getModifiers(); + return mod.contains(Modifier.PUBLIC) || mod.contains(Modifier.PROTECTED); + } + + private boolean isMember(Element e) { + var kind = e.getKind(); + return kind.isField() || switch (kind) { + case METHOD, CONSTRUCTOR -> true; + default -> false; + }; + } + + private void processClassElement(TypeElement te, String version, Types types, Elements elements) { + if (!isDocumented(te)) { + return; + } + processElement(te.getEnclosingElement(), te, types, version); + elements.getAllMembers(te).stream() + .filter(this::isDocumented) + .filter(this::isMember) + .forEach(element -> processElement(te, element, types, version)); + te.getEnclosedElements().stream() + .filter(element -> element.getKind().isDeclaredType()) + .map(TypeElement.class::cast) + .forEach(nestedClass -> processClassElement(nestedClass, version, types, elements)); + } + + private void processElement(Element explicitOwner, Element element, Types types, String version) { + String uniqueId = getElementName(explicitOwner, element, types); + IntroducedIn introduced = classDictionary.computeIfAbsent(uniqueId, _ -> new IntroducedIn()); + if (isPreview(element, uniqueId, version)) { + if (introduced.introducedPreview == null) { + introduced.introducedPreview = version; + } + } else { + if (introduced.introducedStable == null) { + introduced.introducedStable = version; + } + } + } + + private boolean isPreview(Element el, String uniqueId, String currentVersion) { + while (el != null) { + Symbol s = (Symbol) el; + if ((s.flags() & Flags.PREVIEW_API) != 0) { + return true; + } + el = el.getEnclosingElement(); + } + + return LEGACY_PREVIEW_METHODS.getOrDefault(currentVersion, Set.of()) + .contains(uniqueId); + } + + private void checkModule(String moduleName) throws Exception { + Path home = Paths.get(System.getProperty("java.home")); + Path srcZip = home.resolve("lib").resolve("src.zip"); + if (Files.notExists(srcZip)) { + //possibly running over an exploded JDK build, attempt to find a + //co-located full JDK image with src.zip: + Path testJdk = Paths.get(System.getProperty("test.jdk")); + srcZip = testJdk.getParent().resolve("images").resolve("jdk").resolve("lib").resolve("src.zip"); + } + if (!Files.isReadable(srcZip)) { + throw new SkippedException("Skipping Test because src.zip wasn't found or couldn't be read"); + } + URI uri = URI.create("jar:" + srcZip.toUri()); + try (FileSystem zipFO = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + Path root = zipFO.getRootDirectories().iterator().next(); + Path moduleDirectory = root.resolve(moduleName); + try (StandardJavaFileManager fm = + tool.getStandardFileManager(null, null, null)) { + JavacTask ct = (JavacTask) tool.getTask(null, + fm, + null, + List.of("--add-modules", moduleName, "-d", "."), + null, + Collections.singletonList(SimpleJavaFileObject.forSource(URI.create("myfo:/Test.java"), ""))); + ct.analyze(); + Elements elements = ct.getElements(); + elements.getModuleElement("java.base"); + try (EffectiveSourceSinceHelper javadocHelper = EffectiveSourceSinceHelper.create(ct, List.of(root), this)) { + processModuleCheck(elements.getModuleElement(moduleName), ct, moduleDirectory, javadocHelper); + } catch (Exception e) { + e.printStackTrace(); + error("Initiating javadocHelper Failed " + e); + } + if (errorCount > 0) { + throw new Exception("The `@since` checker found " + errorCount + " problems"); + } + } + } + } + + private boolean isExcluded(ModuleElement.ExportsDirective ed ){ + return EXCLUDE_LIST.stream().anyMatch(excludePackage -> + ed.getPackage().toString().equals(excludePackage) || + ed.getPackage().toString().startsWith(excludePackage + ".")); + } + + private void processModuleCheck(ModuleElement moduleElement, JavacTask ct, Path moduleDirectory, EffectiveSourceSinceHelper javadocHelper) { + if (moduleElement == null) { + error("Module element: was null because `elements.getModuleElement(moduleName)` returns null." + + "fixes are needed for this Module"); + } + String moduleVersion = getModuleVersionFromFile(moduleDirectory); + checkModuleOrPackage(javadocHelper, moduleVersion, moduleElement, ct, "Module: "); + for (ModuleElement.ExportsDirective ed : ElementFilter.exportsIn(moduleElement.getDirectives())) { + if (ed.getTargetModules() == null) { + String packageVersion = getPackageVersionFromFile(moduleDirectory, ed); + if (packageVersion != null && !isExcluded(ed)) { + checkModuleOrPackage(javadocHelper, packageVersion, ed.getPackage(), ct, "Package: "); + analyzePackageCheck(ed.getPackage(), ct, javadocHelper); + } // Skip the package if packageVersion is null + } + } + } + + private void checkModuleOrPackage(EffectiveSourceSinceHelper javadocHelper, String moduleVersion, Element moduleElement, JavacTask ct, String elementCategory) { + String id = getElementName(moduleElement, moduleElement, ct.getTypes()); + var elementInfo = classDictionary.get(id); + if (elementInfo == null) { + error("Element :" + id + " was not mapped"); + return; + } + String version = elementInfo.introducedStable; + if (moduleVersion == null) { + error("Unable to retrieve `@since` for " + elementCategory + id); + } else { + String position = javadocHelper.getElementPosition(id); + checkEquals(position, moduleVersion, version, id); + } + } + + private String getModuleVersionFromFile(Path moduleDirectory) { + Path moduleInfoFile = moduleDirectory.resolve("module-info.java"); + String version = null; + if (Files.exists(moduleInfoFile)) { + try { + String moduleInfoContent = Files.readString(moduleInfoFile); + var extractedVersion = extractSinceVersionFromText(moduleInfoContent); + if (extractedVersion != null) { + version = extractedVersion.toString(); + } + } catch (IOException e) { + error("module-info.java not found or couldn't be opened AND this module has no unqualified exports"); + } + } + return version; + } + + private String getPackageVersionFromFile(Path moduleDirectory, ModuleElement.ExportsDirective ed) { + Path pkgInfo = moduleDirectory.resolve(ed.getPackage() + .getQualifiedName() + .toString() + .replace(".", File.separator) + ) + .resolve("package-info.java"); + + if (!Files.exists(pkgInfo)) { + return null; // Skip if the file does not exist + } + + String packageTopVersion = null; + try { + String packageContent = Files.readString(pkgInfo); + var extractedVersion = extractSinceVersionFromText(packageContent); + if (extractedVersion != null) { + packageTopVersion = extractedVersion.toString(); + } else { + error(ed.getPackage().getQualifiedName() + ": package-info.java exists but doesn't contain @since"); + } + } catch (IOException e) { + error(ed.getPackage().getQualifiedName() + ": package-info.java couldn't be opened"); + } + return packageTopVersion; + } + + private void analyzePackageCheck(PackageElement pe, JavacTask ct, EffectiveSourceSinceHelper javadocHelper) { + List typeElements = ElementFilter.typesIn(pe.getEnclosedElements()); + for (TypeElement te : typeElements) { + analyzeClassCheck(te, null, javadocHelper, ct.getTypes(), ct.getElements()); + } + } + + private boolean isNotCommonRecordMethod(TypeElement te, Element element, Types types) { + var isRecord = te.getKind() == ElementKind.RECORD; + if (!isRecord) { + return true; + } + String uniqueId = getElementName(te, element, types); + boolean isCommonMethod = uniqueId.endsWith(".toString()") || + uniqueId.endsWith(".hashCode()") || + uniqueId.endsWith(".equals(java.lang.Object)"); + if (isCommonMethod) { + return false; + } + for (var parameter : te.getEnclosedElements()) { + if (parameter.getKind() == ElementKind.RECORD_COMPONENT) { + if (uniqueId.endsWith(String.format("%s.%s()", te.getSimpleName(), parameter.getSimpleName().toString()))) { + return false; + } + } + } + return true; + } + + private void analyzeClassCheck(TypeElement te, String version, EffectiveSourceSinceHelper javadocHelper, + Types types, Elements elementUtils) { + String currentjdkVersion = String.valueOf(Runtime.version().feature()); + if (!isDocumented(te)) { + return; + } + checkElement(te.getEnclosingElement(), te, types, javadocHelper, version, elementUtils); + te.getEnclosedElements().stream().filter(this::isDocumented) + .filter(this::isMember) + .filter(element -> isNotCommonRecordMethod(te, element, types)) + .forEach(element -> checkElement(te, element, types, javadocHelper, version, elementUtils)); + te.getEnclosedElements().stream() + .filter(element -> element.getKind().isDeclaredType()) + .map(TypeElement.class::cast) + .forEach(nestedClass -> analyzeClassCheck(nestedClass, currentjdkVersion, javadocHelper, types, elementUtils)); + } + + private void checkElement(Element explicitOwner, Element element, Types types, + EffectiveSourceSinceHelper javadocHelper, String currentVersion, Elements elementUtils) { + String uniqueId = getElementName(explicitOwner, element, types); + + if (element.getKind() == ElementKind.METHOD && + element.getEnclosingElement().getKind() == ElementKind.ENUM && + (uniqueId.contains(".values()") || uniqueId.contains(".valueOf(java.lang.String)"))) { + //mandated enum type methods + return; + } + String sinceVersion = null; + var effectiveSince = javadocHelper.effectiveSinceVersion(explicitOwner, element, types, elementUtils); + if (effectiveSince == null) { + // Skip the element if the java file doesn't exist in src.zip + return; + } + sinceVersion = effectiveSince.toString(); + IntroducedIn mappedVersion = classDictionary.get(uniqueId); + if (mappedVersion == null) { + error("Element: " + uniqueId + " was not mapped"); + return; + } + String realMappedVersion = null; + try { + realMappedVersion = isPreview(element, uniqueId, currentVersion) ? + mappedVersion.introducedPreview : + mappedVersion.introducedStable; + } catch (Exception e) { + error("For element " + element + "mappedVersion" + mappedVersion + " is null " + e); + } + String position = javadocHelper.getElementPosition(uniqueId); + checkEquals(position, sinceVersion, realMappedVersion, uniqueId); + } + + private Version extractSinceVersionFromText(String documentation) { + Pattern pattern = Pattern.compile("@since\\s+(\\d+(?:\\.\\d+)?)"); + Matcher matcher = pattern.matcher(documentation); + if (matcher.find()) { + String versionString = matcher.group(1); + try { + if (versionString.equals("1.0")) { + versionString = "1"; //ended up being necessary + } else if (versionString.startsWith("1.")) { + versionString = versionString.substring(2); + } + return Version.parse(versionString); + } catch (NumberFormatException ex) { + error("`@since` value that cannot be parsed: " + versionString); + return null; + } + } else { + return null; + } + } + + private void checkEquals(String prefix, String sinceVersion, String mappedVersion, String name) { + if (sinceVersion == null || mappedVersion == null) { + error(name + ": NULL value for either real or effective `@since` . real/mapped version is=" + + mappedVersion + " while the `@since` in the source code is= " + sinceVersion); + return; + } + if (Integer.parseInt(sinceVersion) < 9) { + sinceVersion = "9"; + } + if (!sinceVersion.equals(mappedVersion)) { + String message = getWrongSinceMessage(prefix, sinceVersion, mappedVersion, name); + error(message); + } + } + private static String getWrongSinceMessage(String prefix, String sinceVersion, String mappedVersion, String elementSimpleName) { + String message; + if (mappedVersion.equals("9")) { + message = elementSimpleName + ": `@since` version is " + sinceVersion + " but the element exists before JDK 10"; + } else { + message = elementSimpleName + ": `@since` version: " + sinceVersion + "; should be: " + mappedVersion; + } + return prefix + message; + } + + private static String getElementName(Element owner, Element element, Types types) { + String prefix = ""; + String suffix = ""; + ElementKind kind = element.getKind(); + if (kind.isField()) { + TypeElement te = (TypeElement) owner; + prefix = "field"; + suffix = ": " + te.getQualifiedName() + ":" + element.getSimpleName(); + } else if (kind == ElementKind.METHOD || kind == ElementKind.CONSTRUCTOR) { + prefix = "method"; + TypeElement te = (TypeElement) owner; + ExecutableElement executableElement = (ExecutableElement) element; + String returnType = types.erasure(executableElement.getReturnType()).toString(); + String methodName = executableElement.getSimpleName().toString(); + String descriptor = executableElement.getParameters().stream() + .map(p -> types.erasure(p.asType()).toString()) + .collect(Collectors.joining(",", "(", ")")); + suffix = ": " + returnType + " " + te.getQualifiedName() + "." + methodName + descriptor; + } else if (kind.isDeclaredType()) { + if (kind.isClass()) { + prefix = "class"; + } else if (kind.isInterface()) { + prefix = "interface"; + } + suffix = ": " + ((TypeElement) element).getQualifiedName(); + } else if (kind == ElementKind.PACKAGE) { + prefix = "package"; + suffix = ": " + ((PackageElement) element).getQualifiedName(); + } else if (kind == ElementKind.MODULE) { + prefix = "module"; + suffix = ": " + ((ModuleElement) element).getQualifiedName(); + } + return prefix + suffix; + } + + //these were preview in before the introduction of the @PreviewFeature + { + LEGACY_PREVIEW_METHODS.put("9", Set.of( + "module: jdk.nio.mapmode", + "module: java.transaction.xa", + "module: jdk.unsupported.desktop", + "module: jdk.jpackage", + "module: java.net.http" + )); + LEGACY_PREVIEW_METHODS.put("10", Set.of( + "module: jdk.nio.mapmode", + "module: java.transaction.xa", + "module: java.net.http", + "module: jdk.unsupported.desktop", + "module: jdk.jpackage" + )); + LEGACY_PREVIEW_METHODS.put("11", Set.of( + "module: jdk.nio.mapmode", + "module: jdk.jpackage" + )); + LEGACY_PREVIEW_METHODS.put("12", Set.of( + "module: jdk.nio.mapmode", + "module: jdk.jpackage", + "method: com.sun.source.tree.ExpressionTree com.sun.source.tree.BreakTree.getValue()", + "method: java.util.List com.sun.source.tree.CaseTree.getExpressions()", + "method: com.sun.source.tree.Tree com.sun.source.tree.CaseTree.getBody()", + "method: com.sun.source.tree.CaseTree.CaseKind com.sun.source.tree.CaseTree.getCaseKind()", + "class: com.sun.source.tree.CaseTree.CaseKind", + "field: com.sun.source.tree.CaseTree.CaseKind:STATEMENT", + "field: com.sun.source.tree.CaseTree.CaseKind:RULE", + "field: com.sun.source.tree.Tree.Kind:SWITCH_EXPRESSION", + "interface: com.sun.source.tree.SwitchExpressionTree", + "method: com.sun.source.tree.ExpressionTree com.sun.source.tree.SwitchExpressionTree.getExpression()", + "method: java.util.List com.sun.source.tree.SwitchExpressionTree.getCases()", + "method: java.lang.Object com.sun.source.tree.TreeVisitor.visitSwitchExpression(com.sun.source.tree.SwitchExpressionTree,java.lang.Object)", + "method: java.lang.Object com.sun.source.util.TreeScanner.visitSwitchExpression(com.sun.source.tree.SwitchExpressionTree,java.lang.Object)", + "method: java.lang.Object com.sun.source.util.SimpleTreeVisitor.visitSwitchExpression(com.sun.source.tree.SwitchExpressionTree,java.lang.Object)" + )); + + LEGACY_PREVIEW_METHODS.put("13", Set.of( + "module: jdk.nio.mapmode", + "module: jdk.jpackage", + "method: java.util.List com.sun.source.tree.CaseTree.getExpressions()", + "method: com.sun.source.tree.Tree com.sun.source.tree.CaseTree.getBody()", + "method: com.sun.source.tree.CaseTree.CaseKind com.sun.source.tree.CaseTree.getCaseKind()", + "class: com.sun.source.tree.CaseTree.CaseKind", + "field: com.sun.source.tree.CaseTree.CaseKind:STATEMENT", + "field: com.sun.source.tree.CaseTree.CaseKind:RULE", + "field: com.sun.source.tree.Tree.Kind:SWITCH_EXPRESSION", + "interface: com.sun.source.tree.SwitchExpressionTree", + "method: com.sun.source.tree.ExpressionTree com.sun.source.tree.SwitchExpressionTree.getExpression()", + "method: java.util.List com.sun.source.tree.SwitchExpressionTree.getCases()", + "method: java.lang.Object com.sun.source.tree.TreeVisitor.visitSwitchExpression(com.sun.source.tree.SwitchExpressionTree,java.lang.Object)", + "method: java.lang.Object com.sun.source.util.TreeScanner.visitSwitchExpression(com.sun.source.tree.SwitchExpressionTree,java.lang.Object)", + "method: java.lang.Object com.sun.source.util.SimpleTreeVisitor.visitSwitchExpression(com.sun.source.tree.SwitchExpressionTree,java.lang.Object)", + "method: java.lang.String java.lang.String.stripIndent()", + "method: java.lang.String java.lang.String.translateEscapes()", + "method: java.lang.String java.lang.String.formatted(java.lang.Object[])", + "class: javax.swing.plaf.basic.motif.MotifLookAndFeel", + "field: com.sun.source.tree.Tree.Kind:YIELD", + "interface: com.sun.source.tree.YieldTree", + "method: com.sun.source.tree.ExpressionTree com.sun.source.tree.YieldTree.getValue()", + "method: java.lang.Object com.sun.source.tree.TreeVisitor.visitYield(com.sun.source.tree.YieldTree,java.lang.Object)", + "method: java.lang.Object com.sun.source.util.SimpleTreeVisitor.visitYield(com.sun.source.tree.YieldTree,java.lang.Object)", + "method: java.lang.Object com.sun.source.util.TreeScanner.visitYield(com.sun.source.tree.YieldTree,java.lang.Object)" + )); + + LEGACY_PREVIEW_METHODS.put("14", Set.of( + "module: jdk.jpackage", + "class: javax.swing.plaf.basic.motif.MotifLookAndFeel", + "field: jdk.jshell.Snippet.SubKind:RECORD_SUBKIND", + "class: javax.lang.model.element.RecordComponentElement", + "method: javax.lang.model.type.TypeMirror javax.lang.model.element.RecordComponentElement.asType()", + "method: java.lang.Object javax.lang.model.element.ElementVisitor.visitRecordComponent(javax.lang.model.element.RecordComponentElement,java.lang.Object)", + "class: javax.lang.model.util.ElementScanner14", + "class: javax.lang.model.util.AbstractElementVisitor14", + "class: javax.lang.model.util.SimpleElementVisitor14", + "method: java.lang.Object javax.lang.model.util.ElementKindVisitor6.visitTypeAsRecord(javax.lang.model.element.TypeElement,java.lang.Object)", + "class: javax.lang.model.util.ElementKindVisitor14", + "method: javax.lang.model.element.RecordComponentElement javax.lang.model.util.Elements.recordComponentFor(javax.lang.model.element.ExecutableElement)", + "method: java.util.List javax.lang.model.util.ElementFilter.recordComponentsIn(java.lang.Iterable)", + "method: java.util.Set javax.lang.model.util.ElementFilter.recordComponentsIn(java.util.Set)", + "method: java.util.List javax.lang.model.element.TypeElement.getRecordComponents()", + "field: javax.lang.model.element.ElementKind:RECORD", + "field: javax.lang.model.element.ElementKind:RECORD_COMPONENT", + "field: javax.lang.model.element.ElementKind:BINDING_VARIABLE", + "field: com.sun.source.tree.Tree.Kind:RECORD", + "field: sun.reflect.annotation.TypeAnnotation.TypeAnnotationTarget:RECORD_COMPONENT", + "class: java.lang.reflect.RecordComponent", + "class: java.lang.runtime.ObjectMethods", + "field: java.lang.annotation.ElementType:RECORD_COMPONENT", + "method: boolean java.lang.Class.isRecord()", + "method: java.lang.reflect.RecordComponent[] java.lang.Class.getRecordComponents()", + "class: java.lang.Record", + "interface: com.sun.source.tree.PatternTree", + "field: com.sun.source.tree.Tree.Kind:BINDING_PATTERN", + "method: com.sun.source.tree.PatternTree com.sun.source.tree.InstanceOfTree.getPattern()", + "interface: com.sun.source.tree.BindingPatternTree", + "method: java.lang.Object com.sun.source.tree.TreeVisitor.visitBindingPattern(com.sun.source.tree.BindingPatternTree,java.lang.Object)" + )); + + LEGACY_PREVIEW_METHODS.put("15", Set.of( + "module: jdk.jpackage", + "field: jdk.jshell.Snippet.SubKind:RECORD_SUBKIND", + "class: javax.lang.model.element.RecordComponentElement", + "method: javax.lang.model.type.TypeMirror javax.lang.model.element.RecordComponentElement.asType()", + "method: java.lang.Object javax.lang.model.element.ElementVisitor.visitRecordComponent(javax.lang.model.element.RecordComponentElement,java.lang.Object)", + "class: javax.lang.model.util.ElementScanner14", + "class: javax.lang.model.util.AbstractElementVisitor14", + "class: javax.lang.model.util.SimpleElementVisitor14", + "method: java.lang.Object javax.lang.model.util.ElementKindVisitor6.visitTypeAsRecord(javax.lang.model.element.TypeElement,java.lang.Object)", + "class: javax.lang.model.util.ElementKindVisitor14", + "method: javax.lang.model.element.RecordComponentElement javax.lang.model.util.Elements.recordComponentFor(javax.lang.model.element.ExecutableElement)", + "method: java.util.List javax.lang.model.util.ElementFilter.recordComponentsIn(java.lang.Iterable)", + "method: java.util.Set javax.lang.model.util.ElementFilter.recordComponentsIn(java.util.Set)", + "method: java.util.List javax.lang.model.element.TypeElement.getRecordComponents()", + "field: javax.lang.model.element.ElementKind:RECORD", + "field: javax.lang.model.element.ElementKind:RECORD_COMPONENT", + "field: javax.lang.model.element.ElementKind:BINDING_VARIABLE", + "field: com.sun.source.tree.Tree.Kind:RECORD", + "field: sun.reflect.annotation.TypeAnnotation.TypeAnnotationTarget:RECORD_COMPONENT", + "class: java.lang.reflect.RecordComponent", + "class: java.lang.runtime.ObjectMethods", + "field: java.lang.annotation.ElementType:RECORD_COMPONENT", + "class: java.lang.Record", + "method: boolean java.lang.Class.isRecord()", + "method: java.lang.reflect.RecordComponent[] java.lang.Class.getRecordComponents()", + "field: javax.lang.model.element.Modifier:SEALED", + "field: javax.lang.model.element.Modifier:NON_SEALED", + "method: javax.lang.model.element.TypeElement:getPermittedSubclasses:()", + "method: java.util.List com.sun.source.tree.ClassTree.getPermitsClause()", + "method: boolean java.lang.Class.isSealed()", + "method: java.lang.constant.ClassDesc[] java.lang.Class.permittedSubclasses()", + "interface: com.sun.source.tree.PatternTree", + "field: com.sun.source.tree.Tree.Kind:BINDING_PATTERN", + "method: com.sun.source.tree.PatternTree com.sun.source.tree.InstanceOfTree.getPattern()", + "interface: com.sun.source.tree.BindingPatternTree", + "method: java.lang.Object com.sun.source.tree.TreeVisitor.visitBindingPattern(com.sun.source.tree.BindingPatternTree,java.lang.Object)" + )); + + LEGACY_PREVIEW_METHODS.put("16", Set.of( + "field: jdk.jshell.Snippet.SubKind:RECORD_SUBKIND", + "field: javax.lang.model.element.Modifier:SEALED", + "field: javax.lang.model.element.Modifier:NON_SEALED", + "method: javax.lang.model.element.TypeElement:getPermittedSubclasses:()", + "method: java.util.List com.sun.source.tree.ClassTree.getPermitsClause()", + "method: boolean java.lang.Class.isSealed()", + "method: java.lang.constant.ClassDesc[] java.lang.Class.permittedSubclasses()" + )); + + // java.lang.foreign existed since JDK 19 and wasn't annotated - went out of preview in JDK 22 + LEGACY_PREVIEW_METHODS.put("19", Set.of( + "package: java.lang.foreign" + )); + LEGACY_PREVIEW_METHODS.put("20", Set.of( + "package: java.lang.foreign" + )); + LEGACY_PREVIEW_METHODS.put("21", Set.of( + "package: java.lang.foreign" + )); + } + + /** + * Helper to find javadoc and resolve @inheritDoc and the effective since version. + */ + + private final class EffectiveSourceSinceHelper implements AutoCloseable { + private static final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + private final JavaFileManager baseFileManager; + private final StandardJavaFileManager fm; + private final Set seenLookupElements = new HashSet<>(); + private final Map signature2Source = new HashMap<>(); + private final Map signature2Location = new HashMap<>(); + + /** + * Create the helper. + * + * @param mainTask JavacTask from which the further Elements originate + * @param sourceLocations paths where source files should be searched + * @param validator enclosing class of the helper, typically the object invoking this method + * @return a EffectiveSourceSinceHelper + */ + + public static EffectiveSourceSinceHelper create(JavacTask mainTask, Collection sourceLocations, SinceChecker validator) { + StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null); + try { + fm.setLocationFromPaths(StandardLocation.MODULE_SOURCE_PATH, sourceLocations); + return validator.new EffectiveSourceSinceHelper(mainTask, fm); + } catch (IOException ex) { + try { + fm.close(); + } catch (IOException closeEx) { + ex.addSuppressed(closeEx); + } + throw new UncheckedIOException(ex); + } + } + + private EffectiveSourceSinceHelper(JavacTask mainTask, StandardJavaFileManager fm) { + this.baseFileManager = ((JavacTaskImpl) mainTask).getContext().get(JavaFileManager.class); + this.fm = fm; + } + + public Version effectiveSinceVersion(Element owner, Element element, Types typeUtils, Elements elementUtils) { + String handle = getElementName(owner, element, typeUtils); + Version since = signature2Source.get(handle); + + if (since == null) { + try { + Element lookupElement = switch (element.getKind()) { + case MODULE, PACKAGE -> element; + default -> elementUtils.getOutermostTypeElement(element); + }; + + if (lookupElement == null) + return null; + + String lookupHandle = getElementName(owner, element, typeUtils); + + if (!seenLookupElements.add(lookupHandle)) { + //we've already processed this top-level, don't try to compute + //the values again: + return null; + } + + Pair source = findSource(lookupElement, elementUtils); + + if (source == null) + return null; + + fillElementCache(source.fst, source.snd, source.fst.getTypes(), source.fst.getElements()); + since = signature2Source.get(handle); + + } catch (IOException ex) { + error("JavadocHelper failed for " + element); + } + } + + return since; + } + + private String getElementPosition(String signature) { + return signature2Location.getOrDefault(signature, ""); + } + + //where: + private void fillElementCache(JavacTask task, CompilationUnitTree cut, Types typeUtils, Elements elementUtils) { + Trees trees = Trees.instance(task); + String fileName = cut.getSourceFile().getName(); + + new TreePathScanner() { + @Override + public Void visitMethod(MethodTree node, Void p) { + handleDeclaration(node, fileName); + return null; + } + + @Override + public Void visitClass(ClassTree node, Void p) { + handleDeclaration(node, fileName); + return super.visitClass(node, p); + } + + @Override + public Void visitVariable(VariableTree node, Void p) { + handleDeclaration(node, fileName); + return null; + } + + @Override + public Void visitModule(ModuleTree node, Void p) { + handleDeclaration(node, fileName); + return null; + } + + @Override + public Void visitBlock(BlockTree node, Void p) { + return null; + } + + @Override + public Void visitPackage(PackageTree node, Void p) { + if (cut.getSourceFile().isNameCompatible("package-info", JavaFileObject.Kind.SOURCE)) { + handleDeclaration(node, fileName); + } + return super.visitPackage(node, p); + } + + private void handleDeclaration(Tree node, String fileName) { + Element currentElement = trees.getElement(getCurrentPath()); + + if (currentElement != null) { + long startPosition = trees.getSourcePositions().getStartPosition(cut, node); + long lineNumber = cut.getLineMap().getLineNumber(startPosition); + String filePathWithLineNumber = String.format("src%s:%s ", fileName, lineNumber); + + signature2Source.put(getElementName(currentElement.getEnclosingElement(), currentElement, typeUtils), computeSinceVersion(currentElement, typeUtils, elementUtils)); + signature2Location.put(getElementName(currentElement.getEnclosingElement(), currentElement, typeUtils), filePathWithLineNumber); + } + } + }.scan(cut, null); + } + + private Version computeSinceVersion(Element element, Types types, + Elements elementUtils) { + String docComment = elementUtils.getDocComment(element); + Version version = null; + if (docComment != null) { + version = extractSinceVersionFromText(docComment); + } + + if (version != null) { + return version; //explicit @since has an absolute priority + } + + if (element.getKind() != ElementKind.MODULE) { + version = effectiveSinceVersion(element.getEnclosingElement().getEnclosingElement(), element.getEnclosingElement(), types, elementUtils); + } + + return version; + } + + private Pair findSource(Element forElement, Elements elementUtils) throws IOException { + String moduleName = elementUtils.getModuleOf(forElement).getQualifiedName().toString(); + String binaryName = switch (forElement.getKind()) { + case MODULE -> "module-info"; + case PACKAGE -> ((QualifiedNameable) forElement).getQualifiedName() + ".package-info"; + default -> elementUtils.getBinaryName((TypeElement) forElement).toString(); + }; + Location packageLocationForModule = fm.getLocationForModule(StandardLocation.MODULE_SOURCE_PATH, moduleName); + JavaFileObject jfo = fm.getJavaFileForInput(packageLocationForModule, + binaryName, + JavaFileObject.Kind.SOURCE); + + if (jfo == null) + return null; + + List jfos = Arrays.asList(jfo); + JavaFileManager patchFM = moduleName != null + ? new PatchModuleFileManager(baseFileManager, jfo, moduleName) + : baseFileManager; + JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, patchFM, d -> { + }, null, null, jfos); + Iterable cuts = task.parse(); + + task.enter(); + + return Pair.of(task, cuts.iterator().next()); + } + + @Override + public void close() throws IOException { + fm.close(); + } + + /** + * Manages files within a patch module. + * Provides custom behavior for handling file locations within a patch module. + * Includes methods to specify module locations, infer module names and determine + * if a location belongs to the patch module path. + */ + private static final class PatchModuleFileManager + extends ForwardingJavaFileManager { + + private final JavaFileObject file; + private final String moduleName; + + public PatchModuleFileManager(JavaFileManager fileManager, + JavaFileObject file, + String moduleName) { + super(fileManager); + this.file = file; + this.moduleName = moduleName; + } + + @Override + public Location getLocationForModule(Location location, + JavaFileObject fo) throws IOException { + return fo == file + ? PATCH_LOCATION + : super.getLocationForModule(location, fo); + } + + @Override + public String inferModuleName(Location location) throws IOException { + return location == PATCH_LOCATION + ? moduleName + : super.inferModuleName(location); + } + + @Override + public boolean hasLocation(Location location) { + return location == StandardLocation.PATCH_MODULE_PATH || + super.hasLocation(location); + } + + private static final Location PATCH_LOCATION = new Location() { + @Override + public String getName() { + return "PATCH_LOCATION"; + } + + @Override + public boolean isOutputLocation() { + return false; + } + + @Override + public boolean isModuleOrientedLocation() { + return false; + } + }; + } + } +} diff --git a/test/jdk/tools/sincechecker/modules/java_base/CheckSince_javaBase.java b/test/jdk/tools/sincechecker/modules/java_base/CheckSince_javaBase.java new file mode 100644 index 00000000000..6d0b9d0e932 --- /dev/null +++ b/test/jdk/tools/sincechecker/modules/java_base/CheckSince_javaBase.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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 8331051 + * @summary Test for `@since` for java.base module + * @library /test/lib + * /test/jdk/tools/sincechecker + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.util + * jdk.compiler/com.sun.tools.javac.code + * @run main SinceChecker java.base --exclude java.lang.classfile + */ From 1cc32237aee03a38bfb0f6679f7328a682ad6ea9 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Wed, 16 Oct 2024 13:36:58 +0000 Subject: [PATCH 77/95] 8336911: ZGC: Division by zero in heuristics after JDK-8332717 Reviewed-by: aboldtch, eosterlund --- src/hotspot/share/gc/z/zDirector.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/hotspot/share/gc/z/zDirector.cpp b/src/hotspot/share/gc/z/zDirector.cpp index 481525c425a..3c0cb660206 100644 --- a/src/hotspot/share/gc/z/zDirector.cpp +++ b/src/hotspot/share/gc/z/zDirector.cpp @@ -535,6 +535,19 @@ static double calculate_young_to_old_worker_ratio(const ZDirectorStats& stats) { const size_t reclaimed_per_old_gc = stats._old_stats._stat_heap._reclaimed_avg; const double current_young_bytes_freed_per_gc_time = double(reclaimed_per_young_gc) / double(young_gc_time); const double current_old_bytes_freed_per_gc_time = double(reclaimed_per_old_gc) / double(old_gc_time); + + if (current_young_bytes_freed_per_gc_time == 0.0) { + if (current_old_bytes_freed_per_gc_time == 0.0) { + // Neither young nor old collections have reclaimed any memory. + // Give them equal priority. + return 1.0; + } + + // Only old collections have reclaimed memory. + // Prioritize old. + return ZOldGCThreads; + } + const double old_vs_young_efficiency_ratio = current_old_bytes_freed_per_gc_time / current_young_bytes_freed_per_gc_time; return old_vs_young_efficiency_ratio; From 7625b29920e95f9b754057fe0a2c4ab0afa5cb0c Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 16 Oct 2024 14:08:10 +0000 Subject: [PATCH 78/95] 8329597: C2: Intrinsify Reference.clear Reviewed-by: rcastanedalo, eosterlund, kvn --- .../gc/z/zBarrierSetAssembler_aarch64.cpp | 10 +- .../gc/z/zBarrierSetAssembler_aarch64.hpp | 4 +- src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad | 3 +- .../cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp | 2 + src/hotspot/cpu/ppc/gc/z/z_ppc.ad | 3 +- .../riscv/gc/z/zBarrierSetAssembler_riscv.cpp | 2 + src/hotspot/cpu/riscv/gc/z/z_riscv.ad | 3 +- .../cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp | 2 + src/hotspot/cpu/x86/gc/z/z_x86_64.ad | 3 +- src/hotspot/share/classfile/vmIntrinsics.hpp | 2 + src/hotspot/share/classfile/vmSymbols.hpp | 1 + src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp | 5 + .../shenandoah/c2/shenandoahBarrierSetC2.cpp | 7 + src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp | 14 +- src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp | 6 +- src/hotspot/share/gc/z/zBarrierSetRuntime.cpp | 8 + src/hotspot/share/gc/z/zBarrierSetRuntime.hpp | 2 + src/hotspot/share/jvmci/vmStructs_jvmci.cpp | 1 + src/hotspot/share/opto/c2compiler.cpp | 2 + src/hotspot/share/opto/library_call.cpp | 44 ++++++ src/hotspot/share/opto/library_call.hpp | 1 + .../java/lang/ref/PhantomReference.java | 13 ++ .../classes/java/lang/ref/Reference.java | 20 ++- .../share/native/libjava/PhantomReference.c | 6 + .../c2/irTests/gc/ReferenceClearTests.java | 138 ++++++++++++++++++ .../bench/java/lang/ref/ReferenceClear.java | 81 ++++++++++ 26 files changed, 362 insertions(+), 21 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceClearTests.java create mode 100644 test/micro/org/openjdk/bench/java/lang/ref/ReferenceClear.java diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp index 466e77a4460..3f1898b6742 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp @@ -1189,6 +1189,8 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ lea(rscratch1, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr())); } else if (stub->is_atomic()) { __ lea(rscratch1, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr())); + } else if (stub->is_nokeepalive()) { + __ lea(rscratch1, RuntimeAddress(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr())); } else { __ lea(rscratch1, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr())); } @@ -1307,11 +1309,11 @@ Label* ZLoadBarrierStubC2Aarch64::entry() { return ZBarrierStubC2::entry(); } -ZStoreBarrierStubC2Aarch64::ZStoreBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic) - : ZStoreBarrierStubC2(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic), _deferred_emit(false) {} +ZStoreBarrierStubC2Aarch64::ZStoreBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive) + : ZStoreBarrierStubC2(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic, is_nokeepalive), _deferred_emit(false) {} -ZStoreBarrierStubC2Aarch64* ZStoreBarrierStubC2Aarch64::create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic) { - ZStoreBarrierStubC2Aarch64* const stub = new (Compile::current()->comp_arena()) ZStoreBarrierStubC2Aarch64(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic); +ZStoreBarrierStubC2Aarch64* ZStoreBarrierStubC2Aarch64::create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive) { + ZStoreBarrierStubC2Aarch64* const stub = new (Compile::current()->comp_arena()) ZStoreBarrierStubC2Aarch64(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic, is_nokeepalive); register_stub(stub); return stub; } diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp index 2f716140ed1..ad3a171c103 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp @@ -280,10 +280,10 @@ class ZStoreBarrierStubC2Aarch64 : public ZStoreBarrierStubC2 { private: bool _deferred_emit; - ZStoreBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic); + ZStoreBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive); public: - static ZStoreBarrierStubC2Aarch64* create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic); + static ZStoreBarrierStubC2Aarch64* create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive); virtual void emit_code(MacroAssembler& masm); }; diff --git a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad index 56d45384779..088f92a0157 100644 --- a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad @@ -91,7 +91,8 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Address z_color(masm, node, rnew_zpointer, rnew_zaddress); } else { bool is_native = (node->barrier_data() & ZBarrierNative) != 0; - ZStoreBarrierStubC2Aarch64* const stub = ZStoreBarrierStubC2Aarch64::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic); + bool is_nokeepalive = (node->barrier_data() & ZBarrierNoKeepalive) != 0; + ZStoreBarrierStubC2Aarch64* const stub = ZStoreBarrierStubC2Aarch64::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic, is_nokeepalive); ZBarrierSetAssembler* bs_asm = ZBarrierSet::assembler(); bs_asm->store_barrier_fast(masm, ref_addr, rnew_zaddress, rnew_zpointer, tmp, true /* in_nmethod */, is_atomic, *stub->entry(), *stub->continuation()); } diff --git a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp index 89ab1b1edee..8a65022126e 100644 --- a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp @@ -943,6 +943,8 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr(), R3_ARG1); } else if (stub->is_atomic()) { __ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr(), R3_ARG1); + } else if (stub->is_nokeepalive()) { + __ call_VM_leaf(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr(), R3_ARG1); } else { __ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr(), R3_ARG1); } diff --git a/src/hotspot/cpu/ppc/gc/z/z_ppc.ad b/src/hotspot/cpu/ppc/gc/z/z_ppc.ad index 017574d40ff..bb696a4738f 100644 --- a/src/hotspot/cpu/ppc/gc/z/z_ppc.ad +++ b/src/hotspot/cpu/ppc/gc/z/z_ppc.ad @@ -83,7 +83,8 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Register z_color(masm, rnew_zpointer, rnew_zaddress); } else { bool is_native = (node->barrier_data() & ZBarrierNative) != 0; - ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, Address(ref_base, disp), rnew_zaddress, rnew_zpointer, is_native, is_atomic); + bool is_nokeepalive = (node->barrier_data() & ZBarrierNoKeepalive) != 0; + ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, Address(ref_base, disp), rnew_zaddress, rnew_zpointer, is_native, is_atomic, is_nokeepalive); ZBarrierSetAssembler* bs_asm = ZBarrierSet::assembler(); bs_asm->store_barrier_fast(masm, ref_base, disp, rnew_zaddress, rnew_zpointer, true /* in_nmethod */, is_atomic, *stub->entry(), *stub->continuation()); } diff --git a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp index cbb918ade00..4a82bd9c2d0 100644 --- a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp @@ -761,6 +761,8 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ la(t0, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr())); } else if (stub->is_atomic()) { __ la(t0, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr())); + } else if (stub->is_nokeepalive()) { + __ la(t0, RuntimeAddress(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr())); } else { __ la(t0, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr())); } diff --git a/src/hotspot/cpu/riscv/gc/z/z_riscv.ad b/src/hotspot/cpu/riscv/gc/z/z_riscv.ad index 5b545fe8012..24669f45eb4 100644 --- a/src/hotspot/cpu/riscv/gc/z/z_riscv.ad +++ b/src/hotspot/cpu/riscv/gc/z/z_riscv.ad @@ -82,7 +82,8 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Address z_color(masm, node, rnew_zpointer, rnew_zaddress, tmp); } else { bool is_native = (node->barrier_data() & ZBarrierNative) != 0; - ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic); + bool is_nokeepalive = (node->barrier_data() & ZBarrierNoKeepalive) != 0; + ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic, is_nokeepalive); ZBarrierSetAssembler* bs_asm = ZBarrierSet::assembler(); bs_asm->store_barrier_fast(masm, ref_addr, rnew_zaddress, rnew_zpointer, tmp, true /* in_nmethod */, is_atomic, *stub->entry(), *stub->continuation()); } diff --git a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp index 65d7c1e3303..bc51a2b4468 100644 --- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp @@ -1260,6 +1260,8 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ call(RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr())); } else if (stub->is_atomic()) { __ call(RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr())); + } else if (stub->is_nokeepalive()) { + __ call(RuntimeAddress(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr())); } else { __ call(RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr())); } diff --git a/src/hotspot/cpu/x86/gc/z/z_x86_64.ad b/src/hotspot/cpu/x86/gc/z/z_x86_64.ad index 455d622acdf..f55ad70e861 100644 --- a/src/hotspot/cpu/x86/gc/z/z_x86_64.ad +++ b/src/hotspot/cpu/x86/gc/z/z_x86_64.ad @@ -91,7 +91,8 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Address } } else { bool is_native = (node->barrier_data() & ZBarrierNative) != 0; - ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic); + bool is_nokeepalive = (node->barrier_data() & ZBarrierNoKeepalive) != 0; + ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic, is_nokeepalive); ZBarrierSetAssembler* bs_asm = ZBarrierSet::assembler(); bs_asm->store_barrier_fast(masm, ref_addr, rnew_zaddress, rnew_zpointer, true /* in_nmethod */, is_atomic, *stub->entry(), *stub->continuation()); } diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index b6ce21797a6..9bb8b2179ae 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -469,6 +469,8 @@ class methodHandle; do_intrinsic(_Reference_get, java_lang_ref_Reference, get_name, void_object_signature, F_R) \ do_intrinsic(_Reference_refersTo0, java_lang_ref_Reference, refersTo0_name, object_boolean_signature, F_RN) \ do_intrinsic(_PhantomReference_refersTo0, java_lang_ref_PhantomReference, refersTo0_name, object_boolean_signature, F_RN) \ + 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") \ diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index a65ab86fa0a..3de6d81f106 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -426,6 +426,7 @@ class SerializeClosure; template(cs_name, "cs") \ template(get_name, "get") \ template(refersTo0_name, "refersTo0") \ + template(clear0_name, "clear0") \ template(put_name, "put") \ template(type_name, "type") \ template(findNative_name, "findNative") \ diff --git a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp index 4ec7e10cd9a..edbf0e90239 100644 --- a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp +++ b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp @@ -327,6 +327,7 @@ Node* G1BarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& val) co bool in_heap = (decorators & IN_HEAP) != 0; bool tightly_coupled_alloc = (decorators & C2_TIGHTLY_COUPLED_ALLOC) != 0; bool need_store_barrier = !(tightly_coupled_alloc && use_ReduceInitialCardMarks()) && (in_heap || anonymous); + bool no_keepalive = (decorators & AS_NO_KEEPALIVE) != 0; if (access.is_oop() && need_store_barrier) { access.set_barrier_data(get_store_barrier(access)); if (tightly_coupled_alloc) { @@ -336,6 +337,10 @@ Node* G1BarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& val) co access.set_barrier_data(access.barrier_data() & ~G1C2BarrierPre); } } + if (no_keepalive) { + // No keep-alive means no need for the pre-barrier. + access.set_barrier_data(access.barrier_data() & ~G1C2BarrierPre); + } return BarrierSetC2::store_at_resolved(access, val); } diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index 7ac9dcc2e81..691c78cd024 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -480,10 +480,17 @@ Node* ShenandoahBarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& const TypePtr* adr_type = access.addr().type(); Node* adr = access.addr().node(); + bool no_keepalive = (decorators & AS_NO_KEEPALIVE) != 0; + if (!access.is_oop()) { return BarrierSetC2::store_at_resolved(access, val); } + if (no_keepalive) { + // No keep-alive means no need for the pre-barrier. + return BarrierSetC2::store_at_resolved(access, val); + } + if (access.is_parse_access()) { C2ParseAccess& parse_access = static_cast(access); GraphKit* kit = parse_access.kit(); diff --git a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp index a46681d131c..12ee400eb2d 100644 --- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp +++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp @@ -239,21 +239,23 @@ void ZLoadBarrierStubC2::emit_code(MacroAssembler& masm) { ZBarrierSet::assembler()->generate_c2_load_barrier_stub(&masm, static_cast(this)); } -ZStoreBarrierStubC2* ZStoreBarrierStubC2::create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic) { +ZStoreBarrierStubC2* ZStoreBarrierStubC2::create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive) { AARCH64_ONLY(fatal("Should use ZStoreBarrierStubC2Aarch64::create")); - ZStoreBarrierStubC2* const stub = new (Compile::current()->comp_arena()) ZStoreBarrierStubC2(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic); + ZStoreBarrierStubC2* const stub = new (Compile::current()->comp_arena()) ZStoreBarrierStubC2(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic, is_nokeepalive); register_stub(stub); return stub; } -ZStoreBarrierStubC2::ZStoreBarrierStubC2(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic) +ZStoreBarrierStubC2::ZStoreBarrierStubC2(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, + bool is_native, bool is_atomic, bool is_nokeepalive) : ZBarrierStubC2(node), _ref_addr(ref_addr), _new_zaddress(new_zaddress), _new_zpointer(new_zpointer), _is_native(is_native), - _is_atomic(is_atomic) {} + _is_atomic(is_atomic), + _is_nokeepalive(is_nokeepalive) {} Address ZStoreBarrierStubC2::ref_addr() const { return _ref_addr; @@ -275,6 +277,10 @@ bool ZStoreBarrierStubC2::is_atomic() const { return _is_atomic; } +bool ZStoreBarrierStubC2::is_nokeepalive() const { + return _is_nokeepalive; +} + void ZStoreBarrierStubC2::emit_code(MacroAssembler& masm) { ZBarrierSet::assembler()->generate_c2_store_barrier_stub(&masm, static_cast(this)); } diff --git a/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp b/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp index bf46780226e..58f75441b91 100644 --- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp +++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp @@ -79,18 +79,20 @@ class ZStoreBarrierStubC2 : public ZBarrierStubC2 { const Register _new_zpointer; const bool _is_native; const bool _is_atomic; + const bool _is_nokeepalive; protected: - ZStoreBarrierStubC2(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic); + ZStoreBarrierStubC2(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive); public: - static ZStoreBarrierStubC2* create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic); + static ZStoreBarrierStubC2* create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive); Address ref_addr() const; Register new_zaddress() const; Register new_zpointer() const; bool is_native() const; bool is_atomic() const; + bool is_nokeepalive() const; virtual void emit_code(MacroAssembler& masm); }; diff --git a/src/hotspot/share/gc/z/zBarrierSetRuntime.cpp b/src/hotspot/share/gc/z/zBarrierSetRuntime.cpp index b41fec3d0a5..c267d9bb24a 100644 --- a/src/hotspot/share/gc/z/zBarrierSetRuntime.cpp +++ b/src/hotspot/share/gc/z/zBarrierSetRuntime.cpp @@ -59,6 +59,10 @@ JRT_LEAF(void, ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing(oo ZBarrier::store_barrier_on_heap_oop_field((zpointer*)p, false /* heal */); JRT_END +JRT_LEAF(void, ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing(oop* p)) + ZBarrier::no_keep_alive_store_barrier_on_heap_oop_field((zpointer*)p); +JRT_END + JRT_LEAF(void, ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing(oop* p)) ZBarrier::store_barrier_on_native_oop_field((zpointer*)p, false /* heal */); JRT_END @@ -126,6 +130,10 @@ address ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr() { return reinterpret_cast
      (store_barrier_on_oop_field_without_healing); } +address ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr() { + return reinterpret_cast
      (no_keepalive_store_barrier_on_oop_field_without_healing); +} + address ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr() { return reinterpret_cast
      (store_barrier_on_native_oop_field_without_healing); } diff --git a/src/hotspot/share/gc/z/zBarrierSetRuntime.hpp b/src/hotspot/share/gc/z/zBarrierSetRuntime.hpp index 8a81f162bf1..8d59be02e68 100644 --- a/src/hotspot/share/gc/z/zBarrierSetRuntime.hpp +++ b/src/hotspot/share/gc/z/zBarrierSetRuntime.hpp @@ -40,6 +40,7 @@ class ZBarrierSetRuntime : public AllStatic { static oopDesc* no_keepalive_load_barrier_on_phantom_oop_field_preloaded(oopDesc* o, oop* p); static void store_barrier_on_oop_field_with_healing(oop* p); static void store_barrier_on_oop_field_without_healing(oop* p); + static void no_keepalive_store_barrier_on_oop_field_without_healing(oop* p); static void store_barrier_on_native_oop_field_without_healing(oop* p); static void load_barrier_on_oop_array(oop* p, size_t length); static void clone(oopDesc* src, oopDesc* dst, size_t size); @@ -54,6 +55,7 @@ class ZBarrierSetRuntime : public AllStatic { static address no_keepalive_load_barrier_on_phantom_oop_field_preloaded_addr(); static address store_barrier_on_oop_field_with_healing_addr(); static address store_barrier_on_oop_field_without_healing_addr(); + static address no_keepalive_store_barrier_on_oop_field_without_healing_addr(); static address store_barrier_on_native_oop_field_without_healing_addr(); static address load_barrier_on_oop_array_addr(); static address clone_addr(); diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 05081197c4b..a26cd5efe23 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -847,6 +847,7 @@ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_store_good)) \ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::no_keepalive_load_barrier_on_weak_oop_field_preloaded)) \ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::no_keepalive_load_barrier_on_phantom_oop_field_preloaded)) \ + ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing)) \ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing)) \ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing)) \ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing)) \ diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index 151c320cadd..1a00f17f505 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -766,6 +766,8 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_Reference_get: case vmIntrinsics::_Reference_refersTo0: case vmIntrinsics::_PhantomReference_refersTo0: + case vmIntrinsics::_Reference_clear0: + case vmIntrinsics::_PhantomReference_clear0: case vmIntrinsics::_Class_cast: case vmIntrinsics::_aescrypt_encryptBlock: case vmIntrinsics::_aescrypt_decryptBlock: diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 4ab4eea6f8f..49750cd2697 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -580,6 +580,8 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_Reference_get: return inline_reference_get(); case vmIntrinsics::_Reference_refersTo0: return inline_reference_refersTo0(false); case vmIntrinsics::_PhantomReference_refersTo0: return inline_reference_refersTo0(true); + case vmIntrinsics::_Reference_clear0: return inline_reference_clear0(false); + case vmIntrinsics::_PhantomReference_clear0: return inline_reference_clear0(true); case vmIntrinsics::_Class_cast: return inline_Class_cast(); @@ -6962,6 +6964,48 @@ bool LibraryCallKit::inline_reference_refersTo0(bool is_phantom) { return true; } +//----------------------------inline_reference_clear0---------------------------- +// void java.lang.ref.Reference.clear0(); +// void java.lang.ref.PhantomReference.clear0(); +bool LibraryCallKit::inline_reference_clear0(bool is_phantom) { + // This matches the implementation in JVM_ReferenceClear, see the comments there. + + // Get arguments + Node* reference_obj = null_check_receiver(); + if (stopped()) return true; + + // Common access parameters + DecoratorSet decorators = IN_HEAP | AS_NO_KEEPALIVE; + decorators |= (is_phantom ? ON_PHANTOM_OOP_REF : ON_WEAK_OOP_REF); + Node* referent_field_addr = basic_plus_adr(reference_obj, java_lang_ref_Reference::referent_offset()); + const TypePtr* referent_field_addr_type = _gvn.type(referent_field_addr)->isa_ptr(); + const Type* val_type = TypeOopPtr::make_from_klass(env()->Object_klass()); + + Node* referent = access_load_at(reference_obj, + referent_field_addr, + referent_field_addr_type, + val_type, + T_OBJECT, + decorators); + + IdealKit ideal(this); +#define __ ideal. + __ if_then(referent, BoolTest::ne, null()); + sync_kit(ideal); + access_store_at(reference_obj, + referent_field_addr, + referent_field_addr_type, + null(), + val_type, + T_OBJECT, + decorators); + __ sync_kit(this); + __ end_if(); + final_sync(ideal); +#undef __ + + return true; +} Node* LibraryCallKit::load_field_from_object(Node* fromObj, const char* fieldName, const char* fieldTypeString, DecoratorSet decorators, bool is_static, diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 4a853045174..c9b2a02d3f1 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -297,6 +297,7 @@ class LibraryCallKit : public GraphKit { bool inline_divmod_methods(vmIntrinsics::ID id); bool inline_reference_get(); bool inline_reference_refersTo0(bool is_phantom); + bool inline_reference_clear0(bool is_phantom); bool inline_Class_cast(); bool inline_aescrypt_Block(vmIntrinsics::ID id); bool inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id); diff --git a/src/java.base/share/classes/java/lang/ref/PhantomReference.java b/src/java.base/share/classes/java/lang/ref/PhantomReference.java index e4747483497..47a989cde46 100644 --- a/src/java.base/share/classes/java/lang/ref/PhantomReference.java +++ b/src/java.base/share/classes/java/lang/ref/PhantomReference.java @@ -77,6 +77,19 @@ boolean refersToImpl(T obj) { @IntrinsicCandidate private native boolean refersTo0(Object o); + /* Override the implementation of Reference.clear. + * Phantom references are weaker than finalization, so the referent + * access needs to be handled differently for garbage collectors that + * do reference processing concurrently. + */ + @Override + void clearImpl() { + clear0(); + } + + @IntrinsicCandidate + private native void clear0(); + /** * Creates a new phantom reference that refers to the given object and * is registered with the given queue. 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 747aa64902f..88cd2a3190b 100644 --- a/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/src/java.base/share/classes/java/lang/ref/Reference.java @@ -403,13 +403,23 @@ boolean refersToImpl(T obj) { * necessary. */ public void clear() { - clear0(); + clearImpl(); } - /* Implementation of clear(), also used by enqueue(). A simple - * assignment of the referent field won't do for some garbage - * collectors. + /* Implementation of clear(). A simple assignment of the referent field + * won't do for some garbage collectors. There is the override for phantom + * references, which requires different semantics. This method is also + * used by enqueue(). + * + *

      This method exists only to avoid making clear0() virtual. Making + * clear0() virtual has the undesirable effect of C2 often preferring + * to call the native implementation over the intrinsic. */ + void clearImpl() { + clear0(); + } + + @IntrinsicCandidate private native void clear0(); /* -- Operations on inactive FinalReferences -- */ @@ -511,7 +521,7 @@ public boolean isEnqueued() { * it was not registered with a queue when it was created */ public boolean enqueue() { - clear0(); // Intentionally clear0() rather than clear() + clearImpl(); // Intentionally clearImpl() to dispatch to overridden method, if needed return this.queue.enqueue(this); } diff --git a/src/java.base/share/native/libjava/PhantomReference.c b/src/java.base/share/native/libjava/PhantomReference.c index b6d6e7297f1..54d4b7681dd 100644 --- a/src/java.base/share/native/libjava/PhantomReference.c +++ b/src/java.base/share/native/libjava/PhantomReference.c @@ -31,3 +31,9 @@ Java_java_lang_ref_PhantomReference_refersTo0(JNIEnv *env, jobject ref, jobject { return JVM_PhantomReferenceRefersTo(env, ref, o); } + +JNIEXPORT void JNICALL +Java_java_lang_ref_PhantomReference_clear0(JNIEnv *env, jobject ref) +{ + JVM_ReferenceClear(env, ref); +} diff --git a/test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceClearTests.java b/test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceClearTests.java new file mode 100644 index 00000000000..c56ffc88778 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceClearTests.java @@ -0,0 +1,138 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.c2.irTests.gc; + +import jdk.test.lib.Asserts; +import compiler.lib.ir_framework.*; +import jdk.test.whitebox.gc.GC; + +import java.lang.ref.*; +import java.util.*; + +/* + * @test + * @bug 8329597 + * @summary Test that Reference.clear intrinsics are properly handled + * @library /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @requires vm.compiler2.enabled + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.c2.irTests.gc.ReferenceClearTests + + */ +public class ReferenceClearTests { + + private static String[] args(String... add) { + List args = new ArrayList<>(); + + // Use PerMethodTrapLimit=0 to compile all branches in the intrinsics. + args.add("-XX:PerMethodTrapLimit=0"); + + // Forcefully inline all methods to reach the intrinsic code. + args.add("-XX:CompileCommand=inline,compiler.c2.irTests.gc.ReferenceClearTests::*"); + args.add("-XX:CompileCommand=inline,java.lang.ref.Reference::*"); + args.add("-XX:CompileCommand=inline,java.lang.ref.PhantomReference::*"); + + // Mix in test config code. + args.addAll(Arrays.asList(add)); + + return args.toArray(new String[0]); + } + + public static void main(String[] args) { + TestFramework framework = new TestFramework(); + + int idx = 0; + if (GC.isSelectedErgonomically() && GC.Serial.isSupported()) { + // Serial does not have SATB/keep-alive barriers at all. + // There are inter-generational barriers on stores, but they are + // folded away for null stores like clear(). + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseSerialGC" + ))); + } + if (GC.isSelectedErgonomically() && GC.Parallel.isSupported()) { + // Parallel does not have SATB/keep-alive barriers at all. + // There are inter-generational barriers on stores, but they + // should be folded away for null stores like clear(). + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseParallelGC" + ))); + } + if (GC.isSelectedErgonomically() && GC.G1.isSupported()) { + // G1 does not have barriers in C2 IR. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseG1GC" + ))); + } + if (GC.isSelectedErgonomically() && GC.Shenandoah.isSupported()) { + // Shenandoah has SATB/keep-alive barriers, but they should not be + // present clear()-s. There are load-reference barriers, which would + // confuse the tests, so we enable only SATB barriers. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:ShenandoahGCMode=passive", + "-XX:+ShenandoahSATBBarrier", + "-XX:+UseShenandoahGC" + ))); + } + if (GC.isSelectedErgonomically() && GC.Z.isSupported()) { + // Z does not have barriers in C2 IR. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseZGC" + ))); + } + framework.start(); + } + + static final Object REF = new Object(); + + static final SoftReference SR = new SoftReference<>(REF); + static final WeakReference WR = new WeakReference<>(REF); + static final PhantomReference PR = new PhantomReference<>(REF, null); + + // We assert there is only a single load and a single store of Reference.referent. + // This serves as signal that no GC barriers are emitted in IR. + + @Test + @IR(counts = { IRNode.STORE, "1", + IRNode.LOAD, "1" }) + public void soft() { + SR.clear(); + } + + @Test + @IR(counts = { IRNode.STORE, "1", + IRNode.LOAD, "1" }) + public void weak() { + WR.clear(); + } + + @Test + @IR(counts = { IRNode.STORE, "1", + IRNode.LOAD, "1" }) + public void phantom() { + PR.clear(); + } + +} diff --git a/test/micro/org/openjdk/bench/java/lang/ref/ReferenceClear.java b/test/micro/org/openjdk/bench/java/lang/ref/ReferenceClear.java new file mode 100644 index 00000000000..6a7b4d69970 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/ref/ReferenceClear.java @@ -0,0 +1,81 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang.ref; + +import java.lang.ref.*; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; + +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(3) +public class ReferenceClear { + + final Reference soft = new SoftReference<>(new Object()); + final Reference weak = new WeakReference<>(new Object()); + final Reference phantom = new PhantomReference<>(new Object(), null); + + @Benchmark + public void soft() { + soft.clear(); + } + + @Benchmark + public void soft_new(Blackhole bh) { + Object ref = new Object(); + bh.consume(ref); + Reference soft = new SoftReference<>(ref); + soft.clear(); + } + + @Benchmark + public void weak() { + weak.clear(); + } + + @Benchmark + public void weak_new(Blackhole bh) { + Object ref = new Object(); + bh.consume(ref); + Reference weak = new WeakReference<>(ref); + weak.clear(); + } + + @Benchmark + public void phantom() { + phantom.clear(); + } + + @Benchmark + public void phantom_new(Blackhole bh) { + Object ref = new Object(); + bh.consume(ref); + Reference phantom = new PhantomReference<>(ref, null); + phantom.clear(); + } + +} From c34fb2c989562206a2506a2fbbfb584e223bb828 Mon Sep 17 00:00:00 2001 From: Gerard Ziemski Date: Wed, 16 Oct 2024 15:32:07 +0000 Subject: [PATCH 79/95] 8338883: Show warning when CreateCoredumpOnCrash set, but core dump will not happen Reviewed-by: dholmes, jsjolen --- src/hotspot/os/posix/os_posix.cpp | 67 +++++++++---------- src/hotspot/os/windows/os_windows.cpp | 60 ++++++++++------- src/hotspot/share/runtime/os.hpp | 2 +- src/hotspot/share/runtime/threads.cpp | 6 ++ src/hotspot/share/utilities/vmError.cpp | 4 +- .../ErrorHandling/CreateCoredumpOnCrash.java | 58 +++++++++++++--- 6 files changed, 124 insertions(+), 73 deletions(-) diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index cc29f209160..78fd2a67888 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -104,49 +104,44 @@ static int clock_tics_per_sec = 100; size_t os::_os_min_stack_allowed = PTHREAD_STACK_MIN; // Check core dump limit and report possible place where core can be found -void os::check_dump_limit(char* buffer, size_t bufferSize) { +void os::check_core_dump_prerequisites(char* buffer, size_t bufferSize, bool check_only) { if (!FLAG_IS_DEFAULT(CreateCoredumpOnCrash) && !CreateCoredumpOnCrash) { jio_snprintf(buffer, bufferSize, "CreateCoredumpOnCrash is disabled from command line"); VMError::record_coredump_status(buffer, false); - return; - } - - int n; - struct rlimit rlim; - bool success; - - char core_path[PATH_MAX]; - n = get_core_path(core_path, PATH_MAX); - - if (n <= 0) { - jio_snprintf(buffer, bufferSize, "core.%d (may not exist)", current_process_id()); - success = true; + } else { + struct rlimit rlim; + bool success = true; + bool warn = true; + char core_path[PATH_MAX]; + if (get_core_path(core_path, PATH_MAX) <= 0) { + jio_snprintf(buffer, bufferSize, "core.%d (may not exist)", current_process_id()); #ifdef LINUX - } else if (core_path[0] == '"') { // redirect to user process - jio_snprintf(buffer, bufferSize, "Core dumps may be processed with %s", core_path); - success = true; + } else if (core_path[0] == '"') { // redirect to user process + jio_snprintf(buffer, bufferSize, "Core dumps may be processed with %s", core_path); #endif - } else if (getrlimit(RLIMIT_CORE, &rlim) != 0) { - jio_snprintf(buffer, bufferSize, "%s (may not exist)", core_path); - success = true; - } else { - switch(rlim.rlim_cur) { - case RLIM_INFINITY: - jio_snprintf(buffer, bufferSize, "%s", core_path); - success = true; - break; - case 0: - jio_snprintf(buffer, bufferSize, "Core dumps have been disabled. To enable core dumping, try \"ulimit -c unlimited\" before starting Java again"); - success = false; - break; - default: - jio_snprintf(buffer, bufferSize, "%s (max size " UINT64_FORMAT " k). To ensure a full core dump, try \"ulimit -c unlimited\" before starting Java again", core_path, uint64_t(rlim.rlim_cur) / K); - success = true; - break; + } else if (getrlimit(RLIMIT_CORE, &rlim) != 0) { + jio_snprintf(buffer, bufferSize, "%s (may not exist)", core_path); + } else { + switch(rlim.rlim_cur) { + case RLIM_INFINITY: + jio_snprintf(buffer, bufferSize, "%s", core_path); + warn = false; + break; + case 0: + jio_snprintf(buffer, bufferSize, "Core dumps have been disabled. To enable core dumping, try \"ulimit -c unlimited\" before starting Java again"); + success = false; + break; + default: + jio_snprintf(buffer, bufferSize, "%s (max size " UINT64_FORMAT " k). To ensure a full core dump, try \"ulimit -c unlimited\" before starting Java again", core_path, uint64_t(rlim.rlim_cur) / K); + break; + } + } + if (!check_only) { + VMError::record_coredump_status(buffer, success); + } else if (warn) { + warning("CreateCoredumpOnCrash specified, but %s", buffer); } } - - VMError::record_coredump_status(buffer, success); } bool os::committed_in_range(address start, size_t size, address& committed_start, size_t& committed_size) { diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 0d5727b98f4..fe093aee116 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -1287,38 +1287,50 @@ void os::shutdown() { static HANDLE dumpFile = nullptr; -// Check if dump file can be created. -void os::check_dump_limit(char* buffer, size_t buffsz) { - bool status = true; +// Check if core dump is active and if a core dump file can be created +void os::check_core_dump_prerequisites(char* buffer, size_t bufferSize, bool check_only) { if (!FLAG_IS_DEFAULT(CreateCoredumpOnCrash) && !CreateCoredumpOnCrash) { - jio_snprintf(buffer, buffsz, "CreateCoredumpOnCrash is disabled from command line"); - status = false; - } - + jio_snprintf(buffer, bufferSize, "CreateCoredumpOnCrash is disabled from command line"); + VMError::record_coredump_status(buffer, false); + } else { + bool success = true; + bool warn = true; #ifndef ASSERT - if (!os::win32::is_windows_server() && FLAG_IS_DEFAULT(CreateCoredumpOnCrash)) { - jio_snprintf(buffer, buffsz, "Minidumps are not enabled by default on client versions of Windows"); - status = false; - } + if (!os::win32::is_windows_server() && FLAG_IS_DEFAULT(CreateCoredumpOnCrash)) { + jio_snprintf(buffer, bufferSize, "Minidumps are not enabled by default on client versions of Windows"); + success = false; + warn = true; + } #endif - if (status) { - const char* cwd = get_current_directory(nullptr, 0); - int pid = current_process_id(); - if (cwd != nullptr) { - jio_snprintf(buffer, buffsz, "%s\\hs_err_pid%u.mdmp", cwd, pid); - } else { - jio_snprintf(buffer, buffsz, ".\\hs_err_pid%u.mdmp", pid); + if (success) { + if (!check_only) { + const char* cwd = get_current_directory(nullptr, 0); + int pid = current_process_id(); + if (cwd != nullptr) { + jio_snprintf(buffer, bufferSize, "%s\\hs_err_pid%u.mdmp", cwd, pid); + } else { + jio_snprintf(buffer, bufferSize, ".\\hs_err_pid%u.mdmp", pid); + } + + if (dumpFile == nullptr && + (dumpFile = CreateFile(buffer, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)) + == INVALID_HANDLE_VALUE) { + jio_snprintf(buffer, bufferSize, "Failed to create minidump file (0x%x).", GetLastError()); + success = false; + } + } else { + // For now on Windows, there are no more checks that we can do + warn = false; + } } - if (dumpFile == nullptr && - (dumpFile = CreateFile(buffer, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)) - == INVALID_HANDLE_VALUE) { - jio_snprintf(buffer, buffsz, "Failed to create minidump file (0x%x).", GetLastError()); - status = false; + if (!check_only) { + VMError::record_coredump_status(buffer, success); + } else if (warn) { + warning("CreateCoredumpOnCrash specified, but %s", buffer); } } - VMError::record_coredump_status(buffer, status); } void os::abort(bool dump_core, void* siginfo, const void* context) { diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 35a5be77b60..65071769bc4 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -939,7 +939,7 @@ class os: AllStatic { // provided buffer as a scratch buffer. The status message which will be written // into the error log either is file location or a short error message, depending // on the checking result. - static void check_dump_limit(char* buffer, size_t bufferSize); + static void check_core_dump_prerequisites(char* buffer, size_t bufferSize, bool check_only = false); // Get the default path to the core file // Returns the length of the string diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index 45aaa769856..b2edad395ce 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -65,6 +65,7 @@ #include "runtime/fieldDescriptor.inline.hpp" #include "runtime/flags/jvmFlagLimit.hpp" #include "runtime/globals.hpp" +#include "runtime/globals_extension.hpp" #include "runtime/handles.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/java.hpp" @@ -665,6 +666,11 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { log_info(os)("Initialized VM with process ID %d", os::current_process_id()); + if (!FLAG_IS_DEFAULT(CreateCoredumpOnCrash) && CreateCoredumpOnCrash) { + char buffer[2*JVM_MAXPATHLEN]; + os::check_core_dump_prerequisites(buffer, sizeof(buffer), true); + } + // Signal Dispatcher needs to be started before VMInit event is posted os::initialize_jdk_signal_support(CHECK_JNI_ERR); diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 0b3ccf9c747..7bbb978b806 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2020 SAP SE. All rights reserved. * Copyright (c) 2023, Red Hat, Inc. and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -1696,7 +1696,7 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt ShowMessageBoxOnError = false; } - os::check_dump_limit(buffer, sizeof(buffer)); + os::check_core_dump_prerequisites(buffer, sizeof(buffer)); // reset signal handlers or exception filter; make sure recursive crashes // are handled properly. diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/CreateCoredumpOnCrash.java b/test/hotspot/jtreg/runtime/ErrorHandling/CreateCoredumpOnCrash.java index 73efc0e5e46..76ea10d77cb 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/CreateCoredumpOnCrash.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/CreateCoredumpOnCrash.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * 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 @@ * java.management * jdk.internal.jvmstat/sun.jvmstat.monitor * @run driver CreateCoredumpOnCrash + * @requires vm.flagless */ import jdk.test.lib.process.ProcessTools; @@ -43,24 +44,61 @@ public static void main(String[] args) { } } + private static String ulimitString(int limit) { + String string = "ulimit -c "; + if (limit != Integer.MAX_VALUE) { + string += limit; + } else { + string += "unlimited"; + } + return string+";"; + } + public static void main(String[] args) throws Exception { runTest("-XX:-CreateCoredumpOnCrash").shouldContain("CreateCoredumpOnCrash turned off, no core file dumped") - .shouldNotHaveExitValue(0); + .shouldNotHaveExitValue(0); if (Platform.isWindows()) { // The old CreateMinidumpOnCrash option should still work runTest("-XX:-CreateMinidumpOnCrash").shouldContain("CreateCoredumpOnCrash turned off, no core file dumped") - .shouldNotHaveExitValue(0); + .shouldNotHaveExitValue(0); } else { - runTest("-XX:+CreateCoredumpOnCrash").shouldNotContain("CreateCoredumpOnCrash turned off, no core file dumped") - .shouldNotHaveExitValue(0); - } + String exec_cmd[] = {"sh", "-c", "ulimit -c"}; + OutputAnalyzer oa = new OutputAnalyzer(Runtime.getRuntime().exec(exec_cmd)); + oa.shouldHaveExitValue(0); + if (!oa.contains("0\n")) { + oa = runTest("-XX:+CreateCoredumpOnCrash"); + oa.shouldContain("Core dump will be written."); + oa.shouldNotHaveExitValue(0); + oa = runTest("-XX:+CreateCoredumpOnCrash", ulimitString(1024)); + oa.shouldContain("warning: CreateCoredumpOnCrash specified, but"); + oa.shouldNotHaveExitValue(0); + + oa = runTest("-XX:+CreateCoredumpOnCrash", ulimitString(0)); + oa.shouldContain("warning: CreateCoredumpOnCrash specified, but"); + oa.shouldNotHaveExitValue(0); + } else { + throw new Exception("ulimit is not set correctly, try 'ulimit -c unlimited' and re-run."); + } + } } + public static OutputAnalyzer runTest(String option) throws Exception { - return new OutputAnalyzer( - ProcessTools.createLimitedTestJavaProcessBuilder( - "-Xmx128m", "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", option, Crasher.class.getName()) - .start()); + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xmx128m", + "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", + option, Crasher.class.getName()); + return new OutputAnalyzer(pb.start()); + } + public static OutputAnalyzer runTest(String option, String limit) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xmx128m", + "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", + option, new String("'"+Crasher.class.getName()+"'")); + String args = ""; + for (String s:pb.command()) { + args += s+" "; + } + String exec_cmd[] = {"sh", "-c", limit+args}; + return new OutputAnalyzer(Runtime.getRuntime().exec(exec_cmd)); } } From 709914fc92dd180c8f081ff70ef476554a04f4ce Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Wed, 16 Oct 2024 16:08:02 +0000 Subject: [PATCH 80/95] 8338023: Support two vector selectFrom API Reviewed-by: psandoz, epeter, sviswanathan --- src/hotspot/cpu/x86/assembler_x86.cpp | 72 +++-- src/hotspot/cpu/x86/assembler_x86.hpp | 7 +- src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp | 27 ++ src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp | 2 + src/hotspot/cpu/x86/x86.ad | 27 ++ src/hotspot/share/adlc/formssel.cpp | 2 +- src/hotspot/share/classfile/vmIntrinsics.hpp | 11 + src/hotspot/share/opto/c2compiler.cpp | 1 + src/hotspot/share/opto/classes.hpp | 1 + src/hotspot/share/opto/library_call.cpp | 2 + src/hotspot/share/opto/library_call.hpp | 1 + src/hotspot/share/opto/matcher.cpp | 1 + src/hotspot/share/opto/vectorIntrinsics.cpp | 185 ++++++++++++- src/hotspot/share/opto/vectornode.cpp | 2 + src/hotspot/share/opto/vectornode.hpp | 15 ++ .../jdk/internal/vm/vector/VectorSupport.java | 18 ++ .../jdk/incubator/vector/Byte128Vector.java | 7 + .../jdk/incubator/vector/Byte256Vector.java | 7 + .../jdk/incubator/vector/Byte512Vector.java | 7 + .../jdk/incubator/vector/Byte64Vector.java | 7 + .../jdk/incubator/vector/ByteMaxVector.java | 7 + .../jdk/incubator/vector/ByteVector.java | 29 ++ .../jdk/incubator/vector/Double128Vector.java | 7 + .../jdk/incubator/vector/Double256Vector.java | 7 + .../jdk/incubator/vector/Double512Vector.java | 7 + .../jdk/incubator/vector/Double64Vector.java | 7 + .../jdk/incubator/vector/DoubleMaxVector.java | 7 + .../jdk/incubator/vector/DoubleVector.java | 29 ++ .../jdk/incubator/vector/Float128Vector.java | 7 + .../jdk/incubator/vector/Float256Vector.java | 7 + .../jdk/incubator/vector/Float512Vector.java | 7 + .../jdk/incubator/vector/Float64Vector.java | 7 + .../jdk/incubator/vector/FloatMaxVector.java | 7 + .../jdk/incubator/vector/FloatVector.java | 29 ++ .../jdk/incubator/vector/Int128Vector.java | 7 + .../jdk/incubator/vector/Int256Vector.java | 7 + .../jdk/incubator/vector/Int512Vector.java | 7 + .../jdk/incubator/vector/Int64Vector.java | 7 + .../jdk/incubator/vector/IntMaxVector.java | 7 + .../jdk/incubator/vector/IntVector.java | 29 ++ .../jdk/incubator/vector/Long128Vector.java | 7 + .../jdk/incubator/vector/Long256Vector.java | 7 + .../jdk/incubator/vector/Long512Vector.java | 7 + .../jdk/incubator/vector/Long64Vector.java | 7 + .../jdk/incubator/vector/LongMaxVector.java | 7 + .../jdk/incubator/vector/LongVector.java | 29 ++ .../jdk/incubator/vector/Short128Vector.java | 7 + .../jdk/incubator/vector/Short256Vector.java | 7 + .../jdk/incubator/vector/Short512Vector.java | 7 + .../jdk/incubator/vector/Short64Vector.java | 7 + .../jdk/incubator/vector/ShortMaxVector.java | 7 + .../jdk/incubator/vector/ShortVector.java | 29 ++ .../classes/jdk/incubator/vector/Vector.java | 55 ++++ .../jdk/incubator/vector/VectorOperators.java | 1 + .../incubator/vector/X-Vector.java.template | 29 ++ .../vector/X-VectorBits.java.template | 7 + .../incubator/vector/Byte128VectorTests.java | 55 ++++ .../incubator/vector/Byte256VectorTests.java | 55 ++++ .../incubator/vector/Byte512VectorTests.java | 55 ++++ .../incubator/vector/Byte64VectorTests.java | 55 ++++ .../incubator/vector/ByteMaxVectorTests.java | 55 ++++ .../vector/Double128VectorTests.java | 55 ++++ .../vector/Double256VectorTests.java | 55 ++++ .../vector/Double512VectorTests.java | 55 ++++ .../incubator/vector/Double64VectorTests.java | 55 ++++ .../vector/DoubleMaxVectorTests.java | 55 ++++ .../incubator/vector/Float128VectorTests.java | 55 ++++ .../incubator/vector/Float256VectorTests.java | 55 ++++ .../incubator/vector/Float512VectorTests.java | 55 ++++ .../incubator/vector/Float64VectorTests.java | 55 ++++ .../incubator/vector/FloatMaxVectorTests.java | 55 ++++ .../incubator/vector/Int128VectorTests.java | 55 ++++ .../incubator/vector/Int256VectorTests.java | 55 ++++ .../incubator/vector/Int512VectorTests.java | 55 ++++ .../incubator/vector/Int64VectorTests.java | 55 ++++ .../incubator/vector/IntMaxVectorTests.java | 55 ++++ .../incubator/vector/Long128VectorTests.java | 55 ++++ .../incubator/vector/Long256VectorTests.java | 55 ++++ .../incubator/vector/Long512VectorTests.java | 55 ++++ .../incubator/vector/Long64VectorTests.java | 55 ++++ .../incubator/vector/LongMaxVectorTests.java | 55 ++++ .../incubator/vector/Short128VectorTests.java | 55 ++++ .../incubator/vector/Short256VectorTests.java | 55 ++++ .../incubator/vector/Short512VectorTests.java | 55 ++++ .../incubator/vector/Short64VectorTests.java | 55 ++++ .../incubator/vector/ShortMaxVectorTests.java | 55 ++++ .../templates/Unit-Miscellaneous.template | 18 ++ .../vector/templates/Unit-header.template | 37 +++ .../incubator/vector/SelectFromBenchmark.java | 251 ++++++++++++++++++ 89 files changed, 2787 insertions(+), 19 deletions(-) create mode 100644 test/micro/org/openjdk/bench/jdk/incubator/vector/SelectFromBenchmark.java diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index c1679cd111f..8b61fd27de0 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -4738,22 +4738,6 @@ void Assembler::vpermpd(XMMRegister dst, XMMRegister src, int imm8, int vector_l emit_int24(0x01, (0xC0 | encode), imm8); } -void Assembler::evpermi2q(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - assert(VM_Version::supports_evex(), ""); - InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); - attributes.set_is_evex_instruction(); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); - emit_int16(0x76, (0xC0 | encode)); -} - -void Assembler::evpermt2b(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - assert(VM_Version::supports_avx512_vbmi(), ""); - InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); - attributes.set_is_evex_instruction(); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); - emit_int16(0x7D, (0xC0 | encode)); -} - void Assembler::evpmultishiftqb(XMMRegister dst, XMMRegister ctl, XMMRegister src, int vector_len) { assert(VM_Version::supports_avx512_vbmi(), ""); InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); @@ -16103,3 +16087,59 @@ void InstructionAttr::set_address_attributes(int tuple_type, int input_size_in_b _input_size_in_bits = input_size_in_bits; } } + +void Assembler::evpermi2b(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx512_vbmi() && (vector_len == Assembler::AVX_512bit || VM_Version::supports_avx512vl()), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x75, (0xC0 | encode)); +} + +void Assembler::evpermi2w(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx512bw() && (vector_len == Assembler::AVX_512bit || VM_Version::supports_avx512vl()), ""); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x75, (0xC0 | encode)); +} + +void Assembler::evpermi2d(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_evex() && (vector_len == Assembler::AVX_512bit || VM_Version::supports_avx512vl()), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x76, (0xC0 | encode)); +} + +void Assembler::evpermi2q(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_evex() && (vector_len == Assembler::AVX_512bit || VM_Version::supports_avx512vl()), ""); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x76, (0xC0 | encode)); +} + +void Assembler::evpermi2ps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_evex() && (vector_len == Assembler::AVX_512bit || VM_Version::supports_avx512vl()), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x77, (0xC0 | encode)); +} + +void Assembler::evpermi2pd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_evex() && (vector_len == Assembler::AVX_512bit || VM_Version::supports_avx512vl()), ""); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x77, (0xC0 | encode)); +} + +void Assembler::evpermt2b(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx512_vbmi(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int16(0x7D, (0xC0 | encode)); +} diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index eace7bb9cc1..696fff5e3eb 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -1962,9 +1962,14 @@ class Assembler : public AbstractAssembler { void vpermilps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void vpermilpd(XMMRegister dst, XMMRegister src, int imm8, int vector_len); void vpermpd(XMMRegister dst, XMMRegister src, int imm8, int vector_len); + void evpmultishiftqb(XMMRegister dst, XMMRegister ctl, XMMRegister src, int vector_len); + void evpermi2b(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void evpermi2w(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void evpermi2d(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void evpermi2q(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void evpermi2ps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void evpermi2pd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void evpermt2b(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); - void evpmultishiftqb(XMMRegister dst, XMMRegister ctl, XMMRegister src, int vector_len); void pause(); diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index aba5344b7e4..0eab2de2c64 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -6475,3 +6475,30 @@ void C2_MacroAssembler::vector_rearrange_int_float(BasicType bt, XMMRegister dst vpermps(dst, shuffle, src, vlen_enc); } } + +void C2_MacroAssembler::select_from_two_vectors_evex(BasicType elem_bt, XMMRegister dst, XMMRegister src1, + XMMRegister src2, int vlen_enc) { + switch(elem_bt) { + case T_BYTE: + evpermi2b(dst, src1, src2, vlen_enc); + break; + case T_SHORT: + evpermi2w(dst, src1, src2, vlen_enc); + break; + case T_INT: + evpermi2d(dst, src1, src2, vlen_enc); + break; + case T_LONG: + evpermi2q(dst, src1, src2, vlen_enc); + break; + case T_FLOAT: + evpermi2ps(dst, src1, src2, vlen_enc); + break; + case T_DOUBLE: + evpermi2pd(dst, src1, src2, vlen_enc); + break; + default: + fatal("Unsupported type %s", type2name(elem_bt)); + break; + } +} diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp index af57546b3d1..5744fedcc64 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp @@ -505,4 +505,6 @@ void vgather8b_offset(BasicType elem_bt, XMMRegister dst, Register base, Register idx_base, Register offset, Register rtmp, int vlen_enc); + void select_from_two_vectors_evex(BasicType elem_bt, XMMRegister dst, XMMRegister src1, XMMRegister src2, int vlen_enc); + #endif // CPU_X86_C2_MACROASSEMBLER_X86_HPP diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index c88fa1ec5ce..7684febb8ae 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -1935,6 +1935,20 @@ bool Matcher::match_rule_supported_vector(int opcode, int vlen, BasicType bt) { return false; } break; + case Op_SelectFromTwoVector: + if (size_in_bits < 128 || (size_in_bits < 512 && !VM_Version::supports_avx512vl())) { + return false; + } + if (bt == T_SHORT && !VM_Version::supports_avx512bw()) { + return false; + } + if (bt == T_BYTE && !VM_Version::supports_avx512_vbmi()) { + return false; + } + if ((bt == T_INT || bt == T_FLOAT || bt == T_DOUBLE) && !VM_Version::supports_evex()) { + return false; + } + break; case Op_MaskAll: if (!VM_Version::supports_evex()) { return false; @@ -10468,3 +10482,16 @@ instruct DoubleClassCheck_reg_reg_vfpclass(rRegI dst, regD src, kReg ktmp, rFlag %} ins_pipe(pipe_slow); %} + + +instruct vector_selectfrom_twovectors_reg_evex(vec index, vec src1, vec src2) +%{ + match(Set index (SelectFromTwoVector (Binary index src1) src2)); + format %{ "select_from_two_vector $index, $src1, $src2 \t!" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + BasicType bt = Matcher::vector_element_basic_type(this); + __ select_from_two_vectors_evex(bt, $index$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vlen_enc); + %} + ins_pipe(pipe_slow); +%} diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index ac2d3d94153..e7dd00fa390 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -4355,7 +4355,7 @@ bool MatchRule::is_vector() const { "Replicate","ReverseV","ReverseBytesV", "RoundDoubleModeV","RotateLeftV" , "RotateRightV", "LoadVector","StoreVector", "LoadVectorGather", "StoreVectorScatter", "LoadVectorGatherMasked", "StoreVectorScatterMasked", - "VectorTest", "VectorLoadMask", "VectorStoreMask", "VectorBlend", "VectorInsert", + "SelectFromTwoVector", "VectorTest", "VectorLoadMask", "VectorStoreMask", "VectorBlend", "VectorInsert", "VectorRearrange", "VectorLoadShuffle", "VectorLoadConst", "VectorCastB2X", "VectorCastS2X", "VectorCastI2X", "VectorCastL2X", "VectorCastF2X", "VectorCastD2X", "VectorCastF2HF", "VectorCastHF2F", diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 9bb8b2179ae..54912a5ded7 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -981,6 +981,17 @@ class methodHandle; "Ljdk/internal/vm/vector/VectorSupport$Vector;") \ do_name(vector_ternary_op_name, "ternaryOp") \ \ + do_intrinsic(_VectorSelectFromTwoVectorOp, jdk_internal_vm_vector_VectorSupport, vector_select_from_op_name, vector_select_from_op_sig, F_S) \ + do_signature(vector_select_from_op_sig, "(Ljava/lang/Class;" \ + "Ljava/lang/Class;" \ + "I" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ + "Ljdk/internal/vm/vector/VectorSupport$SelectFromTwoVector;)" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;") \ + do_name(vector_select_from_op_name, "selectFromTwoVectorOp") \ + \ do_intrinsic(_VectorFromBitsCoerced, jdk_internal_vm_vector_VectorSupport, vector_frombits_coerced_name, vector_frombits_coerced_sig, F_S) \ do_signature(vector_frombits_coerced_sig, "(Ljava/lang/Class;" \ "Ljava/lang/Class;" \ diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index 1a00f17f505..fa0abf2deb1 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -818,6 +818,7 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_VectorLoadMaskedOp: case vmIntrinsics::_VectorStoreOp: case vmIntrinsics::_VectorStoreMaskedOp: + case vmIntrinsics::_VectorSelectFromTwoVectorOp: case vmIntrinsics::_VectorGatherOp: case vmIntrinsics::_VectorScatterOp: case vmIntrinsics::_VectorReductionCoerced: diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 215e48ef9da..de6d48ebdf9 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -482,6 +482,7 @@ macro(Digit) macro(LowerCase) macro(UpperCase) macro(Whitespace) +macro(SelectFromTwoVector) macro(VectorBox) macro(VectorBoxAllocate) macro(VectorUnbox) diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 49750cd2697..c7c9da18e54 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -755,6 +755,8 @@ bool LibraryCallKit::try_to_inline(int predicate) { return inline_vector_extract(); case vmIntrinsics::_VectorCompressExpand: return inline_vector_compress_expand(); + case vmIntrinsics::_VectorSelectFromTwoVectorOp: + return inline_vector_select_from_two_vectors(); case vmIntrinsics::_IndexVector: return inline_index_vector(); case vmIntrinsics::_IndexPartiallyInUpperRange: diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index c9b2a02d3f1..c5437e3bf73 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -374,6 +374,7 @@ class LibraryCallKit : public GraphKit { bool inline_vector_compress_expand(); bool inline_index_vector(); bool inline_index_partially_in_upper_range(); + bool inline_vector_select_from_two_vectors(); Node* gen_call_to_vector_math(int vector_api_op_id, BasicType bt, int num_elem, Node* opd1, Node* opd2); diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index 2031b09ca9d..920178a0d04 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -2436,6 +2436,7 @@ void Matcher::find_shared_post_visit(Node* n, uint opcode) { n->del_req(4); break; } + case Op_SelectFromTwoVector: case Op_LoopLimit: { Node* pair1 = new BinaryNode(n->in(1), n->in(2)); n->set_req(1, pair1); diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index 838f87eac01..fe1807b9588 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -2356,9 +2356,10 @@ bool LibraryCallKit::inline_vector_broadcast_int() { } Node* cnt = argument(6); + const TypeInt* cnt_type = cnt->bottom_type()->isa_int(); + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); - const TypeInt* cnt_type = cnt->bottom_type()->isa_int(); // If CPU supports vector constant rotate instructions pass it directly bool is_const_rotate = is_rotate && cnt_type && cnt_type->is_con() && @@ -2841,6 +2842,188 @@ bool LibraryCallKit::inline_vector_extract() { return true; } +static Node* LowerSelectFromTwoVectorOperation(PhaseGVN& phase, Node* index_vec, Node* src1, Node* src2, const TypeVect* vt) { + int num_elem = vt->length(); + BasicType elem_bt = vt->element_basic_type(); + + // Lower selectFrom operation into its constituent operations. + // SelectFromTwoVectorNode = + // (VectorBlend + // (VectorRearrange SRC1 (WRAPED_INDEX AND (VLEN-1)) + // (VectorRearrange SRC2 (WRAPED_INDEX AND (VLEN-1)) + // MASK) + // Where + // WRAPED_INDEX are computed by wrapping incoming indexes + // to two vector index range [0, VLEN*2) and + // MASK = WRAPED_INDEX < VLEN + // + // IR lowering prevents intrinsification failure and associated argument + // boxing penalties. + // + + const TypeVect* index_vect_type = index_vec->bottom_type()->is_vect(); + BasicType index_elem_bt = index_vect_type->element_basic_type(); + + // Downcast index vector to a type agnostic shuffle representation, shuffle + // indices are held in a byte vector which are later transformed to target + // specific permutation index format by subsequent VectorLoadShuffle. + int cast_vopc = VectorCastNode::opcode(0, index_elem_bt, true); + Node* index_byte_vec = phase.transform(VectorCastNode::make(cast_vopc, index_vec, T_BYTE, num_elem)); + + // Wrap indexes into two vector index range [0, VLEN * 2) + Node* two_vect_lane_cnt_m1 = phase.makecon(TypeInt::make(2 * num_elem - 1)); + Node* bcast_two_vect_lane_cnt_m1_vec = phase.transform(VectorNode::scalar2vector(two_vect_lane_cnt_m1, num_elem, + Type::get_const_basic_type(T_BYTE), false)); + index_byte_vec = phase.transform(VectorNode::make(Op_AndV, index_byte_vec, bcast_two_vect_lane_cnt_m1_vec, + index_byte_vec->bottom_type()->is_vect())); + + // Compute the blend mask for merging two independently permitted vectors + // using shuffle index in two vector index range [0, VLEN * 2). + BoolTest::mask pred = BoolTest::le; + ConINode* pred_node = phase.makecon(TypeInt::make(pred))->as_ConI(); + const TypeVect* vmask_type = TypeVect::makemask(T_BYTE, num_elem); + Node* lane_cnt_m1 = phase.makecon(TypeInt::make(num_elem - 1)); + Node* bcast_lane_cnt_m1_vec = phase.transform(VectorNode::scalar2vector(lane_cnt_m1, num_elem, + Type::get_const_basic_type(T_BYTE), false)); + Node* mask = phase.transform(new VectorMaskCmpNode(pred, index_byte_vec, bcast_lane_cnt_m1_vec, pred_node, vmask_type)); + + // Rearrange expects the indexes to lie within single vector index range [0, VLEN). + index_byte_vec = phase.transform(VectorNode::make(Op_AndV, index_byte_vec, bcast_lane_cnt_m1_vec, + index_byte_vec->bottom_type()->is_vect())); + + // Load indexes from byte vector and appropriately transform them to target + // specific permutation index format. + index_vec = phase.transform(new VectorLoadShuffleNode(index_byte_vec, index_vect_type)); + + vmask_type = TypeVect::makemask(elem_bt, num_elem); + mask = phase.transform(new VectorMaskCastNode(mask, vmask_type)); + + Node* p1 = phase.transform(new VectorRearrangeNode(src1, index_vec)); + Node* p2 = phase.transform(new VectorRearrangeNode(src2, index_vec)); + + return new VectorBlendNode(p2, p1, mask); +} + +// public static +// , +// E> +// V selectFromTwoVectorOp(Class vClass, Class eClass, int length, +// V v1, V v2, V v3, +// SelectFromTwoVector defaultImpl) +bool LibraryCallKit::inline_vector_select_from_two_vectors() { + const TypeInstPtr* vector_klass = gvn().type(argument(0))->isa_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(1))->isa_instptr(); + const TypeInt* vlen = gvn().type(argument(2))->isa_int(); + + if (vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr || vector_klass->const_oop() == nullptr || + elem_klass->const_oop() == nullptr ||!vlen->is_con()) { + log_if_needed(" ** missing constant: vclass=%s etype=%s vlen=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(1)->Opcode()], + NodeClassNames[argument(2)->Opcode()]); + return false; // not enough info for intrinsification + } + + if (!is_klass_initialized(vector_klass)) { + log_if_needed(" ** klass argument not initialized"); + return false; + } + + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type->is_primitive_type()) { + log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); + return false; // should be primitive type + } + + int num_elem = vlen->get_con(); + if (!is_power_of_2(num_elem)) { + log_if_needed(" ** vlen is not power of two=%d", num_elem); + return false; + } + + BasicType elem_bt = elem_type->basic_type(); + BasicType index_elem_bt = elem_bt; + if (elem_bt == T_FLOAT) { + index_elem_bt = T_INT; + } else if (elem_bt == T_DOUBLE) { + index_elem_bt = T_LONG; + } + + bool lowerSelectFromOp = false; + if (!arch_supports_vector(Op_SelectFromTwoVector, num_elem, elem_bt, VecMaskNotUsed)) { + int cast_vopc = VectorCastNode::opcode(-1, elem_bt, true); + if (!arch_supports_vector(Op_VectorMaskCmp, num_elem, T_BYTE, VecMaskNotUsed) || + !arch_supports_vector(Op_AndV, num_elem, T_BYTE, VecMaskNotUsed) || + !arch_supports_vector(Op_VectorMaskCast, num_elem, elem_bt, VecMaskNotUsed) || + !arch_supports_vector(Op_VectorBlend, num_elem, elem_bt, VecMaskUseLoad) || + !arch_supports_vector(Op_VectorRearrange, num_elem, elem_bt, VecMaskNotUsed) || + !arch_supports_vector(cast_vopc, num_elem, T_BYTE, VecMaskNotUsed) || + !arch_supports_vector(Op_VectorLoadShuffle, num_elem, index_elem_bt, VecMaskNotUsed) || + !arch_supports_vector(Op_Replicate, num_elem, T_BYTE, VecMaskNotUsed)) { + log_if_needed(" ** not supported: opc=%d vlen=%d etype=%s ismask=useload", + Op_SelectFromTwoVector, num_elem, type2name(elem_bt)); + return false; // not supported + } + lowerSelectFromOp = true; + } + + int cast_vopc = VectorCastNode::opcode(-1, elem_bt, true); + if (!lowerSelectFromOp) { + if (!arch_supports_vector(Op_AndV, num_elem, index_elem_bt, VecMaskNotUsed) || + !arch_supports_vector(Op_Replicate, num_elem, index_elem_bt, VecMaskNotUsed) || + (is_floating_point_type(elem_bt) && + !arch_supports_vector(cast_vopc, num_elem, index_elem_bt, VecMaskNotUsed))) { + log_if_needed(" ** index wrapping not supported: vlen=%d etype=%s" , + num_elem, type2name(elem_bt)); + return false; // not supported + } + } + + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + Node* opd1 = unbox_vector(argument(3), vbox_type, elem_bt, num_elem); + if (opd1 == nullptr) { + log_if_needed(" ** unbox failed v1=%s", + NodeClassNames[argument(3)->Opcode()]); + return false; + } + Node* opd2 = unbox_vector(argument(4), vbox_type, elem_bt, num_elem); + if (opd2 == nullptr) { + log_if_needed(" ** unbox failed v2=%s", + NodeClassNames[argument(4)->Opcode()]); + return false; + } + Node* opd3 = unbox_vector(argument(5), vbox_type, elem_bt, num_elem); + if (opd3 == nullptr) { + log_if_needed(" ** unbox failed v3=%s", + NodeClassNames[argument(5)->Opcode()]); + return false; + } + + const TypeVect* vt = TypeVect::make(elem_bt, num_elem); + + Node* operation = nullptr; + if (lowerSelectFromOp) { + operation = gvn().transform(LowerSelectFromTwoVectorOperation(gvn(), opd1, opd2, opd3, vt)); + } else { + if (index_elem_bt != elem_bt) { + opd1 = gvn().transform(VectorCastNode::make(cast_vopc, opd1, index_elem_bt, num_elem)); + } + int indexRangeMask = 2 * num_elem - 1; + Node* wrap_mask = gvn().makecon(TypeInteger::make(indexRangeMask, indexRangeMask, Type::WidenMin, index_elem_bt != T_LONG ? T_INT : index_elem_bt)); + Node* wrap_mask_vec = gvn().transform(VectorNode::scalar2vector(wrap_mask, num_elem, Type::get_const_basic_type(index_elem_bt), false)); + opd1 = gvn().transform(VectorNode::make(Op_AndV, opd1, wrap_mask_vec, opd1->bottom_type()->is_vect())); + operation = gvn().transform(VectorNode::make(Op_SelectFromTwoVector, opd1, opd2, opd3, vt)); + } + + // Wrap it up in VectorBox to keep object type information. + Node* vbox = box_vector(operation, vbox_type, elem_bt, num_elem); + set_result(vbox); + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + // public static // , // M extends VectorMask, diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index 094d4dca564..fc1c951cfbd 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -781,6 +781,7 @@ VectorNode* VectorNode::make(int vopc, Node* n1, Node* n2, Node* n3, const TypeV switch (vopc) { case Op_FmaVD: return new FmaVDNode(n1, n2, n3, vt); case Op_FmaVF: return new FmaVFNode(n1, n2, n3, vt); + case Op_SelectFromTwoVector: return new SelectFromTwoVectorNode(n1, n2, n3, vt); case Op_SignumVD: return new SignumVDNode(n1, n2, n3, vt); case Op_SignumVF: return new SignumVFNode(n1, n2, n3, vt); default: @@ -2078,6 +2079,7 @@ Node* VectorBlendNode::Identity(PhaseGVN* phase) { return this; } + #ifndef PRODUCT void VectorBoxAllocateNode::dump_spec(outputStream *st) const { CallStaticJavaNode::dump_spec(st); diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index d23e6b8c926..256664983ff 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -1612,6 +1612,21 @@ class VectorRearrangeNode : public VectorNode { Node* vec_shuffle() const { return in(2); } }; + +// Select elements from two source vectors based on the wrapped indexes held in +// the first vector. +class SelectFromTwoVectorNode : public VectorNode { +public: + SelectFromTwoVectorNode(Node* indexes, Node* src1, Node* src2, const TypeVect* vt) + : VectorNode(indexes, src1, src2, vt) { + assert(is_integral_type(indexes->bottom_type()->is_vect()->element_basic_type()), + "indexes must be an integral vector"); + } + + virtual int Opcode() const; +}; + + class VectorLoadShuffleNode : public VectorNode { public: VectorLoadShuffleNode(Node* in, const TypeVect* vt) diff --git a/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java b/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java index 5c4040d9128..63cab418d46 100644 --- a/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java +++ b/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java @@ -395,6 +395,24 @@ VM binaryOp(int oprId, assert isNonCapturingLambda(defaultImpl) : defaultImpl; return defaultImpl.apply(v1, v2, m); } + /* ============================================================================ */ + + public interface SelectFromTwoVector> { + V apply(V v1, V v2, V v3); + } + + @IntrinsicCandidate + public static + , + E> + V selectFromTwoVectorOp(Class vClass, Class eClass, int length, + V v1, V v2, V v3, + SelectFromTwoVector defaultImpl) { + assert isNonCapturingLambda(defaultImpl) : defaultImpl; + return defaultImpl.apply(v1, v2, v3); + } + + /* ============================================================================ */ /* ============================================================================ */ diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte128Vector.java index 0bc25958a76..3cf25d46f44 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte128Vector.java @@ -506,6 +506,13 @@ public Byte128Vector selectFrom(Vector v, Byte128Mask.class, (Byte128Mask) m); // specialize } + @Override + @ForceInline + public Byte128Vector selectFrom(Vector v1, + Vector v2) { + return (Byte128Vector) + super.selectFromTemplate((Byte128Vector) v1, (Byte128Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte256Vector.java index 639646aa77a..cb9f2679dca 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte256Vector.java @@ -506,6 +506,13 @@ public Byte256Vector selectFrom(Vector v, Byte256Mask.class, (Byte256Mask) m); // specialize } + @Override + @ForceInline + public Byte256Vector selectFrom(Vector v1, + Vector v2) { + return (Byte256Vector) + super.selectFromTemplate((Byte256Vector) v1, (Byte256Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte512Vector.java index 2d8151f6800..f5ff0575703 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte512Vector.java @@ -506,6 +506,13 @@ public Byte512Vector selectFrom(Vector v, Byte512Mask.class, (Byte512Mask) m); // specialize } + @Override + @ForceInline + public Byte512Vector selectFrom(Vector v1, + Vector v2) { + return (Byte512Vector) + super.selectFromTemplate((Byte512Vector) v1, (Byte512Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte64Vector.java index bc8ed7d704d..37e8978d7e8 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte64Vector.java @@ -506,6 +506,13 @@ public Byte64Vector selectFrom(Vector v, Byte64Mask.class, (Byte64Mask) m); // specialize } + @Override + @ForceInline + public Byte64Vector selectFrom(Vector v1, + Vector v2) { + return (Byte64Vector) + super.selectFromTemplate((Byte64Vector) v1, (Byte64Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteMaxVector.java index 597afd8d165..17dcf193ceb 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteMaxVector.java @@ -506,6 +506,13 @@ public ByteMaxVector selectFrom(Vector v, ByteMaxMask.class, (ByteMaxMask) m); // specialize } + @Override + @ForceInline + public ByteMaxVector selectFrom(Vector v1, + Vector v2) { + return (ByteMaxVector) + super.selectFromTemplate((ByteMaxVector) v1, (ByteMaxVector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java index a23bbc7f709..11c0fda80a4 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java @@ -536,6 +536,19 @@ static ByteVector compressHelper(Vector v, VectorMask m) { return r; } + static ByteVector selectFromTwoVectorHelper(Vector indexes, Vector src1, Vector src2) { + int vlen = indexes.length(); + byte[] res = new byte[vlen]; + byte[] vecPayload1 = ((ByteVector)indexes).vec(); + byte[] vecPayload2 = ((ByteVector)src1).vec(); + byte[] vecPayload3 = ((ByteVector)src2).vec(); + for (int i = 0; i < vlen; i++) { + int wrapped_index = VectorIntrinsics.wrapToRange((int)vecPayload1[i], 2 * vlen); + res[i] = wrapped_index >= vlen ? vecPayload3[wrapped_index - vlen] : vecPayload2[wrapped_index]; + } + return ((ByteVector)src1).vectorFactory(res); + } + // Static factories (other than memory operations) // Note: A surprising behavior in javadoc @@ -2575,6 +2588,22 @@ ByteVector selectFromTemplate(ByteVector v, v2.rearrange(v1.toShuffle(), _m)); } + + /** + * {@inheritDoc} + */ + @Override + public abstract + ByteVector selectFrom(Vector v1, Vector v2); + + + /*package-private*/ + @ForceInline + final ByteVector selectFromTemplate(ByteVector v1, ByteVector v2) { + return VectorSupport.selectFromTwoVectorOp(getClass(), byte.class, length(), this, v1, v2, + (vec1, vec2, vec3) -> selectFromTwoVectorHelper(vec1, vec2, vec3)); + } + /// Ternary operations /** diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double128Vector.java index 00840026fff..37607492645 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double128Vector.java @@ -493,6 +493,13 @@ public Double128Vector selectFrom(Vector v, Double128Mask.class, (Double128Mask) m); // specialize } + @Override + @ForceInline + public Double128Vector selectFrom(Vector v1, + Vector v2) { + return (Double128Vector) + super.selectFromTemplate((Double128Vector) v1, (Double128Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double256Vector.java index 4b42deba738..2e31a802550 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double256Vector.java @@ -493,6 +493,13 @@ public Double256Vector selectFrom(Vector v, Double256Mask.class, (Double256Mask) m); // specialize } + @Override + @ForceInline + public Double256Vector selectFrom(Vector v1, + Vector v2) { + return (Double256Vector) + super.selectFromTemplate((Double256Vector) v1, (Double256Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double512Vector.java index c188f990c33..6ed3dd7325c 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double512Vector.java @@ -493,6 +493,13 @@ public Double512Vector selectFrom(Vector v, Double512Mask.class, (Double512Mask) m); // specialize } + @Override + @ForceInline + public Double512Vector selectFrom(Vector v1, + Vector v2) { + return (Double512Vector) + super.selectFromTemplate((Double512Vector) v1, (Double512Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double64Vector.java index 032fa1ac277..2e1b2135001 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double64Vector.java @@ -493,6 +493,13 @@ public Double64Vector selectFrom(Vector v, Double64Mask.class, (Double64Mask) m); // specialize } + @Override + @ForceInline + public Double64Vector selectFrom(Vector v1, + Vector v2) { + return (Double64Vector) + super.selectFromTemplate((Double64Vector) v1, (Double64Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleMaxVector.java index 7251ec82aa6..8d69b6fcbc7 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleMaxVector.java @@ -493,6 +493,13 @@ public DoubleMaxVector selectFrom(Vector v, DoubleMaxMask.class, (DoubleMaxMask) m); // specialize } + @Override + @ForceInline + public DoubleMaxVector selectFrom(Vector v1, + Vector v2) { + return (DoubleMaxVector) + super.selectFromTemplate((DoubleMaxVector) v1, (DoubleMaxVector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java index 6cc12048d46..d7fc2cfa97d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java @@ -525,6 +525,19 @@ static DoubleVector compressHelper(Vector v, VectorMask m) { return r; } + static DoubleVector selectFromTwoVectorHelper(Vector indexes, Vector src1, Vector src2) { + int vlen = indexes.length(); + double[] res = new double[vlen]; + double[] vecPayload1 = ((DoubleVector)indexes).vec(); + double[] vecPayload2 = ((DoubleVector)src1).vec(); + double[] vecPayload3 = ((DoubleVector)src2).vec(); + for (int i = 0; i < vlen; i++) { + int wrapped_index = VectorIntrinsics.wrapToRange((int)vecPayload1[i], 2 * vlen); + res[i] = wrapped_index >= vlen ? vecPayload3[wrapped_index - vlen] : vecPayload2[wrapped_index]; + } + return ((DoubleVector)src1).vectorFactory(res); + } + // Static factories (other than memory operations) // Note: A surprising behavior in javadoc @@ -2417,6 +2430,22 @@ DoubleVector selectFromTemplate(DoubleVector v, v2.rearrange(v1.toShuffle(), _m)); } + + /** + * {@inheritDoc} + */ + @Override + public abstract + DoubleVector selectFrom(Vector v1, Vector v2); + + + /*package-private*/ + @ForceInline + final DoubleVector selectFromTemplate(DoubleVector v1, DoubleVector v2) { + return VectorSupport.selectFromTwoVectorOp(getClass(), double.class, length(), this, v1, v2, + (vec1, vec2, vec3) -> selectFromTwoVectorHelper(vec1, vec2, vec3)); + } + /// Ternary operations diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float128Vector.java index 2e016725f81..79239532cc6 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float128Vector.java @@ -493,6 +493,13 @@ public Float128Vector selectFrom(Vector v, Float128Mask.class, (Float128Mask) m); // specialize } + @Override + @ForceInline + public Float128Vector selectFrom(Vector v1, + Vector v2) { + return (Float128Vector) + super.selectFromTemplate((Float128Vector) v1, (Float128Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float256Vector.java index 00e60835883..5f5a26fd316 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float256Vector.java @@ -493,6 +493,13 @@ public Float256Vector selectFrom(Vector v, Float256Mask.class, (Float256Mask) m); // specialize } + @Override + @ForceInline + public Float256Vector selectFrom(Vector v1, + Vector v2) { + return (Float256Vector) + super.selectFromTemplate((Float256Vector) v1, (Float256Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float512Vector.java index 1f2a792c52c..f8c191ea016 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float512Vector.java @@ -493,6 +493,13 @@ public Float512Vector selectFrom(Vector v, Float512Mask.class, (Float512Mask) m); // specialize } + @Override + @ForceInline + public Float512Vector selectFrom(Vector v1, + Vector v2) { + return (Float512Vector) + super.selectFromTemplate((Float512Vector) v1, (Float512Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float64Vector.java index 6c913ce84a9..9496e598868 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float64Vector.java @@ -493,6 +493,13 @@ public Float64Vector selectFrom(Vector v, Float64Mask.class, (Float64Mask) m); // specialize } + @Override + @ForceInline + public Float64Vector selectFrom(Vector v1, + Vector v2) { + return (Float64Vector) + super.selectFromTemplate((Float64Vector) v1, (Float64Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatMaxVector.java index b9a0a93f912..6f093957262 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatMaxVector.java @@ -493,6 +493,13 @@ public FloatMaxVector selectFrom(Vector v, FloatMaxMask.class, (FloatMaxMask) m); // specialize } + @Override + @ForceInline + public FloatMaxVector selectFrom(Vector v1, + Vector v2) { + return (FloatMaxVector) + super.selectFromTemplate((FloatMaxVector) v1, (FloatMaxVector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java index b962dc55ce3..098eed06095 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java @@ -525,6 +525,19 @@ static FloatVector compressHelper(Vector v, VectorMask m) { return r; } + static FloatVector selectFromTwoVectorHelper(Vector indexes, Vector src1, Vector src2) { + int vlen = indexes.length(); + float[] res = new float[vlen]; + float[] vecPayload1 = ((FloatVector)indexes).vec(); + float[] vecPayload2 = ((FloatVector)src1).vec(); + float[] vecPayload3 = ((FloatVector)src2).vec(); + for (int i = 0; i < vlen; i++) { + int wrapped_index = VectorIntrinsics.wrapToRange((int)vecPayload1[i], 2 * vlen); + res[i] = wrapped_index >= vlen ? vecPayload3[wrapped_index - vlen] : vecPayload2[wrapped_index]; + } + return ((FloatVector)src1).vectorFactory(res); + } + // Static factories (other than memory operations) // Note: A surprising behavior in javadoc @@ -2429,6 +2442,22 @@ FloatVector selectFromTemplate(FloatVector v, v2.rearrange(v1.toShuffle(), _m)); } + + /** + * {@inheritDoc} + */ + @Override + public abstract + FloatVector selectFrom(Vector v1, Vector v2); + + + /*package-private*/ + @ForceInline + final FloatVector selectFromTemplate(FloatVector v1, FloatVector v2) { + return VectorSupport.selectFromTwoVectorOp(getClass(), float.class, length(), this, v1, v2, + (vec1, vec2, vec3) -> selectFromTwoVectorHelper(vec1, vec2, vec3)); + } + /// Ternary operations diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int128Vector.java index f7135e19cb6..4aa1e8044b0 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int128Vector.java @@ -506,6 +506,13 @@ public Int128Vector selectFrom(Vector v, Int128Mask.class, (Int128Mask) m); // specialize } + @Override + @ForceInline + public Int128Vector selectFrom(Vector v1, + Vector v2) { + return (Int128Vector) + super.selectFromTemplate((Int128Vector) v1, (Int128Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int256Vector.java index 474ff974b31..753f96f216f 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int256Vector.java @@ -506,6 +506,13 @@ public Int256Vector selectFrom(Vector v, Int256Mask.class, (Int256Mask) m); // specialize } + @Override + @ForceInline + public Int256Vector selectFrom(Vector v1, + Vector v2) { + return (Int256Vector) + super.selectFromTemplate((Int256Vector) v1, (Int256Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int512Vector.java index 9fec8c0c99f..8e6ed6fc882 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int512Vector.java @@ -506,6 +506,13 @@ public Int512Vector selectFrom(Vector v, Int512Mask.class, (Int512Mask) m); // specialize } + @Override + @ForceInline + public Int512Vector selectFrom(Vector v1, + Vector v2) { + return (Int512Vector) + super.selectFromTemplate((Int512Vector) v1, (Int512Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int64Vector.java index 3b3c0723ee1..98cd39d9beb 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int64Vector.java @@ -506,6 +506,13 @@ public Int64Vector selectFrom(Vector v, Int64Mask.class, (Int64Mask) m); // specialize } + @Override + @ForceInline + public Int64Vector selectFrom(Vector v1, + Vector v2) { + return (Int64Vector) + super.selectFromTemplate((Int64Vector) v1, (Int64Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntMaxVector.java index 5738cb7a4bc..f301161b980 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntMaxVector.java @@ -506,6 +506,13 @@ public IntMaxVector selectFrom(Vector v, IntMaxMask.class, (IntMaxMask) m); // specialize } + @Override + @ForceInline + public IntMaxVector selectFrom(Vector v1, + Vector v2) { + return (IntMaxVector) + super.selectFromTemplate((IntMaxVector) v1, (IntMaxVector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java index 16b5ceecba3..b61e2fc991e 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java @@ -536,6 +536,19 @@ static IntVector compressHelper(Vector v, VectorMask m) { return r; } + static IntVector selectFromTwoVectorHelper(Vector indexes, Vector src1, Vector src2) { + int vlen = indexes.length(); + int[] res = new int[vlen]; + int[] vecPayload1 = ((IntVector)indexes).vec(); + int[] vecPayload2 = ((IntVector)src1).vec(); + int[] vecPayload3 = ((IntVector)src2).vec(); + for (int i = 0; i < vlen; i++) { + int wrapped_index = VectorIntrinsics.wrapToRange((int)vecPayload1[i], 2 * vlen); + res[i] = wrapped_index >= vlen ? vecPayload3[wrapped_index - vlen] : vecPayload2[wrapped_index]; + } + return ((IntVector)src1).vectorFactory(res); + } + // Static factories (other than memory operations) // Note: A surprising behavior in javadoc @@ -2560,6 +2573,22 @@ IntVector selectFromTemplate(IntVector v, v2.rearrange(v1.toShuffle(), _m)); } + + /** + * {@inheritDoc} + */ + @Override + public abstract + IntVector selectFrom(Vector v1, Vector v2); + + + /*package-private*/ + @ForceInline + final IntVector selectFromTemplate(IntVector v1, IntVector v2) { + return VectorSupport.selectFromTwoVectorOp(getClass(), int.class, length(), this, v1, v2, + (vec1, vec2, vec3) -> selectFromTwoVectorHelper(vec1, vec2, vec3)); + } + /// Ternary operations /** diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long128Vector.java index 567789627c6..c65816a4d6c 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long128Vector.java @@ -496,6 +496,13 @@ public Long128Vector selectFrom(Vector v, Long128Mask.class, (Long128Mask) m); // specialize } + @Override + @ForceInline + public Long128Vector selectFrom(Vector v1, + Vector v2) { + return (Long128Vector) + super.selectFromTemplate((Long128Vector) v1, (Long128Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long256Vector.java index 5ef0f121464..7ca3e43e92b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long256Vector.java @@ -496,6 +496,13 @@ public Long256Vector selectFrom(Vector v, Long256Mask.class, (Long256Mask) m); // specialize } + @Override + @ForceInline + public Long256Vector selectFrom(Vector v1, + Vector v2) { + return (Long256Vector) + super.selectFromTemplate((Long256Vector) v1, (Long256Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long512Vector.java index acdb471609f..317cac1f110 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long512Vector.java @@ -496,6 +496,13 @@ public Long512Vector selectFrom(Vector v, Long512Mask.class, (Long512Mask) m); // specialize } + @Override + @ForceInline + public Long512Vector selectFrom(Vector v1, + Vector v2) { + return (Long512Vector) + super.selectFromTemplate((Long512Vector) v1, (Long512Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long64Vector.java index 627f7437367..b13712595db 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long64Vector.java @@ -496,6 +496,13 @@ public Long64Vector selectFrom(Vector v, Long64Mask.class, (Long64Mask) m); // specialize } + @Override + @ForceInline + public Long64Vector selectFrom(Vector v1, + Vector v2) { + return (Long64Vector) + super.selectFromTemplate((Long64Vector) v1, (Long64Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongMaxVector.java index aec3bb89fcd..9edc442be88 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongMaxVector.java @@ -496,6 +496,13 @@ public LongMaxVector selectFrom(Vector v, LongMaxMask.class, (LongMaxMask) m); // specialize } + @Override + @ForceInline + public LongMaxVector selectFrom(Vector v1, + Vector v2) { + return (LongMaxVector) + super.selectFromTemplate((LongMaxVector) v1, (LongMaxVector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java index 15ac2bc7b7f..68166bd9852 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java @@ -536,6 +536,19 @@ static LongVector compressHelper(Vector v, VectorMask m) { return r; } + static LongVector selectFromTwoVectorHelper(Vector indexes, Vector src1, Vector src2) { + int vlen = indexes.length(); + long[] res = new long[vlen]; + long[] vecPayload1 = ((LongVector)indexes).vec(); + long[] vecPayload2 = ((LongVector)src1).vec(); + long[] vecPayload3 = ((LongVector)src2).vec(); + for (int i = 0; i < vlen; i++) { + int wrapped_index = VectorIntrinsics.wrapToRange((int)vecPayload1[i], 2 * vlen); + res[i] = wrapped_index >= vlen ? vecPayload3[wrapped_index - vlen] : vecPayload2[wrapped_index]; + } + return ((LongVector)src1).vectorFactory(res); + } + // Static factories (other than memory operations) // Note: A surprising behavior in javadoc @@ -2426,6 +2439,22 @@ LongVector selectFromTemplate(LongVector v, v2.rearrange(v1.toShuffle(), _m)); } + + /** + * {@inheritDoc} + */ + @Override + public abstract + LongVector selectFrom(Vector v1, Vector v2); + + + /*package-private*/ + @ForceInline + final LongVector selectFromTemplate(LongVector v1, LongVector v2) { + return VectorSupport.selectFromTwoVectorOp(getClass(), long.class, length(), this, v1, v2, + (vec1, vec2, vec3) -> selectFromTwoVectorHelper(vec1, vec2, vec3)); + } + /// Ternary operations /** diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short128Vector.java index fe34886512a..b013e4b2825 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short128Vector.java @@ -506,6 +506,13 @@ public Short128Vector selectFrom(Vector v, Short128Mask.class, (Short128Mask) m); // specialize } + @Override + @ForceInline + public Short128Vector selectFrom(Vector v1, + Vector v2) { + return (Short128Vector) + super.selectFromTemplate((Short128Vector) v1, (Short128Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short256Vector.java index 243e24ad26b..af4c862eaf3 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short256Vector.java @@ -506,6 +506,13 @@ public Short256Vector selectFrom(Vector v, Short256Mask.class, (Short256Mask) m); // specialize } + @Override + @ForceInline + public Short256Vector selectFrom(Vector v1, + Vector v2) { + return (Short256Vector) + super.selectFromTemplate((Short256Vector) v1, (Short256Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short512Vector.java index 41147836089..3bb019f3b7c 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short512Vector.java @@ -506,6 +506,13 @@ public Short512Vector selectFrom(Vector v, Short512Mask.class, (Short512Mask) m); // specialize } + @Override + @ForceInline + public Short512Vector selectFrom(Vector v1, + Vector v2) { + return (Short512Vector) + super.selectFromTemplate((Short512Vector) v1, (Short512Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short64Vector.java index d80d4c4e2ec..905e313e95c 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short64Vector.java @@ -506,6 +506,13 @@ public Short64Vector selectFrom(Vector v, Short64Mask.class, (Short64Mask) m); // specialize } + @Override + @ForceInline + public Short64Vector selectFrom(Vector v1, + Vector v2) { + return (Short64Vector) + super.selectFromTemplate((Short64Vector) v1, (Short64Vector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortMaxVector.java index 799483a6675..5bb1beee6ed 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortMaxVector.java @@ -506,6 +506,13 @@ public ShortMaxVector selectFrom(Vector v, ShortMaxMask.class, (ShortMaxMask) m); // specialize } + @Override + @ForceInline + public ShortMaxVector selectFrom(Vector v1, + Vector v2) { + return (ShortMaxVector) + super.selectFromTemplate((ShortMaxVector) v1, (ShortMaxVector) v2); // specialize + } @ForceInline @Override diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java index fb0512fd5b9..2f1ea210b90 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java @@ -536,6 +536,19 @@ static ShortVector compressHelper(Vector v, VectorMask m) { return r; } + static ShortVector selectFromTwoVectorHelper(Vector indexes, Vector src1, Vector src2) { + int vlen = indexes.length(); + short[] res = new short[vlen]; + short[] vecPayload1 = ((ShortVector)indexes).vec(); + short[] vecPayload2 = ((ShortVector)src1).vec(); + short[] vecPayload3 = ((ShortVector)src2).vec(); + for (int i = 0; i < vlen; i++) { + int wrapped_index = VectorIntrinsics.wrapToRange((int)vecPayload1[i], 2 * vlen); + res[i] = wrapped_index >= vlen ? vecPayload3[wrapped_index - vlen] : vecPayload2[wrapped_index]; + } + return ((ShortVector)src1).vectorFactory(res); + } + // Static factories (other than memory operations) // Note: A surprising behavior in javadoc @@ -2576,6 +2589,22 @@ ShortVector selectFromTemplate(ShortVector v, v2.rearrange(v1.toShuffle(), _m)); } + + /** + * {@inheritDoc} + */ + @Override + public abstract + ShortVector selectFrom(Vector v1, Vector v2); + + + /*package-private*/ + @ForceInline + final ShortVector selectFromTemplate(ShortVector v1, ShortVector v2) { + return VectorSupport.selectFromTwoVectorOp(getClass(), short.class, length(), this, v1, v2, + (vec1, vec2, vec3) -> selectFromTwoVectorHelper(vec1, vec2, vec3)); + } + /// Ternary operations /** diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java index fda073f6863..5b6dd3d09ac 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java @@ -2756,6 +2756,61 @@ public abstract VectorMask compare(VectorOperators.Comparison op, */ public abstract Vector selectFrom(Vector v); + /** + * Using values stored in the lanes of this vector, + * assemble values stored in the second vector {@code v1} + * and third vector {@code v2}. The second and third vectors thus + * serve as a table, whose elements are selected by indexes + * in this vector. + * + * This is a cross-lane operation that rearranges the lane + * elements of the argument vectors, under the control of + * this vector. + * + * For each lane {@code N} of this vector, and for each lane + * value {@code I=wrapIndex(this.lane(N)} in this vector, + * the output lane {@code N} obtains the value from + * the second vector at lane {@code I} if {@code I < VLENGTH}. + * Otherwise, the output lane {@code N} obtains the value from + * the third vector at lane {@code I - VLENGTH}. + * + * Here, {@code VLENGTH} is the result of {@code this.length()}, + * and for integral values {@code wrapIndex} computes the result of + * {@code Math.floorMod(E, 2 * VLENGTH)}, where {@code E} is the index + * to be wrapped. As long as {@code VLENGTH} is a power of two, then the + * result is also equal to {@code E & (2 * VLENGTH - 1)}. + * + * For floating point values {@code wrapIndex} computes + * {@code Math.floorMod(convert(E), 2 * VLENGTH)}, where {@code convert} + * converts the floating point value to an integral value with the same + * number of representational bits - as in converting a double value to + * a long value ({@code (long)doubleVal}), or a float value to an int value + * ({@code (int)floatVal}). + * + * In this way, the result contains only values stored in the + * argument vectors {@code v1} and {@code v2}, but presented in + * an order which depends on the index values in {@code this}. + * + * The result for integral values is the same as the expression + * {@snippet lang=java : + * v1.rearrange( + * this.lanewise(VectorOperators.AND, 2 * VLENGTH - 1).toShuffle(), + * v2) + * } + * when {@code VLENGTH} is a power of two. + * The lane-wise {@code AND} operation results in a vector whose + * elements are in the range {@code [0, 2 * VLENGTH - 1])}. The shuffle + * conversion results in a partially wrapped shuffle whose indexes are + * in the range {@code [-VLENGTH, VLENGTH - 1])}, where exceptional + * indexes are used to select elements in the third vector. + * + * @param v1 the first input vector + * @param v2 the second input vector + * @return the rearrangement of lane elements of {@code v1} and {@code v2} + * @see #rearrange(VectorShuffle,Vector) + */ + public abstract Vector selectFrom(Vector v1, Vector v2); + /** * Using index values stored in the lanes of this vector, * assemble values stored in second vector, under the control diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java index 507fe84bfba..38c4b1c94fe 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java @@ -563,6 +563,7 @@ static boolean opKind(Operator op, int bit) { public static final /*bitwise*/ Associative OR = assoc("OR", "|", VectorSupport.VECTOR_OP_OR, VO_NOFP+VO_ASSOC); /*package-private*/ /** Version of OR which works on float and double too. */ static final Associative OR_UNCHECKED = assoc("OR_UNCHECKED", "|", VectorSupport.VECTOR_OP_OR, VO_ASSOC+VO_PRIVATE); + /** Produce {@code a^b}. Integral only. */ public static final /*bitwise*/ Associative XOR = assoc("XOR", "^", VectorSupport.VECTOR_OP_XOR, VO_NOFP+VO_ASSOC); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template index fcc128ea8c7..b9a48005ccf 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template @@ -550,6 +550,19 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { return r; } + static $abstractvectortype$ selectFromTwoVectorHelper(Vector<$Boxtype$> indexes, Vector<$Boxtype$> src1, Vector<$Boxtype$> src2) { + int vlen = indexes.length(); + $type$[] res = new $type$[vlen]; + $type$[] vecPayload1 = (($abstractvectortype$)indexes).vec(); + $type$[] vecPayload2 = (($abstractvectortype$)src1).vec(); + $type$[] vecPayload3 = (($abstractvectortype$)src2).vec(); + for (int i = 0; i < vlen; i++) { + int wrapped_index = VectorIntrinsics.wrapToRange((int)vecPayload1[i], 2 * vlen); + res[i] = wrapped_index >= vlen ? vecPayload3[wrapped_index - vlen] : vecPayload2[wrapped_index]; + } + return (($abstractvectortype$)src1).vectorFactory(res); + } + // Static factories (other than memory operations) // Note: A surprising behavior in javadoc @@ -2952,6 +2965,22 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { v2.rearrange(v1.toShuffle(), _m)); } + + /** + * {@inheritDoc} + */ + @Override + public abstract + $abstractvectortype$ selectFrom(Vector<$Boxtype$> v1, Vector<$Boxtype$> v2); + + + /*package-private*/ + @ForceInline + final $abstractvectortype$ selectFromTemplate($abstractvectortype$ v1, $abstractvectortype$ v2) { + return VectorSupport.selectFromTwoVectorOp(getClass(), $type$.class, length(), this, v1, v2, + (vec1, vec2, vec3) -> selectFromTwoVectorHelper(vec1, vec2, vec3)); + } + /// Ternary operations #if[BITWISE] diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template index 483962b4e06..9752a795ea7 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template @@ -512,6 +512,13 @@ final class $vectortype$ extends $abstractvectortype$ { $masktype$.class, ($masktype$) m); // specialize } + @Override + @ForceInline + public $vectortype$ selectFrom(Vector<$Boxtype$> v1, + Vector<$Boxtype$> v2) { + return ($vectortype$) + super.selectFromTemplate(($vectortype$) v1, ($vectortype$) v2); // specialize + } #if[FP] @ForceInline diff --git a/test/jdk/jdk/incubator/vector/Byte128VectorTests.java b/test/jdk/jdk/incubator/vector/Byte128VectorTests.java index 77d0dd20974..eda803b3f35 100644 --- a/test/jdk/jdk/incubator/vector/Byte128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte128VectorTests.java @@ -302,6 +302,25 @@ static void assertexpandArraysEquals(byte[] r, byte[] a, boolean[] m, int vector } } + static void assertSelectFromTwoVectorEquals(byte[] r, byte[] order, byte[] a, byte[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(byte[] r, byte[] a, byte[] order, int vector_len) { int i = 0, j = 0; try { @@ -962,6 +981,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> BYTE_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("byte[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (byte)(RAND.nextInt())); + }) + ); + + static final List>> BYTE_GENERATOR_SELECT_FROM_TRIPLES = + BYTE_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] byteBinaryOpProvider() { return BYTE_GENERATOR_PAIRS.stream().map(List::toArray). @@ -989,6 +1020,12 @@ public Object[][] byteTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] byteSelectFromTwoVectorOpProvider() { + return BYTE_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] byteTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5746,6 +5783,24 @@ static void SelectFromByte128VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "byteSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorByte128VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + byte[] a = fa.apply(SPECIES.length()); + byte[] b = fb.apply(SPECIES.length()); + byte[] idx = fc.apply(SPECIES.length()); + byte[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + ByteVector bv = ByteVector.fromArray(SPECIES, b, i); + ByteVector idxv = ByteVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "byteUnaryOpSelectFromMaskProvider") static void SelectFromByte128VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Byte256VectorTests.java b/test/jdk/jdk/incubator/vector/Byte256VectorTests.java index 31e38f633ff..06cc13c0b12 100644 --- a/test/jdk/jdk/incubator/vector/Byte256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte256VectorTests.java @@ -302,6 +302,25 @@ static void assertexpandArraysEquals(byte[] r, byte[] a, boolean[] m, int vector } } + static void assertSelectFromTwoVectorEquals(byte[] r, byte[] order, byte[] a, byte[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(byte[] r, byte[] a, byte[] order, int vector_len) { int i = 0, j = 0; try { @@ -962,6 +981,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> BYTE_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("byte[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (byte)(RAND.nextInt())); + }) + ); + + static final List>> BYTE_GENERATOR_SELECT_FROM_TRIPLES = + BYTE_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] byteBinaryOpProvider() { return BYTE_GENERATOR_PAIRS.stream().map(List::toArray). @@ -989,6 +1020,12 @@ public Object[][] byteTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] byteSelectFromTwoVectorOpProvider() { + return BYTE_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] byteTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5746,6 +5783,24 @@ static void SelectFromByte256VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "byteSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorByte256VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + byte[] a = fa.apply(SPECIES.length()); + byte[] b = fb.apply(SPECIES.length()); + byte[] idx = fc.apply(SPECIES.length()); + byte[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + ByteVector bv = ByteVector.fromArray(SPECIES, b, i); + ByteVector idxv = ByteVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "byteUnaryOpSelectFromMaskProvider") static void SelectFromByte256VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Byte512VectorTests.java b/test/jdk/jdk/incubator/vector/Byte512VectorTests.java index 9204c3ed1ad..a75aa42ef20 100644 --- a/test/jdk/jdk/incubator/vector/Byte512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte512VectorTests.java @@ -302,6 +302,25 @@ static void assertexpandArraysEquals(byte[] r, byte[] a, boolean[] m, int vector } } + static void assertSelectFromTwoVectorEquals(byte[] r, byte[] order, byte[] a, byte[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(byte[] r, byte[] a, byte[] order, int vector_len) { int i = 0, j = 0; try { @@ -962,6 +981,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> BYTE_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("byte[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (byte)(RAND.nextInt())); + }) + ); + + static final List>> BYTE_GENERATOR_SELECT_FROM_TRIPLES = + BYTE_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] byteBinaryOpProvider() { return BYTE_GENERATOR_PAIRS.stream().map(List::toArray). @@ -989,6 +1020,12 @@ public Object[][] byteTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] byteSelectFromTwoVectorOpProvider() { + return BYTE_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] byteTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5746,6 +5783,24 @@ static void SelectFromByte512VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "byteSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorByte512VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + byte[] a = fa.apply(SPECIES.length()); + byte[] b = fb.apply(SPECIES.length()); + byte[] idx = fc.apply(SPECIES.length()); + byte[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + ByteVector bv = ByteVector.fromArray(SPECIES, b, i); + ByteVector idxv = ByteVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "byteUnaryOpSelectFromMaskProvider") static void SelectFromByte512VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Byte64VectorTests.java b/test/jdk/jdk/incubator/vector/Byte64VectorTests.java index 9af640a3133..b621c28a779 100644 --- a/test/jdk/jdk/incubator/vector/Byte64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte64VectorTests.java @@ -302,6 +302,25 @@ static void assertexpandArraysEquals(byte[] r, byte[] a, boolean[] m, int vector } } + static void assertSelectFromTwoVectorEquals(byte[] r, byte[] order, byte[] a, byte[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(byte[] r, byte[] a, byte[] order, int vector_len) { int i = 0, j = 0; try { @@ -962,6 +981,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> BYTE_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("byte[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (byte)(RAND.nextInt())); + }) + ); + + static final List>> BYTE_GENERATOR_SELECT_FROM_TRIPLES = + BYTE_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] byteBinaryOpProvider() { return BYTE_GENERATOR_PAIRS.stream().map(List::toArray). @@ -989,6 +1020,12 @@ public Object[][] byteTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] byteSelectFromTwoVectorOpProvider() { + return BYTE_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] byteTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5746,6 +5783,24 @@ static void SelectFromByte64VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "byteSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorByte64VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + byte[] a = fa.apply(SPECIES.length()); + byte[] b = fb.apply(SPECIES.length()); + byte[] idx = fc.apply(SPECIES.length()); + byte[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + ByteVector bv = ByteVector.fromArray(SPECIES, b, i); + ByteVector idxv = ByteVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "byteUnaryOpSelectFromMaskProvider") static void SelectFromByte64VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java b/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java index 1c0d5362b53..bbd354c958a 100644 --- a/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java @@ -307,6 +307,25 @@ static void assertexpandArraysEquals(byte[] r, byte[] a, boolean[] m, int vector } } + static void assertSelectFromTwoVectorEquals(byte[] r, byte[] order, byte[] a, byte[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(byte[] r, byte[] a, byte[] order, int vector_len) { int i = 0, j = 0; try { @@ -967,6 +986,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> BYTE_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("byte[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (byte)(RAND.nextInt())); + }) + ); + + static final List>> BYTE_GENERATOR_SELECT_FROM_TRIPLES = + BYTE_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] byteBinaryOpProvider() { return BYTE_GENERATOR_PAIRS.stream().map(List::toArray). @@ -994,6 +1025,12 @@ public Object[][] byteTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] byteSelectFromTwoVectorOpProvider() { + return BYTE_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] byteTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5751,6 +5788,24 @@ static void SelectFromByteMaxVectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "byteSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorByteMaxVectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + byte[] a = fa.apply(SPECIES.length()); + byte[] b = fb.apply(SPECIES.length()); + byte[] idx = fc.apply(SPECIES.length()); + byte[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + ByteVector bv = ByteVector.fromArray(SPECIES, b, i); + ByteVector idxv = ByteVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "byteUnaryOpSelectFromMaskProvider") static void SelectFromByteMaxVectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Double128VectorTests.java b/test/jdk/jdk/incubator/vector/Double128VectorTests.java index 4efeb8f2059..05678c4290b 100644 --- a/test/jdk/jdk/incubator/vector/Double128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double128VectorTests.java @@ -321,6 +321,25 @@ static void assertexpandArraysEquals(double[] r, double[] a, boolean[] m, int ve } } + static void assertSelectFromTwoVectorEquals(double[] r, double[] order, double[] a, double[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(double[] r, double[] a, double[] order, int vector_len) { int i = 0, j = 0; try { @@ -1108,6 +1127,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> DOUBLE_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("double[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (double)(RAND.nextInt())); + }) + ); + + static final List>> DOUBLE_GENERATOR_SELECT_FROM_TRIPLES = + DOUBLE_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] doubleBinaryOpProvider() { return DOUBLE_GENERATOR_PAIRS.stream().map(List::toArray). @@ -1135,6 +1166,12 @@ public Object[][] doubleTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] doubleSelectFromTwoVectorOpProvider() { + return DOUBLE_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] doubleTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4799,6 +4836,24 @@ static void SelectFromDouble128VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "doubleSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorDouble128VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + double[] a = fa.apply(SPECIES.length()); + double[] b = fb.apply(SPECIES.length()); + double[] idx = fc.apply(SPECIES.length()); + double[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); + DoubleVector bv = DoubleVector.fromArray(SPECIES, b, i); + DoubleVector idxv = DoubleVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "doubleUnaryOpSelectFromMaskProvider") static void SelectFromDouble128VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Double256VectorTests.java b/test/jdk/jdk/incubator/vector/Double256VectorTests.java index 04b0e7dc0d6..fe59fc85a2e 100644 --- a/test/jdk/jdk/incubator/vector/Double256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double256VectorTests.java @@ -321,6 +321,25 @@ static void assertexpandArraysEquals(double[] r, double[] a, boolean[] m, int ve } } + static void assertSelectFromTwoVectorEquals(double[] r, double[] order, double[] a, double[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(double[] r, double[] a, double[] order, int vector_len) { int i = 0, j = 0; try { @@ -1108,6 +1127,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> DOUBLE_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("double[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (double)(RAND.nextInt())); + }) + ); + + static final List>> DOUBLE_GENERATOR_SELECT_FROM_TRIPLES = + DOUBLE_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] doubleBinaryOpProvider() { return DOUBLE_GENERATOR_PAIRS.stream().map(List::toArray). @@ -1135,6 +1166,12 @@ public Object[][] doubleTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] doubleSelectFromTwoVectorOpProvider() { + return DOUBLE_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] doubleTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4799,6 +4836,24 @@ static void SelectFromDouble256VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "doubleSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorDouble256VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + double[] a = fa.apply(SPECIES.length()); + double[] b = fb.apply(SPECIES.length()); + double[] idx = fc.apply(SPECIES.length()); + double[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); + DoubleVector bv = DoubleVector.fromArray(SPECIES, b, i); + DoubleVector idxv = DoubleVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "doubleUnaryOpSelectFromMaskProvider") static void SelectFromDouble256VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Double512VectorTests.java b/test/jdk/jdk/incubator/vector/Double512VectorTests.java index ad03b8b5c7b..1e5b68ab989 100644 --- a/test/jdk/jdk/incubator/vector/Double512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double512VectorTests.java @@ -321,6 +321,25 @@ static void assertexpandArraysEquals(double[] r, double[] a, boolean[] m, int ve } } + static void assertSelectFromTwoVectorEquals(double[] r, double[] order, double[] a, double[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(double[] r, double[] a, double[] order, int vector_len) { int i = 0, j = 0; try { @@ -1108,6 +1127,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> DOUBLE_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("double[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (double)(RAND.nextInt())); + }) + ); + + static final List>> DOUBLE_GENERATOR_SELECT_FROM_TRIPLES = + DOUBLE_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] doubleBinaryOpProvider() { return DOUBLE_GENERATOR_PAIRS.stream().map(List::toArray). @@ -1135,6 +1166,12 @@ public Object[][] doubleTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] doubleSelectFromTwoVectorOpProvider() { + return DOUBLE_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] doubleTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4799,6 +4836,24 @@ static void SelectFromDouble512VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "doubleSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorDouble512VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + double[] a = fa.apply(SPECIES.length()); + double[] b = fb.apply(SPECIES.length()); + double[] idx = fc.apply(SPECIES.length()); + double[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); + DoubleVector bv = DoubleVector.fromArray(SPECIES, b, i); + DoubleVector idxv = DoubleVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "doubleUnaryOpSelectFromMaskProvider") static void SelectFromDouble512VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Double64VectorTests.java b/test/jdk/jdk/incubator/vector/Double64VectorTests.java index 9321215c3de..b56b4d237f4 100644 --- a/test/jdk/jdk/incubator/vector/Double64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double64VectorTests.java @@ -321,6 +321,25 @@ static void assertexpandArraysEquals(double[] r, double[] a, boolean[] m, int ve } } + static void assertSelectFromTwoVectorEquals(double[] r, double[] order, double[] a, double[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(double[] r, double[] a, double[] order, int vector_len) { int i = 0, j = 0; try { @@ -1108,6 +1127,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> DOUBLE_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("double[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (double)(RAND.nextInt())); + }) + ); + + static final List>> DOUBLE_GENERATOR_SELECT_FROM_TRIPLES = + DOUBLE_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] doubleBinaryOpProvider() { return DOUBLE_GENERATOR_PAIRS.stream().map(List::toArray). @@ -1135,6 +1166,12 @@ public Object[][] doubleTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] doubleSelectFromTwoVectorOpProvider() { + return DOUBLE_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] doubleTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4799,6 +4836,24 @@ static void SelectFromDouble64VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "doubleSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorDouble64VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + double[] a = fa.apply(SPECIES.length()); + double[] b = fb.apply(SPECIES.length()); + double[] idx = fc.apply(SPECIES.length()); + double[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); + DoubleVector bv = DoubleVector.fromArray(SPECIES, b, i); + DoubleVector idxv = DoubleVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "doubleUnaryOpSelectFromMaskProvider") static void SelectFromDouble64VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java b/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java index a6b80376196..43da4f57636 100644 --- a/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java @@ -326,6 +326,25 @@ static void assertexpandArraysEquals(double[] r, double[] a, boolean[] m, int ve } } + static void assertSelectFromTwoVectorEquals(double[] r, double[] order, double[] a, double[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(double[] r, double[] a, double[] order, int vector_len) { int i = 0, j = 0; try { @@ -1113,6 +1132,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> DOUBLE_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("double[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (double)(RAND.nextInt())); + }) + ); + + static final List>> DOUBLE_GENERATOR_SELECT_FROM_TRIPLES = + DOUBLE_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] doubleBinaryOpProvider() { return DOUBLE_GENERATOR_PAIRS.stream().map(List::toArray). @@ -1140,6 +1171,12 @@ public Object[][] doubleTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] doubleSelectFromTwoVectorOpProvider() { + return DOUBLE_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] doubleTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4804,6 +4841,24 @@ static void SelectFromDoubleMaxVectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "doubleSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorDoubleMaxVectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + double[] a = fa.apply(SPECIES.length()); + double[] b = fb.apply(SPECIES.length()); + double[] idx = fc.apply(SPECIES.length()); + double[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); + DoubleVector bv = DoubleVector.fromArray(SPECIES, b, i); + DoubleVector idxv = DoubleVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "doubleUnaryOpSelectFromMaskProvider") static void SelectFromDoubleMaxVectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Float128VectorTests.java b/test/jdk/jdk/incubator/vector/Float128VectorTests.java index 6bad9098544..549199513d5 100644 --- a/test/jdk/jdk/incubator/vector/Float128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float128VectorTests.java @@ -321,6 +321,25 @@ static void assertexpandArraysEquals(float[] r, float[] a, boolean[] m, int vect } } + static void assertSelectFromTwoVectorEquals(float[] r, float[] order, float[] a, float[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(float[] r, float[] a, float[] order, int vector_len) { int i = 0, j = 0; try { @@ -1119,6 +1138,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> FLOAT_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("float[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (float)(RAND.nextInt())); + }) + ); + + static final List>> FLOAT_GENERATOR_SELECT_FROM_TRIPLES = + FLOAT_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] floatBinaryOpProvider() { return FLOAT_GENERATOR_PAIRS.stream().map(List::toArray). @@ -1146,6 +1177,12 @@ public Object[][] floatTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] floatSelectFromTwoVectorOpProvider() { + return FLOAT_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] floatTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4778,6 +4815,24 @@ static void SelectFromFloat128VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "floatSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorFloat128VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + float[] a = fa.apply(SPECIES.length()); + float[] b = fb.apply(SPECIES.length()); + float[] idx = fc.apply(SPECIES.length()); + float[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + FloatVector av = FloatVector.fromArray(SPECIES, a, i); + FloatVector bv = FloatVector.fromArray(SPECIES, b, i); + FloatVector idxv = FloatVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "floatUnaryOpSelectFromMaskProvider") static void SelectFromFloat128VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Float256VectorTests.java b/test/jdk/jdk/incubator/vector/Float256VectorTests.java index e714ace5a78..6d17727c325 100644 --- a/test/jdk/jdk/incubator/vector/Float256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float256VectorTests.java @@ -321,6 +321,25 @@ static void assertexpandArraysEquals(float[] r, float[] a, boolean[] m, int vect } } + static void assertSelectFromTwoVectorEquals(float[] r, float[] order, float[] a, float[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(float[] r, float[] a, float[] order, int vector_len) { int i = 0, j = 0; try { @@ -1119,6 +1138,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> FLOAT_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("float[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (float)(RAND.nextInt())); + }) + ); + + static final List>> FLOAT_GENERATOR_SELECT_FROM_TRIPLES = + FLOAT_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] floatBinaryOpProvider() { return FLOAT_GENERATOR_PAIRS.stream().map(List::toArray). @@ -1146,6 +1177,12 @@ public Object[][] floatTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] floatSelectFromTwoVectorOpProvider() { + return FLOAT_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] floatTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4778,6 +4815,24 @@ static void SelectFromFloat256VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "floatSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorFloat256VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + float[] a = fa.apply(SPECIES.length()); + float[] b = fb.apply(SPECIES.length()); + float[] idx = fc.apply(SPECIES.length()); + float[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + FloatVector av = FloatVector.fromArray(SPECIES, a, i); + FloatVector bv = FloatVector.fromArray(SPECIES, b, i); + FloatVector idxv = FloatVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "floatUnaryOpSelectFromMaskProvider") static void SelectFromFloat256VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Float512VectorTests.java b/test/jdk/jdk/incubator/vector/Float512VectorTests.java index f3c5a316c79..c2290eb7080 100644 --- a/test/jdk/jdk/incubator/vector/Float512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float512VectorTests.java @@ -321,6 +321,25 @@ static void assertexpandArraysEquals(float[] r, float[] a, boolean[] m, int vect } } + static void assertSelectFromTwoVectorEquals(float[] r, float[] order, float[] a, float[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(float[] r, float[] a, float[] order, int vector_len) { int i = 0, j = 0; try { @@ -1119,6 +1138,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> FLOAT_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("float[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (float)(RAND.nextInt())); + }) + ); + + static final List>> FLOAT_GENERATOR_SELECT_FROM_TRIPLES = + FLOAT_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] floatBinaryOpProvider() { return FLOAT_GENERATOR_PAIRS.stream().map(List::toArray). @@ -1146,6 +1177,12 @@ public Object[][] floatTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] floatSelectFromTwoVectorOpProvider() { + return FLOAT_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] floatTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4778,6 +4815,24 @@ static void SelectFromFloat512VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "floatSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorFloat512VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + float[] a = fa.apply(SPECIES.length()); + float[] b = fb.apply(SPECIES.length()); + float[] idx = fc.apply(SPECIES.length()); + float[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + FloatVector av = FloatVector.fromArray(SPECIES, a, i); + FloatVector bv = FloatVector.fromArray(SPECIES, b, i); + FloatVector idxv = FloatVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "floatUnaryOpSelectFromMaskProvider") static void SelectFromFloat512VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Float64VectorTests.java b/test/jdk/jdk/incubator/vector/Float64VectorTests.java index 378c2ae783f..0d50726f644 100644 --- a/test/jdk/jdk/incubator/vector/Float64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float64VectorTests.java @@ -321,6 +321,25 @@ static void assertexpandArraysEquals(float[] r, float[] a, boolean[] m, int vect } } + static void assertSelectFromTwoVectorEquals(float[] r, float[] order, float[] a, float[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(float[] r, float[] a, float[] order, int vector_len) { int i = 0, j = 0; try { @@ -1119,6 +1138,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> FLOAT_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("float[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (float)(RAND.nextInt())); + }) + ); + + static final List>> FLOAT_GENERATOR_SELECT_FROM_TRIPLES = + FLOAT_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] floatBinaryOpProvider() { return FLOAT_GENERATOR_PAIRS.stream().map(List::toArray). @@ -1146,6 +1177,12 @@ public Object[][] floatTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] floatSelectFromTwoVectorOpProvider() { + return FLOAT_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] floatTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4778,6 +4815,24 @@ static void SelectFromFloat64VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "floatSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorFloat64VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + float[] a = fa.apply(SPECIES.length()); + float[] b = fb.apply(SPECIES.length()); + float[] idx = fc.apply(SPECIES.length()); + float[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + FloatVector av = FloatVector.fromArray(SPECIES, a, i); + FloatVector bv = FloatVector.fromArray(SPECIES, b, i); + FloatVector idxv = FloatVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "floatUnaryOpSelectFromMaskProvider") static void SelectFromFloat64VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java b/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java index a2dc38413ec..6a0b1301ab3 100644 --- a/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java @@ -326,6 +326,25 @@ static void assertexpandArraysEquals(float[] r, float[] a, boolean[] m, int vect } } + static void assertSelectFromTwoVectorEquals(float[] r, float[] order, float[] a, float[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(float[] r, float[] a, float[] order, int vector_len) { int i = 0, j = 0; try { @@ -1124,6 +1143,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> FLOAT_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("float[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (float)(RAND.nextInt())); + }) + ); + + static final List>> FLOAT_GENERATOR_SELECT_FROM_TRIPLES = + FLOAT_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] floatBinaryOpProvider() { return FLOAT_GENERATOR_PAIRS.stream().map(List::toArray). @@ -1151,6 +1182,12 @@ public Object[][] floatTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] floatSelectFromTwoVectorOpProvider() { + return FLOAT_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] floatTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4783,6 +4820,24 @@ static void SelectFromFloatMaxVectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "floatSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorFloatMaxVectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + float[] a = fa.apply(SPECIES.length()); + float[] b = fb.apply(SPECIES.length()); + float[] idx = fc.apply(SPECIES.length()); + float[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + FloatVector av = FloatVector.fromArray(SPECIES, a, i); + FloatVector bv = FloatVector.fromArray(SPECIES, b, i); + FloatVector idxv = FloatVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "floatUnaryOpSelectFromMaskProvider") static void SelectFromFloatMaxVectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Int128VectorTests.java b/test/jdk/jdk/incubator/vector/Int128VectorTests.java index 1ee0bbc3197..528d26a952b 100644 --- a/test/jdk/jdk/incubator/vector/Int128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int128VectorTests.java @@ -302,6 +302,25 @@ static void assertexpandArraysEquals(int[] r, int[] a, boolean[] m, int vector_l } } + static void assertSelectFromTwoVectorEquals(int[] r, int[] order, int[] a, int[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(int[] r, int[] a, int[] order, int vector_len) { int i = 0, j = 0; try { @@ -952,6 +971,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> INT_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("int[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (int)(RAND.nextInt())); + }) + ); + + static final List>> INT_GENERATOR_SELECT_FROM_TRIPLES = + INT_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] intBinaryOpProvider() { return INT_GENERATOR_PAIRS.stream().map(List::toArray). @@ -979,6 +1010,12 @@ public Object[][] intTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] intSelectFromTwoVectorOpProvider() { + return INT_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] intTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5779,6 +5816,24 @@ static void SelectFromInt128VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "intSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorInt128VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + int[] a = fa.apply(SPECIES.length()); + int[] b = fb.apply(SPECIES.length()); + int[] idx = fc.apply(SPECIES.length()); + int[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + IntVector bv = IntVector.fromArray(SPECIES, b, i); + IntVector idxv = IntVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "intUnaryOpShuffleMaskProvider") static void SelectFromInt128VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Int256VectorTests.java b/test/jdk/jdk/incubator/vector/Int256VectorTests.java index 5257af21c94..09561e0f3c4 100644 --- a/test/jdk/jdk/incubator/vector/Int256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int256VectorTests.java @@ -302,6 +302,25 @@ static void assertexpandArraysEquals(int[] r, int[] a, boolean[] m, int vector_l } } + static void assertSelectFromTwoVectorEquals(int[] r, int[] order, int[] a, int[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(int[] r, int[] a, int[] order, int vector_len) { int i = 0, j = 0; try { @@ -952,6 +971,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> INT_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("int[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (int)(RAND.nextInt())); + }) + ); + + static final List>> INT_GENERATOR_SELECT_FROM_TRIPLES = + INT_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] intBinaryOpProvider() { return INT_GENERATOR_PAIRS.stream().map(List::toArray). @@ -979,6 +1010,12 @@ public Object[][] intTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] intSelectFromTwoVectorOpProvider() { + return INT_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] intTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5779,6 +5816,24 @@ static void SelectFromInt256VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "intSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorInt256VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + int[] a = fa.apply(SPECIES.length()); + int[] b = fb.apply(SPECIES.length()); + int[] idx = fc.apply(SPECIES.length()); + int[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + IntVector bv = IntVector.fromArray(SPECIES, b, i); + IntVector idxv = IntVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "intUnaryOpShuffleMaskProvider") static void SelectFromInt256VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Int512VectorTests.java b/test/jdk/jdk/incubator/vector/Int512VectorTests.java index 6d4633cc7ae..e6d1aa68f56 100644 --- a/test/jdk/jdk/incubator/vector/Int512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int512VectorTests.java @@ -302,6 +302,25 @@ static void assertexpandArraysEquals(int[] r, int[] a, boolean[] m, int vector_l } } + static void assertSelectFromTwoVectorEquals(int[] r, int[] order, int[] a, int[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(int[] r, int[] a, int[] order, int vector_len) { int i = 0, j = 0; try { @@ -952,6 +971,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> INT_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("int[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (int)(RAND.nextInt())); + }) + ); + + static final List>> INT_GENERATOR_SELECT_FROM_TRIPLES = + INT_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] intBinaryOpProvider() { return INT_GENERATOR_PAIRS.stream().map(List::toArray). @@ -979,6 +1010,12 @@ public Object[][] intTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] intSelectFromTwoVectorOpProvider() { + return INT_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] intTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5779,6 +5816,24 @@ static void SelectFromInt512VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "intSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorInt512VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + int[] a = fa.apply(SPECIES.length()); + int[] b = fb.apply(SPECIES.length()); + int[] idx = fc.apply(SPECIES.length()); + int[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + IntVector bv = IntVector.fromArray(SPECIES, b, i); + IntVector idxv = IntVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "intUnaryOpShuffleMaskProvider") static void SelectFromInt512VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Int64VectorTests.java b/test/jdk/jdk/incubator/vector/Int64VectorTests.java index 7bd1543ed5c..4435d31cd04 100644 --- a/test/jdk/jdk/incubator/vector/Int64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int64VectorTests.java @@ -302,6 +302,25 @@ static void assertexpandArraysEquals(int[] r, int[] a, boolean[] m, int vector_l } } + static void assertSelectFromTwoVectorEquals(int[] r, int[] order, int[] a, int[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(int[] r, int[] a, int[] order, int vector_len) { int i = 0, j = 0; try { @@ -952,6 +971,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> INT_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("int[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (int)(RAND.nextInt())); + }) + ); + + static final List>> INT_GENERATOR_SELECT_FROM_TRIPLES = + INT_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] intBinaryOpProvider() { return INT_GENERATOR_PAIRS.stream().map(List::toArray). @@ -979,6 +1010,12 @@ public Object[][] intTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] intSelectFromTwoVectorOpProvider() { + return INT_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] intTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5779,6 +5816,24 @@ static void SelectFromInt64VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "intSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorInt64VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + int[] a = fa.apply(SPECIES.length()); + int[] b = fb.apply(SPECIES.length()); + int[] idx = fc.apply(SPECIES.length()); + int[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + IntVector bv = IntVector.fromArray(SPECIES, b, i); + IntVector idxv = IntVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "intUnaryOpShuffleMaskProvider") static void SelectFromInt64VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java b/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java index 71d1ce594f9..94dce66d951 100644 --- a/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java @@ -307,6 +307,25 @@ static void assertexpandArraysEquals(int[] r, int[] a, boolean[] m, int vector_l } } + static void assertSelectFromTwoVectorEquals(int[] r, int[] order, int[] a, int[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(int[] r, int[] a, int[] order, int vector_len) { int i = 0, j = 0; try { @@ -957,6 +976,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> INT_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("int[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (int)(RAND.nextInt())); + }) + ); + + static final List>> INT_GENERATOR_SELECT_FROM_TRIPLES = + INT_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] intBinaryOpProvider() { return INT_GENERATOR_PAIRS.stream().map(List::toArray). @@ -984,6 +1015,12 @@ public Object[][] intTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] intSelectFromTwoVectorOpProvider() { + return INT_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] intTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5784,6 +5821,24 @@ static void SelectFromIntMaxVectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "intSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorIntMaxVectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + int[] a = fa.apply(SPECIES.length()); + int[] b = fb.apply(SPECIES.length()); + int[] idx = fc.apply(SPECIES.length()); + int[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + IntVector bv = IntVector.fromArray(SPECIES, b, i); + IntVector idxv = IntVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "intUnaryOpShuffleMaskProvider") static void SelectFromIntMaxVectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Long128VectorTests.java b/test/jdk/jdk/incubator/vector/Long128VectorTests.java index bcec2dee9fe..7e6bf6b7b81 100644 --- a/test/jdk/jdk/incubator/vector/Long128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long128VectorTests.java @@ -259,6 +259,25 @@ static void assertexpandArraysEquals(long[] r, long[] a, boolean[] m, int vector } } + static void assertSelectFromTwoVectorEquals(long[] r, long[] order, long[] a, long[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(long[] r, long[] a, long[] order, int vector_len) { int i = 0, j = 0; try { @@ -942,6 +961,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> LONG_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("long[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (long)(RAND.nextInt())); + }) + ); + + static final List>> LONG_GENERATOR_SELECT_FROM_TRIPLES = + LONG_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] longBinaryOpProvider() { return LONG_GENERATOR_PAIRS.stream().map(List::toArray). @@ -969,6 +1000,12 @@ public Object[][] longTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] longSelectFromTwoVectorOpProvider() { + return LONG_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] longTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5665,6 +5702,24 @@ static void SelectFromLong128VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "longSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorLong128VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + long[] a = fa.apply(SPECIES.length()); + long[] b = fb.apply(SPECIES.length()); + long[] idx = fc.apply(SPECIES.length()); + long[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + LongVector bv = LongVector.fromArray(SPECIES, b, i); + LongVector idxv = LongVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "longUnaryOpSelectFromMaskProvider") static void SelectFromLong128VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Long256VectorTests.java b/test/jdk/jdk/incubator/vector/Long256VectorTests.java index e8f2fb1301c..299b7007ba5 100644 --- a/test/jdk/jdk/incubator/vector/Long256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long256VectorTests.java @@ -259,6 +259,25 @@ static void assertexpandArraysEquals(long[] r, long[] a, boolean[] m, int vector } } + static void assertSelectFromTwoVectorEquals(long[] r, long[] order, long[] a, long[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(long[] r, long[] a, long[] order, int vector_len) { int i = 0, j = 0; try { @@ -942,6 +961,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> LONG_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("long[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (long)(RAND.nextInt())); + }) + ); + + static final List>> LONG_GENERATOR_SELECT_FROM_TRIPLES = + LONG_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] longBinaryOpProvider() { return LONG_GENERATOR_PAIRS.stream().map(List::toArray). @@ -969,6 +1000,12 @@ public Object[][] longTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] longSelectFromTwoVectorOpProvider() { + return LONG_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] longTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5665,6 +5702,24 @@ static void SelectFromLong256VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "longSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorLong256VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + long[] a = fa.apply(SPECIES.length()); + long[] b = fb.apply(SPECIES.length()); + long[] idx = fc.apply(SPECIES.length()); + long[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + LongVector bv = LongVector.fromArray(SPECIES, b, i); + LongVector idxv = LongVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "longUnaryOpSelectFromMaskProvider") static void SelectFromLong256VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Long512VectorTests.java b/test/jdk/jdk/incubator/vector/Long512VectorTests.java index 022f1490fcc..e6eafa02ed0 100644 --- a/test/jdk/jdk/incubator/vector/Long512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long512VectorTests.java @@ -259,6 +259,25 @@ static void assertexpandArraysEquals(long[] r, long[] a, boolean[] m, int vector } } + static void assertSelectFromTwoVectorEquals(long[] r, long[] order, long[] a, long[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(long[] r, long[] a, long[] order, int vector_len) { int i = 0, j = 0; try { @@ -942,6 +961,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> LONG_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("long[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (long)(RAND.nextInt())); + }) + ); + + static final List>> LONG_GENERATOR_SELECT_FROM_TRIPLES = + LONG_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] longBinaryOpProvider() { return LONG_GENERATOR_PAIRS.stream().map(List::toArray). @@ -969,6 +1000,12 @@ public Object[][] longTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] longSelectFromTwoVectorOpProvider() { + return LONG_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] longTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5665,6 +5702,24 @@ static void SelectFromLong512VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "longSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorLong512VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + long[] a = fa.apply(SPECIES.length()); + long[] b = fb.apply(SPECIES.length()); + long[] idx = fc.apply(SPECIES.length()); + long[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + LongVector bv = LongVector.fromArray(SPECIES, b, i); + LongVector idxv = LongVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "longUnaryOpSelectFromMaskProvider") static void SelectFromLong512VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Long64VectorTests.java b/test/jdk/jdk/incubator/vector/Long64VectorTests.java index fe886bf93d8..035db048eb8 100644 --- a/test/jdk/jdk/incubator/vector/Long64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long64VectorTests.java @@ -259,6 +259,25 @@ static void assertexpandArraysEquals(long[] r, long[] a, boolean[] m, int vector } } + static void assertSelectFromTwoVectorEquals(long[] r, long[] order, long[] a, long[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(long[] r, long[] a, long[] order, int vector_len) { int i = 0, j = 0; try { @@ -942,6 +961,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> LONG_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("long[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (long)(RAND.nextInt())); + }) + ); + + static final List>> LONG_GENERATOR_SELECT_FROM_TRIPLES = + LONG_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] longBinaryOpProvider() { return LONG_GENERATOR_PAIRS.stream().map(List::toArray). @@ -969,6 +1000,12 @@ public Object[][] longTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] longSelectFromTwoVectorOpProvider() { + return LONG_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] longTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5665,6 +5702,24 @@ static void SelectFromLong64VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "longSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorLong64VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + long[] a = fa.apply(SPECIES.length()); + long[] b = fb.apply(SPECIES.length()); + long[] idx = fc.apply(SPECIES.length()); + long[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + LongVector bv = LongVector.fromArray(SPECIES, b, i); + LongVector idxv = LongVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "longUnaryOpSelectFromMaskProvider") static void SelectFromLong64VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java b/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java index b77d6eeb118..68ea78db4f0 100644 --- a/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java @@ -264,6 +264,25 @@ static void assertexpandArraysEquals(long[] r, long[] a, boolean[] m, int vector } } + static void assertSelectFromTwoVectorEquals(long[] r, long[] order, long[] a, long[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(long[] r, long[] a, long[] order, int vector_len) { int i = 0, j = 0; try { @@ -947,6 +966,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> LONG_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("long[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (long)(RAND.nextInt())); + }) + ); + + static final List>> LONG_GENERATOR_SELECT_FROM_TRIPLES = + LONG_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] longBinaryOpProvider() { return LONG_GENERATOR_PAIRS.stream().map(List::toArray). @@ -974,6 +1005,12 @@ public Object[][] longTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] longSelectFromTwoVectorOpProvider() { + return LONG_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] longTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5670,6 +5707,24 @@ static void SelectFromLongMaxVectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "longSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorLongMaxVectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + long[] a = fa.apply(SPECIES.length()); + long[] b = fb.apply(SPECIES.length()); + long[] idx = fc.apply(SPECIES.length()); + long[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + LongVector bv = LongVector.fromArray(SPECIES, b, i); + LongVector idxv = LongVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "longUnaryOpSelectFromMaskProvider") static void SelectFromLongMaxVectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Short128VectorTests.java b/test/jdk/jdk/incubator/vector/Short128VectorTests.java index 2a82ada044e..2103be0994c 100644 --- a/test/jdk/jdk/incubator/vector/Short128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short128VectorTests.java @@ -302,6 +302,25 @@ static void assertexpandArraysEquals(short[] r, short[] a, boolean[] m, int vect } } + static void assertSelectFromTwoVectorEquals(short[] r, short[] order, short[] a, short[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(short[] r, short[] a, short[] order, int vector_len) { int i = 0, j = 0; try { @@ -952,6 +971,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> SHORT_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("short[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(RAND.nextInt())); + }) + ); + + static final List>> SHORT_GENERATOR_SELECT_FROM_TRIPLES = + SHORT_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] shortBinaryOpProvider() { return SHORT_GENERATOR_PAIRS.stream().map(List::toArray). @@ -979,6 +1010,12 @@ public Object[][] shortTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] shortSelectFromTwoVectorOpProvider() { + return SHORT_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] shortTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5726,6 +5763,24 @@ static void SelectFromShort128VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "shortSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorShort128VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] idx = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + ShortVector bv = ShortVector.fromArray(SPECIES, b, i); + ShortVector idxv = ShortVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "shortUnaryOpSelectFromMaskProvider") static void SelectFromShort128VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Short256VectorTests.java b/test/jdk/jdk/incubator/vector/Short256VectorTests.java index 69a8432acef..feed6bbe5f3 100644 --- a/test/jdk/jdk/incubator/vector/Short256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short256VectorTests.java @@ -302,6 +302,25 @@ static void assertexpandArraysEquals(short[] r, short[] a, boolean[] m, int vect } } + static void assertSelectFromTwoVectorEquals(short[] r, short[] order, short[] a, short[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(short[] r, short[] a, short[] order, int vector_len) { int i = 0, j = 0; try { @@ -952,6 +971,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> SHORT_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("short[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(RAND.nextInt())); + }) + ); + + static final List>> SHORT_GENERATOR_SELECT_FROM_TRIPLES = + SHORT_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] shortBinaryOpProvider() { return SHORT_GENERATOR_PAIRS.stream().map(List::toArray). @@ -979,6 +1010,12 @@ public Object[][] shortTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] shortSelectFromTwoVectorOpProvider() { + return SHORT_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] shortTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5726,6 +5763,24 @@ static void SelectFromShort256VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "shortSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorShort256VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] idx = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + ShortVector bv = ShortVector.fromArray(SPECIES, b, i); + ShortVector idxv = ShortVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "shortUnaryOpSelectFromMaskProvider") static void SelectFromShort256VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Short512VectorTests.java b/test/jdk/jdk/incubator/vector/Short512VectorTests.java index 8e892a8a48e..a1a1ac6bc37 100644 --- a/test/jdk/jdk/incubator/vector/Short512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short512VectorTests.java @@ -302,6 +302,25 @@ static void assertexpandArraysEquals(short[] r, short[] a, boolean[] m, int vect } } + static void assertSelectFromTwoVectorEquals(short[] r, short[] order, short[] a, short[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(short[] r, short[] a, short[] order, int vector_len) { int i = 0, j = 0; try { @@ -952,6 +971,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> SHORT_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("short[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(RAND.nextInt())); + }) + ); + + static final List>> SHORT_GENERATOR_SELECT_FROM_TRIPLES = + SHORT_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] shortBinaryOpProvider() { return SHORT_GENERATOR_PAIRS.stream().map(List::toArray). @@ -979,6 +1010,12 @@ public Object[][] shortTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] shortSelectFromTwoVectorOpProvider() { + return SHORT_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] shortTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5726,6 +5763,24 @@ static void SelectFromShort512VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "shortSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorShort512VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] idx = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + ShortVector bv = ShortVector.fromArray(SPECIES, b, i); + ShortVector idxv = ShortVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "shortUnaryOpSelectFromMaskProvider") static void SelectFromShort512VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/Short64VectorTests.java b/test/jdk/jdk/incubator/vector/Short64VectorTests.java index 97658d4257d..cc14cccd119 100644 --- a/test/jdk/jdk/incubator/vector/Short64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short64VectorTests.java @@ -302,6 +302,25 @@ static void assertexpandArraysEquals(short[] r, short[] a, boolean[] m, int vect } } + static void assertSelectFromTwoVectorEquals(short[] r, short[] order, short[] a, short[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(short[] r, short[] a, short[] order, int vector_len) { int i = 0, j = 0; try { @@ -952,6 +971,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> SHORT_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("short[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(RAND.nextInt())); + }) + ); + + static final List>> SHORT_GENERATOR_SELECT_FROM_TRIPLES = + SHORT_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] shortBinaryOpProvider() { return SHORT_GENERATOR_PAIRS.stream().map(List::toArray). @@ -979,6 +1010,12 @@ public Object[][] shortTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] shortSelectFromTwoVectorOpProvider() { + return SHORT_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] shortTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5726,6 +5763,24 @@ static void SelectFromShort64VectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "shortSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorShort64VectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] idx = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + ShortVector bv = ShortVector.fromArray(SPECIES, b, i); + ShortVector idxv = ShortVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "shortUnaryOpSelectFromMaskProvider") static void SelectFromShort64VectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java b/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java index 0857c13ef3c..a557494f74c 100644 --- a/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java @@ -307,6 +307,25 @@ static void assertexpandArraysEquals(short[] r, short[] a, boolean[] m, int vect } } + static void assertSelectFromTwoVectorEquals(short[] r, short[] order, short[] a, short[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals(short[] r, short[] a, short[] order, int vector_len) { int i = 0, j = 0; try { @@ -957,6 +976,18 @@ public Object[][] boolUnaryOpProvider() { flatMap(pair -> SHORT_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("short[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (short)(RAND.nextInt())); + }) + ); + + static final List>> SHORT_GENERATOR_SELECT_FROM_TRIPLES = + SHORT_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] shortBinaryOpProvider() { return SHORT_GENERATOR_PAIRS.stream().map(List::toArray). @@ -984,6 +1015,12 @@ public Object[][] shortTernaryOpProvider() { toArray(Object[][]::new); } + @DataProvider + public Object[][] shortSelectFromTwoVectorOpProvider() { + return SHORT_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] shortTernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -5731,6 +5768,24 @@ static void SelectFromShortMaxVectorTests(IntFunction fa, assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "shortSelectFromTwoVectorOpProvider") + static void SelectFromTwoVectorShortMaxVectorTests(IntFunction fa, IntFunction fb, IntFunction fc) { + short[] a = fa.apply(SPECIES.length()); + short[] b = fb.apply(SPECIES.length()); + short[] idx = fc.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + ShortVector bv = ShortVector.fromArray(SPECIES, b, i); + ShortVector idxv = ShortVector.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + @Test(dataProvider = "shortUnaryOpSelectFromMaskProvider") static void SelectFromShortMaxVectorTestsMaskedSmokeTest(IntFunction fa, BiFunction fs, diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template b/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template index 0d3b310f60f..9a020c66d52 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template @@ -297,6 +297,24 @@ assertSelectFromArraysEquals(r, a, order, SPECIES.length()); } + @Test(dataProvider = "$type$SelectFromTwoVectorOpProvider") + static void SelectFromTwoVector$vectorteststype$(IntFunction<$type$[]> fa, IntFunction<$type$[]> fb, IntFunction<$type$[]> fc) { + $type$[] a = fa.apply(SPECIES.length()); + $type$[] b = fb.apply(SPECIES.length()); + $type$[] idx = fc.apply(SPECIES.length()); + $type$[] r = fr.apply(SPECIES.length()); + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < idx.length; i += SPECIES.length()) { + $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); + $abstractvectortype$ bv = $abstractvectortype$.fromArray(SPECIES, b, i); + $abstractvectortype$ idxv = $abstractvectortype$.fromArray(SPECIES, idx, i); + idxv.selectFrom(av, bv).intoArray(r, i); + } + } + assertSelectFromTwoVectorEquals(r, idx, a, b, SPECIES.length()); + } + #if[Int] @Test(dataProvider = "$type$UnaryOpShuffleMaskProvider") #else[Int] diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-header.template b/test/jdk/jdk/incubator/vector/templates/Unit-header.template index 42440881771..2f33ede458a 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-header.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-header.template @@ -397,6 +397,25 @@ relativeError)); } } + static void assertSelectFromTwoVectorEquals($type$[] r, $type$[] order, $type$[] a, $type$[] b, int vector_len) { + int i = 0, j = 0; + boolean is_exceptional_idx = false; + int idx = 0, wrapped_index = 0, oidx = 0; + try { + for (; i < a.length; i += vector_len) { + for (j = 0; j < vector_len; j++) { + idx = i + j; + wrapped_index = Math.floorMod((int)order[idx], 2 * vector_len); + is_exceptional_idx = wrapped_index >= vector_len; + oidx = is_exceptional_idx ? (wrapped_index - vector_len) : wrapped_index; + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx])); + } + } + } catch (AssertionError e) { + Assert.assertEquals(r[idx], (is_exceptional_idx ? b[i + oidx] : a[i + oidx]), "at index #" + idx + ", order = " + order[idx] + ", a = " + a[i + oidx] + ", b = " + b[i + oidx]); + } + } + static void assertSelectFromArraysEquals($type$[] r, $type$[] a, $type$[] order, int vector_len) { int i = 0, j = 0; try { @@ -1221,6 +1240,18 @@ relativeError)); flatMap(pair -> $TYPE$_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). collect(Collectors.toList()); + static final List> SELECT_FROM_INDEX_GENERATORS = List.of( + withToString("$type$[0..VECLEN*2)", (int s) -> { + return fill(s * BUFFER_REPS, + i -> ($type$)(RAND.nextInt())); + }) + ); + + static final List>> $TYPE$_GENERATOR_SELECT_FROM_TRIPLES = + $TYPE$_GENERATOR_PAIRS.stream(). + flatMap(pair -> SELECT_FROM_INDEX_GENERATORS.stream().map(f -> List.of(pair.get(0), pair.get(1), f))). + collect(Collectors.toList()); + @DataProvider public Object[][] $type$BinaryOpProvider() { return $TYPE$_GENERATOR_PAIRS.stream().map(List::toArray). @@ -1248,6 +1279,12 @@ relativeError)); toArray(Object[][]::new); } + @DataProvider + public Object[][] $type$SelectFromTwoVectorOpProvider() { + return $TYPE$_GENERATOR_SELECT_FROM_TRIPLES.stream().map(List::toArray). + toArray(Object[][]::new); + } + @DataProvider public Object[][] $type$TernaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/SelectFromBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/SelectFromBenchmark.java new file mode 100644 index 00000000000..18614617e6c --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/SelectFromBenchmark.java @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * 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.jdk.incubator.vector; + +import java.util.Random; +import java.util.Arrays; +import jdk.incubator.vector.*; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; + +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Thread) +@Fork(jvmArgsPrepend = {"--add-modules=jdk.incubator.vector"}) +public class SelectFromBenchmark { + @Param({"1024","2048"}) + int size; + + byte[] byteindex; + byte[] bytesrc1; + byte[] bytesrc2; + byte[] byteres; + + short[] shortindex; + short[] shortsrc1; + short[] shortsrc2; + short[] shortres; + + int[] intindex; + int[] intsrc1; + int[] intsrc2; + int[] intres; + + long[] longindex; + long[] longsrc1; + long[] longsrc2; + long[] longres; + + float[] floatindex; + float[] floatsrc1; + float[] floatsrc2; + float[] floatres; + + double[] doubleindex; + double[] doublesrc1; + double[] doublesrc2; + double[] doubleres; + + @Setup(Level.Trial) + public void BmSetup() { + Random r = new Random(1024); + byteindex = new byte[size]; + bytesrc1 = new byte[size]; + bytesrc2 = new byte[size]; + byteres = new byte[size]; + + shortindex = new short[size]; + shortsrc1 = new short[size]; + shortsrc2 = new short[size]; + shortres = new short[size]; + + intindex = new int[size]; + intsrc1 = new int[size]; + intsrc2 = new int[size]; + intres = new int[size]; + + longindex = new long[size]; + longsrc1 = new long[size]; + longsrc2 = new long[size]; + longres = new long[size]; + + floatindex = new float[size]; + floatsrc1 = new float[size]; + floatsrc2 = new float[size]; + floatres = new float[size]; + + doubleindex = new double[size]; + doublesrc1 = new double[size]; + doublesrc2 = new double[size]; + doubleres = new double[size]; + + Arrays.fill(bytesrc1, (byte)1); + Arrays.fill(bytesrc2, (byte)2); + + Arrays.fill(shortsrc1, (short)1); + Arrays.fill(shortsrc2, (short)2); + + Arrays.fill(intsrc1, 1); + Arrays.fill(intsrc2, 2); + + Arrays.fill(longsrc1, 1); + Arrays.fill(longsrc2, 2); + + Arrays.fill(floatsrc1, 1.0f); + Arrays.fill(floatsrc2, 2.0f); + + Arrays.fill(doublesrc1, 1.0); + Arrays.fill(doublesrc2, 2.0); + + for (int i = 0; i < size; i++) { + byteindex[i] = (byte)((ByteVector.SPECIES_PREFERRED.length() - 1) & i); + shortindex[i] = (short)((ShortVector.SPECIES_PREFERRED.length() - 1) & i); + intindex[i] = (int)((IntVector.SPECIES_PREFERRED.length() - 1) & i); + longindex[i] = (long)((LongVector.SPECIES_PREFERRED.length() - 1) & i); + floatindex[i] = (float)((FloatVector.SPECIES_PREFERRED.length() - 1) & i); + doubleindex[i] = (double)((DoubleVector.SPECIES_PREFERRED.length() - 1) & i); + } + } + + @Benchmark + public void selectFromByteVector() { + for (int j = 0; j < size; j += ByteVector.SPECIES_PREFERRED.length()) { + ByteVector.fromArray(ByteVector.SPECIES_PREFERRED, byteindex, j) + .selectFrom(ByteVector.fromArray(ByteVector.SPECIES_PREFERRED, bytesrc1, j), + ByteVector.fromArray(ByteVector.SPECIES_PREFERRED, bytesrc2, j)) + .intoArray(byteres, j); + } + } + + @Benchmark + public void rearrangeFromByteVector() { + for (int j = 0; j < size; j += ByteVector.SPECIES_PREFERRED.length()) { + ByteVector.fromArray(ByteVector.SPECIES_PREFERRED, bytesrc1, j) + .rearrange(ByteVector.fromArray(ByteVector.SPECIES_PREFERRED, byteindex, j).toShuffle(), + ByteVector.fromArray(ByteVector.SPECIES_PREFERRED, bytesrc2, j)) + .intoArray(byteres, j); + } + } + + @Benchmark + public void selectFromShortVector() { + for (int j = 0; j < size; j += ShortVector.SPECIES_PREFERRED.length()) { + ShortVector.fromArray(ShortVector.SPECIES_PREFERRED, shortindex, j) + .selectFrom(ShortVector.fromArray(ShortVector.SPECIES_PREFERRED, shortsrc1, j), + ShortVector.fromArray(ShortVector.SPECIES_PREFERRED, shortsrc2, j)) + .intoArray(shortres, j); + } + } + + @Benchmark + public void rearrangeFromShortVector() { + for (int j = 0; j < size; j += ShortVector.SPECIES_PREFERRED.length()) { + ShortVector.fromArray(ShortVector.SPECIES_PREFERRED, shortsrc1, j) + .rearrange(ShortVector.fromArray(ShortVector.SPECIES_PREFERRED, shortindex, j).toShuffle(), + ShortVector.fromArray(ShortVector.SPECIES_PREFERRED, shortsrc2, j)) + .intoArray(shortres, j); + } + } + + @Benchmark + public void selectFromIntVector() { + for (int j = 0; j < size; j += IntVector.SPECIES_PREFERRED.length()) { + IntVector.fromArray(IntVector.SPECIES_PREFERRED, intindex, j) + .selectFrom(IntVector.fromArray(IntVector.SPECIES_PREFERRED, intsrc1, j), + IntVector.fromArray(IntVector.SPECIES_PREFERRED, intsrc2, j)) + .intoArray(intres, j); + } + } + + @Benchmark + public void rearrangeFromIntVector() { + for (int j = 0; j < size; j += IntVector.SPECIES_PREFERRED.length()) { + IntVector.fromArray(IntVector.SPECIES_PREFERRED, intsrc1, j) + .rearrange(IntVector.fromArray(IntVector.SPECIES_PREFERRED, intindex, j).toShuffle(), + IntVector.fromArray(IntVector.SPECIES_PREFERRED, intsrc2, j)) + .intoArray(intres, j); + } + } + + @Benchmark + public void selectFromLongVector() { + for (int j = 0; j < size; j += LongVector.SPECIES_PREFERRED.length()) { + LongVector.fromArray(LongVector.SPECIES_PREFERRED, longindex, j) + .selectFrom(LongVector.fromArray(LongVector.SPECIES_PREFERRED, longsrc1, j), + LongVector.fromArray(LongVector.SPECIES_PREFERRED, longsrc2, j)) + .intoArray(longres, j); + } + } + + @Benchmark + public void rearrangeFromLongVector() { + for (int j = 0; j < size; j += LongVector.SPECIES_PREFERRED.length()) { + LongVector.fromArray(LongVector.SPECIES_PREFERRED, longsrc1, j) + .rearrange(LongVector.fromArray(LongVector.SPECIES_PREFERRED, longindex, j).toShuffle(), + LongVector.fromArray(LongVector.SPECIES_PREFERRED, longsrc2, j)) + .intoArray(longres, j); + } + } + + @Benchmark + public void selectFromFloatVector() { + for (int j = 0; j < size; j += FloatVector.SPECIES_PREFERRED.length()) { + FloatVector.fromArray(FloatVector.SPECIES_PREFERRED, floatindex, j) + .selectFrom(FloatVector.fromArray(FloatVector.SPECIES_PREFERRED, floatsrc1, j), + FloatVector.fromArray(FloatVector.SPECIES_PREFERRED, floatsrc2, j)) + .intoArray(floatres, j); + } + } + + @Benchmark + public void rearrangeFromFloatVector() { + for (int j = 0; j < size; j += FloatVector.SPECIES_PREFERRED.length()) { + FloatVector.fromArray(FloatVector.SPECIES_PREFERRED, floatsrc1, j) + .rearrange(FloatVector.fromArray(FloatVector.SPECIES_PREFERRED, floatindex, j).toShuffle(), + FloatVector.fromArray(FloatVector.SPECIES_PREFERRED, floatsrc2, j)) + .intoArray(floatres, j); + } + } + + @Benchmark + public void selectFromDoubleVector() { + for (int j = 0; j < size; j += DoubleVector.SPECIES_PREFERRED.length()) { + DoubleVector.fromArray(DoubleVector.SPECIES_PREFERRED, doubleindex, j) + .selectFrom(DoubleVector.fromArray(DoubleVector.SPECIES_PREFERRED, doublesrc1, j), + DoubleVector.fromArray(DoubleVector.SPECIES_PREFERRED, doublesrc2, j)) + .intoArray(doubleres, j); + } + } + + @Benchmark + public void rearrangeFromDoubleVector() { + for (int j = 0; j < size; j += DoubleVector.SPECIES_PREFERRED.length()) { + DoubleVector.fromArray(DoubleVector.SPECIES_PREFERRED, doublesrc1, j) + .rearrange(DoubleVector.fromArray(DoubleVector.SPECIES_PREFERRED, doubleindex, j).toShuffle(), + DoubleVector.fromArray(DoubleVector.SPECIES_PREFERRED, doublesrc2, j)) + .intoArray(doubleres, j); + } + } +} From 44151f475fca3cf03299319b2ac9ddc533ba134d Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Wed, 16 Oct 2024 16:27:57 +0000 Subject: [PATCH 81/95] 8342145: File libCreationTimeHelper.c compile fails on Alpine Reviewed-by: mbaesken --- .../BasicFileAttributeView/libCreationTimeHelper.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/libCreationTimeHelper.c b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/libCreationTimeHelper.c index fb518b3b701..0686cc790da 100644 --- a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/libCreationTimeHelper.c +++ b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/libCreationTimeHelper.c @@ -25,8 +25,8 @@ #if defined(__linux__) #include #include +#include #include -#include #include #ifndef STATX_BASIC_STATS #define STATX_BASIC_STATS 0x000007ffU @@ -44,13 +44,20 @@ #define AT_FDCWD -100 #endif +#ifndef __GLIBC__ +// Alpine doesn't know these types, define them +typedef unsigned int __uint32_t; +typedef unsigned short __uint16_t; +typedef unsigned long int __uint64_t; +#endif + /* * Timestamp structure for the timestamps in struct statx. */ struct my_statx_timestamp { - __int64_t tv_sec; + int64_t tv_sec; __uint32_t tv_nsec; - __int32_t __reserved; + int32_t __reserved; }; /* From d4f0ba73f653a3886b17f283b9b6a92db1af52aa Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Wed, 16 Oct 2024 18:26:54 +0000 Subject: [PATCH 82/95] 8342439: Build failure after 8338023 Reviewed-by: liach --- src/hotspot/share/opto/vectorIntrinsics.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index fe1807b9588..6b7483b131f 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -2873,7 +2873,7 @@ static Node* LowerSelectFromTwoVectorOperation(PhaseGVN& phase, Node* index_vec, // Wrap indexes into two vector index range [0, VLEN * 2) Node* two_vect_lane_cnt_m1 = phase.makecon(TypeInt::make(2 * num_elem - 1)); Node* bcast_two_vect_lane_cnt_m1_vec = phase.transform(VectorNode::scalar2vector(two_vect_lane_cnt_m1, num_elem, - Type::get_const_basic_type(T_BYTE), false)); + T_BYTE, false)); index_byte_vec = phase.transform(VectorNode::make(Op_AndV, index_byte_vec, bcast_two_vect_lane_cnt_m1_vec, index_byte_vec->bottom_type()->is_vect())); @@ -2884,7 +2884,7 @@ static Node* LowerSelectFromTwoVectorOperation(PhaseGVN& phase, Node* index_vec, const TypeVect* vmask_type = TypeVect::makemask(T_BYTE, num_elem); Node* lane_cnt_m1 = phase.makecon(TypeInt::make(num_elem - 1)); Node* bcast_lane_cnt_m1_vec = phase.transform(VectorNode::scalar2vector(lane_cnt_m1, num_elem, - Type::get_const_basic_type(T_BYTE), false)); + T_BYTE, false)); Node* mask = phase.transform(new VectorMaskCmpNode(pred, index_byte_vec, bcast_lane_cnt_m1_vec, pred_node, vmask_type)); // Rearrange expects the indexes to lie within single vector index range [0, VLEN). @@ -3012,7 +3012,7 @@ bool LibraryCallKit::inline_vector_select_from_two_vectors() { } int indexRangeMask = 2 * num_elem - 1; Node* wrap_mask = gvn().makecon(TypeInteger::make(indexRangeMask, indexRangeMask, Type::WidenMin, index_elem_bt != T_LONG ? T_INT : index_elem_bt)); - Node* wrap_mask_vec = gvn().transform(VectorNode::scalar2vector(wrap_mask, num_elem, Type::get_const_basic_type(index_elem_bt), false)); + Node* wrap_mask_vec = gvn().transform(VectorNode::scalar2vector(wrap_mask, num_elem, index_elem_bt, false)); opd1 = gvn().transform(VectorNode::make(Op_AndV, opd1, wrap_mask_vec, opd1->bottom_type()->is_vect())); operation = gvn().transform(VectorNode::make(Op_SelectFromTwoVector, opd1, opd2, opd3, vt)); } From ed6809666b12b0de66f68d5e7e389dde1708aaf3 Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Wed, 16 Oct 2024 19:17:27 +0000 Subject: [PATCH 83/95] 8341862: PPC64: C1 unwind_handler fails to unlock synchronized methods with LM_MONITOR Reviewed-by: mdoerr, mbaesken --- src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp | 6 +++++- src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 684c06614a9..92a39e6e356 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -213,7 +213,11 @@ int LIR_Assembler::emit_unwind_handler() { if (method()->is_synchronized()) { monitor_address(0, FrameMap::R4_opr); stub = new MonitorExitStub(FrameMap::R4_opr, true, 0); - __ unlock_object(R5, R6, R4, *stub->entry()); + if (LockingMode == LM_MONITOR) { + __ b(*stub->entry()); + } else { + __ unlock_object(R5, R6, R4, *stub->entry()); + } __ bind(*stub->continuation()); } diff --git a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp index 83fad376d29..ea4d76e200f 100644 --- a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp @@ -114,6 +114,8 @@ void C1_MacroAssembler::lock_object(Register Rmark, Register Roop, Register Rbox /*check without membar and ldarx first*/true); // If compare/exchange succeeded we found an unlocked object and we now have locked it // hence we are done. + } else { + assert(false, "Unhandled LockingMode:%d", LockingMode); } b(done); @@ -168,6 +170,8 @@ void C1_MacroAssembler::unlock_object(Register Rmark, Register Roop, Register Rb MacroAssembler::cmpxchgx_hint_release_lock(), noreg, &slow_int); + } else { + assert(false, "Unhandled LockingMode:%d", LockingMode); } b(done); bind(slow_int); From 285385247aaa262866697ed848040f05f4d94988 Mon Sep 17 00:00:00 2001 From: Yudi Zheng Date: Wed, 16 Oct 2024 19:58:27 +0000 Subject: [PATCH 84/95] 8342332: [JVMCI] Export CompilerToVM::Data::dtanh Reviewed-by: never --- src/hotspot/share/jvmci/vmStructs_jvmci.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index a26cd5efe23..02e6eaf40f3 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -132,6 +132,7 @@ static_field(CompilerToVM::Data, dsin, address) \ static_field(CompilerToVM::Data, dcos, address) \ static_field(CompilerToVM::Data, dtan, address) \ + static_field(CompilerToVM::Data, dtanh, address) \ static_field(CompilerToVM::Data, dexp, address) \ static_field(CompilerToVM::Data, dlog, address) \ static_field(CompilerToVM::Data, dlog10, address) \ From b4ab290fd7c3d914154755a1539b48ba33338c26 Mon Sep 17 00:00:00 2001 From: Valerie Peng Date: Wed, 16 Oct 2024 21:26:57 +0000 Subject: [PATCH 85/95] 8331959: Update PKCS#11 Cryptographic Token Interface to v3.1 Reviewed-by: weijun, ascarpino --- .../share/legal/pkcs11cryptotoken.md | 24 +- .../share/native/libj2pkcs11/pkcs11.h | 18 +- .../share/native/libj2pkcs11/pkcs11f.h | 17 +- .../share/native/libj2pkcs11/pkcs11t.h | 287 +++++++++++------- 4 files changed, 215 insertions(+), 131 deletions(-) diff --git a/src/jdk.crypto.cryptoki/share/legal/pkcs11cryptotoken.md b/src/jdk.crypto.cryptoki/share/legal/pkcs11cryptotoken.md index 08d1e3c713d..7877f54fe6e 100644 --- a/src/jdk.crypto.cryptoki/share/legal/pkcs11cryptotoken.md +++ b/src/jdk.crypto.cryptoki/share/legal/pkcs11cryptotoken.md @@ -1,16 +1,16 @@ -## OASIS PKCS #11 Cryptographic Token Interface v3.0 +## OASIS PKCS #11 Cryptographic Token Interface v3.1 ### OASIS PKCS #11 Cryptographic Token Interface License
       
      -Copyright © OASIS Open 2020. All Rights Reserved.
      +Copyright © OASIS Open 2023. All Rights Reserved.
       
      -    All capitalized terms in the following text have the meanings
      +All capitalized terms in the following text have the meanings
       assigned to them in the OASIS Intellectual Property Rights Policy (the
       "OASIS IPR Policy"). The full Policy may be found at the OASIS website:
      -[http://www.oasis-open.org/policies-guidelines/ipr]
      +[https://www.oasis-open.org/policies-guidelines/ipr/].
       
      -    This document and translations of it may be copied and furnished to
      +This document and translations of it may be copied and furnished to
       others, and derivative works that comment on or otherwise explain it or
       assist in its implementation may be prepared, copied, published, and
       distributed, in whole or in part, without restriction of any kind,
      @@ -23,10 +23,10 @@ Committee (in which case the rules applicable to copyrights, as set
       forth in the OASIS IPR Policy, must be followed) or as required to
       translate it into languages other than English.
       
      -    The limited permissions granted above are perpetual and will not be
      +The limited permissions granted above are perpetual and will not be
       revoked by OASIS or its successors or assigns.
       
      -    This document and the information contained herein is provided on an
      +This document and the information contained herein is provided on an
       "AS IS" basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
       INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
       INFORMATION HEREIN WILL NOT INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED
      @@ -35,7 +35,11 @@ AND ITS MEMBERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
       CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THIS DOCUMENT OR ANY
       PART THEREOF.
       
      -    [OASIS requests that any OASIS Party or any other party that
      +As stated in the OASIS IPR Policy, the following three paragraphs in
      +brackets apply to OASIS Standards Final Deliverable documents (Committee
      +Specifications, OASIS Standards, or Approved Errata).
      +
      +[OASIS requests that any OASIS Party or any other party that
       believes it has patent claims that would necessarily be infringed by
       implementations of this OASIS Standards Final Deliverable, to notify
       OASIS TC Administrator and provide an indication of its willingness to
      @@ -43,7 +47,7 @@ grant patent licenses to such patent claims in a manner consistent with
       the IPR Mode of the OASIS Technical Committee that produced this
       deliverable.]
       
      -    [OASIS invites any party to contact the OASIS TC Administrator if it
      +[OASIS invites any party to contact the OASIS TC Administrator if it
       is aware of a claim of ownership of any patent claims that would
       necessarily be infringed by implementations of this OASIS Standards
       Final Deliverable by a patent holder that is not willing to provide a
      @@ -52,7 +56,7 @@ of the OASIS Technical Committee that produced this OASIS Standards
       Final Deliverable. OASIS may include such claims on its website, but
       disclaims any obligation to do so.]
       
      -    [OASIS takes no position regarding the validity or scope of any
      +[OASIS takes no position regarding the validity or scope of any
       intellectual property or other rights that might be claimed to pertain
       to the implementation or use of the technology described in this OASIS
       Standards Final Deliverable or the extent to which any license under
      diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11.h b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11.h
      index 5b050def520..5933da0e3b7 100644
      --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11.h
      +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11.h
      @@ -1,8 +1,11 @@
      -/* Copyright (c) OASIS Open 2016-2019. All Rights Reserved.
      - * Distributed under the terms of the OASIS IPR Policy,
      - * [http://www.oasis-open.org/policies-guidelines/ipr], AS-IS, WITHOUT ANY
      - * IMPLIED OR EXPRESS WARRANTY; there is no warranty of MERCHANTABILITY, FITNESS FOR A
      - * PARTICULAR PURPOSE or NONINFRINGEMENT of the rights of others.
      +/*
      + * PKCS #11 Specification Version 3.1
      + * OASIS Standard
      + * 23 July 2023
      + * Copyright (c) OASIS Open 2023. All Rights Reserved.
      + * Source: https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/os/include/pkcs11-v3.1/
      + * Latest stage of narrative specification: https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/pkcs11-spec-v3.1.html
      + * TC IPR Statement: https://www.oasis-open.org/committees/pkcs11/ipr.php
        */
       
       #ifndef _PKCS11_H_
      @@ -47,7 +50,7 @@ extern "C" {
        *
        * typedef CK_BYTE CK_PTR CK_BYTE_PTR;
        *
      - * If you're using windows, it might be defined by:
      + * If you're using Windows, it might be defined by:
        *
        * #define CK_PTR *
        *
      @@ -65,7 +68,7 @@ extern "C" {
        *   CK_VOID_PTR pReserved
        * );
        *
      - * If you're using Windows to declare a function in a Win32 cryptoki .dll,
      + * If you're using Windows to declare a function in a Win32 Cryptoki .dll,
        * it might be defined by:
        *
        * #define CK_DECLARE_FUNCTION(returnType, name) \
      @@ -241,4 +244,3 @@ struct CK_FUNCTION_LIST {
       
       #endif /* _PKCS11_H_ */
       
      -
      diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11f.h b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11f.h
      index 0553c1dc73c..80c43400d05 100644
      --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11f.h
      +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11f.h
      @@ -1,12 +1,11 @@
      -/* Copyright (c) OASIS Open 2016, 2019. All Rights Reserved./
      - * /Distributed under the terms of the OASIS IPR Policy,
      - * [http://www.oasis-open.org/policies-guidelines/ipr], AS-IS, WITHOUT ANY
      - * IMPLIED OR EXPRESS WARRANTY; there is no warranty of MERCHANTABILITY, FITNESS FOR A
      - * PARTICULAR PURPOSE or NONINFRINGEMENT of the rights of others.
      - */
      -
      -/* Latest version of the specification:
      - * http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html
      +/*
      + * PKCS #11 Specification Version 3.1
      + * OASIS Standard
      + * 23 July 2023
      + * Copyright (c) OASIS Open 2023. All Rights Reserved.
      + * Source: https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/os/include/pkcs11-v3.1/
      + * Latest stage of narrative specification: https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/pkcs11-spec-v3.1.html
      + * TC IPR Statement: https://www.oasis-open.org/committees/pkcs11/ipr.php
        */
       
       /* This header file contains pretty much everything about all the
      diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11t.h b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11t.h
      index ab6ef326e8b..79d7cf7d7da 100644
      --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11t.h
      +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11t.h
      @@ -1,12 +1,11 @@
      -/* Copyright (c) OASIS Open 2016, 2019. All Rights Reserved./
      - * /Distributed under the terms of the OASIS IPR Policy,
      - * [http://www.oasis-open.org/policies-guidelines/ipr], AS-IS, WITHOUT ANY
      - * IMPLIED OR EXPRESS WARRANTY; there is no warranty of MERCHANTABILITY, FITNESS FOR A
      - * PARTICULAR PURPOSE or NONINFRINGEMENT of the rights of others.
      - */
      -
      -/* Latest version of the specification:
      - * http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html
      +/*
      + * PKCS #11 Specification Version 3.1
      + * OASIS Standard
      + * 23 July 2023
      + * Copyright (c) OASIS Open 2023. All Rights Reserved.
      + * Source: https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/os/include/pkcs11-v3.1/
      + * Latest stage of narrative specification: https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/pkcs11-spec-v3.1.html
      + * TC IPR Statement: https://www.oasis-open.org/committees/pkcs11/ipr.php
        */
       
       /* See top of pkcs11.h for information about the macros that
      @@ -18,7 +17,7 @@
       #define _PKCS11T_H_ 1
       
       #define CRYPTOKI_VERSION_MAJOR          3
      -#define CRYPTOKI_VERSION_MINOR          0
      +#define CRYPTOKI_VERSION_MINOR          1
       #define CRYPTOKI_VERSION_AMENDMENT      0
       
       #define CK_TRUE         1
      @@ -329,8 +328,11 @@ typedef CK_OBJECT_CLASS CK_PTR CK_OBJECT_CLASS_PTR;
       #define CKP_EXTENDED_PROVIDER         0x00000002UL
       #define CKP_AUTHENTICATION_TOKEN      0x00000003UL
       #define CKP_PUBLIC_CERTIFICATES_TOKEN 0x00000004UL
      +#define CKP_COMPLETE_PROVIDER         0x00000005UL
      +#define CKP_HKDF_TLS_TOKEN            0x00000006UL
       #define CKP_VENDOR_DEFINED            0x80000000UL
       
      +
       /* CK_HW_FEATURE_TYPE is a value that identifies the hardware feature type
        * of an object with CK_OBJECT_CLASS equal to CKO_HW_FEATURE.
        */
      @@ -409,9 +411,11 @@ typedef CK_ULONG          CK_KEY_TYPE;
       #define CKK_EC_EDWARDS          0x00000040UL
       #define CKK_EC_MONTGOMERY       0x00000041UL
       #define CKK_HKDF                0x00000042UL
      +
       #define CKK_SHA512_224_HMAC     0x00000043UL
       #define CKK_SHA512_256_HMAC     0x00000044UL
       #define CKK_SHA512_T_HMAC       0x00000045UL
      +#define CKK_HSS                 0x00000046UL
       
       #define CKK_VENDOR_DEFINED      0x80000000UL
       
      @@ -481,9 +485,9 @@ typedef CK_ULONG          CK_ATTRIBUTE_TYPE;
       #define CKA_CERTIFICATE_CATEGORY        0x00000087UL
       #define CKA_JAVA_MIDP_SECURITY_DOMAIN   0x00000088UL
       #define CKA_URL                         0x00000089UL
      -#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY  0x0000008AUL
      -#define CKA_HASH_OF_ISSUER_PUBLIC_KEY   0x0000008BUL
      -#define CKA_NAME_HASH_ALGORITHM         0x0000008CUL
      +#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY  0x0000008aUL
      +#define CKA_HASH_OF_ISSUER_PUBLIC_KEY   0x0000008bUL
      +#define CKA_NAME_HASH_ALGORITHM         0x0000008cUL
       #define CKA_CHECK_VALUE                 0x00000090UL
       
       #define CKA_KEY_TYPE           0x00000100UL
      @@ -496,9 +500,9 @@ typedef CK_ULONG          CK_ATTRIBUTE_TYPE;
       #define CKA_UNWRAP             0x00000107UL
       #define CKA_SIGN               0x00000108UL
       #define CKA_SIGN_RECOVER       0x00000109UL
      -#define CKA_VERIFY             0x0000010AUL
      -#define CKA_VERIFY_RECOVER     0x0000010BUL
      -#define CKA_DERIVE             0x0000010CUL
      +#define CKA_VERIFY             0x0000010aUL
      +#define CKA_VERIFY_RECOVER     0x0000010bUL
      +#define CKA_DERIVE             0x0000010cUL
       #define CKA_START_DATE         0x00000110UL
       #define CKA_END_DATE           0x00000111UL
       #define CKA_MODULUS            0x00000120UL
      @@ -555,12 +559,12 @@ typedef CK_ULONG          CK_ATTRIBUTE_TYPE;
       #define CKA_OTP_TIME_REQUIREMENT      0x00000225UL
       #define CKA_OTP_COUNTER_REQUIREMENT   0x00000226UL
       #define CKA_OTP_PIN_REQUIREMENT       0x00000227UL
      -#define CKA_OTP_COUNTER               0x0000022EUL
      -#define CKA_OTP_TIME                  0x0000022FUL
      -#define CKA_OTP_USER_IDENTIFIER       0x0000022AUL
      -#define CKA_OTP_SERVICE_IDENTIFIER    0x0000022BUL
      -#define CKA_OTP_SERVICE_LOGO          0x0000022CUL
      -#define CKA_OTP_SERVICE_LOGO_TYPE     0x0000022DUL
      +#define CKA_OTP_COUNTER               0x0000022eUL
      +#define CKA_OTP_TIME                  0x0000022fUL
      +#define CKA_OTP_USER_IDENTIFIER       0x0000022aUL
      +#define CKA_OTP_SERVICE_IDENTIFIER    0x0000022bUL
      +#define CKA_OTP_SERVICE_LOGO          0x0000022cUL
      +#define CKA_OTP_SERVICE_LOGO_TYPE     0x0000022dUL
       
       #define CKA_GOSTR3410_PARAMS            0x00000250UL
       #define CKA_GOSTR3411_PARAMS            0x00000251UL
      @@ -586,6 +590,7 @@ typedef CK_ULONG          CK_ATTRIBUTE_TYPE;
       #define CKA_SUPPORTED_CMS_ATTRIBUTES    0x00000503UL
       #define CKA_ALLOWED_MECHANISMS          (CKF_ARRAY_ATTRIBUTE|0x00000600UL)
       #define CKA_PROFILE_ID                  0x00000601UL
      +
       #define CKA_X2RATCHET_BAG               0x00000602UL
       #define CKA_X2RATCHET_BAGSIZE           0x00000603UL
       #define CKA_X2RATCHET_BOBS1STMSG        0x00000604UL
      @@ -603,6 +608,13 @@ typedef CK_ULONG          CK_ATTRIBUTE_TYPE;
       #define CKA_X2RATCHET_NS                0x00000610UL
       #define CKA_X2RATCHET_PNS               0x00000611UL
       #define CKA_X2RATCHET_RK                0x00000612UL
      +/* HSS */
      +#define CKA_HSS_LEVELS                  0x00000617UL
      +#define CKA_HSS_LMS_TYPE                0x00000618UL
      +#define CKA_HSS_LMOTS_TYPE              0x00000619UL
      +#define CKA_HSS_LMS_TYPES               0x0000061aUL
      +#define CKA_HSS_LMOTS_TYPES             0x0000061bUL
      +#define CKA_HSS_KEYS_REMAINING          0x0000061cUL
       
       #define CKA_VENDOR_DEFINED              0x80000000UL
       
      @@ -644,11 +656,11 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_RIPEMD160_RSA_PKCS         0x00000008UL
       #define CKM_RSA_PKCS_OAEP              0x00000009UL
       
      -#define CKM_RSA_X9_31_KEY_PAIR_GEN     0x0000000AUL
      -#define CKM_RSA_X9_31                  0x0000000BUL
      -#define CKM_SHA1_RSA_X9_31             0x0000000CUL
      -#define CKM_RSA_PKCS_PSS               0x0000000DUL
      -#define CKM_SHA1_RSA_PKCS_PSS          0x0000000EUL
      +#define CKM_RSA_X9_31_KEY_PAIR_GEN     0x0000000aUL
      +#define CKM_RSA_X9_31                  0x0000000bUL
      +#define CKM_SHA1_RSA_X9_31             0x0000000cUL
      +#define CKM_RSA_PKCS_PSS               0x0000000dUL
      +#define CKM_SHA1_RSA_PKCS_PSS          0x0000000eUL
       
       #define CKM_DSA_KEY_PAIR_GEN           0x00000010UL
       #define CKM_DSA                        0x00000011UL
      @@ -659,8 +671,8 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_DSA_SHA512                 0x00000016UL
       #define CKM_DSA_SHA3_224               0x00000018UL
       #define CKM_DSA_SHA3_256               0x00000019UL
      -#define CKM_DSA_SHA3_384               0x0000001AUL
      -#define CKM_DSA_SHA3_512               0x0000001BUL
      +#define CKM_DSA_SHA3_384               0x0000001aUL
      +#define CKM_DSA_SHA3_512               0x0000001bUL
       
       #define CKM_DH_PKCS_KEY_PAIR_GEN       0x00000020UL
       #define CKM_DH_PKCS_DERIVE             0x00000021UL
      @@ -682,12 +694,12 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       
       #define CKM_SHA512_224                 0x00000048UL
       #define CKM_SHA512_224_HMAC            0x00000049UL
      -#define CKM_SHA512_224_HMAC_GENERAL    0x0000004AUL
      -#define CKM_SHA512_224_KEY_DERIVATION  0x0000004BUL
      -#define CKM_SHA512_256                 0x0000004CUL
      -#define CKM_SHA512_256_HMAC            0x0000004DUL
      -#define CKM_SHA512_256_HMAC_GENERAL    0x0000004EUL
      -#define CKM_SHA512_256_KEY_DERIVATION  0x0000004FUL
      +#define CKM_SHA512_224_HMAC_GENERAL    0x0000004aUL
      +#define CKM_SHA512_224_KEY_DERIVATION  0x0000004bUL
      +#define CKM_SHA512_256                 0x0000004cUL
      +#define CKM_SHA512_256_HMAC            0x0000004dUL
      +#define CKM_SHA512_256_HMAC_GENERAL    0x0000004eUL
      +#define CKM_SHA512_256_KEY_DERIVATION  0x0000004fUL
       
       #define CKM_SHA512_T                   0x00000050UL
       #define CKM_SHA512_T_HMAC              0x00000051UL
      @@ -781,25 +793,25 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_SECURID                    0x00000282UL
       #define CKM_HOTP_KEY_GEN               0x00000290UL
       #define CKM_HOTP                       0x00000291UL
      -#define CKM_ACTI                       0x000002A0UL
      -#define CKM_ACTI_KEY_GEN               0x000002A1UL
      -
      -#define CKM_SHA3_256                   0x000002B0UL
      -#define CKM_SHA3_256_HMAC              0x000002B1UL
      -#define CKM_SHA3_256_HMAC_GENERAL      0x000002B2UL
      -#define CKM_SHA3_256_KEY_GEN           0x000002B3UL
      -#define CKM_SHA3_224                   0x000002B5UL
      -#define CKM_SHA3_224_HMAC              0x000002B6UL
      -#define CKM_SHA3_224_HMAC_GENERAL      0x000002B7UL
      -#define CKM_SHA3_224_KEY_GEN           0x000002B8UL
      -#define CKM_SHA3_384                   0x000002C0UL
      -#define CKM_SHA3_384_HMAC              0x000002C1UL
      -#define CKM_SHA3_384_HMAC_GENERAL      0x000002C2UL
      -#define CKM_SHA3_384_KEY_GEN           0x000002C3UL
      -#define CKM_SHA3_512                   0x000002D0UL
      -#define CKM_SHA3_512_HMAC              0x000002D1UL
      -#define CKM_SHA3_512_HMAC_GENERAL      0x000002D2UL
      -#define CKM_SHA3_512_KEY_GEN           0x000002D3UL
      +#define CKM_ACTI                       0x000002a0UL
      +#define CKM_ACTI_KEY_GEN               0x000002a1UL
      +
      +#define CKM_SHA3_256                   0x000002b0UL
      +#define CKM_SHA3_256_HMAC              0x000002b1UL
      +#define CKM_SHA3_256_HMAC_GENERAL      0x000002b2UL
      +#define CKM_SHA3_256_KEY_GEN           0x000002b3UL
      +#define CKM_SHA3_224                   0x000002b5UL
      +#define CKM_SHA3_224_HMAC              0x000002b6UL
      +#define CKM_SHA3_224_HMAC_GENERAL      0x000002b7UL
      +#define CKM_SHA3_224_KEY_GEN           0x000002b8UL
      +#define CKM_SHA3_384                   0x000002c0UL
      +#define CKM_SHA3_384_HMAC              0x000002c1UL
      +#define CKM_SHA3_384_HMAC_GENERAL      0x000002c2UL
      +#define CKM_SHA3_384_KEY_GEN           0x000002c3UL
      +#define CKM_SHA3_512                   0x000002d0UL
      +#define CKM_SHA3_512_HMAC              0x000002d1UL
      +#define CKM_SHA3_512_HMAC_GENERAL      0x000002d2UL
      +#define CKM_SHA3_512_KEY_GEN           0x000002d3UL
       
       
       #define CKM_CAST_KEY_GEN               0x00000300UL
      @@ -870,9 +882,9 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_SHA3_256_KEY_DERIVATION    0x00000397UL
       #define CKM_SHA3_224_KEY_DERIVATION    0x00000398UL
       #define CKM_SHA3_384_KEY_DERIVATION    0x00000399UL
      -#define CKM_SHA3_512_KEY_DERIVATION    0x0000039AUL
      -#define CKM_SHAKE_128_KEY_DERIVATION   0x0000039BUL
      -#define CKM_SHAKE_256_KEY_DERIVATION   0x0000039CUL
      +#define CKM_SHA3_512_KEY_DERIVATION    0x0000039aUL
      +#define CKM_SHAKE_128_KEY_DERIVATION   0x0000039bUL
      +#define CKM_SHAKE_256_KEY_DERIVATION   0x0000039cUL
       #define CKM_SHA3_256_KEY_DERIVE  CKM_SHA3_256_KEY_DERIVATION
       #define CKM_SHA3_224_KEY_DERIVE  CKM_SHA3_224_KEY_DERIVATION
       #define CKM_SHA3_384_KEY_DERIVE  CKM_SHA3_384_KEY_DERIVATION
      @@ -880,40 +892,42 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_SHAKE_128_KEY_DERIVE CKM_SHAKE_128_KEY_DERIVATION
       #define CKM_SHAKE_256_KEY_DERIVE CKM_SHAKE_256_KEY_DERIVATION
       
      -#define CKM_PBE_MD2_DES_CBC            0x000003A0UL
      -#define CKM_PBE_MD5_DES_CBC            0x000003A1UL
      -#define CKM_PBE_MD5_CAST_CBC           0x000003A2UL
      -#define CKM_PBE_MD5_CAST3_CBC          0x000003A3UL
      -#define CKM_PBE_MD5_CAST5_CBC          0x000003A4UL /* Deprecated */
      -#define CKM_PBE_MD5_CAST128_CBC        0x000003A4UL
      -#define CKM_PBE_SHA1_CAST5_CBC         0x000003A5UL /* Deprecated */
      -#define CKM_PBE_SHA1_CAST128_CBC       0x000003A5UL
      -#define CKM_PBE_SHA1_RC4_128           0x000003A6UL
      -#define CKM_PBE_SHA1_RC4_40            0x000003A7UL
      -#define CKM_PBE_SHA1_DES3_EDE_CBC      0x000003A8UL
      -#define CKM_PBE_SHA1_DES2_EDE_CBC      0x000003A9UL
      -#define CKM_PBE_SHA1_RC2_128_CBC       0x000003AAUL
      -#define CKM_PBE_SHA1_RC2_40_CBC        0x000003ABUL
      -
      -#define CKM_PKCS5_PBKD2                0x000003B0UL
      -
      -#define CKM_PBA_SHA1_WITH_SHA1_HMAC    0x000003C0UL
      -
      -#define CKM_WTLS_PRE_MASTER_KEY_GEN         0x000003D0UL
      -#define CKM_WTLS_MASTER_KEY_DERIVE          0x000003D1UL
      -#define CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC   0x000003D2UL
      -#define CKM_WTLS_PRF                        0x000003D3UL
      -#define CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE  0x000003D4UL
      -#define CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE  0x000003D5UL
      -
      -#define CKM_TLS12_MAC                       0x000003D8UL
      -#define CKM_TLS12_KDF                       0x000003D9UL
      -#define CKM_TLS12_MASTER_KEY_DERIVE         0x000003E0UL
      -#define CKM_TLS12_KEY_AND_MAC_DERIVE        0x000003E1UL
      -#define CKM_TLS12_MASTER_KEY_DERIVE_DH      0x000003E2UL
      -#define CKM_TLS12_KEY_SAFE_DERIVE           0x000003E3UL
      -#define CKM_TLS_MAC                         0x000003E4UL
      -#define CKM_TLS_KDF                         0x000003E5UL
      +#define CKM_PBE_MD2_DES_CBC            0x000003a0UL
      +#define CKM_PBE_MD5_DES_CBC            0x000003a1UL
      +#define CKM_PBE_MD5_CAST_CBC           0x000003a2UL
      +#define CKM_PBE_MD5_CAST3_CBC          0x000003a3UL
      +#define CKM_PBE_MD5_CAST5_CBC          0x000003a4UL /* Deprecated */
      +#define CKM_PBE_MD5_CAST128_CBC        0x000003a4UL
      +#define CKM_PBE_SHA1_CAST5_CBC         0x000003a5UL /* Deprecated */
      +#define CKM_PBE_SHA1_CAST128_CBC       0x000003a5UL
      +#define CKM_PBE_SHA1_RC4_128           0x000003a6UL
      +#define CKM_PBE_SHA1_RC4_40            0x000003a7UL
      +#define CKM_PBE_SHA1_DES3_EDE_CBC      0x000003a8UL
      +#define CKM_PBE_SHA1_DES2_EDE_CBC      0x000003a9UL
      +#define CKM_PBE_SHA1_RC2_128_CBC       0x000003aaUL
      +#define CKM_PBE_SHA1_RC2_40_CBC        0x000003abUL
      +
      +#define CKM_PKCS5_PBKD2                0x000003b0UL
      +
      +#define CKM_PBA_SHA1_WITH_SHA1_HMAC    0x000003c0UL
      +
      +#define CKM_WTLS_PRE_MASTER_KEY_GEN         0x000003d0UL
      +#define CKM_WTLS_MASTER_KEY_DERIVE          0x000003d1UL
      +#define CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC   0x000003d2UL
      +#define CKM_WTLS_PRF                        0x000003d3UL
      +#define CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE  0x000003d4UL
      +#define CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE  0x000003d5UL
      +
      +#define CKM_TLS10_MAC_SERVER                0x000003d6UL
      +#define CKM_TLS10_MAC_CLIENT                0x000003d7UL
      +#define CKM_TLS12_MAC                       0x000003d8UL
      +#define CKM_TLS12_KDF                       0x000003d9UL
      +#define CKM_TLS12_MASTER_KEY_DERIVE         0x000003e0UL
      +#define CKM_TLS12_KEY_AND_MAC_DERIVE        0x000003e1UL
      +#define CKM_TLS12_MASTER_KEY_DERIVE_DH      0x000003e2UL
      +#define CKM_TLS12_KEY_SAFE_DERIVE           0x000003e3UL
      +#define CKM_TLS_MAC                         0x000003e4UL
      +#define CKM_TLS_KDF                         0x000003e5UL
       
       #define CKM_KEY_WRAP_LYNKS             0x00000400UL
       #define CKM_KEY_WRAP_SET_OAEP          0x00000401UL
      @@ -983,7 +997,7 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_ECDSA_SHA256               0x00001044UL
       #define CKM_ECDSA_SHA384               0x00001045UL
       #define CKM_ECDSA_SHA512               0x00001046UL
      -#define CKM_EC_KEY_PAIR_GEN_W_EXTRA_BITS 0x0000140BUL
      +#define CKM_EC_KEY_PAIR_GEN_W_EXTRA_BITS 0x0000140bUL
       
       #define CKM_ECDH1_DERIVE               0x00001050UL
       #define CKM_ECDH1_COFACTOR_DERIVE      0x00001051UL
      @@ -1012,12 +1026,12 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_AES_GCM                    0x00001087UL
       #define CKM_AES_CCM                    0x00001088UL
       #define CKM_AES_CTS                    0x00001089UL
      -#define CKM_AES_CMAC                   0x0000108AUL
      -#define CKM_AES_CMAC_GENERAL           0x0000108BUL
      +#define CKM_AES_CMAC                   0x0000108aUL
      +#define CKM_AES_CMAC_GENERAL           0x0000108bUL
       
      -#define CKM_AES_XCBC_MAC               0x0000108CUL
      -#define CKM_AES_XCBC_MAC_96            0x0000108DUL
      -#define CKM_AES_GMAC                   0x0000108EUL
      +#define CKM_AES_XCBC_MAC               0x0000108cUL
      +#define CKM_AES_XCBC_MAC_96            0x0000108dUL
      +#define CKM_AES_GMAC                   0x0000108eUL
       
       #define CKM_BLOWFISH_KEY_GEN           0x00001090UL
       #define CKM_BLOWFISH_CBC               0x00001091UL
      @@ -1066,6 +1080,7 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_AES_KEY_WRAP               0x00002109UL     /* WAS: 0x00001090 */
       #define CKM_AES_KEY_WRAP_PAD           0x0000210AUL     /* WAS: 0x00001091 */
       #define CKM_AES_KEY_WRAP_KWP           0x0000210BUL
      +#define CKM_AES_KEY_WRAP_PKCS7         0x0000210CUL
       
       #define CKM_RSA_PKCS_TPM_1_1           0x00004001UL
       #define CKM_RSA_PKCS_OAEP_TPM_1_1      0x00004002UL
      @@ -1125,6 +1140,14 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_SP800_108_FEEDBACK_KDF     0x000003adUL
       #define CKM_SP800_108_DOUBLE_PIPELINE_KDF 0x000003aeUL
       
      +#define CKM_IKE2_PRF_PLUS_DERIVE       0x0000402eUL
      +#define CKM_IKE_PRF_DERIVE             0x0000402fUL
      +#define CKM_IKE1_PRF_DERIVE            0x00004030UL
      +#define CKM_IKE1_EXTENDED_DERIVE       0x00004031UL
      +#define CKM_HSS_KEY_PAIR_GEN           0x00004032UL
      +#define CKM_HSS                        0x00004033UL
      +
      +
       #define CKM_VENDOR_DEFINED             0x80000000UL
       
       typedef CK_MECHANISM_TYPE CK_PTR CK_MECHANISM_TYPE_PTR;
      @@ -1320,6 +1343,7 @@ typedef CK_ULONG          CK_RV;
       #define CKR_FUNCTION_REJECTED                 0x00000200UL
       #define CKR_TOKEN_RESOURCE_EXCEEDED           0x00000201UL
       #define CKR_OPERATION_CANCEL_FAILED           0x00000202UL
      +#define CKR_KEY_EXHAUSTED                     0x00000203UL
       
       #define CKR_VENDOR_DEFINED                    0x80000000UL
       
      @@ -1436,6 +1460,7 @@ typedef CK_RSA_PKCS_MGF_TYPE CK_PTR CK_RSA_PKCS_MGF_TYPE_PTR;
       #define CKG_MGF1_SHA3_384     0x00000008UL
       #define CKG_MGF1_SHA3_512     0x00000009UL
       
      +
       /* CK_RSA_PKCS_OAEP_SOURCE_TYPE  is used to indicate the source
        * of the encoding parameter when formatting a message block
        * for the PKCS #1 OAEP encryption scheme.
      @@ -1701,8 +1726,7 @@ typedef struct CK_DES_CBC_ENCRYPT_DATA_PARAMS {
           CK_ULONG    length;
       } CK_DES_CBC_ENCRYPT_DATA_PARAMS;
       
      -typedef CK_DES_CBC_ENCRYPT_DATA_PARAMS CK_PTR \
      -        CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR;
      +typedef CK_DES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR;
       
       typedef struct CK_AES_CBC_ENCRYPT_DATA_PARAMS {
           CK_BYTE     iv[16];
      @@ -1710,8 +1734,7 @@ typedef struct CK_AES_CBC_ENCRYPT_DATA_PARAMS {
           CK_ULONG    length;
       } CK_AES_CBC_ENCRYPT_DATA_PARAMS;
       
      -typedef CK_AES_CBC_ENCRYPT_DATA_PARAMS CK_PTR \
      -        CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR;
      +typedef CK_AES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR;
       
       /* CK_SKIPJACK_PRIVATE_WRAP_PARAMS provides the parameters to the
        * CKM_SKIPJACK_PRIVATE_WRAP mechanism
      @@ -2051,6 +2074,7 @@ typedef CK_ULONG CK_GENERATOR_FUNCTION;
       #define CKG_GENERATE         0x00000001UL
       #define CKG_GENERATE_COUNTER 0x00000002UL
       #define CKG_GENERATE_RANDOM  0x00000003UL
      +#define CKG_GENERATE_COUNTER_XOR 0x00000004UL
       
       typedef struct CK_GCM_MESSAGE_PARAMS {
           CK_BYTE_PTR           pIv;
      @@ -2061,7 +2085,7 @@ typedef struct CK_GCM_MESSAGE_PARAMS {
           CK_ULONG              ulTagBits;
       } CK_GCM_MESSAGE_PARAMS;
       
      -typedef CK_GCM_MESSAGE_PARAMS CK_GCM_MESSAGE_PARAMS_PTR;
      +typedef CK_GCM_MESSAGE_PARAMS CK_PTR CK_GCM_MESSAGE_PARAMS_PTR;
       
       typedef struct CK_CCM_PARAMS {
           CK_ULONG    ulDataLen;
      @@ -2084,7 +2108,7 @@ typedef struct CK_CCM_MESSAGE_PARAMS {
           CK_ULONG              ulMACLen;
       } CK_CCM_MESSAGE_PARAMS;
       
      -typedef CK_CCM_MESSAGE_PARAMS CK_CCM_MESSAGE_PARAMS_PTR;
      +typedef CK_CCM_MESSAGE_PARAMS CK_PTR CK_CCM_MESSAGE_PARAMS_PTR;
       
       /* Deprecated. Use CK_GCM_PARAMS */
       typedef struct CK_AES_GCM_PARAMS {
      @@ -2339,7 +2363,6 @@ typedef struct CK_SALSA20_PARAMS {
           CK_BYTE_PTR pNonce;
           CK_ULONG    ulNonceBits;
       } CK_SALSA20_PARAMS;
      -
       typedef CK_SALSA20_PARAMS CK_PTR CK_SALSA20_PARAMS_PTR;
       
       typedef struct CK_SALSA20_CHACHA20_POLY1305_PARAMS {
      @@ -2423,6 +2446,7 @@ typedef struct CK_XEDDSA_PARAMS {
       } CK_XEDDSA_PARAMS;
       typedef CK_XEDDSA_PARAMS CK_PTR CK_XEDDSA_PARAMS_PTR;
       
      +/* HKDF params */
       typedef struct CK_HKDF_PARAMS {
           CK_BBOOL          bExtract;
           CK_BBOOL          bExpand;
      @@ -2440,5 +2464,60 @@ typedef CK_HKDF_PARAMS CK_PTR CK_HKDF_PARAMS_PTR;
       #define CKF_HKDF_SALT_DATA   0x00000002UL
       #define CKF_HKDF_SALT_KEY    0x00000004UL
       
      +/* HSS */
      +typedef CK_ULONG                   CK_HSS_LEVELS;
      +typedef CK_ULONG                   CK_LMS_TYPE;
      +typedef CK_ULONG                   CK_LMOTS_TYPE;
      +
      +typedef struct specifiedParams {
      +    CK_HSS_LEVELS levels;
      +    CK_LMS_TYPE   lm_type[8];
      +    CK_LMOTS_TYPE lm_ots_type[8];
      +} specifiedParams;
      +
      +/* IKE Params */
      +typedef struct CK_IKE2_PRF_PLUS_DERIVE_PARAMS {
      +    CK_MECHANISM_TYPE prfMechanism;
      +    CK_BBOOL          bHasSeedKey;
      +    CK_OBJECT_HANDLE  hSeedKey;
      +    CK_BYTE_PTR       pSeedData;
      +    CK_ULONG          ulSeedDataLen;
      +} CK_IKE2_PRF_PLUS_DERIVE_PARAMS;
      +typedef CK_IKE2_PRF_PLUS_DERIVE_PARAMS CK_PTR CK_IKE2_PRF_PLUS_DERIVE_PARAMS_PTR;
      +
      +typedef struct CK_IKE_PRF_DERIVE_PARAMS {
      +    CK_MECHANISM_TYPE prfMechanism;
      +    CK_BBOOL          bDataAsKey;
      +    CK_BBOOL          bRekey;
      +    CK_BYTE_PTR       pNi;
      +    CK_ULONG          ulNiLen;
      +    CK_BYTE_PTR       pNr;
      +    CK_ULONG          ulNrLen;
      +    CK_OBJECT_HANDLE  hNewKey;
      +} CK_IKE_PRF_DERIVE_PARAMS;
      +typedef CK_IKE_PRF_DERIVE_PARAMS CK_PTR CK_IKE_PRF_DERIVE_PARAMS_PTR;
      +
      +typedef struct CK_IKE1_PRF_DERIVE_PARAMS {
      +    CK_MECHANISM_TYPE prfMechanism;
      +    CK_BBOOL          bHasPrevKey;
      +    CK_OBJECT_HANDLE  hKeygxy;
      +    CK_OBJECT_HANDLE  hPrevKey;
      +    CK_BYTE_PTR       pCKYi;
      +    CK_ULONG          ulCKYiLen;
      +    CK_BYTE_PTR       pCKYr;
      +    CK_ULONG          ulCKYrLen;
      +    CK_BYTE           keyNumber;
      +} CK_IKE1_PRF_DERIVE_PARAMS;
      +typedef CK_IKE1_PRF_DERIVE_PARAMS CK_PTR CK_IKE1_PRF_DERIVE_PARAMS_PTR;
      +
      +typedef struct CK_IKE1_EXTENDED_DERIVE_PARAMS {
      +    CK_MECHANISM_TYPE prfMechanism;
      +    CK_BBOOL          bHasKeygxy;
      +    CK_OBJECT_HANDLE  hKeygxy;
      +    CK_BYTE_PTR       pExtraData;
      +    CK_ULONG          ulExtraDataLen;
      +} CK_IKE1_EXTENDED_DERIVE_PARAMS;
      +typedef CK_IKE1_EXTENDED_DERIVE_PARAMS CK_PTR CK_IKE1_EXTENDED_DERIVE_PARAMS_PTR;
      +
       #endif /* _PKCS11T_H_ */
       
      
      From 4e703b285b5b34fdfb342d194cd744660d4c2be1 Mon Sep 17 00:00:00 2001
      From: Alisen Chung 
      Date: Wed, 16 Oct 2024 21:57:15 +0000
      Subject: [PATCH 86/95] 8340140: Open some dialog awt tests 3
      
      Reviewed-by: prr, honkar
      ---
       .../java/awt/Dialog/ClosingParentTest.java    | 100 +++++++++++++++++
       .../awt/Dialog/FileDialogEmptyTitleTest.java  |  59 ++++++++++
       .../java/awt/Dialog/FileDialogUIUpdate.java   |  83 ++++++++++++++
       .../awt/Dialog/MenuAndModalDialogTest.java    | 104 ++++++++++++++++++
       4 files changed, 346 insertions(+)
       create mode 100644 test/jdk/java/awt/Dialog/ClosingParentTest.java
       create mode 100644 test/jdk/java/awt/Dialog/FileDialogEmptyTitleTest.java
       create mode 100644 test/jdk/java/awt/Dialog/FileDialogUIUpdate.java
       create mode 100644 test/jdk/java/awt/Dialog/MenuAndModalDialogTest.java
      
      diff --git a/test/jdk/java/awt/Dialog/ClosingParentTest.java b/test/jdk/java/awt/Dialog/ClosingParentTest.java
      new file mode 100644
      index 00000000000..9b2750ab95f
      --- /dev/null
      +++ b/test/jdk/java/awt/Dialog/ClosingParentTest.java
      @@ -0,0 +1,100 @@
      +/*
      + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
      + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      + *
      + * This code is free software; you can redistribute it and/or modify it
      + * under the terms of the GNU General Public License version 2 only, as
      + * published by the Free Software Foundation.
      + *
      + * This code is distributed in the hope that it will be useful, but WITHOUT
      + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      + * version 2 for more details (a copy is included in the LICENSE file that
      + * accompanied this code).
      + *
      + * You should have received a copy of the GNU General Public License version
      + * 2 along with this work; if not, write to the Free Software Foundation,
      + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      + *
      + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      + * or visit www.oracle.com if you need additional information or have any
      + * questions.
      + */
      +
      +import java.awt.Button;
      +import java.awt.Dialog;
      +import java.awt.Frame;
      +import java.awt.event.ActionEvent;
      +import java.awt.event.ActionListener;
      +import java.awt.event.WindowEvent;
      +import java.awt.event.WindowAdapter;
      +
      +/*
      + * @test
      + * @bug 4336913
      + * @summary On Windows, disable parent window controls while modal dialog is being created.
      + * @library /java/awt/regtesthelpers
      + * @build PassFailJFrame
      + * @run main/manual ClosingParentTest
      + */
      +
      +public class ClosingParentTest {
      +
      +    static String instructions = """
      +            When the test starts, you will see a Frame with a Button
      +            titled 'Show modal dialog with delay'. Press this button
      +            and before the modal Dialog is shown, try to close the
      +            Frame using X button or system menu for windowing systems
      +            which don't provide X button in Window decorations. The
      +            delay before Dialog showing is 5 seconds.
      +            If in test output you see message about WINDOW_CLOSING
      +            being dispatched, then test fails. If no such message
      +            is printed, the test passes.
      +            """;
      +
      +    public static void main(String[] args) throws Exception {
      +        PassFailJFrame.builder()
      +                .title("ClosingParentTest")
      +                .instructions(instructions)
      +                .testTimeOut(5)
      +                .rows(10)
      +                .columns(35)
      +                .testUI(ClosingParentTest::createGUI)
      +                .build()
      +                .awaitAndCheck();
      +    }
      +
      +    public static Frame createGUI() {
      +        Frame frame = new Frame("Main Frame");
      +        Dialog dialog = new Dialog(frame, true);
      +
      +        Button button = new Button("Show modal dialog with delay");
      +        button.addActionListener(new ActionListener() {
      +            public void actionPerformed(ActionEvent e) {
      +                try {
      +                    Thread.currentThread().sleep(5000);
      +                } catch (InterruptedException x) {
      +                    x.printStackTrace();
      +                }
      +
      +                dialog.setVisible(true);
      +            }
      +        });
      +        frame.add(button);
      +        frame.pack();
      +        frame.addWindowListener(new WindowAdapter() {
      +            public void windowClosing(WindowEvent e) {
      +                System.out.println("WINDOW_CLOSING dispatched on the frame");
      +            }
      +        });
      +
      +        dialog.setSize(100, 100);
      +        dialog.addWindowListener(new WindowAdapter() {
      +            public void windowClosing(WindowEvent e) {
      +                dialog.dispose();
      +            }
      +        });
      +
      +        return frame;
      +    }
      +}
      diff --git a/test/jdk/java/awt/Dialog/FileDialogEmptyTitleTest.java b/test/jdk/java/awt/Dialog/FileDialogEmptyTitleTest.java
      new file mode 100644
      index 00000000000..d288a7c18f6
      --- /dev/null
      +++ b/test/jdk/java/awt/Dialog/FileDialogEmptyTitleTest.java
      @@ -0,0 +1,59 @@
      +/*
      + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
      + * 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.FileDialog;
      +import java.awt.Frame;
      +
      +/*
      + * @test
      + * @bug 4177831
      + * @summary solaris: default FileDialog title is not empty
      + * @library /java/awt/regtesthelpers
      + * @build PassFailJFrame
      + * @run main/manual FileDialogEmptyTitleTest
      + */
      +
      +public class FileDialogEmptyTitleTest {
      +    static String instructions = """
      +            Test passes if title of file dialog is empty,
      +            otherwise test failed.
      +            """;
      +
      +    public static void main(String[] args) throws Exception {
      +        PassFailJFrame.builder()
      +                .title("FileDialogEmptyTitleTest")
      +                .instructions(instructions)
      +                .testTimeOut(5)
      +                .rows(10)
      +                .columns(35)
      +                .testUI(FileDialogEmptyTitleTest::createGUI)
      +                .build()
      +                .awaitAndCheck();
      +    }
      +
      +    public static FileDialog createGUI() {
      +        Frame frame = new Frame("invisible dialog owner");
      +        FileDialog fileDialog = new FileDialog(frame);
      +        return fileDialog;
      +    }
      +}
      diff --git a/test/jdk/java/awt/Dialog/FileDialogUIUpdate.java b/test/jdk/java/awt/Dialog/FileDialogUIUpdate.java
      new file mode 100644
      index 00000000000..f97d947991b
      --- /dev/null
      +++ b/test/jdk/java/awt/Dialog/FileDialogUIUpdate.java
      @@ -0,0 +1,83 @@
      +/*
      + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
      + * 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.Button;
      +import java.awt.FileDialog;
      +import java.awt.Frame;
      +import java.awt.event.ActionEvent;
      +import java.awt.event.ActionListener;
      +
      +/*
      + * @test
      + * @bug 4859390
      + * @requires (os.family == "windows")
      + * @summary Verify that FileDialog matches the look
      +    of the native windows FileDialog
      + * @library /java/awt/regtesthelpers
      + * @build PassFailJFrame
      + * @run main/manual FileDialogUIUpdate
      + */
      +
      +public class FileDialogUIUpdate extends Frame {
      +    static String instructions = """
      +            Click the button to show the FileDialog. Then open the Paint
      +            application (usually found in Program Files->Accessories).
      +            Select File->Open from Paint to display a native Open dialog.
      +            Compare the native dialog to the AWT FileDialog.
      +            Specifically, confirm that the Places Bar icons are along the left side (or
      +            not, if the native dialog doesn't have them), and that the
      +            dialogs are both resizable (or not).
      +            If the file dialogs both look the same press Pass.  If not,
      +            press Fail.
      +            """;
      +
      +    public static void main(String[] args) throws Exception {
      +        PassFailJFrame.builder()
      +                .title("FileDialogUIUpdate")
      +                .instructions(instructions)
      +                .testTimeOut(5)
      +                .rows(12)
      +                .columns(35)
      +                .testUI(FileDialogUIUpdate::new)
      +                .build()
      +                .awaitAndCheck();
      +    }
      +
      +    public FileDialogUIUpdate() {
      +        final FileDialog fd = new FileDialog(new Frame("FileDialogUIUpdate frame"),
      +                "Open FileDialog");
      +        Button showButton = new Button("Show FileDialog");
      +        setLayout(new BorderLayout());
      +
      +        fd.setDirectory("c:/");
      +        showButton.addActionListener(new ActionListener() {
      +            public void actionPerformed(ActionEvent e) {
      +                fd.setVisible(true);
      +            }
      +        });
      +
      +        add(showButton);
      +        setSize(200, 200);
      +    }
      +}
      diff --git a/test/jdk/java/awt/Dialog/MenuAndModalDialogTest.java b/test/jdk/java/awt/Dialog/MenuAndModalDialogTest.java
      new file mode 100644
      index 00000000000..c22116ff8df
      --- /dev/null
      +++ b/test/jdk/java/awt/Dialog/MenuAndModalDialogTest.java
      @@ -0,0 +1,104 @@
      +/*
      + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
      + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      + *
      + * This code is free software; you can redistribute it and/or modify it
      + * under the terms of the GNU General Public License version 2 only, as
      + * published by the Free Software Foundation.
      + *
      + * This code is distributed in the hope that it will be useful, but WITHOUT
      + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      + * version 2 for more details (a copy is included in the LICENSE file that
      + * accompanied this code).
      + *
      + * You should have received a copy of the GNU General Public License version
      + * 2 along with this work; if not, write to the Free Software Foundation,
      + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      + *
      + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      + * or visit www.oracle.com if you need additional information or have any
      + * questions.
      + */
      +
      +import java.awt.Button;
      +import java.awt.Dialog;
      +import java.awt.Frame;
      +import java.awt.Menu;
      +import java.awt.MenuBar;
      +import java.awt.MenuItem;
      +import java.awt.event.ActionEvent;
      +import java.awt.event.ActionListener;
      +
      +/*
      + * @test
      + * @bug 4070085
      + * @summary Java program locks up X server
      + * @library /java/awt/regtesthelpers
      + * @build PassFailJFrame
      + * @run main/manual MenuAndModalDialogTest
      + */
      +
      +public class MenuAndModalDialogTest {
      +    static Frame frame;
      +    static String instructions = """
      +            1. Bring up the File Menu and leave it up.
      +            2. In a few seconds, the modal dialog will appear.
      +            3. Verify that your system does not lock up when you push the "OK" button.
      +            """;
      +
      +    public static void main(String[] args) throws Exception {
      +        PassFailJFrame pf = PassFailJFrame.builder()
      +                .title("MenuAndModalDialogTest")
      +                .instructions(instructions)
      +                .testTimeOut(5)
      +                .rows(10)
      +                .columns(35)
      +                .testUI(MenuAndModalDialogTest::createFrame)
      +                .build();
      +
      +        // Allow time to pop up the menu
      +        try {
      +            Thread.currentThread().sleep(5000);
      +        } catch (InterruptedException exception) {
      +        }
      +
      +        createDialog();
      +        pf.awaitAndCheck();
      +    }
      +
      +    public static Frame createFrame() {
      +        frame = new Frame("MenuAndModalDialogTest frame");
      +
      +        MenuBar menuBar = new MenuBar();
      +        frame.setMenuBar(menuBar);
      +
      +        Menu file = new Menu("File");
      +        menuBar.add(file);
      +
      +        MenuItem menuItem = new MenuItem("A Menu Entry");
      +        file.add(menuItem);
      +
      +        frame.setSize(200, 200);
      +        frame.setLocationRelativeTo(null);
      +        return frame;
      +    }
      +
      +    public static void createDialog() {
      +        Dialog dialog = new Dialog(frame);
      +
      +        Button button = new Button("OK");
      +        dialog.add(button);
      +        button.addActionListener(
      +                new ActionListener() {
      +                    public void actionPerformed(ActionEvent e) {
      +                        dialog.dispose();
      +                    }
      +                }
      +        );
      +
      +        dialog.setSize(200, 200);
      +        dialog.setModal(true);
      +        dialog.setVisible(true);
      +    }
      +}
      
      From 3da68900818fc43b777098fe6e244779794d5294 Mon Sep 17 00:00:00 2001
      From: Vlad Zahorodnii 
      Date: Wed, 16 Oct 2024 23:32:41 +0000
      Subject: [PATCH 87/95] 8338751: ConfigureNotify behavior has changed in KWin
       6.2
      
      Reviewed-by: prr, azvegint, serb
      ---
       src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java | 1 +
       1 file changed, 1 insertion(+)
      
      diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java b/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java
      index 069e3b7559b..1c0f8e27ffb 100644
      --- a/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java
      +++ b/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java
      @@ -771,6 +771,7 @@ protected Point getNewLocation(XConfigureEvent xe, int leftInset, int topInset)
                   // TODO this should be the default for every case.
                   switch (runningWM) {
                       case XWM.CDE_WM:
      +                case XWM.KDE2_WM:
                       case XWM.MOTIF_WM:
                       case XWM.METACITY_WM:
                       case XWM.MUTTER_WM:
      
      From 58d39c317e332fda994f66529fcd1a0ea0e53151 Mon Sep 17 00:00:00 2001
      From: Tobias Hartmann 
      Date: Thu, 17 Oct 2024 05:03:09 +0000
      Subject: [PATCH 88/95] 8340313: Crash due to invalid oop in nmethod after C1
       patching
      
      Reviewed-by: tschatzl, kvn, dlong
      ---
       .../cpu/aarch64/nativeInst_aarch64.cpp        |   2 +-
       src/hotspot/cpu/ppc/nativeInst_ppc.cpp        |   4 +-
       src/hotspot/cpu/riscv/nativeInst_riscv.cpp    |   6 +-
       src/hotspot/cpu/s390/nativeInst_s390.cpp      |   4 +-
       src/hotspot/cpu/x86/nativeInst_x86.cpp        |   4 +-
       src/hotspot/share/c1/c1_Runtime1.cpp          |  12 +-
       src/hotspot/share/code/nmethod.cpp            |   2 +-
       src/hotspot/share/runtime/mutexLocker.cpp     |   2 -
       src/hotspot/share/runtime/mutexLocker.hpp     |   1 -
       .../compiler/c1/TestConcurrentPatching.java   | 145 ++++++++++++++++++
       10 files changed, 160 insertions(+), 22 deletions(-)
       create mode 100644 test/hotspot/jtreg/compiler/c1/TestConcurrentPatching.java
      
      diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
      index 770ed2f6c38..1f6d7292389 100644
      --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
      +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
      @@ -78,7 +78,7 @@ address NativeCall::destination() const {
       //
       // Used in the runtime linkage of calls; see class CompiledIC.
       void NativeCall::set_destination_mt_safe(address dest) {
      -  assert((Patching_lock->is_locked() || SafepointSynchronize::is_at_safepoint()) ||
      +  assert((CodeCache_lock->is_locked() || SafepointSynchronize::is_at_safepoint()) ||
                CompiledICLocker::is_safe(addr_at(0)),
                "concurrent code patching");
       
      diff --git a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp
      index 9520f6a94f2..f61328fc736 100644
      --- a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp
      +++ b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp
      @@ -92,10 +92,10 @@ address NativeCall::destination() const {
       // Used in the runtime linkage of calls; see class CompiledIC.
       //
       // Add parameter assert_lock to switch off assertion
      -// during code generation, where no patching lock is needed.
      +// during code generation, where no lock is needed.
       void NativeCall::set_destination_mt_safe(address dest, bool assert_lock) {
         assert(!assert_lock ||
      -         (Patching_lock->is_locked() || SafepointSynchronize::is_at_safepoint()) ||
      +         (CodeCache_lock->is_locked() || SafepointSynchronize::is_at_safepoint()) ||
                CompiledICLocker::is_safe(addr_at(0)),
                "concurrent code patching");
       
      diff --git a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp
      index 6c9e0986869..2a5a8c85af0 100644
      --- a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp
      +++ b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp
      @@ -215,10 +215,10 @@ void NativeShortCall::print() {
       // Used in the runtime linkage of calls; see class CompiledIC.
       //
       // Add parameter assert_lock to switch off assertion
      -// during code generation, where no patching lock is needed.
      +// during code generation, where no lock is needed.
       bool NativeShortCall::set_destination_mt_safe(address dest, bool assert_lock) {
         assert(!assert_lock ||
      -         (Patching_lock->is_locked() || SafepointSynchronize::is_at_safepoint()) ||
      +         (CodeCache_lock->is_locked() || SafepointSynchronize::is_at_safepoint()) ||
                CompiledICLocker::is_safe(instruction_address()),
                "concurrent code patching");
       
      @@ -386,7 +386,7 @@ void NativeFarCall::print() {
       bool NativeFarCall::set_destination_mt_safe(address dest, bool assert_lock) {
         assert(NativeFarCall::is_at(addr_at(0)), "unexpected code at call site");
         assert(!assert_lock ||
      -         (Patching_lock->is_locked() || SafepointSynchronize::is_at_safepoint()) ||
      +         (CodeCache_lock->is_locked() || SafepointSynchronize::is_at_safepoint()) ||
                CompiledICLocker::is_safe(addr_at(0)),
                "concurrent code patching");
       
      diff --git a/src/hotspot/cpu/s390/nativeInst_s390.cpp b/src/hotspot/cpu/s390/nativeInst_s390.cpp
      index 9f083fa8904..4db73395d3a 100644
      --- a/src/hotspot/cpu/s390/nativeInst_s390.cpp
      +++ b/src/hotspot/cpu/s390/nativeInst_s390.cpp
      @@ -658,8 +658,8 @@ void NativeGeneralJump::insert_unconditional(address code_pos, address entry) {
       
       void NativeGeneralJump::replace_mt_safe(address instr_addr, address code_buffer) {
         assert(((intptr_t)instr_addr & (BytesPerWord-1)) == 0, "requirement for mt safe patching");
      -  // Bytes_after_jump cannot change, because we own the Patching_lock.
      -  assert(Patching_lock->owned_by_self(), "must hold lock to patch instruction");
      +  // Bytes_after_jump cannot change, because we own the CodeCache_lock.
      +  assert(CodeCache_lock->owned_by_self(), "must hold lock to patch instruction");
         intptr_t bytes_after_jump = (*(intptr_t*)instr_addr)  & 0x000000000000ffffL; // 2 bytes after jump.
         intptr_t load_const_bytes = (*(intptr_t*)code_buffer) & 0xffffffffffff0000L;
         *(intptr_t*)instr_addr = load_const_bytes | bytes_after_jump;
      diff --git a/src/hotspot/cpu/x86/nativeInst_x86.cpp b/src/hotspot/cpu/x86/nativeInst_x86.cpp
      index 395c3219809..d5021c29ed6 100644
      --- a/src/hotspot/cpu/x86/nativeInst_x86.cpp
      +++ b/src/hotspot/cpu/x86/nativeInst_x86.cpp
      @@ -81,7 +81,7 @@ void NativeCall::insert(address code_pos, address entry) {
       // (spinlock). Then patches the last byte, and then atomically replaces
       // the jmp's with the first 4 byte of the new instruction.
       void NativeCall::replace_mt_safe(address instr_addr, address code_buffer) {
      -  assert(Patching_lock->is_locked() ||
      +  assert(CodeCache_lock->is_locked() ||
                SafepointSynchronize::is_at_safepoint(), "concurrent code patching");
         assert (instr_addr != nullptr, "illegal address for code patching");
       
      @@ -144,7 +144,7 @@ void NativeCall::set_destination_mt_safe(address dest) {
         debug_only(verify());
         // Make sure patching code is locked.  No two threads can patch at the same
         // time but one may be executing this code.
      -  assert(Patching_lock->is_locked() || SafepointSynchronize::is_at_safepoint() ||
      +  assert(CodeCache_lock->is_locked() || SafepointSynchronize::is_at_safepoint() ||
                CompiledICLocker::is_safe(instruction_address()), "concurrent code patching");
         // Both C1 and C2 should now be generating code which aligns the patched address
         // to be within a single cache line.
      diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp
      index 915f00f77c5..3ebd1f42f36 100644
      --- a/src/hotspot/share/c1/c1_Runtime1.cpp
      +++ b/src/hotspot/share/c1/c1_Runtime1.cpp
      @@ -887,7 +887,7 @@ static Klass* resolve_field_return_klass(const methodHandle& caller, int bci, TR
       //             movl reg, [reg1 + ]  (for field offsets)
       //             jmp continue
       //               
      -// patch_stub: jmp Runtim1::patch_code (through a runtime stub)
      +// patch_stub: jmp Runtime1::patch_code (through a runtime stub)
       //             jmp patch_site
       //
       // If the class is being initialized the patch body is rewritten and
      @@ -1103,7 +1103,7 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* current, C1StubId stub_id ))
         // Now copy code back
       
         {
      -    MutexLocker ml_patch (current, Patching_lock, Mutex::_no_safepoint_check_flag);
      +    MutexLocker ml_code (current, CodeCache_lock, Mutex::_no_safepoint_check_flag);
           //
           // Deoptimization may have happened while we waited for the lock.
           // In that case we don't bother to do any patching we just return
      @@ -1268,12 +1268,8 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* current, C1StubId stub_id ))
               }
             }
           }
      -  }
      -
      -  // If we are patching in a non-perm oop, make sure the nmethod
      -  // is on the right list.
      -  {
      -    MutexLocker ml_code (current, CodeCache_lock, Mutex::_no_safepoint_check_flag);
      +    // If we are patching in a non-perm oop, make sure the nmethod
      +    // is on the right list.
           nmethod* nm = CodeCache::find_nmethod(caller_frame.pc());
           guarantee(nm != nullptr, "only nmethods can contain non-perm oops");
       
      diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp
      index 0bab731ac56..7fb72997749 100644
      --- a/src/hotspot/share/code/nmethod.cpp
      +++ b/src/hotspot/share/code/nmethod.cpp
      @@ -2048,7 +2048,7 @@ bool nmethod::make_not_entrant() {
         } // leave critical region under NMethodState_lock
       
       #if INCLUDE_JVMCI
      -  // Invalidate can't occur while holding the Patching lock
      +  // Invalidate can't occur while holding the NMethodState_lock
         JVMCINMethodData* nmethod_data = jvmci_nmethod_data();
         if (nmethod_data != nullptr) {
           nmethod_data->invalidate_nmethod_mirror(this);
      diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp
      index 769c7695192..a0a6e5626e4 100644
      --- a/src/hotspot/share/runtime/mutexLocker.cpp
      +++ b/src/hotspot/share/runtime/mutexLocker.cpp
      @@ -36,7 +36,6 @@
       
       // Mutexes used in the VM (see comment in mutexLocker.hpp):
       
      -Mutex*   Patching_lock                = nullptr;
       Mutex*   NMethodState_lock            = nullptr;
       Monitor* SystemDictionary_lock        = nullptr;
       Mutex*   InvokeMethodTypeTable_lock   = nullptr;
      @@ -233,7 +232,6 @@ void mutex_init() {
         MUTEX_DEFN(Metaspace_lock                  , PaddedMutex  , nosafepoint-3);
         MUTEX_DEFN(MetaspaceCritical_lock          , PaddedMonitor, nosafepoint-1);
       
      -  MUTEX_DEFN(Patching_lock                   , PaddedMutex  , nosafepoint);      // used for safepointing and code patching.
         MUTEX_DEFN(MonitorDeflation_lock           , PaddedMonitor, nosafepoint);      // used for monitor deflation thread operations
         MUTEX_DEFN(Service_lock                    , PaddedMonitor, service);          // used for service thread operations
         MUTEX_DEFN(Notification_lock               , PaddedMonitor, service);          // used for notification thread operations
      diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp
      index 98cb27d0b81..932ca5fce82 100644
      --- a/src/hotspot/share/runtime/mutexLocker.hpp
      +++ b/src/hotspot/share/runtime/mutexLocker.hpp
      @@ -31,7 +31,6 @@
       
       // Mutexes used in the VM.
       
      -extern Mutex*   Patching_lock;                   // a lock used to guard code patching of compiled code
       extern Mutex*   NMethodState_lock;               // a lock used to guard a compiled method state
       extern Monitor* SystemDictionary_lock;           // a lock on the system dictionary
       extern Mutex*   InvokeMethodTypeTable_lock;
      diff --git a/test/hotspot/jtreg/compiler/c1/TestConcurrentPatching.java b/test/hotspot/jtreg/compiler/c1/TestConcurrentPatching.java
      new file mode 100644
      index 00000000000..6a7179d0ce4
      --- /dev/null
      +++ b/test/hotspot/jtreg/compiler/c1/TestConcurrentPatching.java
      @@ -0,0 +1,145 @@
      +/*
      + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
      + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      + *
      + * This code is free software; you can redistribute it and/or modify it
      + * under the terms of the GNU General Public License version 2 only, as
      + * published by the Free Software Foundation.
      + *
      + * This code is distributed in the hope that it will be useful, but WITHOUT
      + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      + * version 2 for more details (a copy is included in the LICENSE file that
      + * accompanied this code).
      + *
      + * You should have received a copy of the GNU General Public License version
      + * 2 along with this work; if not, write to the Free Software Foundation,
      + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      + *
      + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      + * or visit www.oracle.com if you need additional information or have any
      + * questions.
      + */
      +
      +import java.lang.reflect.Method;
      +import java.net.URL;
      +import java.net.URLClassLoader;
      +import java.util.ArrayList;
      +
      +/**
      +  * @test
      +  * @bug 8340313
      +  * @summary Test that concurrent patching of oop immediates is thread-safe in C1.
      +  * @run main/othervm/timeout=480 -Xcomp -XX:CompileCommand=compileonly,TestConcurrentPatching::* -XX:TieredStopAtLevel=1 TestConcurrentPatching
      +  */
      +
      +class MyClass { }
      +
      +class Holder {
      +    public static final MyClass OBJ1 = null;
      +    public static final MyClass OBJ2 = null;
      +    public static final MyClass OBJ3 = null;
      +    public static final MyClass OBJ4 = null;
      +    public static final MyClass OBJ5 = null;
      +    public static final MyClass OBJ6 = null;
      +    public static final MyClass OBJ7 = null;
      +    public static final MyClass OBJ8 = null;
      +    public static final MyClass OBJ9 = null;
      +    public static final MyClass OBJ10 = null;
      +    public static final MyClass OBJ11 = null;
      +    public static final MyClass OBJ12 = null;
      +    public static final MyClass OBJ13 = null;
      +    public static final MyClass OBJ14 = null;
      +    public static final MyClass OBJ15 = null;
      +    public static final MyClass OBJ16 = null;
      +    public static final MyClass OBJ17 = null;
      +    public static final MyClass OBJ18 = null;
      +    public static final MyClass OBJ19 = null;
      +    public static final MyClass OBJ20 = null;
      +}
      +
      +public class TestConcurrentPatching {
      +    // Increase to 100_000 for a good chance of reproducing the issue with a single run
      +    static final int ITERATIONS = 1000;
      +
      +    static Object field;
      +
      +    // 'Holder' class is unloaded on first execution and therefore field
      +    // accesses require patching when the method is C1 compiled (with -Xcomp).
      +    public static void test() {
      +        field = Holder.OBJ1;
      +        field = Holder.OBJ2;
      +        field = Holder.OBJ3;
      +        field = Holder.OBJ4;
      +        field = Holder.OBJ5;
      +        field = Holder.OBJ6;
      +        field = Holder.OBJ7;
      +        field = Holder.OBJ8;
      +        field = Holder.OBJ9;
      +        field = Holder.OBJ10;
      +        field = Holder.OBJ11;
      +        field = Holder.OBJ12;
      +        field = Holder.OBJ13;
      +        field = Holder.OBJ14;
      +        field = Holder.OBJ15;
      +        field = Holder.OBJ16;
      +        field = Holder.OBJ17;
      +        field = Holder.OBJ18;
      +        field = Holder.OBJ19;
      +        field = Holder.OBJ20;
      +    }
      +
      +    // Appendix of invokedynamic call sites is unloaded on first execution and
      +    // therefore requires patching when the method is C1 compiled (with -Xcomp).
      +    public static void testIndy() throws Throwable {
      +          field = (Runnable) () -> { };
      +          field = (Runnable) () -> { };
      +          field = (Runnable) () -> { };
      +          field = (Runnable) () -> { };
      +          field = (Runnable) () -> { };
      +          field = (Runnable) () -> { };
      +          field = (Runnable) () -> { };
      +          field = (Runnable) () -> { };
      +          field = (Runnable) () -> { };
      +          field = (Runnable) () -> { };
      +    }
      +
      +    // Run 'test' by multiple threads to trigger concurrent patching of field accesses
      +    static void runWithThreads(Method method) {
      +        ArrayList threads = new ArrayList<>();
      +        for (int threadIdx = 0; threadIdx < 10; threadIdx++) {
      +            threads.add(new Thread(() -> {
      +                try {
      +                    method.invoke(null);
      +                } catch (Exception e) {
      +                    throw new IllegalStateException(e);
      +                }
      +            }));
      +        }
      +        threads.forEach(Thread::start);
      +        threads.forEach(t -> {
      +            try {
      +                t.join();
      +            } catch (Throwable e) {
      +                throw new IllegalStateException(e);
      +            }
      +        });
      +    }
      +
      +    public static void main(String[] args) throws Exception {
      +        Class thisClass = TestConcurrentPatching.class;
      +        ClassLoader defaultLoader = thisClass.getClassLoader();
      +        URL classesDir = thisClass.getProtectionDomain().getCodeSource().getLocation();
      +
      +        // Load the test class multiple times with a separate class loader to make sure
      +        // that the 'Holder' class is unloaded for each compilation of method 'test'
      +        // and that the appendix of the invokedynamic call site is unloaded for each
      +        // compilation of method 'testIndy'.
      +        for (int i = 0; i < ITERATIONS; ++i) {
      +            URLClassLoader myLoader = URLClassLoader.newInstance(new URL[] {classesDir}, defaultLoader.getParent());
      +            Class testClass = Class.forName(thisClass.getCanonicalName(), true, myLoader);
      +            runWithThreads(testClass.getDeclaredMethod("test"));
      +            runWithThreads(testClass.getDeclaredMethod("testIndy"));
      +        }
      +    }
      +}
      
      From 22a1feea7484c9d640eeac22943d237a0e549942 Mon Sep 17 00:00:00 2001
      From: Christian Hagedorn 
      Date: Thu, 17 Oct 2024 05:08:59 +0000
      Subject: [PATCH 89/95] 8341328: Refactor initial Assertion Predicate creation
       into separate classes
      
      Reviewed-by: thartmann, kvn
      ---
       src/hotspot/share/opto/ifnode.cpp        |   3 +
       src/hotspot/share/opto/loopPredicate.cpp |  81 ++----
       src/hotspot/share/opto/loopTransform.cpp | 122 +++------
       src/hotspot/share/opto/loopnode.hpp      |  15 +-
       src/hotspot/share/opto/predicates.cpp    | 328 +++++++++++++++++++----
       src/hotspot/share/opto/predicates.hpp    |  91 +++++--
       6 files changed, 412 insertions(+), 228 deletions(-)
      
      diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp
      index 4313b2cf907..093cafd1574 100644
      --- a/src/hotspot/share/opto/ifnode.cpp
      +++ b/src/hotspot/share/opto/ifnode.cpp
      @@ -1849,6 +1849,9 @@ void IfNode::dump_spec(outputStream* st) const {
           case AssertionPredicateType::LastValue:
             st->print("#Last Value Assertion Predicate  ");
             break;
      +    case AssertionPredicateType::FinalIv:
      +      st->print("#Final IV Assertion Predicate  ");
      +      break;
           case AssertionPredicateType::None:
             // No Assertion Predicate
             break;
      diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp
      index 5e585a406f2..92f4e3582e9 100644
      --- a/src/hotspot/share/opto/loopPredicate.cpp
      +++ b/src/hotspot/share/opto/loopPredicate.cpp
      @@ -1153,7 +1153,8 @@ void PhaseIdealLoop::loop_predication_follow_branches(Node *n, IdealLoopTree *lo
       
       bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree* loop, IfProjNode* if_success_proj,
                                                         ParsePredicateSuccessProj* parse_predicate_proj, CountedLoopNode* cl,
      -                                                  ConNode* zero, Invariance& invar, Deoptimization::DeoptReason reason) {
      +                                                  ConNode* zero, Invariance& invar,
      +                                                  Deoptimization::DeoptReason deopt_reason) {
         // Following are changed to nonnull when a predicate can be hoisted
         IfNode*   iff  = if_success_proj->in(0)->as_If();
         Node*     test = iff->in(1);
      @@ -1165,7 +1166,7 @@ bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree* loop, IfProjNod
         if (invar.is_invariant(bol)) {
           C->print_method(PHASE_BEFORE_LOOP_PREDICATION_IC, 4, iff);
           // Invariant test
      -    IfProjNode* hoisted_check_predicate_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason,
      +    IfProjNode* hoisted_check_predicate_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, deopt_reason,
                                                                                  iff->Opcode());
           Node* ctrl = hoisted_check_predicate_proj->in(0)->as_If()->in(0);
           BoolNode* hoisted_check_predicate_bool = invar.clone(bol, ctrl)->as_Bool();
      @@ -1206,9 +1207,9 @@ bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree* loop, IfProjNod
           const Node*    cmp    = bol->in(1)->as_Cmp();
           Node*          idx    = cmp->in(1);
           assert(!invar.is_invariant(idx), "index is variant");
      -    Node* rng = cmp->in(2);
      -    assert(rng->Opcode() == Op_LoadRange || iff->is_RangeCheck() || _igvn.type(rng)->is_int()->_lo >= 0, "must be");
      -    assert(invar.is_invariant(rng), "range must be invariant");
      +    Node* range = cmp->in(2);
      +    assert(range->Opcode() == Op_LoadRange || iff->is_RangeCheck() || _igvn.type(range)->is_int()->_lo >= 0, "must be");
      +    assert(invar.is_invariant(range), "range must be invariant");
           int scale    = 1;
           Node* offset = zero;
           bool ok = is_scaled_iv_plus_offset(idx, cl->phi(), &scale, &offset);
      @@ -1237,7 +1238,7 @@ bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree* loop, IfProjNod
           // late schedule will place invariant things in the loop.
           ParsePredicateNode* parse_predicate = parse_predicate_proj->in(0)->as_ParsePredicate();
           Node* ctrl = parse_predicate->in(0);
      -    rng = invar.clone(rng, ctrl);
      +    range = invar.clone(range, ctrl);
           if (offset && offset != zero) {
             assert(invar.is_invariant(offset), "offset must be loop invariant");
             offset = invar.clone(offset, ctrl);
      @@ -1245,10 +1246,10 @@ bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree* loop, IfProjNod
           // If predicate expressions may overflow in the integer range, longs are used.
           bool overflow = false;
           // Test the lower bound
      -    BoolNode* lower_bound_bol = rc_predicate(ctrl, scale, offset, init, limit, stride, rng, false, overflow);
      +    BoolNode* lower_bound_bol = rc_predicate(ctrl, scale, offset, init, limit, stride, range, false, overflow);
       
           const int if_opcode = iff->Opcode();
      -    IfProjNode* lower_bound_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason, overflow ? Op_If : if_opcode);
      +    IfProjNode* lower_bound_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, deopt_reason, overflow ? Op_If : if_opcode);
           IfNode* lower_bound_iff = lower_bound_proj->in(0)->as_If();
           _igvn.hash_delete(lower_bound_iff);
           lower_bound_iff->set_req(1, lower_bound_bol);
      @@ -1257,9 +1258,9 @@ bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree* loop, IfProjNod
           }
       
           // Test the upper bound
      -    BoolNode* upper_bound_bol = rc_predicate(lower_bound_proj, scale, offset, init, limit, stride, rng, true, overflow);
      +    BoolNode* upper_bound_bol = rc_predicate(lower_bound_proj, scale, offset, init, limit, stride, range, true, overflow);
       
      -    IfProjNode* upper_bound_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason, overflow ? Op_If : if_opcode);
      +    IfProjNode* upper_bound_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, deopt_reason, overflow ? Op_If : if_opcode);
           assert(upper_bound_proj->in(0)->as_If()->in(0) == lower_bound_proj, "should dominate");
           IfNode* upper_bound_iff = upper_bound_proj->in(0)->as_If();
           _igvn.hash_delete(upper_bound_iff);
      @@ -1272,8 +1273,8 @@ bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree* loop, IfProjNod
           // upper bound test. We always need to create skeleton predicates in order to properly remove dead loops when later
           // splitting the predicated loop into (unreachable) sub-loops (i.e. done by unrolling, peeling, pre/main/post etc.).
           IfTrueNode* template_assertion_predicate_proj =
      -        add_template_assertion_predicate(iff, loop, hoisted_check_proj, parse_predicate_proj, upper_bound_proj, scale,
      -                                         offset, init, limit, stride, rng, overflow, reason);
      +        create_template_assertion_predicate(if_opcode, cl, parse_predicate_proj, upper_bound_proj, scale, offset, range,
      +                                            deopt_reason);
       
           // Eliminate the old range check in the loop body.
           // When a range check is eliminated, data dependent nodes (Load and range check CastII nodes) are now dependent on 2
      @@ -1309,53 +1310,15 @@ void PhaseIdealLoop::eliminate_hoisted_range_check(IfTrueNode* hoisted_check_pro
       // Each newly created Hoisted Check Predicate is accompanied by two Template Assertion Predicates. Later, we initialize
       // them by making a copy of them when splitting a loop into sub loops. The Assertion Predicates ensure that dead sub
       // loops are removed properly.
      -IfTrueNode* PhaseIdealLoop::add_template_assertion_predicate(IfNode* iff, IdealLoopTree* loop, IfProjNode* if_proj,
      -                                                             ParsePredicateSuccessProj* parse_predicate_proj,
      -                                                             IfProjNode* upper_bound_proj, const int scale, Node* offset,
      -                                                             Node* init, Node* limit, const jint stride,
      -                                                             Node* rng, bool& overflow, Deoptimization::DeoptReason reason) {
      -  // First predicate for the initial value on first loop iteration
      -  Node* opaque_init = new OpaqueLoopInitNode(C, init);
      -  register_new_node(opaque_init, upper_bound_proj);
      -  bool negate = (if_proj->_con != parse_predicate_proj->_con);
      -  BoolNode* bol = rc_predicate(upper_bound_proj, scale, offset, opaque_init, limit, stride, rng,
      -                               (stride > 0) != (scale > 0), overflow);
      -  Node* opaque_bol = new Opaque4Node(C, bol, _igvn.intcon(1)); // This will go away once loop opts are over
      -  C->add_template_assertion_predicate_opaq(opaque_bol);
      -  register_new_node(opaque_bol, upper_bound_proj);
      -  IfTrueNode* new_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason, overflow ? Op_If : iff->Opcode(),
      -                                                     false NOT_PRODUCT(COMMA AssertionPredicateType::InitValue));
      -  _igvn.replace_input_of(new_proj->in(0), 1, opaque_bol);
      -  assert(opaque_init->outcnt() > 0, "should be used");
      -
      -  // Second predicate for init + (current stride - initial stride)
      -  // This is identical to the previous predicate initially but as
      -  // unrolling proceeds current stride is updated.
      -  Node* init_stride = loop->_head->as_CountedLoop()->stride();
      -  Node* opaque_stride = new OpaqueLoopStrideNode(C, init_stride);
      -  register_new_node(opaque_stride, new_proj);
      -  Node* max_value = new SubINode(opaque_stride, init_stride);
      -  register_new_node(max_value, new_proj);
      -  max_value = new AddINode(opaque_init, max_value);
      -  register_new_node(max_value, new_proj);
      -  // init + (current stride - initial stride) is within the loop so narrow its type by leveraging the type of the iv Phi
      -  const Type* type_iv = loop->_head->as_CountedLoop()->phi()->bottom_type();
      -  assert(!type_iv->is_int()->is_con(), "constant indicates one loop iteration for which we bailed out earlier");
      -  max_value = new CastIINode(new_proj, max_value, type_iv);
      -  register_new_node(max_value, new_proj);
      -
      -  bol = rc_predicate(new_proj, scale, offset, max_value, limit, stride, rng, (stride > 0) != (scale > 0),
      -                     overflow);
      -  opaque_bol = new Opaque4Node(C, bol, _igvn.intcon(1));
      -  C->add_template_assertion_predicate_opaq(opaque_bol);
      -  register_new_node(opaque_bol, new_proj);
      -  new_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason, overflow ? Op_If : iff->Opcode(),
      -                                         false NOT_PRODUCT(COMMA AssertionPredicateType::LastValue));
      -  _igvn.replace_input_of(new_proj->in(0), 1, opaque_bol);
      -  assert(max_value->outcnt() > 0, "should be used");
      -  assert(assertion_predicate_has_loop_opaque_node(new_proj->in(0)->as_If()), "unexpected");
      -
      -  return new_proj;
      +IfTrueNode* PhaseIdealLoop::create_template_assertion_predicate(const int if_opcode, CountedLoopNode* loop_head,
      +                                                                ParsePredicateSuccessProj* parse_predicate_proj,
      +                                                                IfProjNode* new_control, const int scale, Node* offset,
      +                                                                Node* range, Deoptimization::DeoptReason deopt_reason) {
      +
      +  TemplateAssertionPredicateCreator template_assertion_predicate_creator(loop_head, scale, offset, range, this);
      +  return template_assertion_predicate_creator.create_with_uncommon_trap(new_control, parse_predicate_proj, deopt_reason,
      +                                                                        if_opcode);
      +
       }
       
       // Insert Hoisted Check Predicates for null checks and range checks and additional Template Assertion Predicates for
      diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp
      index c3cc8532e7d..2286b304534 100644
      --- a/src/hotspot/share/opto/loopTransform.cpp
      +++ b/src/hotspot/share/opto/loopTransform.cpp
      @@ -1461,12 +1461,13 @@ void PhaseIdealLoop::count_opaque_loop_nodes(Node* n, uint& init, uint& stride)
       
       // Create an Initialized Assertion Predicate from the template_assertion_predicate
       IfTrueNode* PhaseIdealLoop::create_initialized_assertion_predicate(IfNode* template_assertion_predicate, Node* new_init,
      -                                                                   Node* new_stride, Node* control) {
      +                                                                   Node* new_stride, Node* new_control) {
         assert(assertion_predicate_has_loop_opaque_node(template_assertion_predicate),
                "must find OpaqueLoop* nodes for Template Assertion Predicate");
      -  InitializedAssertionPredicateCreator initialized_assertion_predicate(template_assertion_predicate, new_init,
      -                                                                       new_stride, this);
      -  IfTrueNode* success_proj = initialized_assertion_predicate.create(control);
      +  InitializedAssertionPredicateCreator initialized_assertion_predicate(this);
      +  IfTrueNode* success_proj = initialized_assertion_predicate.create_from_template(template_assertion_predicate,
      +                                                                                  new_control, new_init, new_stride);
      +
         assert(!assertion_predicate_has_loop_opaque_node(success_proj->in(0)->as_If()),
                "Initialized Assertion Predicates do not have OpaqueLoop* nodes in the bool expression anymore");
         return success_proj;
      @@ -1477,30 +1478,18 @@ IfTrueNode* PhaseIdealLoop::create_initialized_assertion_predicate(IfNode* templ
       // We keep the Opaque4 node since it's still a template. Since the templates are eventually removed after loop opts,
       // these are never executed. We therefore insert a Halt node instead of an uncommon trap.
       Node* PhaseIdealLoop::clone_template_assertion_predicate(IfNode* iff, Node* new_init, Node* predicate, Node* uncommon_proj,
      -                                                         Node* control, IdealLoopTree* outer_loop, Node* input_proj) {
      +                                                         Node* control, IdealLoopTree* outer_loop, Node* new_control) {
         assert(assertion_predicate_has_loop_opaque_node(iff), "must find OpaqueLoop* nodes for Template Assertion Predicate");
         TemplateAssertionExpression template_assertion_expression(iff->in(1)->as_Opaque4());
         assert(new_init->is_OpaqueLoopInit(), "only for creating new Template Assertion Predicates");
         Opaque4Node* new_opaque_node = template_assertion_expression.clone_and_replace_init(new_init, control, this);
      -  Node* proj = predicate->clone();
      -  Node* other_proj = uncommon_proj->clone();
      -  Node* new_iff = iff->clone();
      -  new_iff->set_req(1, new_opaque_node);
      -  proj->set_req(0, new_iff);
      -  other_proj->set_req(0, new_iff);
      -  Node* frame = new ParmNode(C->start(), TypeFunc::FramePtr);
      -  register_new_node(frame, C->start());
      -  Node* halt = new HaltNode(other_proj, frame, "Template Assertion Predicates are always removed before code generation");
      -  _igvn.add_input_to(C->root(), halt);
      -  new_iff->set_req(0, input_proj);
      -
      -  register_control(new_iff, outer_loop == _ltree_root ? _ltree_root : outer_loop->_parent, input_proj);
      -  register_control(proj, outer_loop == _ltree_root ? _ltree_root : outer_loop->_parent, new_iff);
      -  register_control(other_proj, _ltree_root, new_iff);
      -  register_control(halt, _ltree_root, other_proj);
      -  assert(assertion_predicate_has_loop_opaque_node(proj->in(0)->as_If()),
      +  AssertionPredicateIfCreator assertion_predicate_if_creator(this);
      +  IfTrueNode* success_proj =
      +      assertion_predicate_if_creator.create_for_template(new_control, iff->Opcode(), new_opaque_node
      +                                                         NOT_PRODUCT(COMMA iff->assertion_predicate_type()));
      +  assert(assertion_predicate_has_loop_opaque_node(success_proj->in(0)->as_If()),
                "Template Assertion Predicates must have OpaqueLoop* nodes in the bool expression");
      -  return proj;
      +  return success_proj;
       }
       
       void PhaseIdealLoop::copy_assertion_predicates_to_main_loop(CountedLoopNode* pre_head, Node* init, Node* stride,
      @@ -2732,40 +2721,6 @@ bool PhaseIdealLoop::is_scaled_iv_plus_extra_offset(Node* exp1, Node* offset3, N
         return false;
       }
       
      -// Same as PhaseIdealLoop::duplicate_predicates() but for range checks
      -// eliminated by iteration splitting.
      -Node* PhaseIdealLoop::add_range_check_elimination_assertion_predicate(
      -    IdealLoopTree* loop, Node* ctrl, const int scale_con, Node* offset, Node* limit, jint stride_con, Node* value,
      -    const bool is_template NOT_PRODUCT(COMMA AssertionPredicateType assertion_predicate_type)) {
      -  bool overflow = false;
      -  BoolNode* bol = rc_predicate(ctrl, scale_con, offset, value, nullptr, stride_con,
      -                               limit, (stride_con > 0) != (scale_con > 0), overflow);
      -  Node* opaque_assertion_predicate;
      -  if (is_template) {
      -    opaque_assertion_predicate = new Opaque4Node(C, bol, _igvn.intcon(1));
      -  } else {
      -    opaque_assertion_predicate = new OpaqueInitializedAssertionPredicateNode(bol, C);
      -  }
      -  register_new_node(opaque_assertion_predicate, ctrl);
      -  IfNode* new_iff = nullptr;
      -  if (overflow) {
      -    new_iff = new IfNode(ctrl, opaque_assertion_predicate, PROB_MAX, COUNT_UNKNOWN);
      -  } else {
      -    new_iff = new RangeCheckNode(ctrl, opaque_assertion_predicate, PROB_MAX, COUNT_UNKNOWN);
      -  }
      -  register_control(new_iff, loop->_parent, ctrl);
      -  Node* iffalse = new IfFalseNode(new_iff);
      -  register_control(iffalse, _ltree_root, new_iff);
      -  ProjNode* iftrue = new IfTrueNode(new_iff);
      -  register_control(iftrue, loop->_parent, new_iff);
      -  Node *frame = new ParmNode(C->start(), TypeFunc::FramePtr);
      -  register_new_node(frame, C->start());
      -  Node* halt = new HaltNode(iffalse, frame, "range check predicate failed which is impossible");
      -  register_control(halt, _ltree_root, iffalse);
      -  _igvn.add_input_to(C->root(), halt);
      -  return iftrue;
      -}
      -
       //------------------------------do_range_check---------------------------------
       // Eliminate range-checks and other trip-counter vs loop-invariant tests.
       void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) {
      @@ -2974,45 +2929,39 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) {
                 Node* opaque_init = new OpaqueLoopInitNode(C, init);
                 register_new_node(opaque_init, loop_entry);
       
      +          InitializedAssertionPredicateCreator initialized_assertion_predicate_creator(this);
                 if (abs_stride_is_one) {
                   // If the main loop becomes empty and the array access for this range check is sunk out of the loop, the index
                   // for the array access will be set to the index value of the final iteration which could be out of loop.
      -            // Add an Assertion Predicate for that corner case. The final iv is computed from LoopLimit which is the
      -            // LoopNode::limit() only if abs(stride) == 1 otherwise the computation depends on LoopNode::init_trip() as
      -            // well. When LoopLimit only depends on LoopNode::limit(), there are cases where the zero trip guard for the
      -            // main loop doesn't constant fold after range check elimination but, the array access for the final
      +            // Add an Initialized Assertion Predicate for that corner case. The final iv is computed from LoopLimit which
      +            // is the LoopNode::limit() only if abs(stride) == 1 otherwise the computation depends on LoopNode::init_trip()
      +            // as well. When LoopLimit only depends on LoopNode::limit(), there are cases where the zero trip guard for
      +            // the main loop doesn't constant fold after range check elimination but, the array access for the final
                   // iteration of the main loop is out of bound and the index for that access is out of range for the range
                   // check CastII.
      -            loop_entry = add_range_check_elimination_assertion_predicate(loop, loop_entry, scale_con, int_offset,
      -                                                                         int_limit, stride_con, final_iv_placeholder, false);
      +            // Note that we do not need to emit a Template Assertion Predicate to update this predicate. When further
      +            // splitting this loop, the final IV will still be the same. When unrolling the loop, we will remove a
      +            // previously added Initialized Assertion Predicate here. But then abs(stride) is greater than 1, and we
      +            // cannot remove an empty loop with a constant limit when init is not a constant as well. We will use
      +            // a LoopLimitCheck node that can only be folded if the zero grip guard is also foldable.
      +            loop_entry = initialized_assertion_predicate_creator.create(final_iv_placeholder, loop_entry, stride_con,
      +                                                                        scale_con, int_offset, int_limit NOT_PRODUCT(
      +                                                                        COMMA AssertionPredicateType::FinalIv));
                   assert(!assertion_predicate_has_loop_opaque_node(loop_entry->in(0)->as_If()), "unexpected");
                 }
       
      -          // Initialized Assertion Predicate for the value of the initial main-loop.
      -          loop_entry = add_range_check_elimination_assertion_predicate(loop, loop_entry, scale_con, int_offset,
      -                                                                       int_limit, stride_con, init, false);
      -          assert(!assertion_predicate_has_loop_opaque_node(loop_entry->in(0)->as_If()), "unexpected");
      -
                 // Add two Template Assertion Predicates to create new Initialized Assertion Predicates from when either
                 // unrolling or splitting this main-loop further.
      -          loop_entry = add_range_check_elimination_assertion_predicate(
      -              loop, loop_entry, scale_con, int_offset, int_limit, stride_con, opaque_init, true
      -              NOT_PRODUCT(COMMA AssertionPredicateType::InitValue));
      +          TemplateAssertionPredicateCreator template_assertion_predicate_creator(cl, scale_con , int_offset, int_limit,
      +                                                                                 this);
      +          loop_entry = template_assertion_predicate_creator.create_with_halt(loop_entry);
                 assert(assertion_predicate_has_loop_opaque_node(loop_entry->in(0)->as_If()), "unexpected");
       
      -          Node* opaque_stride = new OpaqueLoopStrideNode(C, cl->stride());
      -          register_new_node(opaque_stride, loop_entry);
      -          Node* max_value = new SubINode(opaque_stride, cl->stride());
      -          register_new_node(max_value, loop_entry);
      -          max_value = new AddINode(opaque_init, max_value);
      -          register_new_node(max_value, loop_entry);
      -          // init + (current stride - initial stride) is within the loop so narrow its type by leveraging the type of the iv Phi
      -          max_value = new CastIINode(loop_entry, max_value, loop->_head->as_CountedLoop()->phi()->bottom_type());
      -          register_new_node(max_value, loop_entry);
      -          loop_entry = add_range_check_elimination_assertion_predicate(
      -              loop, loop_entry, scale_con, int_offset, int_limit, stride_con, max_value, true
      -              NOT_PRODUCT(COMMA AssertionPredicateType::LastValue));
      -          assert(assertion_predicate_has_loop_opaque_node(loop_entry->in(0)->as_If()), "unexpected");
      +          // Initialized Assertion Predicate for the value of the initial main-loop.
      +          loop_entry = initialized_assertion_predicate_creator.create(init, loop_entry, stride_con, scale_con,
      +                                                                      int_offset, int_limit NOT_PRODUCT(COMMA
      +                                                                      AssertionPredicateType::InitValue));
      +          assert(!assertion_predicate_has_loop_opaque_node(loop_entry->in(0)->as_If()), "unexpected");
       
               } else {
                 if (PrintOpto) {
      @@ -3072,9 +3021,6 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) {
                 --imax;
               }
             }
      -
      -      C->print_method(PHASE_AFTER_RANGE_CHECK_ELIMINATION, 4, cl);
      -
           } // End of is IF
         }
         if (loop_entry != cl->skip_strip_mined()->in(LoopNode::EntryControl)) {
      @@ -3138,6 +3084,8 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) {
         set_ctrl(opqzm, new_limit_ctrl);
         set_ctrl(iffm->in(1)->in(1), new_limit_ctrl);
         set_ctrl(iffm->in(1), new_limit_ctrl);
      +
      +  C->print_method(PHASE_AFTER_RANGE_CHECK_ELIMINATION, 4, cl);
       }
       
       // Adjust control for node and its inputs (and inputs of its inputs) to be above the pre end
      diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp
      index 3aa67bcb5cb..ef27eb652f7 100644
      --- a/src/hotspot/share/opto/loopnode.hpp
      +++ b/src/hotspot/share/opto/loopnode.hpp
      @@ -951,7 +951,7 @@ class PhaseIdealLoop : public PhaseTransform {
                                                     uint idx_after_post_before_pre, Node* zero_trip_guard_proj_main,
                                                     Node* zero_trip_guard_proj_post, const Node_List& old_new);
         Node* clone_template_assertion_predicate(IfNode* iff, Node* new_init, Node* predicate, Node* uncommon_proj, Node* control,
      -                                           IdealLoopTree* outer_loop, Node* input_proj);
      +                                           IdealLoopTree* outer_loop, Node* new_control);
         IfTrueNode* create_initialized_assertion_predicate(IfNode* template_assertion_predicate, Node* new_init,
                                                            Node* new_stride, Node* control);
         static void count_opaque_loop_nodes(Node* n, uint& init, uint& stride);
      @@ -1384,20 +1384,17 @@ class PhaseIdealLoop : public PhaseTransform {
        private:
         bool loop_predication_impl_helper(IdealLoopTree* loop, IfProjNode* if_success_proj,
                                           ParsePredicateSuccessProj* parse_predicate_proj, CountedLoopNode* cl, ConNode* zero,
      -                                    Invariance& invar, Deoptimization::DeoptReason reason);
      +                                    Invariance& invar, Deoptimization::DeoptReason deopt_reason);
         bool can_create_loop_predicates(const PredicateBlock* profiled_loop_predicate_block) const;
         bool loop_predication_should_follow_branches(IdealLoopTree* loop, float& loop_trip_cnt);
         void loop_predication_follow_branches(Node *c, IdealLoopTree *loop, float loop_trip_cnt,
                                               PathFrequency& pf, Node_Stack& stack, VectorSet& seen,
                                               Node_List& if_proj_list);
      -  IfTrueNode* add_template_assertion_predicate(IfNode* iff, IdealLoopTree* loop, IfProjNode* if_proj,
      -                                               ParsePredicateSuccessProj* parse_predicate_proj,
      -                                               IfProjNode* upper_bound_proj, int scale, Node* offset, Node* init, Node* limit,
      -                                               jint stride, Node* rng, bool& overflow, Deoptimization::DeoptReason reason);
      +  IfTrueNode* create_template_assertion_predicate(int if_opcode, CountedLoopNode* loop_head,
      +                                                  ParsePredicateSuccessProj* parse_predicate_proj,
      +                                                  IfProjNode* new_control, int scale, Node* offset,
      +                                                  Node* range, Deoptimization::DeoptReason deopt_reason);
         void eliminate_hoisted_range_check(IfTrueNode* hoisted_check_proj, IfTrueNode* template_assertion_predicate_proj);
      -  Node* add_range_check_elimination_assertion_predicate(
      -      IdealLoopTree* loop, Node* predicate_proj, int scale_con, Node* offset, Node* limit, int stride_con, Node* value,
      -      bool is_template NOT_PRODUCT(COMMA AssertionPredicateType assertion_predicate_type = AssertionPredicateType::None));
       
         // Helper function to collect predicate for eliminating the useless ones
         void eliminate_useless_predicates();
      diff --git a/src/hotspot/share/opto/predicates.cpp b/src/hotspot/share/opto/predicates.cpp
      index 18eea3a10bc..0e4f264f827 100644
      --- a/src/hotspot/share/opto/predicates.cpp
      +++ b/src/hotspot/share/opto/predicates.cpp
      @@ -23,7 +23,9 @@
        */
       
       #include "precompiled.hpp"
      +#include "opto/addnode.hpp"
       #include "opto/callnode.hpp"
      +#include "opto/castnode.hpp"
       #include "opto/loopnode.hpp"
       #include "opto/node.hpp"
       #include "opto/predicates.hpp"
      @@ -247,11 +249,11 @@ Opaque4Node* TemplateAssertionExpression::clone_and_replace_init(Node* new_init,
       
       // Same as clone() but instead of cloning the OpaqueLoopInit and OpaqueLoopStride node, we replace them with the provided
       // 'new_init' and 'new_stride' nodes, respectively.
      -Opaque4Node* TemplateAssertionExpression::clone_and_replace_init_and_stride(Node* new_init, Node* new_stride,
      -                                                                            Node* new_ctrl,
      +Opaque4Node* TemplateAssertionExpression::clone_and_replace_init_and_stride(Node* new_control, Node* new_init,
      +                                                                            Node* new_stride,
                                                                                   PhaseIdealLoop* phase) {
         ReplaceInitAndStrideStrategy replace_init_and_stride_strategy(new_init, new_stride);
      -  return clone(replace_init_and_stride_strategy, new_ctrl, phase);
      +  return clone(replace_init_and_stride_strategy, new_control, phase);
       }
       
       // Class to collect data nodes from a source to target nodes by following the inputs of the source node recursively.
      @@ -370,86 +372,296 @@ bool TemplateAssertionExpressionNode::is_template_assertion_predicate(Node* node
         return node->is_If() && node->in(1)->is_Opaque4();
       }
       
      -InitializedAssertionPredicateCreator::InitializedAssertionPredicateCreator(IfNode* template_assertion_predicate, Node* new_init,
      -                                                                           Node* new_stride, PhaseIdealLoop* phase)
      -    : _template_assertion_predicate(template_assertion_predicate),
      -      _new_init(new_init),
      -      _new_stride(new_stride),
      -      _phase(phase) {}
      +// This class creates the Assertion Predicate expression to be used for a Template or Initialized Assertion Predicate.
      +class AssertionPredicateExpressionCreator : public StackObj {
      +  PhaseIdealLoop* const _phase;
      +  const jint _stride;
      +  const int _scale;
      +  Node* const _offset;
      +  Node* const _range;
      +  const bool _upper;
       
      -// Create an Initialized Assertion Predicate at the provided control from the _template_assertion_predicate.
      -// We clone the Template Assertion Expression and replace:
      -// - Opaque4 with OpaqueInitializedAssertionPredicate
      -// - OpaqueLoop*Nodes with _new_init and _new_stride, respectively.
      + public:
      +  AssertionPredicateExpressionCreator(const int stride, const int scale, Node* offset, Node* range,
      +                                      PhaseIdealLoop* phase)
      +      : _phase(phase),
      +        _stride(stride),
      +        _scale(scale),
      +        _offset(offset),
      +        _range(range),
      +        _upper((_stride > 0) != (_scale > 0)) {} // Make sure rc_predicate() chooses the "scale*init + offset" case.
      +
      +  // Create the expression for a Template Assertion Predicate with an Opaque4 node.
      +  Opaque4Node* create_for_template(Node* new_control, Node* operand, bool& does_overflow) const {
      +    BoolNode* bool_for_expression =  _phase->rc_predicate(new_control, _scale, _offset, operand, nullptr,
      +                                                          _stride, _range, _upper, does_overflow);
      +    return create_opaque4_node(new_control, bool_for_expression);
      +  }
      +
      + private:
      +  Opaque4Node* create_opaque4_node(Node* new_control, BoolNode* bool_for_expression) const {
      +    Compile* C = _phase->C;
      +    Opaque4Node* new_expression = new Opaque4Node(C, bool_for_expression, _phase->igvn().intcon(1));
      +    C->add_template_assertion_predicate_opaq(new_expression);
      +    _phase->register_new_node(new_expression, new_control);
      +    return new_expression;
      +  }
      +
      + public:
      +  // Create the expression for an Initialized Assertion Predicate with an OpaqueInitializedAssertionPredicate node.
      +  OpaqueInitializedAssertionPredicateNode* create_for_initialized(Node* new_control, Node* operand,
      +                                                                  bool& does_overflow) const {
      +    BoolNode* bool_for_expression = _phase->rc_predicate(new_control, _scale, _offset, operand, nullptr,
      +                                                         _stride, _range, _upper, does_overflow);
      +    return create_opaque_initialized_assertion_predicate_node(new_control, bool_for_expression);
      +  }
      +
      + private:
      +  OpaqueInitializedAssertionPredicateNode* create_opaque_initialized_assertion_predicate_node(
      +      Node* new_control, BoolNode* bool_for_expression) const {
      +    OpaqueInitializedAssertionPredicateNode* new_expression =
      +        new OpaqueInitializedAssertionPredicateNode(bool_for_expression, _phase->C);
      +    _phase->register_new_node(new_expression, new_control);
      +    return new_expression;
      +  }
      +};
      +
      +// Creates an If with a success and a fail path with the given assertion_expression. The only difference to
      +// create_for_initialized() is that we use a template specific Halt message on the fail path.
      +IfTrueNode* AssertionPredicateIfCreator::create_for_template(Node* new_control, const int if_opcode,
      +                                                             Node* assertion_expression NOT_PRODUCT(COMMA
      +                                                             const AssertionPredicateType assertion_predicate_type)) {
      +  const char* halt_message = "Template Assertion Predicates are always removed before code generation";
      +  return create(new_control, if_opcode, assertion_expression, halt_message NOT_PRODUCT(COMMA assertion_predicate_type));
      +}
      +
      +// Creates an If with a success and a fail path with the given assertion_expression. The only difference to
      +// create_for_template() is that we use a initialized specific Halt message on the fail path.
      +IfTrueNode* AssertionPredicateIfCreator::create_for_initialized(Node* new_control, const int if_opcode,
      +                                                                Node* assertion_expression NOT_PRODUCT(COMMA
      +                                                                const AssertionPredicateType assertion_predicate_type)) {
      +  const char* halt_message = "Initialized Assertion Predicate cannot fail";
      +  return create(new_control, if_opcode, assertion_expression, halt_message NOT_PRODUCT(COMMA assertion_predicate_type));
      +}
      +
      +// Creates the If node for an Assertion Predicate with a success path and a fail path having a Halt node:
       //
      -//             /         init                 stride
      -//             |           |                    |
      -//             |  OpaqueLoopInitNode  OpaqueLoopStrideNode                      /       _new_init    _new_stride
      -//  Template   |                 \     /                                        |              \     /
      -//  Assertion  |                   ...                               Assertion  |                ...
      -//  Expression |                    |                                Expression |                 |
      -//             |                   Bool                                         |              new Bool
      -//             |                    |                                           |                 |
      -//             \                 Opaque4           ======>          control     \  OpaqueInitializedAssertionPredicate
      -//                                  |                                      \      /
      -//                                 If                                       new If
      -//                               /    \                                     /    \
      -//                         success     fail path                   new success   new Halt
      -//                           proj    (Halt or UCT)                     proj
      +//      new_control   assertion_expression
      +//                \   /
      +//                 If
      +//               /    \
      +//        success     fail path
      +//           proj      with Halt
       //
      -IfTrueNode* InitializedAssertionPredicateCreator::create(Node* control) {
      -  IdealLoopTree* loop = _phase->get_loop(control);
      -  OpaqueInitializedAssertionPredicateNode* assertion_expression = create_assertion_expression(control);
      -  IfNode* if_node = create_if_node(control, assertion_expression, loop);
      -  create_fail_path(if_node, loop);
      +IfTrueNode* AssertionPredicateIfCreator::create(Node* new_control, const int if_opcode, Node* assertion_expression,
      +                                                const char* halt_message NOT_PRODUCT(COMMA
      +                                                const AssertionPredicateType assertion_predicate_type)) {
      +  assert(assertion_expression->is_Opaque4() || assertion_expression->is_OpaqueInitializedAssertionPredicate(),
      +         "not a valid assertion expression");
      +  IdealLoopTree* loop = _phase->get_loop(new_control);
      +  IfNode* if_node = create_if_node(new_control, if_opcode, assertion_expression, loop
      +                                   NOT_PRODUCT(COMMA assertion_predicate_type));
      +  create_fail_path(if_node, loop, halt_message);
         return create_success_path(if_node, loop);
       }
       
      -// Create a new Assertion Expression to be used as bool input for the Initialized Assertion Predicate IfNode.
      -OpaqueInitializedAssertionPredicateNode* InitializedAssertionPredicateCreator::create_assertion_expression(Node* control) {
      -  Opaque4Node* template_opaque = _template_assertion_predicate->in(1)->as_Opaque4();
      -  TemplateAssertionExpression template_assertion_expression(template_opaque);
      -  Opaque4Node* tmp_opaque = template_assertion_expression.clone_and_replace_init_and_stride(_new_init, _new_stride,
      -                                                                                            control, _phase);
      -  OpaqueInitializedAssertionPredicateNode* assertion_expression =
      -      new OpaqueInitializedAssertionPredicateNode(tmp_opaque->in(1)->as_Bool(), _phase->C);
      -  _phase->register_new_node(assertion_expression, control);
      -  return assertion_expression;
      -}
      -
      -IfNode* InitializedAssertionPredicateCreator::create_if_node(Node* control,
      -                                                             OpaqueInitializedAssertionPredicateNode* assertion_expression,
      -                                                             IdealLoopTree* loop) {
      -  const int if_opcode = _template_assertion_predicate->Opcode();
      -  NOT_PRODUCT(const AssertionPredicateType assertion_predicate_type = _template_assertion_predicate->assertion_predicate_type();)
      -  IfNode* if_node = if_opcode == Op_If ?
      -      new IfNode(control, assertion_expression, PROB_MAX, COUNT_UNKNOWN NOT_PRODUCT(COMMA assertion_predicate_type)) :
      -      new RangeCheckNode(control, assertion_expression, PROB_MAX, COUNT_UNKNOWN NOT_PRODUCT(COMMA assertion_predicate_type));
      -  _phase->register_control(if_node, loop, control);
      +IfNode* AssertionPredicateIfCreator::create_if_node(Node* new_control, const int if_opcode, Node* assertion_expression,
      +                                                    IdealLoopTree* loop NOT_PRODUCT(COMMA
      +                                                    const AssertionPredicateType assertion_predicate_type)) {
      +  IfNode* if_node;
      +  if (if_opcode == Op_If) {
      +    if_node = new IfNode(new_control, assertion_expression, PROB_MAX, COUNT_UNKNOWN
      +                         NOT_PRODUCT(COMMA assertion_predicate_type));
      +  } else {
      +    assert(if_opcode == Op_RangeCheck, "must be range check");
      +    if_node = new RangeCheckNode(new_control, assertion_expression, PROB_MAX, COUNT_UNKNOWN
      +                                 NOT_PRODUCT(COMMA assertion_predicate_type));
      +  }
      +  _phase->register_control(if_node, loop, new_control);
         return if_node;
       }
       
      -IfTrueNode* InitializedAssertionPredicateCreator::create_success_path(IfNode* if_node, IdealLoopTree* loop) {
      +IfTrueNode* AssertionPredicateIfCreator::create_success_path(IfNode* if_node, IdealLoopTree* loop) {
         IfTrueNode* success_proj = new IfTrueNode(if_node);
         _phase->register_control(success_proj, loop, if_node);
         return success_proj;
       }
       
      -void InitializedAssertionPredicateCreator::create_fail_path(IfNode* if_node, IdealLoopTree* loop) {
      +void AssertionPredicateIfCreator::create_fail_path(IfNode* if_node, IdealLoopTree* loop, const char* halt_message) {
         IfFalseNode* fail_proj = new IfFalseNode(if_node);
         _phase->register_control(fail_proj, loop, if_node);
      -  create_halt_node(fail_proj, loop);
      +  create_halt_node(fail_proj, loop, halt_message);
       }
       
      -void InitializedAssertionPredicateCreator::create_halt_node(IfFalseNode* fail_proj, IdealLoopTree* loop) {
      +void AssertionPredicateIfCreator::create_halt_node(IfFalseNode* fail_proj, IdealLoopTree* loop,
      +                                                   const char* halt_message) {
         StartNode* start_node = _phase->C->start();
         Node* frame = new ParmNode(start_node, TypeFunc::FramePtr);
         _phase->register_new_node(frame, start_node);
      -  Node* halt = new HaltNode(fail_proj, frame, "Initialized Assertion Predicate cannot fail");
      +  Node* halt = new HaltNode(fail_proj, frame, halt_message);
         _phase->igvn().add_input_to(_phase->C->root(), halt);
         _phase->register_control(halt, loop, fail_proj);
       }
       
      +// Creates an init and last value Template Assertion Predicate connected together from a Parse Predicate with an UCT on
      +// the failing path. Returns the success projection of the last value Template Assertion Predicate.
      +IfTrueNode* TemplateAssertionPredicateCreator::create_with_uncommon_trap(
      +    Node* new_control, ParsePredicateSuccessProj* parse_predicate_success_proj,
      +    const Deoptimization::DeoptReason deopt_reason, const int if_opcode) {
      +  OpaqueLoopInitNode* opaque_init = create_opaque_init(new_control);
      +  bool does_overflow;
      +  Opaque4Node* template_assertion_predicate_expression = create_for_init_value(new_control, opaque_init,
      +                                                                               does_overflow);
      +  IfTrueNode* template_predicate_success_proj =
      +      create_if_node_with_uncommon_trap(template_assertion_predicate_expression, parse_predicate_success_proj,
      +                                        deopt_reason, if_opcode, does_overflow
      +                                        NOT_PRODUCT(COMMA AssertionPredicateType::InitValue));
      +  template_assertion_predicate_expression = create_for_last_value(template_predicate_success_proj, opaque_init,
      +                                                                  does_overflow);
      +  return create_if_node_with_uncommon_trap(template_assertion_predicate_expression, parse_predicate_success_proj,
      +                                           deopt_reason, if_opcode, does_overflow
      +                                           NOT_PRODUCT(COMMA AssertionPredicateType::LastValue));
      +}
      +
      +OpaqueLoopInitNode* TemplateAssertionPredicateCreator::create_opaque_init(Node* new_control) {
      +  OpaqueLoopInitNode* opaque_init = new OpaqueLoopInitNode(_phase->C, _loop_head->init_trip());
      +  _phase->register_new_node(opaque_init, new_control);
      +  return opaque_init;
      +}
      +
      +Opaque4Node* TemplateAssertionPredicateCreator::create_for_init_value(Node* new_control, OpaqueLoopInitNode* opaque_init,
      +                                                                      bool& does_overflow) const {
      +  AssertionPredicateExpressionCreator expression_creator(_loop_head->stride_con(), _scale, _offset, _range, _phase);
      +  return expression_creator.create_for_template(new_control, opaque_init, does_overflow);
      +}
      +
      +IfTrueNode* TemplateAssertionPredicateCreator::create_if_node_with_uncommon_trap(
      +    Opaque4Node* template_assertion_predicate_expression, ParsePredicateSuccessProj* parse_predicate_success_proj,
      +    const Deoptimization::DeoptReason deopt_reason, const int if_opcode, const bool does_overflow
      +    NOT_PRODUCT(COMMA AssertionPredicateType assertion_predicate_type)) {
      +  IfTrueNode* success_proj = _phase->create_new_if_for_predicate(parse_predicate_success_proj, nullptr, deopt_reason,
      +                                                                 does_overflow ? Op_If : if_opcode, false
      +                                                                 NOT_PRODUCT(COMMA assertion_predicate_type));
      +  _phase->igvn().replace_input_of(success_proj->in(0), 1, template_assertion_predicate_expression);
      +  return success_proj;
      +}
      +
      +Opaque4Node* TemplateAssertionPredicateCreator::create_for_last_value(Node* new_control, OpaqueLoopInitNode* opaque_init,
      +                                                                      bool& does_overflow) const {
      +  Node* last_value = create_last_value(new_control, opaque_init);
      +  AssertionPredicateExpressionCreator expression_creator(_loop_head->stride_con(), _scale, _offset, _range, _phase);
      +  return expression_creator.create_for_template(new_control, last_value, does_overflow);
      +}
      +
      +Node* TemplateAssertionPredicateCreator::create_last_value(Node* new_control, OpaqueLoopInitNode* opaque_init) const {
      +  Node* init_stride = _loop_head->stride();
      +  Node* opaque_stride = new OpaqueLoopStrideNode(_phase->C, init_stride);
      +  _phase->register_new_node(opaque_stride, new_control);
      +  Node* last_value = new SubINode(opaque_stride, init_stride);
      +  _phase->register_new_node(last_value, new_control);
      +  last_value = new AddINode(opaque_init, last_value);
      +  _phase->register_new_node(last_value, new_control);
      +  // init + (current stride - initial stride) is within the loop so narrow its type by leveraging the type of the iv phi
      +  last_value = new CastIINode(new_control, last_value, _loop_head->phi()->bottom_type());
      +  _phase->register_new_node(last_value, new_control);
      +  return last_value;
      +}
      +
      +IfTrueNode* TemplateAssertionPredicateCreator::create_if_node_with_halt(
      +    Node* new_control, Opaque4Node* template_assertion_predicate_expression, bool does_overflow
      +    NOT_PRODUCT(COMMA AssertionPredicateType assertion_predicate_type)) {
      +  AssertionPredicateIfCreator assertion_predicate_if_creator(_phase);
      +  return assertion_predicate_if_creator.create_for_template(new_control, does_overflow ? Op_If : Op_RangeCheck,
      +                                                            template_assertion_predicate_expression
      +                                                            NOT_PRODUCT(COMMA assertion_predicate_type));
      +}
      +
      +// Creates an init and last value Template Assertion Predicate connected together with a Halt node on the failing path.
      +// Returns the success projection of the last value Template Assertion Predicate latter.
      +IfTrueNode* TemplateAssertionPredicateCreator::create_with_halt(Node* new_control) {
      +  OpaqueLoopInitNode* opaque_init = create_opaque_init(new_control);
      +  bool does_overflow;
      +  Opaque4Node* template_assertion_predicate_expression = create_for_init_value(new_control, opaque_init,
      +                                                                               does_overflow);
      +  IfTrueNode* template_predicate_success_proj =
      +      create_if_node_with_halt(new_control, template_assertion_predicate_expression, does_overflow
      +                               NOT_PRODUCT(COMMA AssertionPredicateType::InitValue));
      +  template_assertion_predicate_expression = create_for_last_value(template_predicate_success_proj, opaque_init,
      +                                                                  does_overflow);
      +  return create_if_node_with_halt(template_predicate_success_proj, template_assertion_predicate_expression,
      +                                  does_overflow NOT_PRODUCT(COMMA AssertionPredicateType::LastValue));
      +}
      +
      +InitializedAssertionPredicateCreator::InitializedAssertionPredicateCreator(PhaseIdealLoop* phase)
      +    : _phase(phase) {}
      +
      +// Create an Initialized Assertion Predicate from the provided template_assertion_predicate at 'new_control'.
      +// We clone the Template Assertion Expression and replace:
      +// - Opaque4 with OpaqueInitializedAssertionPredicate
      +// - OpaqueLoop*Nodes with new_init and _ew_stride, respectively.
      +//
      +//             /         init                 stride
      +//             |           |                    |
      +//             |  OpaqueLoopInitNode  OpaqueLoopStrideNode                      /        new_init    new_stride
      +//  Template   |                 \     /                                        |              \     /
      +//  Assertion  |                   ...                               Assertion  |                ...
      +//  Expression |                    |                                Expression |                 |
      +//             |                   Bool                                         |              new Bool
      +//             |                    |                                           |                 |
      +//             \                 Opaque4           ======>      new_control     \  OpaqueInitializedAssertionPredicate
      +//                                  |                                      \      /
      +//                                 If                                       new If
      +//                               /    \                                     /    \
      +//                         success     fail path                   new success   new Halt
      +//                           proj    (Halt or UCT)                     proj
      +//
      +IfTrueNode* InitializedAssertionPredicateCreator::create_from_template(IfNode* template_assertion_predicate,
      +                                                                       Node* new_control, Node* new_init,
      +                                                                       Node* new_stride) {
      +  OpaqueInitializedAssertionPredicateNode* assertion_expression =
      +      create_assertion_expression_from_template(template_assertion_predicate, new_control, new_init, new_stride);
      +  return create_control_nodes(new_control, template_assertion_predicate->Opcode(), assertion_expression
      +                              NOT_PRODUCT(COMMA template_assertion_predicate->assertion_predicate_type()));
      +}
      +
      +// Create a new Initialized Assertion Predicate directly without a template.
      +IfTrueNode* InitializedAssertionPredicateCreator::create(Node* operand, Node* new_control, const jint stride,
      +                                                         const int scale, Node* offset, Node* range NOT_PRODUCT(COMMA
      +                                                         AssertionPredicateType assertion_predicate_type)) {
      +  AssertionPredicateExpressionCreator expression_creator(stride, scale, offset, range, _phase);
      +  bool does_overflow;
      +  OpaqueInitializedAssertionPredicateNode* assertion_expression =
      +      expression_creator.create_for_initialized(new_control, operand, does_overflow);
      +  return create_control_nodes(new_control, does_overflow ? Op_If : Op_RangeCheck, assertion_expression
      +                              NOT_PRODUCT(COMMA assertion_predicate_type));
      +}
      +
      +// Creates the CFG nodes for the Initialized Assertion Predicate.
      +IfTrueNode* InitializedAssertionPredicateCreator::create_control_nodes(
      +    Node* new_control, const int if_opcode, OpaqueInitializedAssertionPredicateNode* assertion_expression
      +    NOT_PRODUCT(COMMA AssertionPredicateType assertion_predicate_type)) {
      +  AssertionPredicateIfCreator assertion_predicate_if_creator(_phase);
      +  return assertion_predicate_if_creator.create_for_initialized(new_control, if_opcode, assertion_expression
      +                                                               NOT_PRODUCT(COMMA assertion_predicate_type));
      +}
      +
      +// Create a new Assertion Expression based from the given template to be used as bool input for the Initialized
      +// Assertion Predicate IfNode.
      +OpaqueInitializedAssertionPredicateNode*
      +InitializedAssertionPredicateCreator::create_assertion_expression_from_template(IfNode* template_assertion_predicate,
      +                                                                                Node* new_control, Node* new_init,
      +                                                                                Node* new_stride) {
      +  Opaque4Node* template_opaque = template_assertion_predicate->in(1)->as_Opaque4();
      +  TemplateAssertionExpression template_assertion_expression(template_opaque);
      +  Opaque4Node* tmp_opaque = template_assertion_expression.clone_and_replace_init_and_stride(new_control, new_init,
      +                                                                                            new_stride,
      +                                                                                            _phase);
      +  OpaqueInitializedAssertionPredicateNode* assertion_expression =
      +      new OpaqueInitializedAssertionPredicateNode(tmp_opaque->in(1)->as_Bool(), _phase->C);
      +  _phase->register_new_node(assertion_expression, new_control);
      +  return assertion_expression;
      +}
      +
       #ifndef PRODUCT
       void PredicateBlock::dump() const {
         dump("");
      diff --git a/src/hotspot/share/opto/predicates.hpp b/src/hotspot/share/opto/predicates.hpp
      index b38b888cc3d..0a5d6226c8c 100644
      --- a/src/hotspot/share/opto/predicates.hpp
      +++ b/src/hotspot/share/opto/predicates.hpp
      @@ -207,7 +207,9 @@ class TemplateAssertionPredicate;
       enum class AssertionPredicateType {
         None, // Not an Assertion Predicate
         InitValue,
      -  LastValue
      +  LastValue,
      +  // Used for the Initialized Assertion Predicate emitted during Range Check Elimination for the final IV value.
      +  FinalIv
       };
       #endif // NOT PRODUCT
       
      @@ -442,8 +444,9 @@ class TemplateAssertionExpression : public StackObj {
       
        public:
         Opaque4Node* clone(Node* new_ctrl, PhaseIdealLoop* phase);
      -  Opaque4Node* clone_and_replace_init(Node* new_init, Node* new_ctrl,PhaseIdealLoop* phase);
      -  Opaque4Node* clone_and_replace_init_and_stride(Node* new_init, Node* new_stride, Node* new_ctrl, PhaseIdealLoop* phase);
      +  Opaque4Node* clone_and_replace_init(Node* new_init, Node* new_ctrl, PhaseIdealLoop* phase);
      +  Opaque4Node* clone_and_replace_init_and_stride(Node* new_control, Node* new_init, Node* new_stride,
      +                                                 PhaseIdealLoop* phase);
       };
       
       // Class to represent a node being part of a Template Assertion Expression. Note that this is not an IR node.
      @@ -520,26 +523,84 @@ class TemplateAssertionExpressionNode : public StackObj {
         }
       };
       
      -// This class creates a new Initialized Assertion Predicate.
      +// This class is used to create the actual If node with a success path and a fail path with a Halt node.
      +class AssertionPredicateIfCreator : public StackObj {
      +  PhaseIdealLoop* const _phase;
      +
      + public:
      +  explicit AssertionPredicateIfCreator(PhaseIdealLoop* const phase) : _phase(phase) {}
      +  NONCOPYABLE(AssertionPredicateIfCreator);
      +
      +  IfTrueNode* create_for_initialized(Node* new_control, int if_opcode, Node* assertion_expression
      +                                     NOT_PRODUCT(COMMA const AssertionPredicateType assertion_predicate_type));
      +  IfTrueNode* create_for_template(Node* new_control, int if_opcode, Node* assertion_expression
      +                                  NOT_PRODUCT(COMMA const AssertionPredicateType assertion_predicate_type));
      + private:
      +  IfTrueNode* create(Node* new_control, int if_opcode, Node* assertion_expression, const char* halt_message
      +                     NOT_PRODUCT(COMMA const AssertionPredicateType assertion_predicate_type));
      +  IfNode* create_if_node(Node* new_control, int if_opcode, Node* assertion_expression, IdealLoopTree* loop
      +                         NOT_PRODUCT(COMMA const AssertionPredicateType assertion_predicate_type));
      +  IfTrueNode* create_success_path(IfNode* if_node, IdealLoopTree* loop);
      +  void create_fail_path(IfNode* if_node, IdealLoopTree* loop, const char* halt_message);
      +  void create_halt_node(IfFalseNode* fail_proj, IdealLoopTree* loop, const char* halt_message);
      +};
      +
      +// This class is used to create a Template Assertion Predicate either with an UCT or a Halt Node from scratch.
      +class TemplateAssertionPredicateCreator : public StackObj {
      +  CountedLoopNode* const _loop_head;
      +  const int _scale;
      +  Node* const _offset;
      +  Node* const _range;
      +  PhaseIdealLoop* const _phase;
      +
      +  OpaqueLoopInitNode* create_opaque_init(Node* new_control);
      +  Opaque4Node* create_for_init_value(Node* new_control, OpaqueLoopInitNode* opaque_init, bool& does_overflow) const;
      +  Opaque4Node* create_for_last_value(Node* new_control, OpaqueLoopInitNode* opaque_init, bool& does_overflow) const;
      +  Node* create_last_value(Node* new_control, OpaqueLoopInitNode* opaque_init) const;
      +  IfTrueNode* create_if_node_with_uncommon_trap(Opaque4Node* template_assertion_predicate_expression,
      +                                                ParsePredicateSuccessProj* parse_predicate_success_proj,
      +                                                Deoptimization::DeoptReason deopt_reason, int if_opcode,
      +                                                bool does_overflow
      +                                                NOT_PRODUCT(COMMA AssertionPredicateType assertion_predicate_type));
      +  IfTrueNode* create_if_node_with_halt(Node* new_control, Opaque4Node* template_assertion_predicate_expression,
      +                                       bool does_overflow
      +                                       NOT_PRODUCT(COMMA AssertionPredicateType assertion_predicate_type));
      +
      + public:
      +  TemplateAssertionPredicateCreator(CountedLoopNode* loop_head, int scale, Node* offset, Node* range,
      +                                    PhaseIdealLoop* phase)
      +      : _loop_head(loop_head),
      +        _scale(scale),
      +        _offset(offset),
      +        _range(range),
      +        _phase(phase) {}
      +  NONCOPYABLE(TemplateAssertionPredicateCreator);
      +
      +  IfTrueNode* create_with_uncommon_trap(Node* new_control, ParsePredicateSuccessProj* parse_predicate_success_proj,
      +                                        Deoptimization::DeoptReason deopt_reason, int if_opcode);
      +  IfTrueNode* create_with_halt(Node* new_control);
      +};
      +
      +// This class creates a new Initialized Assertion Predicate either from a template or from scratch.
       class InitializedAssertionPredicateCreator : public StackObj {
      -  IfNode* const _template_assertion_predicate;
      -  Node* const _new_init;
      -  Node* const _new_stride;
         PhaseIdealLoop* const _phase;
       
        public:
      -  InitializedAssertionPredicateCreator(IfNode* template_assertion_predicate, Node* new_init, Node* new_stride,
      -                                       PhaseIdealLoop* phase);
      +  explicit InitializedAssertionPredicateCreator(PhaseIdealLoop* phase);
         NONCOPYABLE(InitializedAssertionPredicateCreator);
       
      -  IfTrueNode* create(Node* control);
      +  IfTrueNode* create_from_template(IfNode* template_assertion_predicate, Node* new_control, Node* new_init,
      +                                   Node* new_stride);
      +  IfTrueNode* create(Node* operand, Node* new_control, jint stride, int scale, Node* offset, Node* range
      +                     NOT_PRODUCT(COMMA AssertionPredicateType assertion_predicate_type));
       
        private:
      -  OpaqueInitializedAssertionPredicateNode* create_assertion_expression(Node* control);
      -  IfNode* create_if_node(Node* control, OpaqueInitializedAssertionPredicateNode* assertion_expression, IdealLoopTree* loop);
      -  void create_fail_path(IfNode* if_node, IdealLoopTree* loop);
      -  void create_halt_node(IfFalseNode* fail_proj, IdealLoopTree* loop);
      -  IfTrueNode* create_success_path(IfNode* if_node, IdealLoopTree* loop);
      +  OpaqueInitializedAssertionPredicateNode* create_assertion_expression_from_template(IfNode* template_assertion_predicate,
      +                                                                                     Node* new_control, Node* new_init,
      +                                                                                     Node* new_stride);
      +  IfTrueNode* create_control_nodes(Node* new_control, int if_opcode,
      +                                   OpaqueInitializedAssertionPredicateNode* assertion_expression
      +                                   NOT_PRODUCT(COMMA AssertionPredicateType assertion_predicate_type));
       };
       
       // This class iterates through all predicates of a Regular Predicate Block and applies the given visitor to each.
      
      From f6fe5eaf1a24ba083e7ecf28b02290020ccab142 Mon Sep 17 00:00:00 2001
      From: Tejesh R 
      Date: Thu, 17 Oct 2024 05:48:45 +0000
      Subject: [PATCH 90/95] 8341072: Open source several AWT Canvas and Rectangle
       related tests
      
      Reviewed-by: prr
      ---
       test/jdk/java/awt/Canvas/MultiDitherTest.java | 422 ++++++++++++++++++
       .../java/awt/Canvas/MultiGraphicsTest.java    | 139 ++++++
       .../jdk/java/awt/Canvas/NoEventsLeakTest.java |  96 ++++
       test/jdk/java/awt/Canvas/duke_404.gif         | Bin 0 -> 5529 bytes
       .../java/awt/Rectangle/IntersectionTest.java  |  88 ++++
       5 files changed, 745 insertions(+)
       create mode 100644 test/jdk/java/awt/Canvas/MultiDitherTest.java
       create mode 100644 test/jdk/java/awt/Canvas/MultiGraphicsTest.java
       create mode 100644 test/jdk/java/awt/Canvas/NoEventsLeakTest.java
       create mode 100644 test/jdk/java/awt/Canvas/duke_404.gif
       create mode 100644 test/jdk/java/awt/Rectangle/IntersectionTest.java
      
      diff --git a/test/jdk/java/awt/Canvas/MultiDitherTest.java b/test/jdk/java/awt/Canvas/MultiDitherTest.java
      new file mode 100644
      index 00000000000..ab592280c80
      --- /dev/null
      +++ b/test/jdk/java/awt/Canvas/MultiDitherTest.java
      @@ -0,0 +1,422 @@
      +/*
      + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
      + * 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 6616089
      + * @summary Displays a dithered Canvas on all available GraphicsConfigurations
      + * @library /java/awt/regtesthelpers
      + * @build PassFailJFrame
      + * @run main/manual MultiDitherTest
      + */
      +
      +import java.awt.BorderLayout;
      +import java.awt.Button;
      +import java.awt.Canvas;
      +import java.awt.Choice;
      +import java.awt.Color;
      +import java.awt.Dimension;
      +import java.awt.EventQueue;
      +import java.awt.FlowLayout;
      +import java.awt.FontMetrics;
      +import java.awt.Frame;
      +import java.awt.Graphics;
      +import java.awt.GraphicsConfiguration;
      +import java.awt.GraphicsDevice;
      +import java.awt.GraphicsEnvironment;
      +import java.awt.Image;
      +import java.awt.Label;
      +import java.awt.LayoutManager;
      +import java.awt.Panel;
      +import java.awt.TextField;
      +import java.awt.event.ActionEvent;
      +import java.awt.event.ActionListener;
      +import java.awt.event.WindowAdapter;
      +import java.awt.event.WindowEvent;
      +import java.awt.image.ColorModel;
      +import java.awt.image.MemoryImageSource;
      +import java.util.List;
      +
      +public class MultiDitherTest extends Panel implements Runnable {
      +    final static int NOOP = 0;
      +    final static int RED = 1;
      +    final static int GREEN = 2;
      +    final static int BLUE = 3;
      +    final static int ALPHA = 4;
      +    final static int SATURATION = 5;
      +    final static String calcString = "Calculating...";
      +    static LayoutManager dcLayout = new FlowLayout(FlowLayout.CENTER, 10, 5);
      +    Thread runner;
      +    DitherControls XControls;
      +    DitherControls YControls;
      +    DitherCanvas canvas;
      +
      +    public static void main(String[] args) throws Exception {
      +        String INSTRUCTIONS = """
      +                Depending on the GraphicsConfiguration, the dithering may be in
      +                color or in grayscale and/or display at a lower bitdepth.
      +                The number of GraphicsConfigurations will be printed in the
      +                TextArea below as the test is starting up.
      +                Ensure that there are as many Frames created as there are
      +                available GraphicsConfigurations.
      +                Examine each Frame to ensure it displays the dither pattern.
      +                If all Canvases display correctly, the test PASSES.
      +                Otherwise, the test FAILS.
      +                The GC button runs the garbage collector.
      +                This button can be ignored for now.
      +
      +                           """;
      +        PassFailJFrame passFailJFrame = PassFailJFrame.builder()
      +                .title("Test Instructions")
      +                .instructions(INSTRUCTIONS)
      +                .rows((int) INSTRUCTIONS.lines().count() + 2)
      +                .columns(35)
      +                .build();
      +
      +        EventQueue.invokeAndWait(() -> {
      +            for (Frame frame : MultiDitherTest.initialize()) {
      +                PassFailJFrame.addTestWindow(frame);
      +                frame.setVisible(true);
      +            }
      +        });
      +        passFailJFrame.awaitAndCheck();
      +    }
      +
      +    public MultiDitherTest(GraphicsConfiguration gc) {
      +        String xSpec, ySpec;
      +        int[] xValues = new int[2];
      +        int[] yValues = new int[2];
      +
      +        xSpec = "red";
      +        ySpec = "blue";
      +        int xMethod = colorMethod(xSpec, xValues);
      +        int yMethod = colorMethod(ySpec, yValues);
      +
      +        setLayout(new BorderLayout());
      +        XControls = new DitherControls(this, xValues[0], xValues[1],
      +                xMethod, false);
      +        YControls = new DitherControls(this, yValues[0], yValues[1],
      +                yMethod, true);
      +        YControls.addRenderButton();
      +        YControls.addGCButton();
      +        add("North", XControls);
      +        add("South", YControls);
      +        add("Center", canvas = new DitherCanvas(gc));
      +    }
      +
      +    private static List initialize() {
      +        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
      +        GraphicsDevice[] gds = ge.getScreenDevices();
      +        Frame[] frames = new Frame[0];
      +        System.out.println(gds.length + " screens detected");
      +
      +        for (int j = 0; j < gds.length; j++) {
      +
      +            GraphicsDevice gd = gds[j];
      +            GraphicsConfiguration[] gcs = gd.getConfigurations();
      +            frames = new Frame[gcs.length];
      +            System.out.println(gcs.length + " GraphicsConfigurations available on screen " + j);
      +            for (int i = 0; i < gcs.length; i++) {
      +                Frame f = new Frame("MultiDitherTest " + (i + 1), gcs[i]);
      +                f.setLayout(new BorderLayout());
      +                f.setLocation(gcs[i].getBounds().x + 100 + (i * 10),
      +                        gcs[i].getBounds().y + 100 + (i * 10));
      +                MultiDitherTest ditherTest = new MultiDitherTest(gcs[i]);
      +                f.add("Center", ditherTest);
      +                f.pack();
      +                f.addWindowListener(new WindowAdapter() {
      +                    public void windowClosing(WindowEvent ev) {
      +                        ev.getWindow().dispose();
      +                    }
      +                });
      +                f.setVisible(true);
      +                ditherTest.start();
      +                frames[i] = f;
      +            }
      +
      +        }
      +        return List.of(frames);
      +    }
      +
      +    int colorMethod(String s, int[] values) {
      +        int method = NOOP;
      +
      +        if (s == null) {
      +            s = "";
      +        }
      +
      +        String lower = s.toLowerCase();
      +        if (lower.startsWith("red")) {
      +            method = RED;
      +            lower = lower.substring(3);
      +        } else if (lower.startsWith("green")) {
      +            method = GREEN;
      +            lower = lower.substring(5);
      +        } else if (lower.startsWith("blue")) {
      +            method = BLUE;
      +            lower = lower.substring(4);
      +        } else if (lower.startsWith("alpha")) {
      +            method = ALPHA;
      +            lower = lower.substring(4);
      +        } else if (lower.startsWith("saturation")) {
      +            method = SATURATION;
      +            lower = lower.substring(10);
      +        }
      +
      +        if (method == NOOP) {
      +            values[0] = 0;
      +            values[1] = 0;
      +            return method;
      +        }
      +
      +        int begval = 0;
      +        int endval = 255;
      +
      +        try {
      +            int dash = lower.indexOf('-');
      +            if (dash < 0) {
      +                begval = endval = Integer.parseInt(lower);
      +            } else {
      +                begval = Integer.parseInt(lower.substring(0, dash));
      +                endval = Integer.parseInt(lower.substring(dash + 1));
      +            }
      +        } catch (Exception e) {
      +        }
      +
      +        if (begval < 0) {
      +            begval = 0;
      +        }
      +        if (endval < 0) {
      +            endval = 0;
      +        }
      +        if (begval > 255) {
      +            begval = 255;
      +        }
      +        if (endval > 255) {
      +            endval = 255;
      +        }
      +
      +        values[0] = begval;
      +        values[1] = endval;
      +
      +        return method;
      +    }
      +
      +    public void start() {
      +        runner = new Thread(this);
      +        runner.start();
      +    }
      +
      +    public void stop() {
      +        runner = null;
      +    }
      +
      +    public void destroy() {
      +        remove(XControls);
      +        remove(YControls);
      +        remove(canvas);
      +    }
      +
      +    void applyMethod(int[] c, int method, int step, int total, int[] values) {
      +        if (method == NOOP) {
      +            return;
      +        }
      +        int val = ((total < 2)
      +                ? values[0]
      +                : values[0] + ((values[1] - values[0]) * step / (total - 1)));
      +        switch (method) {
      +            case RED:
      +                c[0] = val;
      +                break;
      +            case GREEN:
      +                c[1] = val;
      +                break;
      +            case BLUE:
      +                c[2] = val;
      +                break;
      +            case ALPHA:
      +                c[3] = val;
      +                break;
      +            case SATURATION:
      +                int max = Math.max(Math.max(c[0], c[1]), c[2]);
      +                int min = max * (255 - val) / 255;
      +                if (c[0] == 0) c[0] = min;
      +                if (c[1] == 0) c[1] = min;
      +                if (c[2] == 0) c[2] = min;
      +                break;
      +        }
      +    }
      +
      +    public void run() {
      +        canvas.setImage(null);
      +        Image img = calculateImage();
      +        synchronized (this) {
      +            if (img != null && runner == Thread.currentThread()) {
      +                canvas.setImage(img);
      +            }
      +        }
      +    }
      +
      +    /**
      +     * Calculates and returns the image.  Halts the calculation and returns
      +     * null if the Application is stopped during the calculation.
      +     */
      +    Image calculateImage() {
      +        Thread me = Thread.currentThread();
      +
      +        int width = canvas.getSize().width;
      +        int height = canvas.getSize().height;
      +        int[] xValues = new int[2];
      +        int[] yValues = new int[2];
      +        int xMethod = XControls.getParams(xValues);
      +        int yMethod = YControls.getParams(yValues);
      +        int[] pixels = new int[width * height];
      +        int[] c = new int[4];
      +        int index = 0;
      +
      +        for (int j = 0; j < height; j++) {
      +            for (int i = 0; i < width; i++) {
      +                c[0] = c[1] = c[2] = 0;
      +                c[3] = 255;
      +                if (xMethod < yMethod) {
      +                    applyMethod(c, xMethod, i, width, xValues);
      +                    applyMethod(c, yMethod, j, height, yValues);
      +                } else {
      +                    applyMethod(c, yMethod, j, height, yValues);
      +                    applyMethod(c, xMethod, i, width, xValues);
      +                }
      +                pixels[index++] = ((c[3] << 24) |
      +                        (c[0] << 16) |
      +                        (c[1] << 8) |
      +                        (c[2] << 0));
      +            }
      +            // Poll once per row to see if we've been told to stop.
      +            if (runner != me) {
      +                return null;
      +            }
      +        }
      +
      +        return createImage(new MemoryImageSource(width, height,
      +                ColorModel.getRGBdefault(), pixels, 0, width));
      +    }
      +
      +    static class DitherCanvas extends Canvas {
      +        Image img;
      +        GraphicsConfiguration mGC;
      +
      +        public DitherCanvas(GraphicsConfiguration gc) {
      +            super(gc);
      +            mGC = gc;
      +        }
      +
      +        public GraphicsConfiguration getGraphicsConfig() {
      +            return mGC;
      +        }
      +
      +        public void paint(Graphics g) {
      +            int w = getSize().width;
      +            int h = getSize().height;
      +            if (img == null) {
      +                super.paint(g);
      +                g.setColor(Color.black);
      +                FontMetrics fm = g.getFontMetrics();
      +                int x = (w - fm.stringWidth(calcString)) / 2;
      +                int y = h / 2;
      +                g.drawString(calcString, x, y);
      +            } else {
      +                g.drawImage(img, 0, 0, w, h, this);
      +            }
      +        }
      +
      +        public void update(Graphics g) {
      +            paint(g);
      +        }
      +
      +        public Dimension getMinimumSize() {
      +            return new Dimension(20, 20);
      +        }
      +
      +        public Dimension getPreferredSize() {
      +            return new Dimension(200, 200);
      +        }
      +
      +        public Image getImage() {
      +            return img;
      +        }
      +
      +        public void setImage(Image img) {
      +            this.img = img;
      +            paint(getGraphics());
      +        }
      +    }
      +
      +    static class DitherControls extends Panel implements ActionListener {
      +        TextField start;
      +        TextField end;
      +        Button button;
      +        Choice choice;
      +        MultiDitherTest panel;
      +        Button gc;
      +
      +        public DitherControls(MultiDitherTest app, int s, int e, int type,
      +                              boolean vertical) {
      +            panel = app;
      +            setLayout(dcLayout);
      +            add(new Label(vertical ? "Vertical" : "Horizontal"));
      +            add(choice = new Choice());
      +            choice.addItem("Noop");
      +            choice.addItem("Red");
      +            choice.addItem("Green");
      +            choice.addItem("Blue");
      +            choice.addItem("Alpha");
      +            choice.addItem("Saturation");
      +            choice.select(type);
      +            add(start = new TextField(Integer.toString(s), 4));
      +            add(end = new TextField(Integer.toString(e), 4));
      +        }
      +
      +        public void addRenderButton() {
      +            add(button = new Button("New Image"));
      +            button.addActionListener(this);
      +        }
      +
      +        public void addGCButton() {
      +            add(gc = new Button("GC"));
      +            gc.addActionListener(this);
      +        }
      +
      +        public int getParams(int[] values) {
      +            values[0] = Integer.parseInt(start.getText());
      +            values[1] = Integer.parseInt(end.getText());
      +            return choice.getSelectedIndex();
      +        }
      +
      +        public void actionPerformed(ActionEvent e) {
      +            if (e.getSource() == button) {
      +                panel.start();
      +            } else if (e.getSource() == gc) {
      +                System.gc();
      +            }
      +        }
      +    }
      +}
      \ No newline at end of file
      diff --git a/test/jdk/java/awt/Canvas/MultiGraphicsTest.java b/test/jdk/java/awt/Canvas/MultiGraphicsTest.java
      new file mode 100644
      index 00000000000..c38d6041c2b
      --- /dev/null
      +++ b/test/jdk/java/awt/Canvas/MultiGraphicsTest.java
      @@ -0,0 +1,139 @@
      +/*
      + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
      + * 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 6616089
      + * @summary Display an image in all available GraphicsConfigurations
      + * @library /java/awt/regtesthelpers
      + * @build PassFailJFrame
      + * @run main/manual MultiGraphicsTest
      + */
      +
      +import java.awt.BorderLayout;
      +import java.awt.Canvas;
      +import java.awt.Dimension;
      +import java.awt.Frame;
      +import java.awt.Graphics;
      +import java.awt.GraphicsConfiguration;
      +import java.awt.GraphicsDevice;
      +import java.awt.GraphicsEnvironment;
      +import java.awt.Image;
      +import java.awt.MediaTracker;
      +import java.awt.Toolkit;
      +import java.awt.event.WindowAdapter;
      +import java.awt.event.WindowEvent;
      +import java.net.URL;
      +import java.util.List;
      +
      +public class MultiGraphicsTest extends Canvas {
      +    final static String IMAGEFILE = "duke_404.gif";
      +    static Image jim;
      +    MediaTracker tracker;
      +    int w, h;
      +
      +    public static void main(String[] args) throws Exception {
      +        String INSTRUCTIONS = """
      +                This test displays several Windows containing an image,
      +                one Window for each available GraphicsConfiguration.
      +                Depending on the GraphicsConfiguration, images may be
      +                displayed in color or in grayscale and/or displayed at a
      +                lower bitdepth.
      +                The number of GraphicsConfigurations will be printed below
      +                as the test is starting up.
      +                Ensure that there are as many Windows created as there are
      +                available GraphicsConfigurations.
      +                Examine each Window to ensure it displays Duke_404.
      +                If all Canvases display correctly, the test PASSES.
      +                Otherwise, the test FAILS."
      +                """;
      +        PassFailJFrame.builder()
      +                .title("Test Instructions")
      +                .instructions(INSTRUCTIONS)
      +                .rows((int) INSTRUCTIONS.lines().count() + 2)
      +                .columns(35)
      +                .testUI(initialize())
      +                .build()
      +                .awaitAndCheck();
      +    }
      +
      +    public MultiGraphicsTest(GraphicsConfiguration gc) {
      +        super(gc);
      +        tracker = new MediaTracker(this);
      +        tracker.addImage(jim, 0);
      +        try {
      +            tracker.waitForAll();
      +        } catch (java.lang.InterruptedException e) {
      +            System.err.println(e);
      +        }
      +        w = jim.getWidth(this);
      +        h = jim.getHeight(this);
      +    }
      +
      +    private static List initialize() {
      +        URL imgURL;
      +        imgURL = MultiGraphicsTest.class.getResource(IMAGEFILE);
      +        if (imgURL == null) {
      +            System.err.println("Unable to locate " + IMAGEFILE);
      +            return null;
      +        }
      +        jim = Toolkit.getDefaultToolkit().getImage(imgURL);
      +
      +        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
      +        GraphicsDevice gd = ge.getDefaultScreenDevice();
      +        GraphicsConfiguration[] gc = gd.getConfigurations();
      +        Frame[] frames = new Frame[gc.length];
      +        System.out.println(gc.length + " available GraphicsConfigurations");
      +        for (int i = 0; i < gc.length; i++) {
      +            Frame f = new Frame("GraphicsTest " + (i + 1));
      +            f.setLayout(new BorderLayout());
      +            f.setLocation(100 + (i * 10), 100 + (i * 10));
      +            MultiGraphicsTest gcTest = new MultiGraphicsTest(gc[i]);
      +            f.add("Center", gcTest);
      +            f.pack();
      +            f.addWindowListener(new WindowAdapter() {
      +                public void windowClosing(WindowEvent ev) {
      +                    ev.getWindow().setVisible(false);
      +                }
      +            });
      +            frames[i] = f;
      +        }
      +        return List.of(frames);
      +    }
      +
      +    public void paint(Graphics g) {
      +        g.drawImage(jim, 0, 0, w, h, this);
      +    }
      +
      +    public void update(Graphics g) {
      +        paint(g);
      +    }
      +
      +    public Dimension getMinimumSize() {
      +        return new Dimension(w, h);
      +    }
      +
      +    public Dimension getPreferredSize() {
      +        return new Dimension(w, h);
      +    }
      +}
      diff --git a/test/jdk/java/awt/Canvas/NoEventsLeakTest.java b/test/jdk/java/awt/Canvas/NoEventsLeakTest.java
      new file mode 100644
      index 00000000000..4768775224a
      --- /dev/null
      +++ b/test/jdk/java/awt/Canvas/NoEventsLeakTest.java
      @@ -0,0 +1,96 @@
      +/*
      + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
      + * 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 4250354
      + * @key headful
      + * @summary tests that JNI global refs are cleaned up correctly
      + * @run main/timeout=600 NoEventsLeakTest
      + */
      +
      +import java.awt.Canvas;
      +import java.awt.EventQueue;
      +import java.awt.Frame;
      +
      +public class NoEventsLeakTest extends Frame {
      +    static final int nLoopCount = 1000;
      +
      +    private static void initialize() {
      +        NoEventsLeakTest app = new NoEventsLeakTest();
      +        boolean result = app.run();
      +        if (result) {
      +            throw new RuntimeException("Memory leak in Component");
      +        }
      +        System.out.println("Test Passed");
      +    }
      +
      +    public boolean run() {
      +        setSize(10, 10);
      +        addNotify();
      +        for (int i = 0; i < nLoopCount; i++) {
      +            Canvas panel = new TestCanvas();
      +            add(panel, 0);
      +            remove(0);
      +            panel = null;
      +            System.gc();
      +        }
      +        try {
      +            Thread.currentThread().sleep(1000);
      +        } catch (InterruptedException e) {
      +        }
      +        System.gc();
      +        System.out.println("Checking");
      +        return ((TestCanvas.created - TestCanvas.finalized) > 800);
      +    }
      +
      +    public static void main(String[] args) throws Exception {
      +        EventQueue.invokeAndWait(NoEventsLeakTest::initialize);
      +    }
      +}
      +
      +class TestCanvas extends Canvas {
      +    static int finalized = 0;
      +    static int created = 0;
      +    static final int nLoopPrint = 100;
      +
      +    public TestCanvas() {
      +        if (created % nLoopPrint == 0) {
      +            System.out.println("Created " + getClass() + " " + created);
      +        }
      +        created++;
      +    }
      +
      +    @SuppressWarnings("removal")
      +    protected void finalize() {
      +        try {
      +            super.finalize();
      +            if (finalized % nLoopPrint == 0) {
      +                System.out.println("Finalized " + getClass() + " " + finalized);
      +            }
      +            finalized++;
      +        } catch (Throwable t) {
      +            System.out.println("Exception in " + getClass() + ": " + t);
      +        }
      +    }
      +}
      diff --git a/test/jdk/java/awt/Canvas/duke_404.gif b/test/jdk/java/awt/Canvas/duke_404.gif
      new file mode 100644
      index 0000000000000000000000000000000000000000..4958e0d0dfa8c059b704e11fe173f23091cfe8fd
      GIT binary patch
      literal 5529
      zcmWmB`6JVh5C@(K>XYx)-NeKppymWcs``g#**;)0yd+MuK)!p5{PMlCzR{ly){}mMUpS3jw
      zj<7@$L$!=D39JB&)-MX>2O6DdA6vi5yNF;eC0d-(GkrBNH(GV!H(q%I(VDRezO1Pk
      zvMr(RxcvFV-E*6A#x+U3+PY#2L9JHzrk`qI?@H6r?z(ub%-DR7IeYQdRC-#lj7*te
      z+XndA^hnN~bK#a3*`(}XrELkm-GBYBd7m$DSjh>5+R%u_?rwh@tJX%dZ*O!D9Jo=@D5?H5F6e(Ab2*
      z)t)>hZ$9190>Jd(a6OPL#vSXyXuv9IJ!nGf;O|$jozZR}ftTvhU_?vtJg3Ln1+T%YV{8ZFxSjM
      z?*qtir%~zny4-q(MzEb>DV}C;n>yS6@m5*j16|__c^^2XTE^i$%SXIR$Lf7T>S(Ze
      zj+acIOz#jFd2@6bREXCmTDSrRBgNrjxLeq`
      zodjEYZ`l7RQejg|kP|RYQa{T&%N+L9kPIa%b56Gk8e)!uEZfXY+j}|S5aQ~=4>-B8
      zrLZr7vJ8@hULpw~S#NhrA$KXlQrOBEBj-nDr}g5$(LRh+7OXe7;v5I$q6CcCqzm^V
      zuaJxkdYtg&VeSp}quu|F9s~ER_@0WB+9U=5bB=HXH&I>=QwH&b
      zt3$YRezbVIW*RHIXUI=4v~?&@H{Ix|AN5j-%#T`rNvnh$o8K#<65LdYh`q13{0S0@
      z=x@yF9yL04ZHl+_YijKF4!KQY-fnJmz
      zT3@Q@f3OKw9~BiiUINY}JsZ{8{ci5vn$Qc6Zs}&$t{YvG=_U;Tb#IzM2tECj2OTHr
      z{Is~e{tTFG5RmTyz9cci&bPkbSa!ob7x(=7j$d-+$PYh@-r*DA&NMXw{z_0%fyb=e
      z);r`RNF0f*OZ)q7rA5gQk`zs?t}-39$TEuRdz5K`PL%SQdi+cw%JreeCq9|aU{x{b
      z-uG;siSM%O;`)xk&Q_4hA}2QQOwY8}8TM{@%sbP%%OIr}=HpujYwhxXVhC0MOhUB1
      z1E5HGc}Kr4Tep`erpw%-w|Fq6aDTkkC^bEycF%Ph4{DYWSPi%14*d$o
      zQ*}eyj}(LYYi6i4UByq0b0ks?NK+jLm3R3?&`}q*%9B&MNUj~f6KatsZjrRufQ%Ekp|%O+hX`vAp-j4)gAI~s_#g=
      zlN}E>kCel7ifjj5xNv{EnlPtwC{M`v}?z26y3(J8h8M3UlWR^#@L5kT9`}_aPBv*a68=oS_cn5zAE}T^X#uk7(>T&;cs@`X7BvTNUojZ$Udf470lMx>$qj6TpfS&rQVDYW5ZQ}f%1
      zZufHn)cgIXFXQdJbG}l%$a@utH8)l3Y%m|UUgx7M8Ht2s?3#D>SX@m-x|*^Epd&bi^x;RlVcd-(DhP+oPiz2k@gYI>5BinIDCD!Kbxb6>FnT6}QwgvVZR76z@!c`5p&|2d+YC
      zA+v(s^4W9VS>@Z#*irW_1;b){VWRLmPRuqIa^Ul25nMIu6aBtVXGU7gMx@<4&~e%N
      zc5&lzFF2s5fPmhMWw_-A@3iBiAXp3oejG}cW*`ojXrx}IjN#RAh=
      zP%!JbjtW_P?8A3wXN=mib8}N`eWAaNHm^U*DwCRP!Gc@!{Ie&_eq;tNg&)KUfHWZJ
      zxe+=W3VJF7(-gXYGSRR4cJ=ZR>5$bD8GLG^w(ztKTuI1(rm}_sj-5=5%~CkZNyKdk
      zy>Egcs5l?(_I@B+H`Vg=Wz$BSCM^c+HV=!1t$l2b5lnZYwP#6gBirz1CX~&^{1W3bPp{^;
      zZg#==y<-MLr#I@JK?X=sVSrgW8@+Z5+K>x;6of2FB0lxy)Whj{7fR1MMY|`^L|@A<
      zL{wbLuNX)sy47aik^}qeE3Bu&XZRY5h^<@+!F2(<5>jdV&!)G^4TK(B%fgK#8Sx)p
      zRosiP1yp$%EkHH~a6WqANrc)ECJo4HHLuKojr0(AT;}+nUH8(|&V-6bjbeyx*e#T5
      zdwAfCltm?JM%m{clN$V>IlQ#ZXnK?L3}~YSaj*$KH)7
      z5oKvMEx?k?>OigNSZw7%N}Wf4?wPau#SM|i&S?HrYW~N7-?&&8^Q7+bOs(Jw7R`eN
      z6vR&*)ijBZGFGY^s-8qu{R2A~8GFmAy1@prRKl%R;A~QYK{&#i)MU*_|Mf}MQPc2k
      zN0fPnQ^iQsb$fnGPQ@O71FR5n+6Uv(gUIK&v=+E<9imusnw^a??hIG`wy3L=#)IGM
      z2WDh{L4dXhL(z&K*GBx4gWV7w*QtG6l7-+8#ds?bzkhC0P@=y;6*ayF-<>eFBo}Q~
      zY62XV)9CPBNH!mjo3&rVl_J+EqpC+-o#T;TMSy!oxzA;yxzyQKY0e|Ltb9IcFN8CK
      z;~J-6vnfu`jFHcbUHw%}_Q5Chd3dKg0vuqXt1@V0`1eXgbW&=eOCN?&
      zb=-@Z6L0++dv_VDKZkGXAVnFy3XGEW7ki^6VjzW$$^salw5?DK&H+5o{@WEPQK+dv>i5(j$!X!XzjY@rAAjU_2$Gtn7k`SiYk_YkwD>x
      zIyyz}JM33q162LG301FHbT5zGSZb
      zo$HiqK>oxX1M;LO#!A&!hFCY=d!l+sk-@T&gn+X!9WL%$p^ASia7%Cp*XJ^jg#Q
      znn@|biYctfwta9#<3M6cWTl}lAnR2C)H~FmzFXxgAO%7PNx{TQH%mBOtaJP9GXi=q
      zz9g~$-cKX}CVGkhceuIeYi{7MZE_$6zXF`p0|XYv9G)=}>YY^rqfK1hBo16k;00-5MRWjpf;fG+r>?NAwv^M;Gx9GhoNzpdJuV^T?+t~HsQ-8Y_&cvZi{~`D)gT0UtDouh{5ndipo&jtkf%#H&0S|1=qmF#FUgIPw6XbD(_op*PP1o1H
      z%V)p;@kgH!X1^b!b`znw560%ud*{$32y%!~G)*TJYxB(N
      znZV4EJC*t7EuZeldF%7uVwC%b-mwK~*%O7*iHpO9Aq^yLlABtDdNmq|0R!V~@&?ihAZvdJdw1YQKATa(nsUQc`9{b$--d_HAzOBCkG`p1T8bsS%6+71j_ATNU1(tg$`_jZOSY{9xYXl
      zTSLj#aT?e{sGw97Rh!M^6w3xUGkG}&u7O}!6)7o
      zr^}IkRwixv#2@4r_n;Y&W|A4=U+-gs4Ye7SJ=|W;Dqig$e^TW!fN2*~b@$ldT+A}MGZGsZmfQ_|D~8#(H~=>
      zf$llYH2-$NDleu?$9TCY7X9@sQkT8>H2Hz2Q|USP!w-ih|8*+uh~6HnLPyt<8
      
      literal 0
      HcmV?d00001
      
      diff --git a/test/jdk/java/awt/Rectangle/IntersectionTest.java b/test/jdk/java/awt/Rectangle/IntersectionTest.java
      new file mode 100644
      index 00000000000..41680b2e7b6
      --- /dev/null
      +++ b/test/jdk/java/awt/Rectangle/IntersectionTest.java
      @@ -0,0 +1,88 @@
      +/*
      + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
      + * 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 4147957
      + * @key headful
      + * @summary Test to verify setClip with invalid rect changes rect to valid
      + * @run main IntersectionTest
      + */
      +
      +import java.awt.EventQueue;
      +import java.awt.Frame;
      +import java.awt.Graphics;
      +import java.awt.Panel;
      +import java.awt.Rectangle;
      +import java.awt.Robot;
      +
      +public class IntersectionTest {
      +    public static Frame frame;
      +
      +    public static void main(String[] args) throws Exception {
      +        Robot robot = new Robot();
      +        try {
      +            robot.setAutoDelay(100);
      +            EventQueue.invokeAndWait(() -> {
      +                TestFrame panel = new TestFrame();
      +                frame = new Frame("Rectangle Intersection Test");
      +                frame.add(panel);
      +
      +                frame.pack();
      +                frame.setVisible(true);
      +            });
      +            robot.waitForIdle();
      +            robot.delay(200);
      +        } finally {
      +            EventQueue.invokeAndWait(() -> {
      +                if (frame != null) {
      +                    frame.dispose();
      +                }
      +            });
      +        }
      +    }
      +}
      +
      +class TestFrame extends Panel {
      +    @Override
      +    public void paint(Graphics g) {
      +        Rectangle r1 = new Rectangle(0, 0, 100, 100);
      +        Rectangle r2 = new Rectangle(200, 200, 20, 20);
      +        Rectangle r3 = r1.intersection(r2);
      +        System.out.println("intersect:(" + (int) r3.getX() + "," +
      +                (int) r3.getY() + "," + (int) r3.getWidth() + "," +
      +                (int) r3.getHeight() + ")");
      +        g.setClip(r3);
      +        Rectangle r4 = g.getClipBounds();
      +        System.out.println("getClipBounds:(" + (int) r4.getX() + "," +
      +                (int) r4.getY() + "," + (int) r4.getWidth() + "," +
      +                (int) r4.getHeight() + ")");
      +
      +        if ((r4.getWidth() <= 0) || (r4.getHeight() <= 0)) {
      +            System.out.println("Test Passed");
      +        } else {
      +            throw new RuntimeException("IntersectionTest failed. " +
      +                    "Non-empty clip bounds.");
      +        }
      +    }
      +}
      
      From e0dabfb4bfd93a4407518177043d3dbc85c4bbd9 Mon Sep 17 00:00:00 2001
      From: Tejesh R 
      Date: Thu, 17 Oct 2024 06:38:23 +0000
      Subject: [PATCH 91/95] 8340279: Open source several AWT Dialog tests - Batch 2
      
      Reviewed-by: abhiscxk, prr
      ---
       test/jdk/ProblemList.txt                      |   2 +
       .../DialogSystemMenu/DialogSystemMenu.java    | 122 ++++++++++++++
       .../awt/Dialog/DialogSystemMenu/icon24x24.gif | Bin 0 -> 108 bytes
       .../awt/Dialog/DialogSystemMenu/iconone.gif   | Bin 0 -> 109 bytes
       .../awt/Dialog/DialogSystemMenu/icontwo.gif   | Bin 0 -> 109 bytes
       .../java/awt/Dialog/FileDialogFilterTest.java |  68 ++++++++
       .../PrintToFileTest/PrintToFileFrame.java     |  40 +++++
       .../PrintToFileTest/PrintToFileGranted.java   |  70 ++++++++
       .../PrintToFileTest/PrintToFileRevoked.java   |  69 ++++++++
       .../java/awt/Dialog/PrintToFileTest/granted   |  10 ++
       .../java/awt/Dialog/PrintToFileTest/revoked   |   9 ++
       .../awt/Dialog/TopmostModalDialogTest.java    | 152 ++++++++++++++++++
       12 files changed, 542 insertions(+)
       create mode 100644 test/jdk/java/awt/Dialog/DialogSystemMenu/DialogSystemMenu.java
       create mode 100644 test/jdk/java/awt/Dialog/DialogSystemMenu/icon24x24.gif
       create mode 100644 test/jdk/java/awt/Dialog/DialogSystemMenu/iconone.gif
       create mode 100644 test/jdk/java/awt/Dialog/DialogSystemMenu/icontwo.gif
       create mode 100644 test/jdk/java/awt/Dialog/FileDialogFilterTest.java
       create mode 100644 test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileFrame.java
       create mode 100644 test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileGranted.java
       create mode 100644 test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileRevoked.java
       create mode 100644 test/jdk/java/awt/Dialog/PrintToFileTest/granted
       create mode 100644 test/jdk/java/awt/Dialog/PrintToFileTest/revoked
       create mode 100644 test/jdk/java/awt/Dialog/TopmostModalDialogTest.java
      
      diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt
      index 942e15002bd..8aa3fea3fde 100644
      --- a/test/jdk/ProblemList.txt
      +++ b/test/jdk/ProblemList.txt
      @@ -478,6 +478,8 @@ java/awt/KeyboardFocusmanager/ConsumeNextMnemonicKeyTypedTest/ConsumeNextMnemoni
       
       java/awt/Window/GetScreenLocation/GetScreenLocationTest.java 8225787 linux-x64
       java/awt/Dialog/MakeWindowAlwaysOnTop/MakeWindowAlwaysOnTop.java 8266243 macosx-aarch64
      +java/awt/Dialog/PrintToFileTest/PrintToFileRevoked.java 8029249 macosx-all
      +java/awt/Dialog/PrintToFileTest/PrintToFileGranted.java 8029249 macosx-all
       java/awt/Dialog/ChoiceModalDialogTest.java 8161475 macosx-all
       java/awt/Dialog/FileDialogUserFilterTest.java 8001142 generic-all
       
      diff --git a/test/jdk/java/awt/Dialog/DialogSystemMenu/DialogSystemMenu.java b/test/jdk/java/awt/Dialog/DialogSystemMenu/DialogSystemMenu.java
      new file mode 100644
      index 00000000000..3f1639e90a4
      --- /dev/null
      +++ b/test/jdk/java/awt/Dialog/DialogSystemMenu/DialogSystemMenu.java
      @@ -0,0 +1,122 @@
      +/*
      + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
      + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      + *
      + * This code is free software; you can redistribute it and/or modify it
      + * under the terms of the GNU General Public License version 2 only, as
      + * published by the Free Software Foundation.
      + *
      + * This code is distributed in the hope that it will be useful, but WITHOUT
      + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      + * version 2 for more details (a copy is included in the LICENSE file that
      + * accompanied this code).
      + *
      + * You should have received a copy of the GNU General Public License version
      + * 2 along with this work; if not, write to the Free Software Foundation,
      + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      + *
      + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      + * or visit www.oracle.com if you need additional information or have any
      + * questions.
      + */
      +
      +import java.awt.Dialog;
      +import java.awt.Frame;
      +import java.awt.event.WindowListener;
      +import java.util.List;
      +
      +/*
      + * @test
      + * @bug 4058953 4094035
      + * @summary Test to verify system menu of a dialog on win32
      + * @requires (os.family == "windows")
      + * @library /java/awt/regtesthelpers
      + * @build PassFailJFrame
      + * @run main/manual DialogSystemMenu
      + */
      +
      +public class DialogSystemMenu {
      +    public static void main(String[] args) throws Exception {
      +        String INSTRUCTIONS = """
      +                1. Check the following on the first dialog window:
      +                    Right-clicking on the title bar
      +                    should bring up a system menu.
      +                    The system menu should not allow any
      +                    of the Maximize, Minimize and
      +                    Restore actions
      +
      +                2. The second dialog should be non-resizable
      +                    and have no application icon.
      +                """;
      +        PassFailJFrame.builder()
      +                .title("Test Instructions")
      +                .instructions(INSTRUCTIONS)
      +                .rows((int) INSTRUCTIONS.lines().count() + 2)
      +                .columns(35)
      +                .testUI(initialize())
      +                .build()
      +                .awaitAndCheck();
      +    }
      +
      +    public static List initialize() {
      +        Frame frame = new java.awt.Frame("Parent Frame");
      +        String txt = """
      +                This is a resizable dialog
      +                Right-clicking on the title bar
      +                should bring up a system menu
      +                The system menu should not
      +                allow any
      +                of the Maximize, Minimize and
      +                Restore actions
      +                """;
      +        String txt_non = """
      +                This is a non-resizable dialog
      +                It should be really non-resizable
      +                and have no application icon
      +                """;
      +        TestApp resizable = new TestApp(frame, "Test for 4058953", txt, true);
      +        resizable.setLocation(0, 0);
      +
      +        TestApp non_resizable = new TestApp(frame, "Test for 4094035", txt_non, false);
      +        non_resizable.setLocation(320, 0);
      +        return List.of(resizable, non_resizable);
      +    }
      +}
      +
      +
      +class TestApp extends Dialog implements WindowListener {
      +    public TestApp(java.awt.Frame parent, String title, String txt, boolean resize) {
      +        super(parent, title, false);
      +
      +        java.awt.TextArea ta = new java.awt.TextArea(txt);
      +        ta.setEditable(false);
      +        this.add(ta, "Center");
      +        this.addWindowListener(this);
      +        this.setSize(300, 200);
      +        this.setResizable(resize);
      +    }
      +
      +
      +    public void windowOpened(java.awt.event.WindowEvent myEvent) {
      +    }
      +
      +    public void windowClosed(java.awt.event.WindowEvent myEvent) {
      +    }
      +
      +    public void windowIconified(java.awt.event.WindowEvent myEvent) {
      +    }
      +
      +    public void windowDeiconified(java.awt.event.WindowEvent myEvent) {
      +    }
      +
      +    public void windowActivated(java.awt.event.WindowEvent myEvent) {
      +    }
      +
      +    public void windowDeactivated(java.awt.event.WindowEvent myEvent) {
      +    }
      +
      +    public void windowClosing(java.awt.event.WindowEvent myEvent) {
      +        this.dispose();
      +    }
      +}
      diff --git a/test/jdk/java/awt/Dialog/DialogSystemMenu/icon24x24.gif b/test/jdk/java/awt/Dialog/DialogSystemMenu/icon24x24.gif
      new file mode 100644
      index 0000000000000000000000000000000000000000..dfb9987339745f83f65005909bcd38c01ee8ce3c
      GIT binary patch
      literal 108
      zcmZ?wbh9u|lwgox_`tyM|Nnmm1_m7<2J$5s7?`|!`d4zk<$uVi80D$GH(Yvop@Kx-
      zqMJ4Tk{e&$+M07KO5f~O@xJAI^>>|Yvgdc7vG`@qR>gHYi&#~c#v2vSWeQY(dR%ID
      MNon_{oeT`t0M0-uUH||9
      
      literal 0
      HcmV?d00001
      
      diff --git a/test/jdk/java/awt/Dialog/DialogSystemMenu/iconone.gif b/test/jdk/java/awt/Dialog/DialogSystemMenu/iconone.gif
      new file mode 100644
      index 0000000000000000000000000000000000000000..698ba29d839e0d67621ecf3983a3e476cc0d9bab
      GIT binary patch
      literal 109
      zcmZ?wbhEHbRA5kG*vtR|4Pe{=rW$~}hK2(N4m310{0EBvXZR1MfHaT=1Uf)LpiTt_
      t1{NiQlb);hTD(5H`+tH)UrOe@j8*G$UhgZ=IbV`{zhd3{Ju-|8)&R|BDqsKr
      
      literal 0
      HcmV?d00001
      
      diff --git a/test/jdk/java/awt/Dialog/DialogSystemMenu/icontwo.gif b/test/jdk/java/awt/Dialog/DialogSystemMenu/icontwo.gif
      new file mode 100644
      index 0000000000000000000000000000000000000000..7f344ed1df07e4f9ee91f8cbcdc7b0050613f551
      GIT binary patch
      literal 109
      zcmZ?wbhEHbRA5kG*vtR|4Pe{=rW$~}hK2(N4m310{0EBvXZR1MfHaT=1Uf)LpiTt_
      t1{S4&6P~O0TD(5H`+tH)UrOe@j8*G$UhgZ=IbV`{zhd3{Ju-|8)&SANDt`a~
      
      literal 0
      HcmV?d00001
      
      diff --git a/test/jdk/java/awt/Dialog/FileDialogFilterTest.java b/test/jdk/java/awt/Dialog/FileDialogFilterTest.java
      new file mode 100644
      index 00000000000..e30d8ea58a2
      --- /dev/null
      +++ b/test/jdk/java/awt/Dialog/FileDialogFilterTest.java
      @@ -0,0 +1,68 @@
      +/*
      + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
      + * 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.FileDialog;
      +import java.awt.Frame;
      +import java.io.File;
      +import java.io.FilenameFilter;
      +
      +/*
      + * @test
      + * @bug 4364256
      + * @summary Test to File Dialog filter
      + * @requires (os.family == "windows")
      + * @library /java/awt/regtesthelpers
      + * @build PassFailJFrame
      + * @run main/manual FileDialogFilterTest
      + */
      +
      +public class FileDialogFilterTest {
      +    public static void main(String[] args) throws Exception {
      +        String INSTRUCTIONS = """
      +                   Run the test, make sure a file dialog
      +                   comes up with no crash. If the file dialog
      +                   comes up successfully then press PASS, else FAIL.
      +                """;
      +        PassFailJFrame.builder()
      +                .title("Test Instructions")
      +                .instructions(INSTRUCTIONS)
      +                .rows((int) INSTRUCTIONS.lines().count() + 2)
      +                .columns(35)
      +                .testUI(initialize())
      +                .build()
      +                .awaitAndCheck();
      +    }
      +
      +    public static FileDialog initialize() {
      +        FileDialog fDlg = new FileDialog(new Frame());
      +        fDlg.addNotify();
      +        fDlg.setFilenameFilter(new MyFilter());
      +        return fDlg;
      +    }
      +}
      +
      +class MyFilter implements FilenameFilter {
      +    public boolean accept(File dir, String name) {
      +        return true;
      +    }
      +}
      diff --git a/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileFrame.java b/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileFrame.java
      new file mode 100644
      index 00000000000..a117622d570
      --- /dev/null
      +++ b/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileFrame.java
      @@ -0,0 +1,40 @@
      +import java.awt.Button;
      +import java.awt.FlowLayout;
      +import java.awt.Frame;
      +import java.awt.PrintJob;
      +import java.awt.event.ActionEvent;
      +import java.awt.event.ActionListener;
      +
      +class PrintToFileFrame extends Frame implements ActionListener {
      +    Button nativeDlg = new Button("Show print dialog");
      +
      +    public PrintToFileFrame() {
      +        this.setLayout(new FlowLayout());
      +        add(nativeDlg);
      +        nativeDlg.addActionListener(this);
      +
      +        setSize(300, 300);
      +    }
      +
      +    @SuppressWarnings("removal")
      +    public void actionPerformed(ActionEvent ae) {
      +        if (System.getSecurityManager() == null) {
      +            throw new RuntimeException("Security manager isn't installed.");
      +        }
      +
      +        try {
      +            System.getSecurityManager().checkPrintJobAccess();
      +            System.out.println("checkPrintJobAccess - OK");
      +        } catch (SecurityException e) {
      +            System.out.println("checkPrintJobAccess - ERROR " + e);
      +        }
      +
      +        PrintJob printJob = getToolkit().getPrintJob(this, null, null);
      +
      +        if (printJob != null) {
      +            System.out.println("Print Job: " + printJob);
      +        } else {
      +            System.out.println("Print Job is null.");
      +        }
      +    }
      +}
      diff --git a/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileGranted.java b/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileGranted.java
      new file mode 100644
      index 00000000000..05d73123d98
      --- /dev/null
      +++ b/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileGranted.java
      @@ -0,0 +1,70 @@
      +/*
      + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
      + * 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.print.PrinterJob;
      +
      +/*
      + * @test
      + * @bug 6275359
      + * @summary Test to verify system menu of a dialog on win32
      + * @requires (os.family == "windows")
      + * @library /java/awt/regtesthelpers
      + * @build PassFailJFrame
      + * @compile PrintToFileFrame.java
      + * @compile PrintToFileGranted.java
      + * @run main/manual/policy=granted/othervm PrintToFileGranted
      + */
      +
      +public class PrintToFileGranted {
      +    public static void main(String[] args) throws Exception {
      +        String INSTRUCTIONS;
      +        if (isPrintSupport()) {
      +            INSTRUCTIONS = """
      +                    1. Click on 'Show file dialog' button A print dialog will come up
      +                    2. If checkbox 'Print to file' is enabled then the test passed
      +                       else the test failed
      +                    3. Close the print dialog before pressing PASS or FAIL buttons
      +                    """;
      +        } else {
      +            INSTRUCTIONS = """
      +                    1. The test requires printer installed in your system,
      +                       but there is no printers found
      +                       Please install one and re-run the test
      +                    """;
      +        }
      +
      +        PassFailJFrame.builder()
      +                .title("Test Instructions")
      +                .instructions(INSTRUCTIONS)
      +                .rows((int) INSTRUCTIONS.lines().count() + 2)
      +                .columns(35)
      +                .testUI(new PrintToFileFrame())
      +                .build()
      +                .awaitAndCheck();
      +    }
      +
      +    public static boolean isPrintSupport() {
      +        PrinterJob pj = PrinterJob.getPrinterJob();
      +        return pj.getPrintService() != null;
      +    }
      +}
      diff --git a/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileRevoked.java b/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileRevoked.java
      new file mode 100644
      index 00000000000..7c724e97bed
      --- /dev/null
      +++ b/test/jdk/java/awt/Dialog/PrintToFileTest/PrintToFileRevoked.java
      @@ -0,0 +1,69 @@
      +/*
      + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
      + * 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.print.PrinterJob;
      +
      +/*
      + * @test
      + * @bug 6275359
      + * @summary Test to verify Printing ignores Security permissions
      + *          using native dialog
      + * @requires (os.family == "windows")
      + * @library /java/awt/regtesthelpers
      + * @build PassFailJFrame
      + * @compile PrintToFileRevoked.java
      + * @run main/manual/policy=revoked/othervm PrintToFileRevoked
      + */
      +
      +public class PrintToFileRevoked {
      +    public static void main(String[] args) throws Exception {
      +        String INSTRUCTIONS;
      +        if (isPrintSupport()) {
      +            INSTRUCTIONS = """
      +                    1. Click on 'Show file dialog' button A print dialog will come up
      +                    2. If checkbox 'Print to file' is disabled then the test passed
      +                       else the test failed
      +                    3. Close the print dialog before pressing PASS or FAIL buttons
      +                    """;
      +        } else {
      +            INSTRUCTIONS = """
      +                    1. The test requires printer installed in your system,
      +                       but there is no printers found
      +                       Please install one and re-run the test
      +                    """;
      +        }
      +        PassFailJFrame.builder()
      +                .title("Test Instructions")
      +                .instructions(INSTRUCTIONS)
      +                .rows((int) INSTRUCTIONS.lines().count() + 2)
      +                .columns(35)
      +                .testUI(new PrintToFileFrame())
      +                .build()
      +                .awaitAndCheck();
      +    }
      +
      +    public static boolean isPrintSupport() {
      +        PrinterJob pj = PrinterJob.getPrinterJob();
      +        return pj.getPrintService() != null;
      +    }
      +}
      diff --git a/test/jdk/java/awt/Dialog/PrintToFileTest/granted b/test/jdk/java/awt/Dialog/PrintToFileTest/granted
      new file mode 100644
      index 00000000000..e73b0fdf3cd
      --- /dev/null
      +++ b/test/jdk/java/awt/Dialog/PrintToFileTest/granted
      @@ -0,0 +1,10 @@
      +/* AUTOMATICALLY GENERATED ON Thu Jan 03 15:48:39 PST 2002*/
      +/* DO NOT EDIT */
      +
      +grant {
      + permission java.lang.RuntimePermission "queuePrintJob";
      + permission java.util.PropertyPermission "*", "read";
      + permission java.io.FilePermission "<>", "read";
      + permission java.io.FilePermission "<>", "write";
      + permission java.lang.RuntimePermission "accessClassInPackage.sun.util.locale.provider";
      +};
      diff --git a/test/jdk/java/awt/Dialog/PrintToFileTest/revoked b/test/jdk/java/awt/Dialog/PrintToFileTest/revoked
      new file mode 100644
      index 00000000000..d2545e15e11
      --- /dev/null
      +++ b/test/jdk/java/awt/Dialog/PrintToFileTest/revoked
      @@ -0,0 +1,9 @@
      +/* AUTOMATICALLY GENERATED ON Thu Jan 03 15:48:39 PST 2002*/
      +/* DO NOT EDIT */
      +
      +grant {
      + permission java.lang.RuntimePermission "queuePrintJob";
      + permission java.util.PropertyPermission "*", "read";
      + permission java.lang.RuntimePermission "accessClassInPackage.sun.util.locale.provider";
      +};
      +
      diff --git a/test/jdk/java/awt/Dialog/TopmostModalDialogTest.java b/test/jdk/java/awt/Dialog/TopmostModalDialogTest.java
      new file mode 100644
      index 00000000000..7b91d47e248
      --- /dev/null
      +++ b/test/jdk/java/awt/Dialog/TopmostModalDialogTest.java
      @@ -0,0 +1,152 @@
      +/*
      + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
      + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      + *
      + * This code is free software; you can redistribute it and/or modify it
      + * under the terms of the GNU General Public License version 2 only, as
      + * published by the Free Software Foundation.
      + *
      + * This code is distributed in the hope that it will be useful, but WITHOUT
      + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      + * version 2 for more details (a copy is included in the LICENSE file that
      + * accompanied this code).
      + *
      + * You should have received a copy of the GNU General Public License version
      + * 2 along with this work; if not, write to the Free Software Foundation,
      + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      + *
      + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      + * or visit www.oracle.com if you need additional information or have any
      + * questions.
      + */
      +
      +import java.awt.Button;
      +import java.awt.Dialog;
      +import java.awt.Frame;
      +import java.awt.GridLayout;
      +import java.awt.Window;
      +import java.awt.event.ActionEvent;
      +import java.awt.event.ActionListener;
      +import java.awt.event.WindowAdapter;
      +import java.awt.event.WindowEvent;
      +
      +/*
      + * @test
      + * @bug 4940645
      + * @summary Test to verify setAlwaysOnTop(true) does
      + *          work in modal dialog in Windows
      + * @requires (os.family == "windows" | os.family == "linux" )
      + * @library /java/awt/regtesthelpers
      + * @build PassFailJFrame
      + * @run main/manual TopmostModalDialogTest
      + */
      +
      +public class TopmostModalDialogTest {
      +    public static void main(String[] args) throws Exception {
      +        String INSTRUCTIONS = """
      +                (This test verifies that modal dialog can be made always on top
      +                This test should only be run on the platforms which support always-on-top windows
      +                Such platforms are: Windows, Linux with GNOME2/Metacity window manager,
      +                Solaris with GNOME2/Metacity window manager
      +                If you are not running on any of these platforms, please select 'Pass' to skip testing
      +                If you are unsure on which platform you are running please select 'Pass')
      +
      +                1. After test started you see a frame with \\"Main Frame\\" title
      +                   It contains three buttons. Every button starts one of test stage
      +                   You should test all three stages
      +                2. After you press button to start the stage. It shows modal dialog
      +                   This modal dialog should be always-on-top window
      +                3. Since it's a modal the only way to test this is try to cover it
      +                   using some native window
      +                4. If you will able to cover it be native window - test FAILS, otherwise - PASS
      +
      +                Note: in stages #2 and #3 dialog is initially shown as regular modal dialogs
      +                You will see \\"Let's wait\\" message in the message area below
      +                Please wait until message \\"Let's make it topmost\\" will be printed in the area
      +                After that you can continue testing.
      +                """;
      +        PassFailJFrame.builder()
      +                .title("Test Instructions")
      +                .instructions(INSTRUCTIONS)
      +                .rows((int) INSTRUCTIONS.lines().count() + 2)
      +                .columns(35)
      +                .testUI(initialize())
      +                .logArea(8)
      +                .build()
      +                .awaitAndCheck();
      +    }
      +
      +    public static Frame initialize() {
      +        final Tester tester = new Tester();
      +        Frame frame = new Frame("Main Frame");
      +        frame.setLayout(new GridLayout(3, 1));
      +        for (int i = 0; i < 3; i++) {
      +            Button btn = new Button("Stage #" + i);
      +            frame.add(btn);
      +            btn.addActionListener(tester);
      +        }
      +        frame.pack();
      +        return frame;
      +    }
      +}
      +
      +class Tester implements ActionListener {
      +    public void actionPerformed(ActionEvent e) {
      +        String command = e.getActionCommand();
      +        PassFailJFrame.log(command);
      +        int cmd = Integer.parseInt(command.substring(command.length() - 1));
      +        PassFailJFrame.log("" + cmd);
      +        Dialog dlg = new Dialog(new Frame(""), "Modal Dialog", true);
      +        dlg.setBounds(100, 100, 100, 100);
      +        dlg.addWindowListener(new WindowAdapter() {
      +            public void windowClosing(WindowEvent we) {
      +                Window self = we.getWindow();
      +                Window owner = self.getOwner();
      +                if (owner != null) {
      +                    owner.dispose();
      +                } else {
      +                    self.dispose();
      +                }
      +            }
      +        });
      +
      +        switch (cmd) {
      +            case 0:
      +                dlg.setAlwaysOnTop(true);
      +                dlg.setVisible(true);
      +                break;
      +            case 1:
      +                (new Thread(new TopmostMaker(dlg))).start();
      +                dlg.setVisible(true);
      +                break;
      +            case 2:
      +                dlg.setFocusableWindowState(false);
      +                (new Thread(new TopmostMaker(dlg))).start();
      +                dlg.setVisible(true);
      +                break;
      +            default:
      +                PassFailJFrame.log("Unsupported operation :(");
      +        }
      +    }
      +}
      +
      +class TopmostMaker implements Runnable {
      +    final Window wnd;
      +
      +    public TopmostMaker(Window wnd) {
      +        this.wnd = wnd;
      +    }
      +
      +    public void run() {
      +        PassFailJFrame.log("Let's wait");
      +        try {
      +            Thread.sleep(1000);
      +        } catch (InterruptedException ie) {
      +            PassFailJFrame.log("Test was interrupted. " + ie);
      +            ie.printStackTrace();
      +        }
      +        PassFailJFrame.log("Let's make it topmost");
      +        wnd.setAlwaysOnTop(true);
      +    }
      +}
      
      From fa39e84d64d79f6c66f98110e98d2562f35681e1 Mon Sep 17 00:00:00 2001
      From: Richard Reingruber 
      Date: Thu, 17 Oct 2024 07:19:54 +0000
      Subject: [PATCH 92/95] 8342042: PPC64: compiler_fast_unlock_object flags
       failure instead of success
      
      Reviewed-by: mdoerr, aboldtch, fbredberg
      ---
       src/hotspot/cpu/ppc/macroAssembler_ppc.cpp | 20 ++++++++------------
       1 file changed, 8 insertions(+), 12 deletions(-)
      
      diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
      index a194c030a61..0d7e202779f 100644
      --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
      +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
      @@ -2739,14 +2739,14 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe
         // Check if there is a successor.
         ld(temp, in_bytes(ObjectMonitor::succ_offset()), current_header);
         cmpdi(flag, temp, 0);
      -  bne(flag, success);  // If so we are done.
      +  // Invert equal bit
      +  crnand(flag, Assembler::equal, flag, Assembler::equal);
      +  beq(flag, success);  // If there is a successor we are done.
       
         // Save the monitor pointer in the current thread, so we can try
         // to reacquire the lock in SharedRuntime::monitor_exit_helper().
         std(current_header, in_bytes(JavaThread::unlocked_inflated_monitor_offset()), R16_thread);
      -
      -  crxor(flag, Assembler::equal, flag, Assembler::equal); // Set flag = NE => slow path
      -  b(failure);
      +  b(failure); // flag == NE
       
         // flag == EQ indicates success, decrement held monitor count
         // flag == NE indicates failure
      @@ -3053,7 +3053,6 @@ void MacroAssembler::compiler_fast_unlock_lightweight_object(ConditionRegister f
       
           bind(not_recursive);
       
      -    Label set_eq_unlocked;
           const Register t2 = tmp2;
       
           // Set owner to null.
      @@ -3075,17 +3074,14 @@ void MacroAssembler::compiler_fast_unlock_lightweight_object(ConditionRegister f
           // Check if there is a successor.
           ld(t, in_bytes(ObjectMonitor::succ_offset()), monitor);
           cmpdi(CCR0, t, 0);
      -    bne(CCR0, set_eq_unlocked); // If so we are done.
      +    // Invert equal bit
      +    crnand(flag, Assembler::equal, flag, Assembler::equal);
      +    beq(CCR0, unlocked); // If there is a successor we are done.
       
           // Save the monitor pointer in the current thread, so we can try
           // to reacquire the lock in SharedRuntime::monitor_exit_helper().
           std(monitor, in_bytes(JavaThread::unlocked_inflated_monitor_offset()), R16_thread);
      -
      -    crxor(CCR0, Assembler::equal, CCR0, Assembler::equal); // Set flag = NE => slow path
      -    b(slow_path);
      -
      -    bind(set_eq_unlocked);
      -    crorc(CCR0, Assembler::equal, CCR0, Assembler::equal); // Set flag = EQ => fast path
      +    b(slow_path); // flag == NE
         }
       
         bind(unlocked);
      
      From f9208fadde8141e18a025ddb6ce28423861ba391 Mon Sep 17 00:00:00 2001
      From: Richard Reingruber 
      Date: Thu, 17 Oct 2024 07:21:42 +0000
      Subject: [PATCH 93/95] 8341715: PPC64: ObjectMonitor::_owner should be reset
       unconditionally in nmethod unlocking
      
      Reviewed-by: mdoerr, lucy
      ---
       src/hotspot/cpu/ppc/macroAssembler_ppc.cpp | 33 +++++++++++++++++-----
       1 file changed, 26 insertions(+), 7 deletions(-)
      
      diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
      index 0d7e202779f..f036caa0675 100644
      --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
      +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
      @@ -2651,7 +2651,19 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register
         // flag == NE indicates failure
         bind(success);
         inc_held_monitor_count(temp);
      +#ifdef ASSERT
      +  // Check that unlocked label is reached with flag == EQ.
      +  Label flag_correct;
      +  beq(flag, flag_correct);
      +  stop("compiler_fast_lock_object: Flag != EQ");
      +#endif
         bind(failure);
      +#ifdef ASSERT
      +  // Check that slow_path label is reached with flag == NE.
      +  bne(flag, flag_correct);
      +  stop("compiler_fast_lock_object: Flag != NE");
      +  bind(flag_correct);
      +#endif
       }
       
       void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Register oop, Register box,
      @@ -2701,17 +2713,12 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe
         bind(object_has_monitor);
         STATIC_ASSERT(markWord::monitor_value <= INT_MAX);
         addi(current_header, current_header, -(int)markWord::monitor_value); // monitor
      -  ld(temp,             in_bytes(ObjectMonitor::owner_offset()), current_header);
      -
      -  // In case of LM_LIGHTWEIGHT, we may reach here with (temp & ObjectMonitor::ANONYMOUS_OWNER) != 0.
      -  // This is handled like owner thread mismatches: We take the slow path.
      -  cmpd(flag, temp, R16_thread);
      -  bne(flag, failure);
       
         ld(displaced_header, in_bytes(ObjectMonitor::recursions_offset()), current_header);
      -
         addic_(displaced_header, displaced_header, -1);
         blt(CCR0, notRecursive); // Not recursive if negative after decrement.
      +
      +  // Recursive unlock
         std(displaced_header, in_bytes(ObjectMonitor::recursions_offset()), current_header);
         if (flag == CCR0) { // Otherwise, flag is already EQ, here.
           crorc(CCR0, Assembler::equal, CCR0, Assembler::equal); // Set CCR0 EQ
      @@ -2752,7 +2759,19 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe
         // flag == NE indicates failure
         bind(success);
         dec_held_monitor_count(temp);
      +#ifdef ASSERT
      +  // Check that unlocked label is reached with flag == EQ.
      +  Label flag_correct;
      +  beq(flag, flag_correct);
      +  stop("compiler_fast_unlock_object: Flag != EQ");
      +#endif
         bind(failure);
      +#ifdef ASSERT
      +  // Check that slow_path label is reached with flag == NE.
      +  bne(flag, flag_correct);
      +  stop("compiler_fast_unlock_object: Flag != NE");
      +  bind(flag_correct);
      +#endif
       }
       
       void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister flag, Register obj, Register box,
      
      From 8862ca076f9be1c0b5f4bc2639ab9c1f60de308c Mon Sep 17 00:00:00 2001
      From: Kevin Walls 
      Date: Thu, 17 Oct 2024 08:01:14 +0000
      Subject: [PATCH 94/95] 8342338: Remove redundant IIOPURLTest.java
      
      Reviewed-by: cjplummer, amenkov
      ---
       .../mandatory/connection/IIOPURLTest.java     | 79 -------------------
       1 file changed, 79 deletions(-)
       delete mode 100644 test/jdk/javax/management/remote/mandatory/connection/IIOPURLTest.java
      
      diff --git a/test/jdk/javax/management/remote/mandatory/connection/IIOPURLTest.java b/test/jdk/javax/management/remote/mandatory/connection/IIOPURLTest.java
      deleted file mode 100644
      index cdbf829a9b2..00000000000
      --- a/test/jdk/javax/management/remote/mandatory/connection/IIOPURLTest.java
      +++ /dev/null
      @@ -1,79 +0,0 @@
      -/*
      - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
      - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      - *
      - * This code is free software; you can redistribute it and/or modify it
      - * under the terms of the GNU General Public License version 2 only, as
      - * published by the Free Software Foundation.
      - *
      - * This code is distributed in the hope that it will be useful, but WITHOUT
      - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      - * version 2 for more details (a copy is included in the LICENSE file that
      - * accompanied this code).
      - *
      - * You should have received a copy of the GNU General Public License version
      - * 2 along with this work; if not, write to the Free Software Foundation,
      - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      - *
      - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      - * or visit www.oracle.com if you need additional information or have any
      - * questions.
      - */
      -
      -/*
      - * @test
      - * @bug 4886799
      - * @summary Check that IIOP URLs have /ior/ in the path
      - * @author Eamonn McManus
      - *
      - * @run clean IIOPURLTest
      - * @run build IIOPURLTest
      - * @run main IIOPURLTest
      - */
      -
      -import javax.management.MBeanServer;
      -import javax.management.MBeanServerConnection;
      -import javax.management.MBeanServerFactory;
      -import javax.management.Notification;
      -import javax.management.NotificationListener;
      -import javax.management.ObjectName;
      -
      -import javax.management.remote.JMXConnector;
      -import javax.management.remote.JMXConnectorFactory;
      -import javax.management.remote.JMXConnectorServer;
      -import javax.management.remote.JMXConnectorServerFactory;
      -import javax.management.remote.JMXServiceURL;
      -
      -public class IIOPURLTest {
      -
      -    public static void main(String[] args) throws Exception {
      -        JMXServiceURL inputAddr =
      -            new JMXServiceURL("service:jmx:iiop://");
      -        JMXConnectorServer s;
      -        try {
      -            s = JMXConnectorServerFactory.newJMXConnectorServer(inputAddr, null, null);
      -        } catch (java.net.MalformedURLException x) {
      -            try {
      -                Class.forName("javax.management.remote.rmi._RMIConnectionImpl_Tie");
      -                throw new RuntimeException("MalformedURLException thrown but iiop appears to be supported");
      -            } catch (ClassNotFoundException expected) { }
      -            System.out.println("IIOP protocol not supported, test skipped");
      -            return;
      -        }
      -        MBeanServer mbs = MBeanServerFactory.createMBeanServer();
      -        mbs.registerMBean(s, new ObjectName("a:b=c"));
      -        s.start();
      -        JMXServiceURL outputAddr = s.getAddress();
      -        if (!outputAddr.getURLPath().startsWith("/ior/IOR:")) {
      -            throw new RuntimeException("URL path should start with \"/ior/IOR:\": " +
      -                                       outputAddr);
      -        }
      -        System.out.println("IIOP URL path looks OK: " + outputAddr);
      -        JMXConnector c = JMXConnectorFactory.connect(outputAddr);
      -        System.out.println("Successfully got default domain: " +
      -                           c.getMBeanServerConnection().getDefaultDomain());
      -        c.close();
      -        s.stop();
      -    }
      -}
      
      From 7a64fbbb9292f4d65a6970206dec1a7d7645046b Mon Sep 17 00:00:00 2001
      From: Simon Tooke 
      Date: Thu, 17 Oct 2024 08:06:37 +0000
      Subject: [PATCH 95/95] 8338851: Hoist os::Posix::realpath() to os::realpath()
       and implement on Windows
      
      Reviewed-by: dholmes, stuefe, jwaters
      ---
       src/hotspot/os/aix/os_aix.cpp                 |  6 +-
       src/hotspot/os/bsd/os_bsd.cpp                 |  6 +-
       src/hotspot/os/linux/os_linux.cpp             |  6 +-
       src/hotspot/os/linux/os_perf_linux.cpp        |  2 +-
       src/hotspot/os/posix/os_posix.cpp             |  5 +-
       src/hotspot/os/posix/os_posix.hpp             |  7 --
       src/hotspot/os/windows/os_windows.cpp         | 22 +++++
       src/hotspot/share/runtime/os.hpp              |  7 ++
       .../share/services/diagnosticCommand.cpp      |  4 +-
       .../share/utilities/globalDefinitions.hpp     |  2 +-
       test/hotspot/gtest/runtime/test_os.cpp        | 80 +++++++++++++++++++
       11 files changed, 123 insertions(+), 24 deletions(-)
      
      diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp
      index 63aa53f0a23..5022272d1b4 100644
      --- a/src/hotspot/os/aix/os_aix.cpp
      +++ b/src/hotspot/os/aix/os_aix.cpp
      @@ -1293,7 +1293,7 @@ void os::jvm_path(char *buf, jint buflen) {
         Dl_info dlinfo;
         int ret = dladdr(CAST_FROM_FN_PTR(void *, os::jvm_path), &dlinfo);
         assert(ret != 0, "cannot locate libjvm");
      -  char* rp = os::Posix::realpath((char *)dlinfo.dli_fname, buf, buflen);
      +  char* rp = os::realpath((char *)dlinfo.dli_fname, buf, buflen);
         assert(rp != nullptr, "error in realpath(): maybe the 'path' argument is too long?");
       
         if (Arguments::sun_java_launcher_is_altjvm()) {
      @@ -1324,7 +1324,7 @@ void os::jvm_path(char *buf, jint buflen) {
               }
               assert(strstr(p, "/libjvm") == p, "invalid library name");
       
      -        rp = os::Posix::realpath(java_home_var, buf, buflen);
      +        rp = os::realpath(java_home_var, buf, buflen);
               if (rp == nullptr) {
                 return;
               }
      @@ -1345,7 +1345,7 @@ void os::jvm_path(char *buf, jint buflen) {
                 snprintf(buf + len, buflen-len, "/hotspot/libjvm.so");
               } else {
                 // Go back to path of .so
      -          rp = os::Posix::realpath((char *)dlinfo.dli_fname, buf, buflen);
      +          rp = os::realpath((char *)dlinfo.dli_fname, buf, buflen);
                 if (rp == nullptr) {
                   return;
                 }
      diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp
      index 18818268c1f..6b3bf060d6f 100644
      --- a/src/hotspot/os/bsd/os_bsd.cpp
      +++ b/src/hotspot/os/bsd/os_bsd.cpp
      @@ -1509,7 +1509,7 @@ void os::jvm_path(char *buf, jint buflen) {
         assert(ret, "cannot locate libjvm");
         char *rp = nullptr;
         if (ret && dli_fname[0] != '\0') {
      -    rp = os::Posix::realpath(dli_fname, buf, buflen);
      +    rp = os::realpath(dli_fname, buf, buflen);
         }
         if (rp == nullptr) {
           return;
      @@ -1541,7 +1541,7 @@ void os::jvm_path(char *buf, jint buflen) {
               p = strrchr(buf, '/');
               assert(strstr(p, "/libjvm") == p, "invalid library name");
       
      -        rp = os::Posix::realpath(java_home_var, buf, buflen);
      +        rp = os::realpath(java_home_var, buf, buflen);
               if (rp == nullptr) {
                 return;
               }
      @@ -1575,7 +1575,7 @@ void os::jvm_path(char *buf, jint buflen) {
                 snprintf(buf + len, buflen-len, "/libjvm%s", JNI_LIB_SUFFIX);
               } else {
                 // Fall back to path of current library
      -          rp = os::Posix::realpath(dli_fname, buf, buflen);
      +          rp = os::realpath(dli_fname, buf, buflen);
                 if (rp == nullptr) {
                   return;
                 }
      diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp
      index c80663fec3d..514268a7400 100644
      --- a/src/hotspot/os/linux/os_linux.cpp
      +++ b/src/hotspot/os/linux/os_linux.cpp
      @@ -2763,7 +2763,7 @@ void os::jvm_path(char *buf, jint buflen) {
         assert(ret, "cannot locate libjvm");
         char *rp = nullptr;
         if (ret && dli_fname[0] != '\0') {
      -    rp = os::Posix::realpath(dli_fname, buf, buflen);
      +    rp = os::realpath(dli_fname, buf, buflen);
         }
         if (rp == nullptr) {
           return;
      @@ -2797,7 +2797,7 @@ void os::jvm_path(char *buf, jint buflen) {
               }
               assert(strstr(p, "/libjvm") == p, "invalid library name");
       
      -        rp = os::Posix::realpath(java_home_var, buf, buflen);
      +        rp = os::realpath(java_home_var, buf, buflen);
               if (rp == nullptr) {
                 return;
               }
      @@ -2818,7 +2818,7 @@ void os::jvm_path(char *buf, jint buflen) {
                 snprintf(buf + len, buflen-len, "/hotspot/libjvm.so");
               } else {
                 // Go back to path of .so
      -          rp = os::Posix::realpath(dli_fname, buf, buflen);
      +          rp = os::realpath(dli_fname, buf, buflen);
                 if (rp == nullptr) {
                   return;
                 }
      diff --git a/src/hotspot/os/linux/os_perf_linux.cpp b/src/hotspot/os/linux/os_perf_linux.cpp
      index a31a58e55af..996f83611b0 100644
      --- a/src/hotspot/os/linux/os_perf_linux.cpp
      +++ b/src/hotspot/os/linux/os_perf_linux.cpp
      @@ -789,7 +789,7 @@ char* SystemProcessInterface::SystemProcesses::ProcessIterator::get_exe_path() {
       
         jio_snprintf(buffer, PATH_MAX, "/proc/%s/exe", _entry->d_name);
         buffer[PATH_MAX - 1] = '\0';
      -  return os::Posix::realpath(buffer, _exePath, PATH_MAX);
      +  return os::realpath(buffer, _exePath, PATH_MAX);
       }
       
       char* SystemProcessInterface::SystemProcesses::ProcessIterator::allocate_string(const char* str) const {
      diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp
      index 78fd2a67888..75253843593 100644
      --- a/src/hotspot/os/posix/os_posix.cpp
      +++ b/src/hotspot/os/posix/os_posix.cpp
      @@ -1029,10 +1029,10 @@ char* os::Posix::describe_pthread_attr(char* buf, size_t buflen, const pthread_a
         return buf;
       }
       
      -char* os::Posix::realpath(const char* filename, char* outbuf, size_t outbuflen) {
      +char* os::realpath(const char* filename, char* outbuf, size_t outbuflen) {
       
         if (filename == nullptr || outbuf == nullptr || outbuflen < 1) {
      -    assert(false, "os::Posix::realpath: invalid arguments.");
      +    assert(false, "os::realpath: invalid arguments.");
           errno = EINVAL;
           return nullptr;
         }
      @@ -1067,7 +1067,6 @@ char* os::Posix::realpath(const char* filename, char* outbuf, size_t outbuflen)
           }
         }
         return result;
      -
       }
       
       int os::stat(const char *path, struct stat *sbuf) {
      diff --git a/src/hotspot/os/posix/os_posix.hpp b/src/hotspot/os/posix/os_posix.hpp
      index d872a6dae89..cac5b250cdf 100644
      --- a/src/hotspot/os/posix/os_posix.hpp
      +++ b/src/hotspot/os/posix/os_posix.hpp
      @@ -73,13 +73,6 @@ class os::Posix {
         // to buf with len buflen; buf is returned.
         static char* describe_pthread_attr(char* buf, size_t buflen, const pthread_attr_t* attr);
       
      -  // A safe implementation of realpath which will not cause a buffer overflow if the resolved path
      -  //   is longer than PATH_MAX.
      -  // On success, returns 'outbuf', which now contains the path.
      -  // On error, it will return null and set errno. The content of 'outbuf' is undefined.
      -  // On truncation error ('outbuf' too small), it will return null and set errno to ENAMETOOLONG.
      -  static char* realpath(const char* filename, char* outbuf, size_t outbuflen);
      -
         // Returns true if given uid is root.
         static bool is_root(uid_t uid);
       
      diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp
      index fe093aee116..66a6a92f5e0 100644
      --- a/src/hotspot/os/windows/os_windows.cpp
      +++ b/src/hotspot/os/windows/os_windows.cpp
      @@ -5397,6 +5397,28 @@ void os::funlockfile(FILE* fp) {
         _unlock_file(fp);
       }
       
      +char* os::realpath(const char* filename, char* outbuf, size_t outbuflen) {
      +
      +  if (filename == nullptr || outbuf == nullptr || outbuflen < 1) {
      +    assert(false, "os::realpath: invalid arguments.");
      +    errno = EINVAL;
      +    return nullptr;
      +  }
      +
      +  char* result = nullptr;
      +  ALLOW_C_FUNCTION(::_fullpath, char* p = ::_fullpath(nullptr, filename, 0);)
      +  if (p != nullptr) {
      +    if (strlen(p) < outbuflen) {
      +      strcpy(outbuf, p);
      +      result = outbuf;
      +    } else {
      +      errno = ENAMETOOLONG;
      +    }
      +    ALLOW_C_FUNCTION(::free, ::free(p);) // *not* os::free
      +  }
      +  return result;
      +}
      +
       // Map a block of memory.
       char* os::pd_map_memory(int fd, const char* file_name, size_t file_offset,
                               char *addr, size_t bytes, bool read_only,
      diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp
      index 65071769bc4..323b8d2df8d 100644
      --- a/src/hotspot/share/runtime/os.hpp
      +++ b/src/hotspot/share/runtime/os.hpp
      @@ -668,6 +668,13 @@ class os: AllStatic {
         static void flockfile(FILE* fp);
         static void funlockfile(FILE* fp);
       
      +  // A safe implementation of realpath which will not cause a buffer overflow if the resolved path
      +  // is longer than PATH_MAX.
      +  // On success, returns 'outbuf', which now contains the path.
      +  // On error, it will return null and set errno. The content of 'outbuf' is undefined.
      +  // On truncation error ('outbuf' too small), it will return null and set errno to ENAMETOOLONG.
      +  static char* realpath(const char* filename, char* outbuf, size_t outbuflen);
      +
         static int compare_file_modified_times(const char* file1, const char* file2);
       
         static bool same_files(const char* file1, const char* file2);
      diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp
      index 78c655a43fc..4263d2a1d4b 100644
      --- a/src/hotspot/share/services/diagnosticCommand.cpp
      +++ b/src/hotspot/share/services/diagnosticCommand.cpp
      @@ -1202,12 +1202,10 @@ void SystemDumpMapDCmd::execute(DCmdSource source, TRAPS) {
             output()->print_cr("(NMT is disabled, will not annotate mappings).");
           }
           MemMapPrinter::print_all_mappings(&fs);
      -#ifndef _WIN64
           // For the readers convenience, resolve path name.
           char tmp[JVM_MAXPATHLEN];
      -    const char* absname = os::Posix::realpath(name, tmp, sizeof(tmp));
      +    const char* absname = os::realpath(name, tmp, sizeof(tmp));
           name = absname != nullptr ? absname : name;
      -#endif
           output()->print_cr("Memory map dumped to \"%s\".", name);
         } else {
           output()->print_cr("Failed to open \"%s\" for writing (%s).", name, os::strerror(errno));
      diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp
      index 6d385165b8b..25af626c628 100644
      --- a/src/hotspot/share/utilities/globalDefinitions.hpp
      +++ b/src/hotspot/share/utilities/globalDefinitions.hpp
      @@ -210,7 +210,7 @@ FORBID_C_FUNCTION(char* strdup(const char *s), "use os::strdup");
       FORBID_C_FUNCTION(char* strndup(const char *s, size_t n), "don't use");
       FORBID_C_FUNCTION(int posix_memalign(void **memptr, size_t alignment, size_t size), "don't use");
       FORBID_C_FUNCTION(void* aligned_alloc(size_t alignment, size_t size), "don't use");
      -FORBID_C_FUNCTION(char* realpath(const char* path, char* resolved_path), "use os::Posix::realpath");
      +FORBID_C_FUNCTION(char* realpath(const char* path, char* resolved_path), "use os::realpath");
       FORBID_C_FUNCTION(char* get_current_dir_name(void), "use os::get_current_directory()");
       FORBID_C_FUNCTION(char* getwd(char *buf), "use os::get_current_directory()");
       FORBID_C_FUNCTION(wchar_t* wcsdup(const wchar_t *s), "don't use");
      diff --git a/test/hotspot/gtest/runtime/test_os.cpp b/test/hotspot/gtest/runtime/test_os.cpp
      index 78e5212ab37..fe3c634056c 100644
      --- a/test/hotspot/gtest/runtime/test_os.cpp
      +++ b/test/hotspot/gtest/runtime/test_os.cpp
      @@ -405,6 +405,86 @@ TEST_VM(os, jio_snprintf) {
         test_snprintf(jio_snprintf, false);
       }
       
      +#ifndef MAX_PATH
      +#define MAX_PATH    (2 * K)
      +#endif
      +
      +TEST_VM(os, realpath) {
      +  // POSIX requires that the file exists; Windows tests for a valid drive letter
      +  // but may or may not test if the file exists. */
      +  static const char* nosuchpath = "/1234567890123456789";
      +  static const char* tmppath = "/tmp";
      +
      +  char buffer[MAX_PATH];
      +
      +  // Test a non-existant path, but provide a short buffer.
      +  errno = 0;
      +  const char* returnedBuffer = os::realpath(nosuchpath, buffer, sizeof(nosuchpath) - 2);
      +  // Reports ENOENT on Linux, ENAMETOOLONG on Windows.
      +  EXPECT_TRUE(returnedBuffer == nullptr);
      +#ifdef _WINDOWS
      +  EXPECT_TRUE(errno == ENAMETOOLONG);
      +#else
      +  EXPECT_TRUE(errno == ENOENT);
      +#endif
      +
      +  // Test a non-existant path, but provide an adequate buffer.
      +  errno = 0;
      +  buffer[0] = 0;
      +  returnedBuffer = os::realpath(nosuchpath, buffer, sizeof(nosuchpath) + 3);
      +  // Reports ENOENT on Linux, may return 0 (and report an error) or buffer on some versions of Windows.
      +#ifdef _WINDOWS
      +  if (returnedBuffer != nullptr) {
      +    EXPECT_TRUE(returnedBuffer == buffer);
      +  } else {
      +    EXPECT_TRUE(errno != 0);
      +  }
      +#else
      +  EXPECT_TRUE(returnedBuffer == nullptr);
      +  EXPECT_TRUE(errno == ENOENT);
      +#endif
      +
      +  // Test an existing path using a large buffer.
      +  errno = 0;
      +  returnedBuffer = os::realpath(tmppath, buffer, MAX_PATH);
      +  EXPECT_TRUE(returnedBuffer == buffer);
      +
      +  // Test an existing path using a buffer that is too small on a normal macOS install.
      +  errno = 0;
      +  returnedBuffer = os::realpath(tmppath, buffer, strlen(tmppath) + 3);
      +  // On MacOS, /tmp is a symlink to /private/tmp, so doesn't fit in a small buffer.
      +#ifndef __APPLE__
      +  EXPECT_TRUE(returnedBuffer == buffer);
      +#else
      +  EXPECT_TRUE(returnedBuffer == nullptr);
      +  EXPECT_TRUE(errno == ENAMETOOLONG);
      +#endif
      +
      +  // Test an existing path using a buffer that is too small.
      +  errno = 0;
      +  returnedBuffer = os::realpath(tmppath, buffer, strlen(tmppath) - 1);
      +  EXPECT_TRUE(returnedBuffer == nullptr);
      +  EXPECT_TRUE(errno == ENAMETOOLONG);
      +
      +  // The following tests cause an assert inside os::realpath() in fastdebug mode:
      +#ifndef ASSERT
      +  errno = 0;
      +  returnedBuffer = os::realpath(nullptr, buffer, sizeof(buffer));
      +  EXPECT_TRUE(returnedBuffer == nullptr);
      +  EXPECT_TRUE(errno == EINVAL);
      +
      +  errno = 0;
      +  returnedBuffer = os::realpath(tmppath, nullptr, sizeof(buffer));
      +  EXPECT_TRUE(returnedBuffer == nullptr);
      +  EXPECT_TRUE(errno == EINVAL);
      +
      +  errno = 0;
      +  returnedBuffer = os::realpath(tmppath, buffer, 0);
      +  EXPECT_TRUE(returnedBuffer == nullptr);
      +  EXPECT_TRUE(errno == EINVAL);
      +#endif
      +}
      +
       #ifdef __APPLE__
       // Not all macOS versions can use os::reserve_memory (i.e. anon_mmap) API
       // to reserve executable memory, so before attempting to use it,