This commit is contained in:
Jesper Wilhelmsson 2022-07-08 02:07:36 +00:00
commit 01b9f95c62
28 changed files with 755 additions and 138 deletions

View file

@ -723,7 +723,7 @@ void MacroAssembler::emit_static_call_stub() {
isb(); isb();
mov_metadata(rmethod, (Metadata*)NULL); mov_metadata(rmethod, (Metadata*)NULL);
// Jump to the entry point of the i2c stub. // Jump to the entry point of the c2i stub.
movptr(rscratch1, 0); movptr(rscratch1, 0);
br(rscratch1); br(rscratch1);
} }

View file

@ -1013,22 +1013,65 @@ static void gen_continuation_enter(MacroAssembler* masm,
int& exception_offset, int& exception_offset,
OopMapSet*oop_maps, OopMapSet*oop_maps,
int& frame_complete, int& frame_complete,
int& stack_slots) { int& stack_slots,
int& interpreted_entry_offset,
int& compiled_entry_offset) {
//verify_oop_args(masm, method, sig_bt, regs); //verify_oop_args(masm, method, sig_bt, regs);
Address resolve(SharedRuntime::get_resolve_static_call_stub(), Address resolve(SharedRuntime::get_resolve_static_call_stub(), relocInfo::static_call_type);
relocInfo::static_call_type);
stack_slots = 2; // will be overwritten
address start = __ pc(); address start = __ pc();
Label call_thaw, exit; Label call_thaw, exit;
// i2i entry used at interp_only_mode only
interpreted_entry_offset = __ pc() - start;
{
#ifdef ASSERT
Label is_interp_only;
__ ldrw(rscratch1, Address(rthread, JavaThread::interp_only_mode_offset()));
__ cbnzw(rscratch1, is_interp_only);
__ stop("enterSpecial interpreter entry called when not in interp_only_mode");
__ bind(is_interp_only);
#endif
// Read interpreter arguments into registers (this is an ad-hoc i2c adapter)
__ ldr(c_rarg1, Address(esp, Interpreter::stackElementSize*2));
__ ldr(c_rarg2, Address(esp, Interpreter::stackElementSize*1));
__ ldr(c_rarg3, Address(esp, Interpreter::stackElementSize*0));
__ push_cont_fastpath(rthread);
__ enter();
stack_slots = 2; // will be adjusted in setup
OopMap* map = continuation_enter_setup(masm, stack_slots);
// The frame is complete here, but we only record it for the compiled entry, so the frame would appear unsafe,
// but that's okay because at the very worst we'll miss an async sample, but we're in interp_only_mode anyway.
fill_continuation_entry(masm);
__ cmp(c_rarg2, (u1)0);
__ br(Assembler::NE, call_thaw);
address mark = __ pc();
__ trampoline_call1(resolve, NULL, false);
oop_maps->add_gc_map(__ pc() - start, map);
__ post_call_nop();
__ b(exit);
CodeBuffer* cbuf = masm->code_section()->outer();
CompiledStaticCall::emit_to_interp_stub(*cbuf, mark);
}
// compiled entry
__ align(CodeEntryAlignment);
compiled_entry_offset = __ pc() - start;
__ enter(); __ enter();
stack_slots = 2; // will be adjusted in setup
OopMap* map = continuation_enter_setup(masm, stack_slots); OopMap* map = continuation_enter_setup(masm, stack_slots);
frame_complete = __ pc() - start;
// Frame is now completed as far as size and linkage.
frame_complete =__ pc() - start;
fill_continuation_entry(masm); fill_continuation_entry(masm);
@ -1036,7 +1079,6 @@ static void gen_continuation_enter(MacroAssembler* masm,
__ br(Assembler::NE, call_thaw); __ br(Assembler::NE, call_thaw);
address mark = __ pc(); address mark = __ pc();
__ trampoline_call1(resolve, NULL, false); __ trampoline_call1(resolve, NULL, false);
oop_maps->add_gc_map(__ pc() - start, map); oop_maps->add_gc_map(__ pc() - start, map);
@ -1079,7 +1121,7 @@ static void gen_continuation_enter(MacroAssembler* masm,
} }
CodeBuffer* cbuf = masm->code_section()->outer(); CodeBuffer* cbuf = masm->code_section()->outer();
address stub = CompiledStaticCall::emit_to_interp_stub(*cbuf, mark); CompiledStaticCall::emit_to_interp_stub(*cbuf, mark);
} }
static void gen_special_dispatch(MacroAssembler* masm, static void gen_special_dispatch(MacroAssembler* masm,
@ -1169,11 +1211,12 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
if (method->is_continuation_enter_intrinsic()) { if (method->is_continuation_enter_intrinsic()) {
vmIntrinsics::ID iid = method->intrinsic_id(); vmIntrinsics::ID iid = method->intrinsic_id();
intptr_t start = (intptr_t)__ pc(); intptr_t start = (intptr_t)__ pc();
int vep_offset = ((intptr_t)__ pc()) - start; int vep_offset = 0;
int exception_offset = 0; int exception_offset = 0;
int frame_complete = 0; int frame_complete = 0;
int stack_slots = 0; int stack_slots = 0;
OopMapSet* oop_maps = new OopMapSet(); OopMapSet* oop_maps = new OopMapSet();
int interpreted_entry_offset = -1;
gen_continuation_enter(masm, gen_continuation_enter(masm,
method, method,
in_sig_bt, in_sig_bt,
@ -1181,7 +1224,9 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
exception_offset, exception_offset,
oop_maps, oop_maps,
frame_complete, frame_complete,
stack_slots); stack_slots,
interpreted_entry_offset,
vep_offset);
__ flush(); __ flush();
nmethod* nm = nmethod::new_native_nmethod(method, nmethod* nm = nmethod::new_native_nmethod(method,
compile_id, compile_id,
@ -1193,7 +1238,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
in_ByteSize(-1), in_ByteSize(-1),
oop_maps, oop_maps,
exception_offset); exception_offset);
ContinuationEntry::set_enter_code(nm); ContinuationEntry::set_enter_code(nm, interpreted_entry_offset);
return nm; return nm;
} }

View file

@ -1276,7 +1276,9 @@ static void gen_continuation_enter(MacroAssembler* masm,
int& exception_offset, int& exception_offset,
OopMapSet* oop_maps, OopMapSet* oop_maps,
int& frame_complete, int& frame_complete,
int& stack_slots) { int& stack_slots,
int& interpreted_entry_offset,
int& compiled_entry_offset) {
// enterSpecial(Continuation c, boolean isContinue, boolean isVirtualThread) // enterSpecial(Continuation c, boolean isContinue, boolean isVirtualThread)
int pos_cont_obj = 0; int pos_cont_obj = 0;
@ -1298,8 +1300,68 @@ static void gen_continuation_enter(MacroAssembler* masm,
// Utility methods kill rax, make sure there are no collisions // Utility methods kill rax, make sure there are no collisions
assert_different_registers(rax, reg_cont_obj, reg_is_cont, reg_is_virtual); assert_different_registers(rax, reg_cont_obj, reg_is_cont, reg_is_virtual);
AddressLiteral resolve(SharedRuntime::get_resolve_static_call_stub(),
relocInfo::static_call_type);
address start = __ pc(); address start = __ pc();
Label L_thaw, L_exit;
// i2i entry used at interp_only_mode only
interpreted_entry_offset = __ pc() - start;
{
#ifdef ASSERT
Label is_interp_only;
__ cmpb(Address(r15_thread, JavaThread::interp_only_mode_offset()), 0);
__ jcc(Assembler::notEqual, is_interp_only);
__ stop("enterSpecial interpreter entry called when not in interp_only_mode");
__ bind(is_interp_only);
#endif
__ pop(rax); // return address
// Read interpreter arguments into registers (this is an ad-hoc i2c adapter)
__ movptr(c_rarg1, Address(rsp, Interpreter::stackElementSize*2));
__ movl(c_rarg2, Address(rsp, Interpreter::stackElementSize*1));
__ movl(c_rarg3, Address(rsp, Interpreter::stackElementSize*0));
__ andptr(rsp, -16); // Ensure compiled code always sees stack at proper alignment
__ push(rax); // return address
__ push_cont_fastpath();
__ enter();
stack_slots = 2; // will be adjusted in setup
OopMap* map = continuation_enter_setup(masm, stack_slots);
// The frame is complete here, but we only record it for the compiled entry, so the frame would appear unsafe,
// but that's okay because at the very worst we'll miss an async sample, but we're in interp_only_mode anyway.
__ verify_oop(reg_cont_obj);
fill_continuation_entry(masm, reg_cont_obj, reg_is_virtual);
// If continuation, call to thaw. Otherwise, resolve the call and exit.
__ testptr(reg_is_cont, reg_is_cont);
__ jcc(Assembler::notZero, L_thaw);
// --- Resolve path
// Make sure the call is patchable
__ align(BytesPerWord, __ offset() + NativeCall::displacement_offset);
// Emit stub for static call
CodeBuffer* cbuf = masm->code_section()->outer();
address stub = CompiledStaticCall::emit_to_interp_stub(*cbuf, __ pc());
if (stub == nullptr) {
fatal("CodeCache is full at gen_continuation_enter");
}
__ call(resolve);
oop_maps->add_gc_map(__ pc() - start, map);
__ post_call_nop();
__ jmp(L_exit);
}
// compiled entry
__ align(CodeEntryAlignment);
compiled_entry_offset = __ pc() - start;
__ enter(); __ enter();
stack_slots = 2; // will be adjusted in setup stack_slots = 2; // will be adjusted in setup
@ -1312,8 +1374,6 @@ static void gen_continuation_enter(MacroAssembler* masm,
fill_continuation_entry(masm, reg_cont_obj, reg_is_virtual); fill_continuation_entry(masm, reg_cont_obj, reg_is_virtual);
Label L_thaw, L_exit;
// If isContinue, call to thaw. Otherwise, call Continuation.enter(Continuation c, boolean isContinue) // If isContinue, call to thaw. Otherwise, call Continuation.enter(Continuation c, boolean isContinue)
__ testptr(reg_is_cont, reg_is_cont); __ testptr(reg_is_cont, reg_is_cont);
__ jccb(Assembler::notZero, L_thaw); __ jccb(Assembler::notZero, L_thaw);
@ -1334,8 +1394,6 @@ static void gen_continuation_enter(MacroAssembler* masm,
// SharedRuntime::find_callee_info_helper() which calls // SharedRuntime::find_callee_info_helper() which calls
// LinkResolver::resolve_continuation_enter() which resolves the call to // LinkResolver::resolve_continuation_enter() which resolves the call to
// Continuation.enter(Continuation c, boolean isContinue). // Continuation.enter(Continuation c, boolean isContinue).
AddressLiteral resolve(SharedRuntime::get_resolve_static_call_stub(),
relocInfo::static_call_type);
__ call(resolve); __ call(resolve);
oop_maps->add_gc_map(__ pc() - start, map); oop_maps->add_gc_map(__ pc() - start, map);
@ -1474,17 +1532,20 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
if (method->is_continuation_enter_intrinsic()) { if (method->is_continuation_enter_intrinsic()) {
vmIntrinsics::ID iid = method->intrinsic_id(); vmIntrinsics::ID iid = method->intrinsic_id();
intptr_t start = (intptr_t)__ pc(); intptr_t start = (intptr_t)__ pc();
int vep_offset = ((intptr_t)__ pc()) - start; int vep_offset = 0;
int exception_offset = 0; int exception_offset = 0;
int frame_complete = 0; int frame_complete = 0;
int stack_slots = 0; int stack_slots = 0;
OopMapSet* oop_maps = new OopMapSet(); OopMapSet* oop_maps = new OopMapSet();
int interpreted_entry_offset = -1;
gen_continuation_enter(masm, gen_continuation_enter(masm,
in_regs, in_regs,
exception_offset, exception_offset,
oop_maps, oop_maps,
frame_complete, frame_complete,
stack_slots); stack_slots,
interpreted_entry_offset,
vep_offset);
__ flush(); __ flush();
nmethod* nm = nmethod::new_native_nmethod(method, nmethod* nm = nmethod::new_native_nmethod(method,
compile_id, compile_id,
@ -1496,7 +1557,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
in_ByteSize(-1), in_ByteSize(-1),
oop_maps, oop_maps,
exception_offset); exception_offset);
ContinuationEntry::set_enter_code(nm); ContinuationEntry::set_enter_code(nm, interpreted_entry_offset);
return nm; return nm;
} }

View file

@ -39,6 +39,7 @@
#include "oops/method.inline.hpp" #include "oops/method.inline.hpp"
#include "oops/oop.inline.hpp" #include "oops/oop.inline.hpp"
#include "oops/symbol.hpp" #include "oops/symbol.hpp"
#include "runtime/continuationEntry.hpp"
#include "runtime/handles.inline.hpp" #include "runtime/handles.inline.hpp"
#include "runtime/icache.hpp" #include "runtime/icache.hpp"
#include "runtime/safepoint.hpp" #include "runtime/safepoint.hpp"
@ -653,6 +654,13 @@ void CompiledStaticCall::compute_entry(const methodHandle& m, bool caller_is_nme
} }
} }
void CompiledStaticCall::compute_entry_for_continuation_entry(const methodHandle& m, StaticCallInfo& info) {
if (ContinuationEntry::is_interpreted_call(instruction_address())) {
info._to_interpreter = true;
info._entry = m()->get_c2i_entry();
}
}
address CompiledDirectStaticCall::find_stub_for(address instruction) { address CompiledDirectStaticCall::find_stub_for(address instruction) {
// Find reloc. information containing this call-site // Find reloc. information containing this call-site
RelocIterator iter((nmethod*)NULL, instruction); RelocIterator iter((nmethod*)NULL, instruction);

View file

@ -345,6 +345,7 @@ class CompiledStaticCall : public ResourceObj {
// Compute entry point given a method // Compute entry point given a method
static void compute_entry(const methodHandle& m, bool caller_is_nmethod, StaticCallInfo& info); static void compute_entry(const methodHandle& m, bool caller_is_nmethod, StaticCallInfo& info);
void compute_entry_for_continuation_entry(const methodHandle& m, StaticCallInfo& info);
public: public:
// Clean static call (will force resolving on next use) // Clean static call (will force resolving on next use)

View file

@ -61,6 +61,7 @@
#include "prims/methodHandles.hpp" #include "prims/methodHandles.hpp"
#include "runtime/arguments.hpp" #include "runtime/arguments.hpp"
#include "runtime/atomic.hpp" #include "runtime/atomic.hpp"
#include "runtime/continuationEntry.hpp"
#include "runtime/frame.inline.hpp" #include "runtime/frame.inline.hpp"
#include "runtime/handles.inline.hpp" #include "runtime/handles.inline.hpp"
#include "runtime/init.hpp" #include "runtime/init.hpp"
@ -1328,7 +1329,7 @@ void Method::set_code(const methodHandle& mh, CompiledMethod *code) {
assert(mh->_from_interpreted_entry == NULL, "initialized incorrectly"); // see link_method assert(mh->_from_interpreted_entry == NULL, "initialized incorrectly"); // see link_method
// This is the entry used when we're in interpreter-only mode; see InterpreterMacroAssembler::jump_from_interpreted // This is the entry used when we're in interpreter-only mode; see InterpreterMacroAssembler::jump_from_interpreted
mh->_i2i_entry = mh->get_i2c_entry(); mh->_i2i_entry = ContinuationEntry::interpreted_entry();
// This must come last, as it is what's tested in LinkResolver::resolve_static_call // This must come last, as it is what's tested in LinkResolver::resolve_static_call
Atomic::release_store(&mh->_from_interpreted_entry , mh->get_i2c_entry()); Atomic::release_store(&mh->_from_interpreted_entry , mh->get_i2c_entry());
} else if (!mh->is_method_handle_intrinsic()) { } else if (!mh->is_method_handle_intrinsic()) {

View file

@ -23,6 +23,7 @@
*/ */
#include "precompiled.hpp" #include "precompiled.hpp"
#include "code/compiledIC.hpp"
#include "code/nmethod.hpp" #include "code/nmethod.hpp"
#include "runtime/continuation.hpp" #include "runtime/continuation.hpp"
#include "runtime/continuationEntry.inline.hpp" #include "runtime/continuationEntry.inline.hpp"
@ -34,10 +35,32 @@
int ContinuationEntry::_return_pc_offset = 0; int ContinuationEntry::_return_pc_offset = 0;
address ContinuationEntry::_return_pc = nullptr; address ContinuationEntry::_return_pc = nullptr;
CompiledMethod* ContinuationEntry::_enter_special = nullptr;
int ContinuationEntry::_interpreted_entry_offset = 0;
void ContinuationEntry::set_enter_code(CompiledMethod* cm) { void ContinuationEntry::set_enter_code(CompiledMethod* cm, int interpreted_entry_offset) {
assert(_return_pc_offset != 0, ""); assert(_return_pc_offset != 0, "");
_return_pc = cm->code_begin() + _return_pc_offset; _return_pc = cm->code_begin() + _return_pc_offset;
_enter_special = cm;
_interpreted_entry_offset = interpreted_entry_offset;
assert(_enter_special->code_contains(compiled_entry()), "entry not in enterSpecial");
assert(_enter_special->code_contains(interpreted_entry()), "entry not in enterSpecial");
assert(interpreted_entry() < compiled_entry(), "unexpected code layout");
}
address ContinuationEntry::compiled_entry() {
return _enter_special->verified_entry_point();
}
address ContinuationEntry::interpreted_entry() {
return _enter_special->code_begin() + _interpreted_entry_offset;
}
bool ContinuationEntry::is_interpreted_call(address call_address) {
assert(_enter_special->code_contains(call_address), "call not in enterSpecial");
assert(call_address >= interpreted_entry(), "unexpected location");
return call_address < compiled_entry();
} }
ContinuationEntry* ContinuationEntry::from_frame(const frame& f) { ContinuationEntry* ContinuationEntry::from_frame(const frame& f) {

View file

@ -53,10 +53,13 @@ public:
public: public:
static int _return_pc_offset; // friend gen_continuation_enter static int _return_pc_offset; // friend gen_continuation_enter
static void set_enter_code(CompiledMethod* nm); // friend SharedRuntime::generate_native_wrapper static void set_enter_code(CompiledMethod* cm, int interpreted_entry_offset);
static bool is_interpreted_call(address call_address);
private: private:
static address _return_pc; static address _return_pc;
static CompiledMethod* _enter_special;
static int _interpreted_entry_offset;
private: private:
ContinuationEntry* _parent; ContinuationEntry* _parent;
@ -90,6 +93,11 @@ public:
intptr_t* entry_sp() const { return (intptr_t*)this; } intptr_t* entry_sp() const { return (intptr_t*)this; }
intptr_t* entry_fp() const; intptr_t* entry_fp() const;
static address compiled_entry();
static address interpreted_entry();
static CompiledMethod* enter_special() { return _enter_special; }
int argsize() const { return _argsize; } int argsize() const { return _argsize; }
void set_argsize(int value) { _argsize = value; } void set_argsize(int value) { _argsize = value; }

View file

@ -1343,6 +1343,9 @@ bool SharedRuntime::resolve_sub_helper_internal(methodHandle callee_method, cons
return true; // skip patching for JVMCI return true; // skip patching for JVMCI
} }
CompiledStaticCall* ssc = caller_nm->compiledStaticCall_before(caller_frame.pc()); CompiledStaticCall* ssc = caller_nm->compiledStaticCall_before(caller_frame.pc());
if (is_nmethod && caller_nm->method()->is_continuation_enter_intrinsic()) {
ssc->compute_entry_for_continuation_entry(callee_method, static_call_info);
}
if (ssc->is_clean()) ssc->set(static_call_info); if (ssc->is_clean()) ssc->set(static_call_info);
} }
} }
@ -1557,10 +1560,32 @@ JRT_END
// resolve a static call and patch code // resolve a static call and patch code
JRT_BLOCK_ENTRY(address, SharedRuntime::resolve_static_call_C(JavaThread* current )) JRT_BLOCK_ENTRY(address, SharedRuntime::resolve_static_call_C(JavaThread* current ))
methodHandle callee_method; methodHandle callee_method;
bool enter_special = false;
JRT_BLOCK JRT_BLOCK
callee_method = SharedRuntime::resolve_helper(false, false, CHECK_NULL); callee_method = SharedRuntime::resolve_helper(false, false, CHECK_NULL);
current->set_vm_result_2(callee_method()); current->set_vm_result_2(callee_method());
if (current->is_interp_only_mode()) {
RegisterMap reg_map(current, false);
frame stub_frame = current->last_frame();
assert(stub_frame.is_runtime_frame(), "must be a runtimeStub");
frame caller = stub_frame.sender(&reg_map);
enter_special = caller.cb() != NULL && caller.cb()->is_compiled()
&& caller.cb()->as_compiled_method()->method()->is_continuation_enter_intrinsic();
}
JRT_BLOCK_END JRT_BLOCK_END
if (current->is_interp_only_mode() && enter_special) {
// enterSpecial is compiled and calls this method to resolve the call to Continuation::enter
// but in interp_only_mode we need to go to the interpreted entry
// The c2i won't patch in this mode -- see fixup_callers_callsite
//
// This should probably be done in all cases, not just enterSpecial (see JDK-8218403),
// but that's part of a larger fix, and the situation is worse for enterSpecial, as it has no
// interpreted version.
return callee_method->get_c2i_entry();
}
// return compiled code entry point after potential safepoints // return compiled code entry point after potential safepoints
assert(callee_method->verified_code_entry() != NULL, " Jump to zero!"); assert(callee_method->verified_code_entry() != NULL, " Jump to zero!");
return callee_method->verified_code_entry(); return callee_method->verified_code_entry();
@ -1991,6 +2016,9 @@ JRT_LEAF(void, SharedRuntime::fixup_callers_callsite(Method* method, address cal
// Get the return PC for the passed caller PC. // Get the return PC for the passed caller PC.
address return_pc = caller_pc + frame::pc_return_offset; address return_pc = caller_pc + frame::pc_return_offset;
assert(!JavaThread::current()->is_interp_only_mode() || !nm->method()->is_continuation_enter_intrinsic()
|| ContinuationEntry::is_interpreted_call(return_pc), "interp_only_mode but not in enterSpecial interpreted entry");
// There is a benign race here. We could be attempting to patch to a compiled // There is a benign race here. We could be attempting to patch to a compiled
// entry point at the same time the callee is being deoptimized. If that is // entry point at the same time the callee is being deoptimized. If that is
// the case then entry_point may in fact point to a c2i and we'd patch the // the case then entry_point may in fact point to a c2i and we'd patch the
@ -2027,6 +2055,13 @@ JRT_LEAF(void, SharedRuntime::fixup_callers_callsite(Method* method, address cal
typ != relocInfo::static_stub_type) { typ != relocInfo::static_stub_type) {
return; return;
} }
if (nm->method()->is_continuation_enter_intrinsic()) {
assert(ContinuationEntry::is_interpreted_call(call->instruction_address()) == JavaThread::current()->is_interp_only_mode(),
"mode: %d", JavaThread::current()->is_interp_only_mode());
if (ContinuationEntry::is_interpreted_call(call->instruction_address())) {
return;
}
}
address destination = call->destination(); address destination = call->destination();
if (should_fixup_call_destination(destination, entry_point, caller_pc, moop, cb)) { if (should_fixup_call_destination(destination, entry_point, caller_pc, moop, cb)) {
call->set_destination_mt_safe(entry_point); call->set_destination_mt_safe(entry_point);
@ -3056,7 +3091,7 @@ void AdapterHandlerLibrary::create_native_wrapper(const methodHandle& method) {
CodeBuffer buffer(buf); CodeBuffer buffer(buf);
if (method->is_continuation_enter_intrinsic()) { if (method->is_continuation_enter_intrinsic()) {
buffer.initialize_stubs_size(64); buffer.initialize_stubs_size(128);
} }
struct { double data[20]; } locs_buf; struct { double data[20]; } locs_buf;

View file

@ -120,12 +120,15 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
return this instanceof PaddingLayout; return this instanceof PaddingLayout;
} }
// the following methods have to copy the same Javadoc as in MemoryLayout, or subclasses will just show
// the Object methods javadoc
/** /**
* {@return the hash code value for this layout} * {@return the hash code value for this layout}
*/ */
@Override @Override
public int hashCode() { public int hashCode() {
return name.hashCode() << Long.hashCode(alignment); return Objects.hash(name, size, alignment);
} }
/** /**
@ -134,28 +137,27 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
* the same kind, have the same size, name and alignment constraints. Furthermore, depending on the layout kind, additional * the same kind, have the same size, name and alignment constraints. Furthermore, depending on the layout kind, additional
* conditions must be satisfied: * conditions must be satisfied:
* <ul> * <ul>
* <li>two value layouts are considered equal if they have the same byte order (see {@link ValueLayout#order()})</li> * <li>two value layouts are considered equal if they have the same {@linkplain ValueLayout#order() order},
* and {@linkplain ValueLayout#carrier() carrier}</li>
* <li>two sequence layouts are considered equal if they have the same element count (see {@link SequenceLayout#elementCount()}), and * <li>two sequence layouts are considered equal if they have the same element count (see {@link SequenceLayout#elementCount()}), and
* if their element layouts (see {@link SequenceLayout#elementLayout()}) are also equal</li> * if their element layouts (see {@link SequenceLayout#elementLayout()}) are also equal</li>
* <li>two group layouts are considered equal if they are of the same kind (see {@link GroupLayout#isStruct()}, * <li>two group layouts are considered equal if they are of the same kind (see {@link GroupLayout#isStruct()},
* {@link GroupLayout#isUnion()}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal</li> * {@link GroupLayout#isUnion()}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal</li>
* </ul> * </ul>
* *
* @param that the object to be compared for equality with this layout. * @param other the object to be compared for equality with this layout.
* @return {@code true} if the specified object is equal to this layout. * @return {@code true} if the specified object is equal to this layout.
*/ */
@Override @Override
public boolean equals(Object that) { public boolean equals(Object other) {
if (this == that) { if (this == other) {
return true; return true;
} }
if (!(that instanceof AbstractLayout)) { return other instanceof AbstractLayout otherLayout &&
return false; name.equals(otherLayout.name) &&
} size == otherLayout.size &&
alignment == otherLayout.alignment;
return Objects.equals(name, ((AbstractLayout) that).name) &&
Objects.equals(alignment, ((AbstractLayout) that).alignment);
} }
/** /**

View file

@ -143,10 +143,9 @@ public final class GroupLayout extends AbstractLayout implements MemoryLayout {
if (!super.equals(other)) { if (!super.equals(other)) {
return false; return false;
} }
if (!(other instanceof GroupLayout g)) { return other instanceof GroupLayout otherGroup &&
return false; kind == otherGroup.kind &&
} elements.equals(otherGroup.elements);
return kind.equals(g.kind) && elements.equals(g.elements);
} }
/** /**

View file

@ -584,17 +584,18 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro
* the same kind, have the same size, name and alignment constraints. Furthermore, depending on the layout kind, additional * the same kind, have the same size, name and alignment constraints. Furthermore, depending on the layout kind, additional
* conditions must be satisfied: * conditions must be satisfied:
* <ul> * <ul>
* <li>two value layouts are considered equal if they have the same byte order (see {@link ValueLayout#order()})</li> * <li>two value layouts are considered equal if they have the same {@linkplain ValueLayout#order() order},
* and {@linkplain ValueLayout#carrier() carrier}</li>
* <li>two sequence layouts are considered equal if they have the same element count (see {@link SequenceLayout#elementCount()}), and * <li>two sequence layouts are considered equal if they have the same element count (see {@link SequenceLayout#elementCount()}), and
* if their element layouts (see {@link SequenceLayout#elementLayout()}) are also equal</li> * if their element layouts (see {@link SequenceLayout#elementLayout()}) are also equal</li>
* <li>two group layouts are considered equal if they are of the same kind (see {@link GroupLayout#isStruct()}, * <li>two group layouts are considered equal if they are of the same kind (see {@link GroupLayout#isStruct()},
* {@link GroupLayout#isUnion()}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal</li> * {@link GroupLayout#isUnion()}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal</li>
* </ul> * </ul>
* *
* @param that the object to be compared for equality with this layout. * @param other the object to be compared for equality with this layout.
* @return {@code true} if the specified object is equal to this layout. * @return {@code true} if the specified object is equal to this layout.
*/ */
boolean equals(Object that); boolean equals(Object other);
/** /**
* {@return the hash code value for this layout} * {@return the hash code value for this layout}

View file

@ -124,7 +124,6 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
* @param elementCounts an array of element counts, of which at most one can be {@code -1}. * @param elementCounts an array of element counts, of which at most one can be {@code -1}.
* @return a sequence layout where element layouts in the flattened projection of this * @return a sequence layout where element layouts in the flattened projection of this
* sequence layout (see {@link #flatten()}) are re-arranged into one or more nested sequence layouts. * sequence layout (see {@link #flatten()}) are re-arranged into one or more nested sequence layouts.
* @throws UnsupportedOperationException if this sequence layout does not have an element count.
* @throws IllegalArgumentException if two or more element counts are set to {@code -1}, or if one * @throws IllegalArgumentException if two or more element counts are set to {@code -1}, or if one
* or more element count is {@code <= 0} (but other than {@code -1}) or, if, after any required inference, * or more element count is {@code <= 0} (but other than {@code -1}) or, if, after any required inference,
* multiplying the element counts does not yield the same element count as the flattened projection of this * multiplying the element counts does not yield the same element count as the flattened projection of this
@ -187,8 +186,6 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
* } * }
* @return a sequence layout with the same size as this layout (but, possibly, with different * @return a sequence layout with the same size as this layout (but, possibly, with different
* element count), whose element layout is not a sequence layout. * element count), whose element layout is not a sequence layout.
* @throws UnsupportedOperationException if this sequence layout, or one of the nested sequence layouts being
* flattened, does not have an element count.
*/ */
public SequenceLayout flatten() { public SequenceLayout flatten() {
long count = elementCount(); long count = elementCount();
@ -214,10 +211,9 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
if (!super.equals(other)) { if (!super.equals(other)) {
return false; return false;
} }
if (!(other instanceof SequenceLayout s)) { return other instanceof SequenceLayout otherSeq &&
return false; elemCount == otherSeq.elemCount &&
} elementLayout.equals(otherSeq.elementLayout);
return elemCount == s.elemCount && elementLayout.equals(s.elementLayout);
} }
@Override @Override

View file

@ -93,6 +93,9 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
return new ValueLayout(carrier, Objects.requireNonNull(order), bitSize(), alignment, name()); return new ValueLayout(carrier, Objects.requireNonNull(order), bitSize(), alignment, name());
} }
/**
* {@inheritDoc}
*/
@Override @Override
public String toString() { public String toString() {
char descriptor = carrier == MemoryAddress.class ? 'A' : carrier.descriptorString().charAt(0); char descriptor = carrier == MemoryAddress.class ? 'A' : carrier.descriptorString().charAt(0);
@ -102,6 +105,9 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
return decorateLayoutString(String.format("%s%d", descriptor, bitSize())); return decorateLayoutString(String.format("%s%d", descriptor, bitSize()));
} }
/**
* {@inheritDoc}
*/
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (this == other) { if (this == other) {
@ -110,13 +116,9 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
if (!super.equals(other)) { if (!super.equals(other)) {
return false; return false;
} }
if (!(other instanceof ValueLayout v)) { return other instanceof ValueLayout otherValue &&
return false; carrier.equals(otherValue.carrier) &&
} order.equals(otherValue.order);
return carrier.equals(v.carrier) &&
order.equals(v.order) &&
bitSize() == v.bitSize() &&
alignment == v.alignment;
} }
/** /**
@ -171,7 +173,7 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
* @return a var handle which can be used to dereference a multi-dimensional array, featuring {@code shape.length + 1} * @return a var handle which can be used to dereference a multi-dimensional array, featuring {@code shape.length + 1}
* {@code long} coordinates. * {@code long} coordinates.
* @throws IllegalArgumentException if {@code shape[i] < 0}, for at least one index {@code i}. * @throws IllegalArgumentException if {@code shape[i] < 0}, for at least one index {@code i}.
* @throws UnsupportedOperationException if the layout path has one or more elements with incompatible alignment constraints. * @throws UnsupportedOperationException if {@code bitAlignment() > bitSize()}.
* @see MethodHandles#memorySegmentViewVarHandle * @see MethodHandles#memorySegmentViewVarHandle
* @see MemoryLayout#varHandle(PathElement...) * @see MemoryLayout#varHandle(PathElement...)
* @see SequenceLayout * @see SequenceLayout
@ -198,9 +200,12 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
return carrier; return carrier;
} }
/**
* {@inheritDoc}
*/
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(super.hashCode(), order, bitSize(), alignment); return Objects.hash(super.hashCode(), order, carrier);
} }
@Override @Override

View file

@ -737,23 +737,18 @@ public interface Map<K, V> {
* *
* @param function the function to apply to each entry * @param function the function to apply to each entry
* @throws UnsupportedOperationException if the {@code set} operation * @throws UnsupportedOperationException if the {@code set} operation
* is not supported by this map's entry set iterator. * is not supported by this map's entry set iterator.
* @throws ClassCastException if the class of a replacement value * @throws ClassCastException if the class of a replacement value
* prevents it from being stored in this map * prevents it from being stored in this map
* @throws NullPointerException if the specified function is null, or the
* specified replacement value is null, and this map does not permit null
* values
* @throws ClassCastException if a replacement value is of an inappropriate
* type for this map
* (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>) * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if function or a replacement value is null, * @throws NullPointerException if the specified function is null, or if a
* and this map does not permit null keys or values * replacement value is null and this map does not permit null values
* (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>) * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
* @throws IllegalArgumentException if some property of a replacement value * @throws IllegalArgumentException if some property of a replacement value
* prevents it from being stored in this map * prevents it from being stored in this map
* (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>) * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
* @throws ConcurrentModificationException if an entry is found to be * @throws ConcurrentModificationException if an entry is found to be
* removed during iteration * removed during iteration
* @since 1.8 * @since 1.8
*/ */
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {

View file

@ -137,7 +137,7 @@ import java.util.Stack;
* @see com.sun.java_cup.internal.runtime.virtual_parse_stack * @see com.sun.java_cup.internal.runtime.virtual_parse_stack
* @author Frank Flannery * @author Frank Flannery
* *
* @LastModified: June 2022 * @LastModified: July 2022
*/ */
public abstract class lr_parser { public abstract class lr_parser {
@ -150,6 +150,10 @@ public abstract class lr_parser {
private int opCount = 0; private int opCount = 0;
private int totalOpCount = 0; private int totalOpCount = 0;
private int lastSym; private int lastSym;
private boolean overLimit = false;
public int grpLimit = 0;
public int opLimit = 0;
public int totalOpLimit = 0;
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
/*--- Constructor(s) ----------------------------------------*/ /*--- Constructor(s) ----------------------------------------*/
@ -376,11 +380,13 @@ public abstract class lr_parser {
grpCount++; grpCount++;
} }
opCount++; // function opCount++; // function
totalOpCount++;
isLiteral = false; isLiteral = false;
} else if (contains(sym.OPERATORS, s.sym)) { } else if (contains(sym.OPERATORS, s.sym)) {
// axis nodetest is counted as one step, so not counted if last=DCOLON // axis nodetest is counted as one step, so not counted if last=DCOLON
if (lastSym != sym.DCOLON) { if (lastSym != sym.DCOLON) {
opCount++; opCount++;
totalOpCount++;
} }
isLiteral = false; isLiteral = false;
} }
@ -390,6 +396,16 @@ public abstract class lr_parser {
} }
lastSym = s.sym; lastSym = s.sym;
/*
* Sets the overLimit status as soon as the count of operators is over the
* limit, which in turn triggers the XPathParser to report an error.
*/
if (grpLimit > 0 && grpCount > grpLimit
|| opLimit > 0 && opCount > opLimit
|| totalOpLimit > 0 && totalOpCount > totalOpLimit) {
overLimit = true;
}
return s; return s;
} }
@ -591,12 +607,14 @@ public abstract class lr_parser {
/* do user initialization */ /* do user initialization */
user_init(); user_init();
isLiteral = false; isLiteral = false;
overLimit = false;
grpCount = 0; grpCount = 0;
opCount = 0; opCount = 0;
lastSym = -1; lastSym = -1;
/* get the first token */ /* get the first token */
cur_token = scan(); cur_token = scan();
if (overLimit) return null;
/* push dummy Symbol with start state to get us underway */ /* push dummy Symbol with start state to get us underway */
stack.removeAllElements(); stack.removeAllElements();
@ -671,12 +689,16 @@ public abstract class lr_parser {
lhs_sym = stack.peek(); lhs_sym = stack.peek();
} }
} }
if (overLimit) return null;
} }
totalOpCount += opCount;
return lhs_sym; return lhs_sym;
} }
public boolean isOverLimit() {
return overLimit;
}
/** /**
* Returns the count of operators in XPath expressions. * Returns the count of operators in XPath expressions.
* *

View file

@ -43,12 +43,9 @@ import jdk.xml.internal.XMLSecurityManager.Limit;
* CUP v0.11b generated parser. * CUP v0.11b generated parser.
* This class was generated by CUP v0.11b on Nov 12, 2019. * This class was generated by CUP v0.11b on Nov 12, 2019.
* *
* @LastModified: Jan 2022 * @LastModified: July 2022
*/ */
public class XPathParser extends lr_parser { public class XPathParser extends lr_parser {
private int grpLimit = 0;
private int opLimit = 0;
private int totalOpLimit = 0;
/** /**
* Default constructor. * Default constructor.
@ -1118,29 +1115,37 @@ public class XPathParser extends lr_parser {
_expression = expression; _expression = expression;
_lineNumber = lineNumber; _lineNumber = lineNumber;
Symbol s = super.parse(); Symbol s = super.parse();
int grpCount = getCount(ID_GROUP); /*
int opCount = getCount(ID_OPERATOR); * While the Java CUP parser is used for parsing symbols, the error
int totalOpCount = getCount(ID_TOTAL_OPERATOR); * report mechanism has so far been kept within the Xalan implementation.
* An error, i.e. the count of operators is over the limit, is
* therefore handled here.
*/
if (isOverLimit()) {
int grpCount = getCount(ID_GROUP);
int opCount = getCount(ID_OPERATOR);
int totalOpCount = getCount(ID_TOTAL_OPERATOR);
String errCode = null; String errCode = null;
Object[] params = null; Object[] params = null;
if (grpLimit > 0 && grpCount > grpLimit) { if (grpLimit > 0 && grpCount > grpLimit) {
errCode = ErrorMsg.XPATH_GROUP_LIMIT; errCode = ErrorMsg.XPATH_GROUP_LIMIT;
params = new Object[]{grpCount, grpLimit, params = new Object[]{grpCount, grpLimit,
_xmlSM.getStateLiteral(Limit.XPATH_GROUP_LIMIT)}; _xmlSM.getStateLiteral(Limit.XPATH_GROUP_LIMIT)};
} else if (opLimit > 0 && opCount > opLimit) { } else if (opLimit > 0 && opCount > opLimit) {
errCode = ErrorMsg.XPATH_OPERATOR_LIMIT; errCode = ErrorMsg.XPATH_OPERATOR_LIMIT;
params = new Object[]{opCount, opLimit, params = new Object[]{opCount, opLimit,
_xmlSM.getStateLiteral(Limit.XPATH_OP_LIMIT)}; _xmlSM.getStateLiteral(Limit.XPATH_OP_LIMIT)};
} else if (totalOpLimit > 0 && totalOpCount > totalOpLimit) { } else if (totalOpLimit > 0 && totalOpCount > totalOpLimit) {
errCode = ErrorMsg.XPATH_TOTAL_OPERATOR_LIMIT; errCode = ErrorMsg.XPATH_TOTAL_OPERATOR_LIMIT;
params = new Object[]{totalOpCount, totalOpLimit, params = new Object[]{totalOpCount, totalOpLimit,
_xmlSM.getStateLiteral(Limit.XPATH_TOTALOP_LIMIT)}; _xmlSM.getStateLiteral(Limit.XPATH_TOTALOP_LIMIT)};
} }
if (errCode != null) { if (errCode != null) {
_parser.reportError(Constants.FATAL, _parser.reportError(Constants.FATAL,
new ErrorMsg(errCode, lineNumber, params)); new ErrorMsg(errCode, lineNumber, params));
throw new RuntimeException(ErrorMsg.XPATH_LIMIT); throw new RuntimeException(ErrorMsg.XPATH_LIMIT);
}
} }
return s; return s;
} catch (IllegalCharException e) { } catch (IllegalCharException e) {

View file

@ -1694,9 +1694,7 @@ public class Attr extends JCTree.Visitor {
// Attribute all cases and // Attribute all cases and
// check that there are no duplicate case labels or default clauses. // check that there are no duplicate case labels or default clauses.
Set<Object> labels = new HashSet<>(); // The set of case labels. Set<Object> constants = new HashSet<>(); // The set of case constants.
List<Type> coveredTypesForPatterns = List.nil();
List<Type> coveredTypesForConstants = List.nil();
boolean hasDefault = false; // Is there a default label? boolean hasDefault = false; // Is there a default label?
boolean hasUnconditionalPattern = false; // Is there a unconditional pattern? boolean hasUnconditionalPattern = false; // Is there a unconditional pattern?
boolean lastPatternErroneous = false; // Has the last pattern erroneous type? boolean lastPatternErroneous = false; // Has the last pattern erroneous type?
@ -1732,10 +1730,8 @@ public class Attr extends JCTree.Visitor {
Symbol sym = enumConstant(expr, seltype); Symbol sym = enumConstant(expr, seltype);
if (sym == null) { if (sym == null) {
log.error(expr.pos(), Errors.EnumLabelMustBeUnqualifiedEnum); log.error(expr.pos(), Errors.EnumLabelMustBeUnqualifiedEnum);
} else if (!labels.add(sym)) { } else if (!constants.add(sym)) {
log.error(label.pos(), Errors.DuplicateCaseLabel); log.error(label.pos(), Errors.DuplicateCaseLabel);
} else {
checkCaseLabelDominated(label.pos(), coveredTypesForConstants, sym.type);
} }
} else if (errorEnumSwitch) { } else if (errorEnumSwitch) {
//error recovery: the selector is erroneous, and all the case labels //error recovery: the selector is erroneous, and all the case labels
@ -1765,10 +1761,8 @@ public class Attr extends JCTree.Visitor {
} }
} else if (!stringSwitch && !types.isAssignable(seltype, syms.intType)) { } else if (!stringSwitch && !types.isAssignable(seltype, syms.intType)) {
log.error(label.pos(), Errors.ConstantLabelNotCompatible(pattype, seltype)); log.error(label.pos(), Errors.ConstantLabelNotCompatible(pattype, seltype));
} else if (!labels.add(pattype.constValue())) { } else if (!constants.add(pattype.constValue())) {
log.error(c.pos(), Errors.DuplicateCaseLabel); log.error(c.pos(), Errors.DuplicateCaseLabel);
} else {
checkCaseLabelDominated(label.pos(), coveredTypesForConstants, types.boxedTypeOrType(pattype));
} }
} }
} }
@ -1820,13 +1814,6 @@ public class Attr extends JCTree.Visitor {
hasUnconditionalPattern = true; hasUnconditionalPattern = true;
} }
lastPatternErroneous = patternType.isErroneous(); lastPatternErroneous = patternType.isErroneous();
checkCaseLabelDominated(pat.pos(), coveredTypesForPatterns, patternType);
if (!patternType.isErroneous()) {
coveredTypesForConstants = coveredTypesForConstants.prepend(patternType);
if (unguarded) {
coveredTypesForPatterns = coveredTypesForPatterns.prepend(patternType);
}
}
} else { } else {
Assert.error(); Assert.error();
} }
@ -1850,6 +1837,7 @@ public class Attr extends JCTree.Visitor {
} }
if (patternSwitch) { if (patternSwitch) {
chk.checkSwitchCaseStructure(cases); chk.checkSwitchCaseStructure(cases);
chk.checkSwitchCaseLabelDominated(cases);
} }
if (switchTree.hasTag(SWITCH)) { if (switchTree.hasTag(SWITCH)) {
((JCSwitch) switchTree).hasUnconditionalPattern = ((JCSwitch) switchTree).hasUnconditionalPattern =
@ -1875,14 +1863,6 @@ public class Attr extends JCTree.Visitor {
switchScope.enter(((JCVariableDecl) stat).sym); switchScope.enter(((JCVariableDecl) stat).sym);
} }
} }
private void checkCaseLabelDominated(DiagnosticPosition pos,
List<Type> coveredTypes, Type patternType) {
for (Type existing : coveredTypes) {
if (types.isSubtype(patternType, existing)) {
log.error(pos, Errors.PatternDominated);
}
}
}
// where // where
/** Return the selected enumeration constant symbol, or null. */ /** Return the selected enumeration constant symbol, or null. */
private Symbol enumConstant(JCTree tree, Type enumType) { private Symbol enumConstant(JCTree tree, Type enumType) {

View file

@ -4376,6 +4376,93 @@ public class Check {
} }
} }
void checkSwitchCaseLabelDominated(List<JCCase> cases) {
List<JCCaseLabel> caseLabels = List.nil();
for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
JCCase c = l.head;
for (JCCaseLabel label : c.labels) {
if (label.hasTag(DEFAULTCASELABEL) || TreeInfo.isNullCaseLabel(label)) {
continue;
}
Type currentType = labelType(label);
for (JCCaseLabel testCaseLabel : caseLabels) {
Type testType = labelType(testCaseLabel);
if (types.isSubtype(currentType, testType) &&
!currentType.hasTag(ERROR) && !testType.hasTag(ERROR)) {
//the current label is potentially dominated by the existing (test) label, check:
boolean dominated = false;
if (label instanceof JCConstantCaseLabel) {
dominated |= !(testCaseLabel instanceof JCConstantCaseLabel);
} else if (label instanceof JCPatternCaseLabel patternCL &&
testCaseLabel instanceof JCPatternCaseLabel testPatternCaseLabel &&
TreeInfo.unguardedCaseLabel(testCaseLabel)) {
dominated = patternDominated(testPatternCaseLabel.pat,
patternCL.pat);
}
if (dominated) {
log.error(label.pos(), Errors.PatternDominated);
}
}
}
caseLabels = caseLabels.prepend(label);
}
}
}
//where:
private Type labelType(JCCaseLabel label) {
return types.erasure(switch (label.getTag()) {
case PATTERNCASELABEL -> ((JCPatternCaseLabel) label).pat.type;
case CONSTANTCASELABEL -> types.boxedTypeOrType(((JCConstantCaseLabel) label).expr.type);
default -> throw Assert.error("Unexpected tree kind: " + label.getTag());
});
}
private boolean patternDominated(JCPattern existingPattern, JCPattern currentPattern) {
Type existingPatternType = types.erasure(existingPattern.type);
Type currentPatternType = types.erasure(currentPattern.type);
if (existingPatternType.isPrimitive() ^ currentPatternType.isPrimitive()) {
return false;
}
if (existingPatternType.isPrimitive()) {
return types.isSameType(existingPatternType, currentPatternType);
} else {
if (!types.isSubtype(currentPatternType, existingPatternType)) {
return false;
}
}
while (existingPattern instanceof JCParenthesizedPattern parenthesized) {
existingPattern = parenthesized.pattern;
}
while (currentPattern instanceof JCParenthesizedPattern parenthesized) {
currentPattern = parenthesized.pattern;
}
if (currentPattern instanceof JCBindingPattern) {
return existingPattern instanceof JCBindingPattern;
} else if (currentPattern instanceof JCRecordPattern currentRecordPattern) {
if (existingPattern instanceof JCBindingPattern) {
return true;
} else if (existingPattern instanceof JCRecordPattern existingRecordPattern) {
List<JCPattern> existingNested = existingRecordPattern.nested;
List<JCPattern> currentNested = currentRecordPattern.nested;
if (existingNested.size() != currentNested.size()) {
return false;
}
while (existingNested.nonEmpty()) {
if (!patternDominated(existingNested.head, currentNested.head)) {
return false;
}
existingNested = existingNested.tail;
currentNested = currentNested.tail;
}
return true;
} else {
Assert.error("Unknown pattern: " + existingPattern.getTag());
}
} else {
Assert.error("Unknown pattern: " + currentPattern.getTag());
}
return false;
}
/** check if a type is a subtype of Externalizable, if that is available. */ /** check if a type is a subtype of Externalizable, if that is available. */
boolean isExternalizable(Type t) { boolean isExternalizable(Type t) {
try { try {

View file

@ -25,6 +25,7 @@
package jdk.javadoc.internal.doclets.toolkit.taglets; package jdk.javadoc.internal.doclets.toolkit.taglets;
import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -32,7 +33,6 @@ import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
@ -160,22 +160,21 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
var utils = writer.configuration().utils; var utils = writer.configuration().utils;
Content result = writer.getOutputInstance(); Content result = writer.getOutputInstance();
var documentedInThisCall = new HashSet<String>(); var documentedInThisCall = new HashSet<String>();
for (Entry<ThrowsTree, ExecutableElement> entry : throwsTags.entrySet()) { Map<ThrowsTree, ExecutableElement> flattenedExceptions = flatten(throwsTags, writer);
Element e = entry.getValue(); flattenedExceptions.forEach((ThrowsTree t, ExecutableElement e) -> {
var ch = utils.getCommentHelper(e); var ch = utils.getCommentHelper(e);
ThrowsTree tag = entry.getKey(); Element te = ch.getException(t);
Element te = ch.getException(tag); String excName = t.getExceptionName().toString();
String excName = tag.getExceptionName().toString();
TypeMirror substituteType = typeSubstitutions.get(excName); TypeMirror substituteType = typeSubstitutions.get(excName);
if (alreadyDocumented.contains(excName) if (alreadyDocumented.contains(excName)
|| (te != null && alreadyDocumented.contains(utils.getFullyQualifiedName(te, false))) || (te != null && alreadyDocumented.contains(utils.getFullyQualifiedName(te, false)))
|| (substituteType != null && alreadyDocumented.contains(substituteType.toString()))) { || (substituteType != null && alreadyDocumented.contains(substituteType.toString()))) {
continue; return;
} }
if (alreadyDocumented.isEmpty() && documentedInThisCall.isEmpty()) { if (alreadyDocumented.isEmpty() && documentedInThisCall.isEmpty()) {
result.add(writer.getThrowsHeader()); result.add(writer.getThrowsHeader());
} }
result.add(writer.throwsTagOutput(e, tag, substituteType)); result.add(writer.throwsTagOutput(e, t, substituteType));
if (substituteType != null) { if (substituteType != null) {
documentedInThisCall.add(substituteType.toString()); documentedInThisCall.add(substituteType.toString());
} else { } else {
@ -183,11 +182,57 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
? utils.getFullyQualifiedName(te, false) ? utils.getFullyQualifiedName(te, false)
: excName); : excName);
} }
} });
alreadyDocumented.addAll(documentedInThisCall); alreadyDocumented.addAll(documentedInThisCall);
return result; return result;
} }
/*
* A single @throws tag from an overriding method can correspond to multiple
* @throws tags from an overridden method.
*/
private Map<ThrowsTree, ExecutableElement> flatten(Map<ThrowsTree, ExecutableElement> throwsTags,
TagletWriter writer) {
Map<ThrowsTree, ExecutableElement> result = new LinkedHashMap<>();
throwsTags.forEach((tag, taggedElement) -> {
var expandedTags = expand(tag, taggedElement, writer);
assert Collections.disjoint(result.entrySet(), expandedTags.entrySet());
result.putAll(expandedTags);
});
return result;
}
private Map<ThrowsTree, ExecutableElement> expand(ThrowsTree tag,
ExecutableElement e,
TagletWriter writer) {
// This method uses Map.of() to create maps of size zero and one.
// While such maps are effectively ordered, the syntax is more
// compact than that of LinkedHashMap.
// peek into @throws description
if (tag.getDescription().stream().noneMatch(d -> d.getKind() == DocTree.Kind.INHERIT_DOC)) {
// nothing to inherit
return Map.of(tag, e);
}
var input = new DocFinder.Input(writer.configuration().utils, e, this, new DocFinder.DocTreeInfo(tag, e), false, true);
var output = DocFinder.search(writer.configuration(), input);
if (output.tagList.size() <= 1) {
// outer code will handle this trivial case of inheritance
return Map.of(tag, e);
}
if (tag.getDescription().size() > 1) {
// there's more to description than just {@inheritDoc}
// it's likely a documentation error
var ch = writer.configuration().utils.getCommentHelper(e);
writer.configuration().getMessages().error(ch.getDocTreePath(tag), "doclet.inheritDocWithinInappropriateTag");
return Map.of();
}
Map<ThrowsTree, ExecutableElement> tags = new LinkedHashMap<>();
output.tagList.forEach(t -> tags.put((ThrowsTree) t, (ExecutableElement) output.holder));
return tags;
}
/** /**
* Inherit throws documentation for exceptions that were declared but not * Inherit throws documentation for exceptions that were declared but not
* documented. * documented.

View file

@ -38,5 +38,3 @@ vmTestbase/nsk/jvmti/scenarios/sampling/SP07/sp07t002/TestDescription.java 82456
vmTestbase/vm/mlvm/mixed/stress/regression/b6969574/INDIFY_Test.java 8265295 linux-x64,windows-x64 vmTestbase/vm/mlvm/mixed/stress/regression/b6969574/INDIFY_Test.java 8265295 linux-x64,windows-x64
serviceability/jvmti/VMObjectAlloc/VMObjectAllocTest.java 8288430 generic-all serviceability/jvmti/VMObjectAlloc/VMObjectAllocTest.java 8288430 generic-all
serviceability/jvmti/vthread/ContStackDepthTest/ContStackDepthTest.java 8288949 generic-all

View file

@ -68,6 +68,8 @@ vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage006/TestDescription.java
#### ####
## NSK JDWP Tests failing with wrapper ## NSK JDWP Tests failing with wrapper
vmTestbase/nsk/jdwp/ThreadReference/ForceEarlyReturn/forceEarlyReturn002/forceEarlyReturn002.java 8286789 generic-all
########## ##########
## NSK JDB Tests failing with wrapper ## NSK JDB Tests failing with wrapper

View file

@ -44,3 +44,5 @@ serviceability/sa/TestJmapCoreMetaspace.java 8268722,8268636
serviceability/sa/TestJhsdbJstackMixed.java 8248912 generic-all serviceability/sa/TestJhsdbJstackMixed.java 8248912 generic-all
serviceability/sa/ClhsdbPstack.java#process 8248912 generic-all serviceability/sa/ClhsdbPstack.java#process 8248912 generic-all
serviceability/sa/ClhsdbPstack.java#core 8248912 generic-all serviceability/sa/ClhsdbPstack.java#core 8248912 generic-all
vmTestbase/gc/gctests/MemoryEaterMT/MemoryEaterMT.java 8289582 windows-x64

View file

@ -753,6 +753,7 @@ jdk/jfr/startupargs/TestStartName.java 8214685 windows-
jdk/jfr/startupargs/TestStartDuration.java 8214685 windows-x64 jdk/jfr/startupargs/TestStartDuration.java 8214685 windows-x64
jdk/jfr/jvm/TestWaste.java 8282427 generic-all jdk/jfr/jvm/TestWaste.java 8282427 generic-all
jdk/jfr/api/consumer/recordingstream/TestOnEvent.java 8255404 linux-x64 jdk/jfr/api/consumer/recordingstream/TestOnEvent.java 8255404 linux-x64
jdk/jfr/event/runtime/TestActiveSettingEvent.java 8287832 generic-all
############################################################################ ############################################################################

View file

@ -23,7 +23,7 @@
/* /*
* @test * @test
* @bug 8067757 * @bug 8067757 6509045
* @library /tools/lib ../../lib * @library /tools/lib ../../lib
* @modules jdk.compiler/com.sun.tools.javac.api * @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main * jdk.compiler/com.sun.tools.javac.main
@ -350,4 +350,218 @@ public class TestOneToMany extends JavadocTester {
<dd><code><a href="MySubException.html" title="class in x">MySubException</a></code> - if that</dd> <dd><code><a href="MySubException.html" title="class in x">MySubException</a></code> - if that</dd>
</dl>"""); </dl>""");
} }
@Test
public void testUncheckedExceptionTag(Path base) throws Exception {
var src = base.resolve("src");
tb.writeJavaFiles(src, """
package x;
public class MyRuntimeException extends RuntimeException { }
""", """
package x;
public interface I {
/**
* @throws MyRuntimeException if this
* @throws MyRuntimeException if that
*/
void m();
}
""", """
package x;
public interface I1 extends I {
/**
* @throws MyRuntimeException {@inheritDoc}
*/
@Override
void m();
}
""", """
package x;
public class IImpl implements I {
/**
* @throws MyRuntimeException {@inheritDoc}
*/
@Override
public void m() { }
}
""", """
package x;
public class C {
/**
* @throws MyRuntimeException if this
* @throws MyRuntimeException if that
*/
public void m();
}
""", """
package x;
public class C1 extends C {
/**
* @throws MyRuntimeException {@inheritDoc}
*/
@Override
public void m() { }
}
""");
javadoc("-d", base.resolve("out").toString(),
"-sourcepath", src.toString(),
"x");
checkExit(Exit.OK);
checkOutput("x/IImpl.html", true, """
<dl class="notes">
<dt>Specified by:</dt>
<dd><code><a href="I.html#m()">m</a></code>&nbsp;in interface&nbsp;<code><a href="I.html" title="interface in x">I</a></code></dd>
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if this</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if that</dd>
</dl>""");
checkOutput("x/I1.html", true, """
<dl class="notes">
<dt>Specified by:</dt>
<dd><code><a href="I.html#m()">m</a></code>&nbsp;in interface&nbsp;<code><a href="I.html" title="interface in x">I</a></code></dd>
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if this</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if that</dd>
</dl>""");
checkOutput("x/C1.html", true, """
<dl class="notes">
<dt>Overrides:</dt>
<dd><code><a href="C.html#m()">m</a></code>&nbsp;in class&nbsp;<code><a href="C.html" title="class in x">C</a></code></dd>
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if this</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if that</dd>
</dl>""");
}
@Test
public void testWholeShebang(Path base) throws Exception {
var src = base.resolve("src");
tb.writeJavaFiles(src, """
package x;
public class MyRuntimeException extends RuntimeException { }
""", """
package x;
public interface I {
/**
* @throws MyRuntimeException always
*/
void m();
}
""", """
package x;
public interface I1 extends I {
/**
* @throws MyRuntimeException sometimes
* @throws MyRuntimeException rarely
* @throws MyRuntimeException "{@inheritDoc}"
*/
@Override
void m();
}
""", """
package x;
public interface I2 extends I1 {
/**
* @throws MyRuntimeException occasionally
* @throws MyRuntimeException {@inheritDoc}
* @throws MyRuntimeException frequently
*/
@Override
void m() throws MyRuntimeException,
MyRuntimeException,
MyRuntimeException,
MyRuntimeException;
}
""");
javadoc("-d", base.resolve("out").toString(),
"-sourcepath", src.toString(),
"x");
checkExit(Exit.OK);
checkOutput("x/I.html", true, """
<dl class="notes">
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - always</dd>
</dl>""");
checkOutput("x/I1.html", true, """
<dl class="notes">
<dt>Specified by:</dt>
<dd><code><a href="I.html#m()">m</a></code>&nbsp;in interface&nbsp;<code><a href="I.html" title="interface in x">I</a></code></dd>
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - sometimes</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - rarely</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - "always"</dd>
</dl>""");
checkOutput("x/I2.html", true, """
<dl class="notes">
<dt>Specified by:</dt>
<dd><code><a href="I.html#m()">m</a></code>&nbsp;in interface&nbsp;<code><a href="I.html" title="interface in x">I</a></code></dd>
<dt>Specified by:</dt>
<dd><code><a href="I1.html#m()">m</a></code>&nbsp;in interface&nbsp;<code><a href="I1.html" title="interface in x">I1</a></code></dd>
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - occasionally</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - sometimes</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - rarely</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - "always"</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - frequently</dd>
</dl>""");
}
@Test
public void testError(Path base) throws Exception {
var src = base.resolve("src");
tb.writeJavaFiles(src, """
package x;
public class MyRuntimeException extends RuntimeException { }
""", """
package x;
public interface I {
/**
* @throws MyRuntimeException sometimes
* @throws MyRuntimeException rarely
*/
void m();
}
""", """
package x;
public interface I1 extends I {
/**
* @throws MyRuntimeException "{@inheritDoc}"
*/
@Override
void m();
}
""");
javadoc("-d", base.resolve("out").toString(),
"-sourcepath", src.toString(),
"x");
checkExit(Exit.ERROR);
checkOutput(Output.OUT, true, """
I1.java:6: error: @inheritDoc cannot be used within this tag
* @throws MyRuntimeException "{@inheritDoc}"
^
""");
}
} }

View file

@ -136,4 +136,79 @@ public class Domination {
} }
} }
int testRecordPatternsDominated1() {
record R(int a) {}
Object o = null;
switch (o) {
case R r: return 1;
case R(int a): return -1;
}
}
int testRecordPatternsDominated2() {
record R(int a) {}
Object o = null;
switch (o) {
case R(int a): return 1;
case R(int a): return -1;
}
}
int testRecordPatternsDominated3() {
record R(int a) {}
Object o = null;
switch (o) {
case R r when guard(): return 1;
case R(int a): return -1;
}
}
int testRecordPatternsDominated4() {
record R(int a) {}
Object o = null;
switch (o) {
case R(int a) when guard(): return 1;
case R(int a): return -1;
}
}
boolean guard() {
return false;
}
int testRecordPatternsDominated5() {
record R(int a) {}
Object o = null;
switch (o) {
case ((R r)): return 1;
case ((R(int a))): return -1;
}
}
int testRecordPatternsDominated6() {
record R(int a) {}
Object o = null;
switch (o) {
case ((R(int a))): return 1;
case ((R(int a))): return -1;
}
}
int testRecordPatternsDominated7() {
record R(int a) {}
Object o = null;
switch (o) {
case R r when true: return 1;
case R(int a): return -1;
}
}
int testRecordPatternsDominated8() {
record R(int a) {}
Object o = null;
switch (o) {
case R(int a) when true: return 1;
case R(int a): return -1;
}
}
} }

View file

@ -10,6 +10,12 @@ Domination.java:102:18: compiler.err.pattern.dominated
Domination.java:113:18: compiler.err.pattern.dominated Domination.java:113:18: compiler.err.pattern.dominated
Domination.java:124:18: compiler.err.pattern.dominated Domination.java:124:18: compiler.err.pattern.dominated
Domination.java:135:18: compiler.err.pattern.dominated Domination.java:135:18: compiler.err.pattern.dominated
Domination.java:144:18: compiler.err.pattern.dominated
Domination.java:153:18: compiler.err.pattern.dominated
Domination.java:184:18: compiler.err.pattern.dominated
Domination.java:193:18: compiler.err.pattern.dominated
Domination.java:202:18: compiler.err.pattern.dominated
Domination.java:211:18: compiler.err.pattern.dominated
- compiler.note.preview.filename: Domination.java, DEFAULT - compiler.note.preview.filename: Domination.java, DEFAULT
- compiler.note.preview.recompile - compiler.note.preview.recompile
12 errors 18 errors

View file

@ -32,11 +32,11 @@ SwitchErrors.java:172:18: compiler.err.pattern.expected
SwitchErrors.java:178:78: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null) SwitchErrors.java:178:78: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:184:73: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null) SwitchErrors.java:184:73: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:191:21: compiler.err.flows.through.to.pattern SwitchErrors.java:191:21: compiler.err.flows.through.to.pattern
SwitchErrors.java:200:44: compiler.err.pattern.dominated SwitchErrors.java:200:44: compiler.err.flows.through.from.pattern
SwitchErrors.java:218:29: compiler.err.unconditional.pattern.and.default SwitchErrors.java:218:29: compiler.err.unconditional.pattern.and.default
SwitchErrors.java:225:21: compiler.err.flows.through.to.pattern SwitchErrors.java:225:21: compiler.err.flows.through.to.pattern
SwitchErrors.java:225:47: compiler.err.flows.through.from.pattern SwitchErrors.java:225:47: compiler.err.flows.through.from.pattern
SwitchErrors.java:232:44: compiler.err.pattern.dominated SwitchErrors.java:232:44: compiler.err.flows.through.from.pattern
SwitchErrors.java:232:47: compiler.err.flows.through.from.pattern SwitchErrors.java:232:47: compiler.err.flows.through.from.pattern
SwitchErrors.java:244:18: compiler.err.duplicate.unconditional.pattern SwitchErrors.java:244:18: compiler.err.duplicate.unconditional.pattern
SwitchErrors.java:249:18: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, java.lang.Integer) SwitchErrors.java:249:18: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, java.lang.Integer)
@ -55,4 +55,4 @@ SwitchErrors.java:164:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:237:9: compiler.err.not.exhaustive.statement SwitchErrors.java:237:9: compiler.err.not.exhaustive.statement
- compiler.note.preview.filename: SwitchErrors.java, DEFAULT - compiler.note.preview.filename: SwitchErrors.java, DEFAULT
- compiler.note.preview.recompile - compiler.note.preview.recompile
55 errors 55 errors