mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
Merge
This commit is contained in:
commit
01b9f95c62
28 changed files with 755 additions and 138 deletions
|
@ -723,7 +723,7 @@ void MacroAssembler::emit_static_call_stub() {
|
|||
isb();
|
||||
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);
|
||||
br(rscratch1);
|
||||
}
|
||||
|
|
|
@ -1013,21 +1013,64 @@ static void gen_continuation_enter(MacroAssembler* masm,
|
|||
int& exception_offset,
|
||||
OopMapSet*oop_maps,
|
||||
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);
|
||||
Address resolve(SharedRuntime::get_resolve_static_call_stub(),
|
||||
relocInfo::static_call_type);
|
||||
Address resolve(SharedRuntime::get_resolve_static_call_stub(), relocInfo::static_call_type);
|
||||
|
||||
stack_slots = 2; // will be overwritten
|
||||
address start = __ pc();
|
||||
|
||||
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.
|
||||
|
||||
// Frame is now completed as far as size and linkage.
|
||||
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();
|
||||
stack_slots = 2; // will be adjusted in setup
|
||||
OopMap* map = continuation_enter_setup(masm, stack_slots);
|
||||
frame_complete = __ pc() - start;
|
||||
|
||||
fill_continuation_entry(masm);
|
||||
|
@ -1036,7 +1079,6 @@ static void gen_continuation_enter(MacroAssembler* masm,
|
|||
__ br(Assembler::NE, call_thaw);
|
||||
|
||||
address mark = __ pc();
|
||||
|
||||
__ trampoline_call1(resolve, NULL, false);
|
||||
|
||||
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();
|
||||
address stub = CompiledStaticCall::emit_to_interp_stub(*cbuf, mark);
|
||||
CompiledStaticCall::emit_to_interp_stub(*cbuf, mark);
|
||||
}
|
||||
|
||||
static void gen_special_dispatch(MacroAssembler* masm,
|
||||
|
@ -1169,11 +1211,12 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
|
|||
if (method->is_continuation_enter_intrinsic()) {
|
||||
vmIntrinsics::ID iid = method->intrinsic_id();
|
||||
intptr_t start = (intptr_t)__ pc();
|
||||
int vep_offset = ((intptr_t)__ pc()) - start;
|
||||
int vep_offset = 0;
|
||||
int exception_offset = 0;
|
||||
int frame_complete = 0;
|
||||
int stack_slots = 0;
|
||||
OopMapSet* oop_maps = new OopMapSet();
|
||||
int interpreted_entry_offset = -1;
|
||||
gen_continuation_enter(masm,
|
||||
method,
|
||||
in_sig_bt,
|
||||
|
@ -1181,7 +1224,9 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
|
|||
exception_offset,
|
||||
oop_maps,
|
||||
frame_complete,
|
||||
stack_slots);
|
||||
stack_slots,
|
||||
interpreted_entry_offset,
|
||||
vep_offset);
|
||||
__ flush();
|
||||
nmethod* nm = nmethod::new_native_nmethod(method,
|
||||
compile_id,
|
||||
|
@ -1193,7 +1238,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
|
|||
in_ByteSize(-1),
|
||||
oop_maps,
|
||||
exception_offset);
|
||||
ContinuationEntry::set_enter_code(nm);
|
||||
ContinuationEntry::set_enter_code(nm, interpreted_entry_offset);
|
||||
return nm;
|
||||
}
|
||||
|
||||
|
|
|
@ -1276,7 +1276,9 @@ static void gen_continuation_enter(MacroAssembler* masm,
|
|||
int& exception_offset,
|
||||
OopMapSet* oop_maps,
|
||||
int& frame_complete,
|
||||
int& stack_slots) {
|
||||
int& stack_slots,
|
||||
int& interpreted_entry_offset,
|
||||
int& compiled_entry_offset) {
|
||||
|
||||
// enterSpecial(Continuation c, boolean isContinue, boolean isVirtualThread)
|
||||
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
|
||||
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();
|
||||
|
||||
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();
|
||||
|
||||
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);
|
||||
|
||||
Label L_thaw, L_exit;
|
||||
|
||||
// If isContinue, call to thaw. Otherwise, call Continuation.enter(Continuation c, boolean isContinue)
|
||||
__ testptr(reg_is_cont, reg_is_cont);
|
||||
__ jccb(Assembler::notZero, L_thaw);
|
||||
|
@ -1334,8 +1394,6 @@ static void gen_continuation_enter(MacroAssembler* masm,
|
|||
// SharedRuntime::find_callee_info_helper() which calls
|
||||
// LinkResolver::resolve_continuation_enter() which resolves the call to
|
||||
// Continuation.enter(Continuation c, boolean isContinue).
|
||||
AddressLiteral resolve(SharedRuntime::get_resolve_static_call_stub(),
|
||||
relocInfo::static_call_type);
|
||||
__ call(resolve);
|
||||
|
||||
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()) {
|
||||
vmIntrinsics::ID iid = method->intrinsic_id();
|
||||
intptr_t start = (intptr_t)__ pc();
|
||||
int vep_offset = ((intptr_t)__ pc()) - start;
|
||||
int vep_offset = 0;
|
||||
int exception_offset = 0;
|
||||
int frame_complete = 0;
|
||||
int stack_slots = 0;
|
||||
OopMapSet* oop_maps = new OopMapSet();
|
||||
int interpreted_entry_offset = -1;
|
||||
gen_continuation_enter(masm,
|
||||
in_regs,
|
||||
exception_offset,
|
||||
oop_maps,
|
||||
frame_complete,
|
||||
stack_slots);
|
||||
stack_slots,
|
||||
interpreted_entry_offset,
|
||||
vep_offset);
|
||||
__ flush();
|
||||
nmethod* nm = nmethod::new_native_nmethod(method,
|
||||
compile_id,
|
||||
|
@ -1496,7 +1557,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
|
|||
in_ByteSize(-1),
|
||||
oop_maps,
|
||||
exception_offset);
|
||||
ContinuationEntry::set_enter_code(nm);
|
||||
ContinuationEntry::set_enter_code(nm, interpreted_entry_offset);
|
||||
return nm;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "oops/method.inline.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "oops/symbol.hpp"
|
||||
#include "runtime/continuationEntry.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
#include "runtime/icache.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) {
|
||||
// Find reloc. information containing this call-site
|
||||
RelocIterator iter((nmethod*)NULL, instruction);
|
||||
|
|
|
@ -345,6 +345,7 @@ class CompiledStaticCall : public ResourceObj {
|
|||
|
||||
// Compute entry point given a method
|
||||
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:
|
||||
// Clean static call (will force resolving on next use)
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
#include "prims/methodHandles.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/continuationEntry.hpp"
|
||||
#include "runtime/frame.inline.hpp"
|
||||
#include "runtime/handles.inline.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
|
||||
|
||||
// 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
|
||||
Atomic::release_store(&mh->_from_interpreted_entry , mh->get_i2c_entry());
|
||||
} else if (!mh->is_method_handle_intrinsic()) {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "code/compiledIC.hpp"
|
||||
#include "code/nmethod.hpp"
|
||||
#include "runtime/continuation.hpp"
|
||||
#include "runtime/continuationEntry.inline.hpp"
|
||||
|
@ -34,10 +35,32 @@
|
|||
|
||||
int ContinuationEntry::_return_pc_offset = 0;
|
||||
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, "");
|
||||
_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) {
|
||||
|
|
|
@ -53,10 +53,13 @@ public:
|
|||
|
||||
public:
|
||||
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:
|
||||
static address _return_pc;
|
||||
static CompiledMethod* _enter_special;
|
||||
static int _interpreted_entry_offset;
|
||||
|
||||
private:
|
||||
ContinuationEntry* _parent;
|
||||
|
@ -90,6 +93,11 @@ public:
|
|||
intptr_t* entry_sp() const { return (intptr_t*)this; }
|
||||
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; }
|
||||
void set_argsize(int value) { _argsize = value; }
|
||||
|
||||
|
|
|
@ -1343,6 +1343,9 @@ bool SharedRuntime::resolve_sub_helper_internal(methodHandle callee_method, cons
|
|||
return true; // skip patching for JVMCI
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1557,10 +1560,32 @@ JRT_END
|
|||
// resolve a static call and patch code
|
||||
JRT_BLOCK_ENTRY(address, SharedRuntime::resolve_static_call_C(JavaThread* current ))
|
||||
methodHandle callee_method;
|
||||
bool enter_special = false;
|
||||
JRT_BLOCK
|
||||
callee_method = SharedRuntime::resolve_helper(false, false, CHECK_NULL);
|
||||
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(®_map);
|
||||
enter_special = caller.cb() != NULL && caller.cb()->is_compiled()
|
||||
&& caller.cb()->as_compiled_method()->method()->is_continuation_enter_intrinsic();
|
||||
}
|
||||
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
|
||||
assert(callee_method->verified_code_entry() != NULL, " Jump to zero!");
|
||||
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.
|
||||
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
|
||||
// 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
|
||||
|
@ -2027,6 +2055,13 @@ JRT_LEAF(void, SharedRuntime::fixup_callers_callsite(Method* method, address cal
|
|||
typ != relocInfo::static_stub_type) {
|
||||
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();
|
||||
if (should_fixup_call_destination(destination, entry_point, caller_pc, moop, cb)) {
|
||||
call->set_destination_mt_safe(entry_point);
|
||||
|
@ -3056,7 +3091,7 @@ void AdapterHandlerLibrary::create_native_wrapper(const methodHandle& method) {
|
|||
CodeBuffer buffer(buf);
|
||||
|
||||
if (method->is_continuation_enter_intrinsic()) {
|
||||
buffer.initialize_stubs_size(64);
|
||||
buffer.initialize_stubs_size(128);
|
||||
}
|
||||
|
||||
struct { double data[20]; } locs_buf;
|
||||
|
|
|
@ -120,12 +120,15 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
|
|||
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}
|
||||
*/
|
||||
@Override
|
||||
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
|
||||
* conditions must be satisfied:
|
||||
* <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
|
||||
* 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()},
|
||||
* {@link GroupLayout#isUnion()}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal</li>
|
||||
* </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.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object that) {
|
||||
if (this == that) {
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(that instanceof AbstractLayout)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Objects.equals(name, ((AbstractLayout) that).name) &&
|
||||
Objects.equals(alignment, ((AbstractLayout) that).alignment);
|
||||
return other instanceof AbstractLayout otherLayout &&
|
||||
name.equals(otherLayout.name) &&
|
||||
size == otherLayout.size &&
|
||||
alignment == otherLayout.alignment;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -143,10 +143,9 @@ public final class GroupLayout extends AbstractLayout implements MemoryLayout {
|
|||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
if (!(other instanceof GroupLayout g)) {
|
||||
return false;
|
||||
}
|
||||
return kind.equals(g.kind) && elements.equals(g.elements);
|
||||
return other instanceof GroupLayout otherGroup &&
|
||||
kind == otherGroup.kind &&
|
||||
elements.equals(otherGroup.elements);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
* conditions must be satisfied:
|
||||
* <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
|
||||
* 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()},
|
||||
* {@link GroupLayout#isUnion()}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal</li>
|
||||
* </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.
|
||||
*/
|
||||
boolean equals(Object that);
|
||||
boolean equals(Object other);
|
||||
|
||||
/**
|
||||
* {@return the hash code value for this layout}
|
||||
|
|
|
@ -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}.
|
||||
* @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.
|
||||
* @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
|
||||
* 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
|
||||
|
@ -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
|
||||
* 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() {
|
||||
long count = elementCount();
|
||||
|
@ -214,10 +211,9 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
|
|||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
if (!(other instanceof SequenceLayout s)) {
|
||||
return false;
|
||||
}
|
||||
return elemCount == s.elemCount && elementLayout.equals(s.elementLayout);
|
||||
return other instanceof SequenceLayout otherSeq &&
|
||||
elemCount == otherSeq.elemCount &&
|
||||
elementLayout.equals(otherSeq.elementLayout);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -93,6 +93,9 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
|
|||
return new ValueLayout(carrier, Objects.requireNonNull(order), bitSize(), alignment, name());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
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()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
|
@ -110,13 +116,9 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
|
|||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
if (!(other instanceof ValueLayout v)) {
|
||||
return false;
|
||||
}
|
||||
return carrier.equals(v.carrier) &&
|
||||
order.equals(v.order) &&
|
||||
bitSize() == v.bitSize() &&
|
||||
alignment == v.alignment;
|
||||
return other instanceof ValueLayout otherValue &&
|
||||
carrier.equals(otherValue.carrier) &&
|
||||
order.equals(otherValue.order);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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}
|
||||
* {@code long} coordinates.
|
||||
* @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 MemoryLayout#varHandle(PathElement...)
|
||||
* @see SequenceLayout
|
||||
|
@ -198,9 +200,12 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
|
|||
return carrier;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), order, bitSize(), alignment);
|
||||
return Objects.hash(super.hashCode(), order, carrier);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -740,14 +740,9 @@ public interface Map<K, V> {
|
|||
* is not supported by this map's entry set iterator.
|
||||
* @throws ClassCastException if the class of a replacement value
|
||||
* 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>)
|
||||
* @throws NullPointerException if function or a replacement value is null,
|
||||
* and this map does not permit null keys or values
|
||||
* @throws NullPointerException if the specified function is null, or if a
|
||||
* 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>)
|
||||
* @throws IllegalArgumentException if some property of a replacement value
|
||||
* prevents it from being stored in this map
|
||||
|
|
|
@ -137,7 +137,7 @@ import java.util.Stack;
|
|||
* @see com.sun.java_cup.internal.runtime.virtual_parse_stack
|
||||
* @author Frank Flannery
|
||||
*
|
||||
* @LastModified: June 2022
|
||||
* @LastModified: July 2022
|
||||
*/
|
||||
|
||||
public abstract class lr_parser {
|
||||
|
@ -150,6 +150,10 @@ public abstract class lr_parser {
|
|||
private int opCount = 0;
|
||||
private int totalOpCount = 0;
|
||||
private int lastSym;
|
||||
private boolean overLimit = false;
|
||||
public int grpLimit = 0;
|
||||
public int opLimit = 0;
|
||||
public int totalOpLimit = 0;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
/*--- Constructor(s) ----------------------------------------*/
|
||||
|
@ -376,11 +380,13 @@ public abstract class lr_parser {
|
|||
grpCount++;
|
||||
}
|
||||
opCount++; // function
|
||||
totalOpCount++;
|
||||
isLiteral = false;
|
||||
} else if (contains(sym.OPERATORS, s.sym)) {
|
||||
// axis nodetest is counted as one step, so not counted if last=DCOLON
|
||||
if (lastSym != sym.DCOLON) {
|
||||
opCount++;
|
||||
totalOpCount++;
|
||||
}
|
||||
isLiteral = false;
|
||||
}
|
||||
|
@ -390,6 +396,16 @@ public abstract class lr_parser {
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -591,12 +607,14 @@ public abstract class lr_parser {
|
|||
/* do user initialization */
|
||||
user_init();
|
||||
isLiteral = false;
|
||||
overLimit = false;
|
||||
grpCount = 0;
|
||||
opCount = 0;
|
||||
lastSym = -1;
|
||||
|
||||
/* get the first token */
|
||||
cur_token = scan();
|
||||
if (overLimit) return null;
|
||||
|
||||
/* push dummy Symbol with start state to get us underway */
|
||||
stack.removeAllElements();
|
||||
|
@ -671,12 +689,16 @@ public abstract class lr_parser {
|
|||
lhs_sym = stack.peek();
|
||||
}
|
||||
}
|
||||
if (overLimit) return null;
|
||||
}
|
||||
|
||||
totalOpCount += opCount;
|
||||
return lhs_sym;
|
||||
}
|
||||
|
||||
public boolean isOverLimit() {
|
||||
return overLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the count of operators in XPath expressions.
|
||||
*
|
||||
|
|
|
@ -43,12 +43,9 @@ import jdk.xml.internal.XMLSecurityManager.Limit;
|
|||
* CUP v0.11b generated parser.
|
||||
* This class was generated by CUP v0.11b on Nov 12, 2019.
|
||||
*
|
||||
* @LastModified: Jan 2022
|
||||
* @LastModified: July 2022
|
||||
*/
|
||||
public class XPathParser extends lr_parser {
|
||||
private int grpLimit = 0;
|
||||
private int opLimit = 0;
|
||||
private int totalOpLimit = 0;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
|
@ -1118,6 +1115,13 @@ public class XPathParser extends lr_parser {
|
|||
_expression = expression;
|
||||
_lineNumber = lineNumber;
|
||||
Symbol s = super.parse();
|
||||
/*
|
||||
* While the Java CUP parser is used for parsing symbols, the error
|
||||
* 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);
|
||||
|
@ -1142,6 +1146,7 @@ public class XPathParser extends lr_parser {
|
|||
new ErrorMsg(errCode, lineNumber, params));
|
||||
throw new RuntimeException(ErrorMsg.XPATH_LIMIT);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
} catch (IllegalCharException e) {
|
||||
ErrorMsg err = new ErrorMsg(ErrorMsg.ILLEGAL_CHAR_ERR,
|
||||
|
|
|
@ -1694,9 +1694,7 @@ public class Attr extends JCTree.Visitor {
|
|||
|
||||
// Attribute all cases and
|
||||
// check that there are no duplicate case labels or default clauses.
|
||||
Set<Object> labels = new HashSet<>(); // The set of case labels.
|
||||
List<Type> coveredTypesForPatterns = List.nil();
|
||||
List<Type> coveredTypesForConstants = List.nil();
|
||||
Set<Object> constants = new HashSet<>(); // The set of case constants.
|
||||
boolean hasDefault = false; // Is there a default label?
|
||||
boolean hasUnconditionalPattern = false; // Is there a unconditional pattern?
|
||||
boolean lastPatternErroneous = false; // Has the last pattern erroneous type?
|
||||
|
@ -1732,10 +1730,8 @@ public class Attr extends JCTree.Visitor {
|
|||
Symbol sym = enumConstant(expr, seltype);
|
||||
if (sym == null) {
|
||||
log.error(expr.pos(), Errors.EnumLabelMustBeUnqualifiedEnum);
|
||||
} else if (!labels.add(sym)) {
|
||||
} else if (!constants.add(sym)) {
|
||||
log.error(label.pos(), Errors.DuplicateCaseLabel);
|
||||
} else {
|
||||
checkCaseLabelDominated(label.pos(), coveredTypesForConstants, sym.type);
|
||||
}
|
||||
} else if (errorEnumSwitch) {
|
||||
//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)) {
|
||||
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);
|
||||
} else {
|
||||
checkCaseLabelDominated(label.pos(), coveredTypesForConstants, types.boxedTypeOrType(pattype));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1820,13 +1814,6 @@ public class Attr extends JCTree.Visitor {
|
|||
hasUnconditionalPattern = true;
|
||||
}
|
||||
lastPatternErroneous = patternType.isErroneous();
|
||||
checkCaseLabelDominated(pat.pos(), coveredTypesForPatterns, patternType);
|
||||
if (!patternType.isErroneous()) {
|
||||
coveredTypesForConstants = coveredTypesForConstants.prepend(patternType);
|
||||
if (unguarded) {
|
||||
coveredTypesForPatterns = coveredTypesForPatterns.prepend(patternType);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Assert.error();
|
||||
}
|
||||
|
@ -1850,6 +1837,7 @@ public class Attr extends JCTree.Visitor {
|
|||
}
|
||||
if (patternSwitch) {
|
||||
chk.checkSwitchCaseStructure(cases);
|
||||
chk.checkSwitchCaseLabelDominated(cases);
|
||||
}
|
||||
if (switchTree.hasTag(SWITCH)) {
|
||||
((JCSwitch) switchTree).hasUnconditionalPattern =
|
||||
|
@ -1875,14 +1863,6 @@ public class Attr extends JCTree.Visitor {
|
|||
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
|
||||
/** Return the selected enumeration constant symbol, or null. */
|
||||
private Symbol enumConstant(JCTree tree, Type enumType) {
|
||||
|
|
|
@ -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. */
|
||||
boolean isExternalizable(Type t) {
|
||||
try {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -32,7 +33,6 @@ import java.util.Iterator;
|
|||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
|
@ -160,22 +160,21 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
|||
var utils = writer.configuration().utils;
|
||||
Content result = writer.getOutputInstance();
|
||||
var documentedInThisCall = new HashSet<String>();
|
||||
for (Entry<ThrowsTree, ExecutableElement> entry : throwsTags.entrySet()) {
|
||||
Element e = entry.getValue();
|
||||
Map<ThrowsTree, ExecutableElement> flattenedExceptions = flatten(throwsTags, writer);
|
||||
flattenedExceptions.forEach((ThrowsTree t, ExecutableElement e) -> {
|
||||
var ch = utils.getCommentHelper(e);
|
||||
ThrowsTree tag = entry.getKey();
|
||||
Element te = ch.getException(tag);
|
||||
String excName = tag.getExceptionName().toString();
|
||||
Element te = ch.getException(t);
|
||||
String excName = t.getExceptionName().toString();
|
||||
TypeMirror substituteType = typeSubstitutions.get(excName);
|
||||
if (alreadyDocumented.contains(excName)
|
||||
|| (te != null && alreadyDocumented.contains(utils.getFullyQualifiedName(te, false)))
|
||||
|| (substituteType != null && alreadyDocumented.contains(substituteType.toString()))) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
if (alreadyDocumented.isEmpty() && documentedInThisCall.isEmpty()) {
|
||||
result.add(writer.getThrowsHeader());
|
||||
}
|
||||
result.add(writer.throwsTagOutput(e, tag, substituteType));
|
||||
result.add(writer.throwsTagOutput(e, t, substituteType));
|
||||
if (substituteType != null) {
|
||||
documentedInThisCall.add(substituteType.toString());
|
||||
} else {
|
||||
|
@ -183,11 +182,57 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
|||
? utils.getFullyQualifiedName(te, false)
|
||||
: excName);
|
||||
}
|
||||
}
|
||||
});
|
||||
alreadyDocumented.addAll(documentedInThisCall);
|
||||
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
|
||||
* documented.
|
||||
|
|
|
@ -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
|
||||
|
||||
serviceability/jvmti/VMObjectAlloc/VMObjectAllocTest.java 8288430 generic-all
|
||||
|
||||
serviceability/jvmti/vthread/ContStackDepthTest/ContStackDepthTest.java 8288949 generic-all
|
||||
|
|
|
@ -68,6 +68,8 @@ vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage006/TestDescription.java
|
|||
####
|
||||
## NSK JDWP Tests failing with wrapper
|
||||
|
||||
vmTestbase/nsk/jdwp/ThreadReference/ForceEarlyReturn/forceEarlyReturn002/forceEarlyReturn002.java 8286789 generic-all
|
||||
|
||||
|
||||
##########
|
||||
## NSK JDB Tests failing with wrapper
|
||||
|
|
|
@ -44,3 +44,5 @@ serviceability/sa/TestJmapCoreMetaspace.java 8268722,8268636
|
|||
serviceability/sa/TestJhsdbJstackMixed.java 8248912 generic-all
|
||||
serviceability/sa/ClhsdbPstack.java#process 8248912 generic-all
|
||||
serviceability/sa/ClhsdbPstack.java#core 8248912 generic-all
|
||||
|
||||
vmTestbase/gc/gctests/MemoryEaterMT/MemoryEaterMT.java 8289582 windows-x64
|
||||
|
|
|
@ -753,6 +753,7 @@ jdk/jfr/startupargs/TestStartName.java 8214685 windows-
|
|||
jdk/jfr/startupargs/TestStartDuration.java 8214685 windows-x64
|
||||
jdk/jfr/jvm/TestWaste.java 8282427 generic-all
|
||||
jdk/jfr/api/consumer/recordingstream/TestOnEvent.java 8255404 linux-x64
|
||||
jdk/jfr/event/runtime/TestActiveSettingEvent.java 8287832 generic-all
|
||||
|
||||
############################################################################
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8067757
|
||||
* @bug 8067757 6509045
|
||||
* @library /tools/lib ../../lib
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
* 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>
|
||||
</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> in interface <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> in interface <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> in class <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> in interface <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> in interface <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> in interface <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}"
|
||||
^
|
||||
""");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,12 @@ Domination.java:102:18: compiler.err.pattern.dominated
|
|||
Domination.java:113:18: compiler.err.pattern.dominated
|
||||
Domination.java:124: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.recompile
|
||||
12 errors
|
||||
18 errors
|
|
@ -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: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: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:225:21: compiler.err.flows.through.to.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: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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue