8197405: Improve messages of AbstractMethodErrors and IncompatibleClassChangeErrors
Reviewed-by: coleenp, dholmes, mdoerr, njian
This commit is contained in:
parent
10259cf594
commit
507c62fc76
@ -293,7 +293,8 @@ address TemplateInterpreterGenerator::generate_abstract_entry(void) {
|
||||
|
||||
// throw exception
|
||||
__ call_VM(noreg, CAST_FROM_FN_PTR(address,
|
||||
InterpreterRuntime::throw_AbstractMethodError));
|
||||
InterpreterRuntime::throw_AbstractMethodErrorWithMethod),
|
||||
rmethod);
|
||||
// the call_VM checks for exception, so we should never return here.
|
||||
__ should_not_reach_here();
|
||||
|
||||
|
@ -3440,6 +3440,8 @@ void TemplateTable::invokeinterface(int byte_no) {
|
||||
|
||||
Label no_such_interface, no_such_method;
|
||||
|
||||
// Preserve method for throw_AbstractMethodErrorVerbose.
|
||||
__ mov(r16, rmethod);
|
||||
// Receiver subtype check against REFC.
|
||||
// Superklass in r0. Subklass in r3. Blows rscratch2, r13
|
||||
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||
@ -3460,8 +3462,10 @@ void TemplateTable::invokeinterface(int byte_no) {
|
||||
__ subw(rmethod, rmethod, Method::itable_index_max);
|
||||
__ negw(rmethod, rmethod);
|
||||
|
||||
// Preserve recvKlass for throw_AbstractMethodErrorVerbose.
|
||||
__ mov(rlocals, r3);
|
||||
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||
r3, r0, rmethod,
|
||||
rlocals, r0, rmethod,
|
||||
// outputs: method, scan temp. reg
|
||||
rmethod, r13,
|
||||
no_such_interface);
|
||||
@ -3490,7 +3494,8 @@ void TemplateTable::invokeinterface(int byte_no) {
|
||||
// throw exception
|
||||
__ restore_bcp(); // bcp must be correct for exception handler (was destroyed)
|
||||
__ restore_locals(); // make sure locals pointer is correct as well (was destroyed)
|
||||
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError));
|
||||
// Pass arguments for generating a verbose error message.
|
||||
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodErrorVerbose), r3, r16);
|
||||
// the call_VM checks for exception, so we should never return here.
|
||||
__ should_not_reach_here();
|
||||
|
||||
@ -3498,8 +3503,9 @@ void TemplateTable::invokeinterface(int byte_no) {
|
||||
// throw exception
|
||||
__ restore_bcp(); // bcp must be correct for exception handler (was destroyed)
|
||||
__ restore_locals(); // make sure locals pointer is correct as well (was destroyed)
|
||||
// Pass arguments for generating a verbose error message.
|
||||
__ call_VM(noreg, CAST_FROM_FN_PTR(address,
|
||||
InterpreterRuntime::throw_IncompatibleClassChangeError));
|
||||
InterpreterRuntime::throw_IncompatibleClassChangeErrorVerbose), r3, r0);
|
||||
// the call_VM checks for exception, so we should never return here.
|
||||
__ should_not_reach_here();
|
||||
return;
|
||||
|
@ -201,7 +201,12 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) {
|
||||
__ br(rscratch1);
|
||||
|
||||
__ bind(L_no_such_interface);
|
||||
__ far_jump(RuntimeAddress(StubRoutines::throw_IncompatibleClassChangeError_entry()));
|
||||
// Handle IncompatibleClassChangeError in itable stubs.
|
||||
// More detailed error message.
|
||||
// We force resolving of the call site by jumping to the "handle
|
||||
// wrong method" stub, and so let the interpreter runtime do all the
|
||||
// dirty work.
|
||||
__ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub()));
|
||||
|
||||
__ flush();
|
||||
|
||||
|
@ -158,8 +158,13 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) {
|
||||
|
||||
__ bind(L_no_such_interface);
|
||||
|
||||
assert(StubRoutines::throw_IncompatibleClassChangeError_entry() != NULL, "check initialization order");
|
||||
__ jump(StubRoutines::throw_IncompatibleClassChangeError_entry(), relocInfo::runtime_call_type, Rtemp);
|
||||
// Handle IncompatibleClassChangeError in itable stubs.
|
||||
// More detailed error message.
|
||||
// We force resolving of the call site by jumping to the "handle
|
||||
// wrong method" stub, and so let the interpreter runtime do all the
|
||||
// dirty work.
|
||||
assert(SharedRuntime::get_handle_wrong_method_stub() != NULL, "check initialization order");
|
||||
__ jump(SharedRuntime::get_handle_wrong_method_stub(), relocInfo::runtime_call_type, Rtemp);
|
||||
|
||||
masm->flush();
|
||||
|
||||
|
@ -452,8 +452,8 @@ address TemplateInterpreterGenerator::generate_abstract_entry(void) {
|
||||
|
||||
// This is not a leaf but we have a JavaFrameAnchor now and we will
|
||||
// check (create) exceptions afterward so this is ok.
|
||||
__ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError),
|
||||
R16_thread);
|
||||
__ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodErrorWithMethod),
|
||||
R16_thread, R19_method);
|
||||
|
||||
// Pop the C frame and restore LR.
|
||||
__ pop_frame();
|
||||
|
@ -3688,11 +3688,15 @@ void TemplateTable::invokeinterface(int byte_no) {
|
||||
|
||||
// Vtable entry was NULL => Throw abstract method error.
|
||||
__ bind(Lthrow_ame);
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError));
|
||||
// Pass arguments for generating a verbose error message.
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodErrorVerbose),
|
||||
Rrecv_klass, Rmethod);
|
||||
|
||||
// Interface was not found => Throw incompatible class change error.
|
||||
__ bind(L_no_such_interface);
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeError));
|
||||
// Pass arguments for generating a verbose error message.
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeErrorVerbose),
|
||||
Rrecv_klass, Rinterface_klass);
|
||||
DEBUG_ONLY( __ should_not_reach_here(); )
|
||||
|
||||
// Special case of invokeinterface called for virtual method of
|
||||
|
@ -2105,7 +2105,7 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm,
|
||||
// blocking or unlocking.
|
||||
// An OOP result (handle) is done specially in the slow-path code.
|
||||
//--------------------------------------------------------------------
|
||||
switch (ret_type) { //GLGLGL
|
||||
switch (ret_type) {
|
||||
case T_VOID: break; // Nothing to do!
|
||||
case T_FLOAT: break; // Got it where we want it (unless slow-path)
|
||||
case T_DOUBLE: break; // Got it where we want it (unless slow-path)
|
||||
|
@ -458,7 +458,8 @@ address TemplateInterpreterGenerator::generate_abstract_entry(void) {
|
||||
__ save_return_pc(); // Save Z_R14.
|
||||
__ push_frame_abi160(0); // Without new frame the RT call could overwrite the saved Z_R14.
|
||||
|
||||
__ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError), Z_thread);
|
||||
__ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodErrorWithMethod),
|
||||
Z_thread, Z_method);
|
||||
|
||||
__ pop_frame();
|
||||
__ restore_return_pc(); // Restore Z_R14.
|
||||
@ -686,7 +687,7 @@ address TemplateInterpreterGenerator::generate_return_entry_for (TosState state,
|
||||
return entry;
|
||||
}
|
||||
|
||||
address TemplateInterpreterGenerator::generate_deopt_entry_for (TosState state,
|
||||
address TemplateInterpreterGenerator::generate_deopt_entry_for(TosState state,
|
||||
int step,
|
||||
address continuation) {
|
||||
address entry = __ pc();
|
||||
|
@ -3742,8 +3742,12 @@ void TemplateTable::invokeinterface(int byte_no) {
|
||||
// Throw exception.
|
||||
__ restore_bcp(); // Bcp must be correct for exception handler (was destroyed).
|
||||
__ restore_locals(); // Make sure locals pointer is correct as well (was destroyed).
|
||||
// Pass arguments for generating a verbose error message.
|
||||
__ z_lgr(Z_tmp_1, method); // Prevent register clash.
|
||||
__ call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError));
|
||||
CAST_FROM_FN_PTR(address,
|
||||
InterpreterRuntime::throw_AbstractMethodErrorVerbose),
|
||||
klass, Z_tmp_1);
|
||||
// The call_VM checks for exception, so we should never return here.
|
||||
__ should_not_reach_here();
|
||||
|
||||
@ -3752,8 +3756,11 @@ void TemplateTable::invokeinterface(int byte_no) {
|
||||
// Throw exception.
|
||||
__ restore_bcp(); // Bcp must be correct for exception handler (was destroyed).
|
||||
__ restore_locals(); // Make sure locals pointer is correct as well (was destroyed).
|
||||
// Pass arguments for generating a verbose error message.
|
||||
__ call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeError));
|
||||
CAST_FROM_FN_PTR(address,
|
||||
InterpreterRuntime::throw_IncompatibleClassChangeErrorVerbose),
|
||||
klass, interface);
|
||||
// The call_VM checks for exception, so we should never return here.
|
||||
__ should_not_reach_here();
|
||||
|
||||
|
@ -191,7 +191,7 @@ address TemplateInterpreterGenerator::generate_abstract_entry(void) {
|
||||
address entry = __ pc();
|
||||
// abstract method entry
|
||||
// throw exception
|
||||
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError));
|
||||
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodErrorWithMethod), G5_method);
|
||||
// the call_VM checks for exception, so we should never return here.
|
||||
__ should_not_reach_here();
|
||||
return entry;
|
||||
|
@ -3137,8 +3137,10 @@ void TemplateTable::invokeinterface(int byte_no) {
|
||||
__ sub(Rindex, Method::itable_index_max, Rindex);
|
||||
__ neg(Rindex);
|
||||
|
||||
// Preserve O2_Klass for throw_AbstractMethodErrorVerbose
|
||||
__ mov(O2_Klass, O4);
|
||||
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||
O2_Klass, Rinterface, Rindex,
|
||||
O4, Rinterface, Rindex,
|
||||
// outputs: method, scan temp reg, temp reg
|
||||
G5_method, Rscratch, Rtemp,
|
||||
L_no_such_interface);
|
||||
@ -3147,7 +3149,9 @@ void TemplateTable::invokeinterface(int byte_no) {
|
||||
{
|
||||
Label ok;
|
||||
__ br_notnull_short(G5_method, Assembler::pt, ok);
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError));
|
||||
// Pass arguments for generating a verbose error message.
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodErrorVerbose),
|
||||
O2_Klass, Rmethod);
|
||||
__ should_not_reach_here();
|
||||
__ bind(ok);
|
||||
}
|
||||
@ -3160,7 +3164,9 @@ void TemplateTable::invokeinterface(int byte_no) {
|
||||
__ call_from_interpreter(Rcall, Gargs, Rret);
|
||||
|
||||
__ bind(L_no_such_interface);
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeError));
|
||||
// Pass arguments for generating a verbose error message.
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeErrorVerbose),
|
||||
O2_Klass, Rinterface);
|
||||
__ should_not_reach_here();
|
||||
}
|
||||
|
||||
@ -3536,7 +3542,7 @@ void TemplateTable::instanceof() {
|
||||
void TemplateTable::_breakpoint() {
|
||||
|
||||
// Note: We get here even if we are single stepping..
|
||||
// jbug inists on setting breakpoints at every bytecode
|
||||
// jbug insists on setting breakpoints at every bytecode
|
||||
// even if we are in single step mode.
|
||||
|
||||
transition(vtos, vtos);
|
||||
|
@ -212,7 +212,12 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) {
|
||||
__ delayed()->nop();
|
||||
|
||||
__ bind(L_no_such_interface);
|
||||
AddressLiteral icce(StubRoutines::throw_IncompatibleClassChangeError_entry());
|
||||
// Handle IncompatibleClassChangeError in itable stubs.
|
||||
// More detailed error message.
|
||||
// We force resolving of the call site by jumping to the "handle
|
||||
// wrong method" stub, and so let the interpreter runtime do all the
|
||||
// dirty work.
|
||||
AddressLiteral icce(SharedRuntime::get_handle_wrong_method_stub());
|
||||
__ jump_to(icce, G3_scratch);
|
||||
__ delayed()->restore();
|
||||
|
||||
|
@ -4425,7 +4425,7 @@ class StubGenerator: public StubCodeGenerator {
|
||||
* c_rarg0 - x address
|
||||
* c_rarg1 - x length
|
||||
* c_rarg2 - y address
|
||||
* c_rarg3 - y lenth
|
||||
* c_rarg3 - y length
|
||||
* not Win64
|
||||
* c_rarg4 - z address
|
||||
* c_rarg5 - z length
|
||||
|
@ -1354,7 +1354,7 @@ address TemplateInterpreterGenerator::generate_abstract_entry(void) {
|
||||
__ restore_locals(); // make sure locals pointer is correct as well (was destroyed)
|
||||
|
||||
// throw exception
|
||||
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError));
|
||||
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodErrorWithMethod), rbx);
|
||||
// the call_VM checks for exception, so we should never return here.
|
||||
__ should_not_reach_here();
|
||||
|
||||
|
@ -3872,6 +3872,8 @@ void TemplateTable::invokeinterface(int byte_no) {
|
||||
|
||||
Label no_such_interface, no_such_method;
|
||||
|
||||
// Preserve method for throw_AbstractMethodErrorVerbose.
|
||||
__ mov(rcx, rbx);
|
||||
// Receiver subtype check against REFC.
|
||||
// Superklass in rax. Subklass in rdx. Blows rcx, rdi.
|
||||
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||
@ -3893,8 +3895,10 @@ void TemplateTable::invokeinterface(int byte_no) {
|
||||
__ subl(rbx, Method::itable_index_max);
|
||||
__ negl(rbx);
|
||||
|
||||
// Preserve recvKlass for throw_AbstractMethodErrorVerbose.
|
||||
__ mov(rlocals, rdx);
|
||||
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||
rdx, rax, rbx,
|
||||
rlocals, rax, rbx,
|
||||
// outputs: method, scan temp. reg
|
||||
rbx, rbcp,
|
||||
no_such_interface);
|
||||
@ -3926,8 +3930,19 @@ void TemplateTable::invokeinterface(int byte_no) {
|
||||
__ pop(rbx); // pop return address (pushed by prepare_invoke)
|
||||
__ restore_bcp(); // rbcp must be correct for exception handler (was destroyed)
|
||||
__ restore_locals(); // make sure locals pointer is correct as well (was destroyed)
|
||||
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError));
|
||||
// the call_VM checks for exception, so we should never return here.
|
||||
// Pass arguments for generating a verbose error message.
|
||||
#ifdef _LP64
|
||||
Register recvKlass = c_rarg1;
|
||||
Register method = c_rarg2;
|
||||
if (recvKlass != rdx) { __ movq(recvKlass, rdx); }
|
||||
if (method != rcx) { __ movq(method, rcx); }
|
||||
#else
|
||||
Register recvKlass = rdx;
|
||||
Register method = rcx;
|
||||
#endif
|
||||
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodErrorVerbose),
|
||||
recvKlass, method);
|
||||
// The call_VM checks for exception, so we should never return here.
|
||||
__ should_not_reach_here();
|
||||
|
||||
__ bind(no_such_interface);
|
||||
@ -3935,8 +3950,10 @@ void TemplateTable::invokeinterface(int byte_no) {
|
||||
__ pop(rbx); // pop return address (pushed by prepare_invoke)
|
||||
__ restore_bcp(); // rbcp must be correct for exception handler (was destroyed)
|
||||
__ restore_locals(); // make sure locals pointer is correct as well (was destroyed)
|
||||
__ call_VM(noreg, CAST_FROM_FN_PTR(address,
|
||||
InterpreterRuntime::throw_IncompatibleClassChangeError));
|
||||
// Pass arguments for generating a verbose error message.
|
||||
LP64_ONLY( if (recvKlass != rdx) { __ movq(recvKlass, rdx); } )
|
||||
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeErrorVerbose),
|
||||
recvKlass, rax);
|
||||
// the call_VM checks for exception, so we should never return here.
|
||||
__ should_not_reach_here();
|
||||
}
|
||||
|
@ -212,7 +212,12 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) {
|
||||
__ jmp(Address(method, Method::from_compiled_offset()));
|
||||
|
||||
__ bind(L_no_such_interface);
|
||||
__ jump(RuntimeAddress(StubRoutines::throw_IncompatibleClassChangeError_entry()));
|
||||
// Handle IncompatibleClassChangeError in itable stubs.
|
||||
// More detailed error message.
|
||||
// We force resolving of the call site by jumping to the "handle
|
||||
// wrong method" stub, and so let the interpreter runtime do all the
|
||||
// dirty work.
|
||||
__ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub()));
|
||||
|
||||
__ flush();
|
||||
|
||||
|
@ -182,10 +182,10 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) {
|
||||
const Register method = rbx;
|
||||
__ load_klass(recv_klass_reg, j_rarg0); // restore recv_klass_reg
|
||||
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||
recv_klass_reg, holder_klass_reg, itable_index,
|
||||
// outputs: method, scan temp. reg
|
||||
method, temp_reg,
|
||||
L_no_such_interface);
|
||||
recv_klass_reg, holder_klass_reg, itable_index,
|
||||
// outputs: method, scan temp. reg
|
||||
method, temp_reg,
|
||||
L_no_such_interface);
|
||||
|
||||
// If we take a trap while this arg is on the stack we will not
|
||||
// be able to walk the stack properly. This is not an issue except
|
||||
@ -213,7 +213,12 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) {
|
||||
__ jmp(Address(method, Method::from_compiled_offset()));
|
||||
|
||||
__ bind(L_no_such_interface);
|
||||
__ jump(RuntimeAddress(StubRoutines::throw_IncompatibleClassChangeError_entry()));
|
||||
// Handle IncompatibleClassChangeError in itable stubs.
|
||||
// More detailed error message.
|
||||
// We force resolving of the call site by jumping to the "handle
|
||||
// wrong method" stub, and so let the interpreter runtime do all the
|
||||
// dirty work.
|
||||
__ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub()));
|
||||
|
||||
__ flush();
|
||||
|
||||
|
@ -2589,17 +2589,19 @@ run:
|
||||
if (ki->interface_klass() == iclass) break;
|
||||
}
|
||||
// If the interface isn't found, this class doesn't implement this
|
||||
// interface. The link resolver checks this but only for the first
|
||||
// interface. The link resolver checks this but only for the first
|
||||
// time this interface is called.
|
||||
if (i == int2->itable_length()) {
|
||||
VM_JAVA_ERROR(vmSymbols::java_lang_IncompatibleClassChangeError(), "", note_no_trap);
|
||||
CALL_VM(InterpreterRuntime::throw_IncompatibleClassChangeErrorVerbose(THREAD, rcvr->klass(), iclass),
|
||||
handle_exception);
|
||||
}
|
||||
int mindex = interface_method->itable_index();
|
||||
|
||||
itableMethodEntry* im = ki->first_method_entry(rcvr->klass());
|
||||
callee = im[mindex].method();
|
||||
if (callee == NULL) {
|
||||
VM_JAVA_ERROR(vmSymbols::java_lang_AbstractMethodError(), "", note_no_trap);
|
||||
CALL_VM(InterpreterRuntime::throw_AbstractMethodErrorVerbose(THREAD, rcvr->klass(), interface_method),
|
||||
handle_exception);
|
||||
}
|
||||
|
||||
// Profile virtual call.
|
||||
|
@ -487,8 +487,8 @@ IRT_ENTRY(address, InterpreterRuntime::exception_handler_for_exception(JavaThrea
|
||||
ResourceMark rm(thread);
|
||||
stringStream tempst;
|
||||
tempst.print("interpreter method <%s>\n"
|
||||
" at bci %d for thread " INTPTR_FORMAT,
|
||||
h_method->print_value_string(), current_bci, p2i(thread));
|
||||
" at bci %d for thread " INTPTR_FORMAT " (%s)",
|
||||
h_method->print_value_string(), current_bci, p2i(thread), thread->name());
|
||||
Exceptions::log_exception(h_exception, tempst);
|
||||
}
|
||||
// Don't go paging in something which won't be used.
|
||||
@ -582,11 +582,45 @@ IRT_ENTRY(void, InterpreterRuntime::throw_AbstractMethodError(JavaThread* thread
|
||||
THROW(vmSymbols::java_lang_AbstractMethodError());
|
||||
IRT_END
|
||||
|
||||
// This method is called from the "abstract_entry" of the interpreter.
|
||||
// At that point, the arguments have already been removed from the stack
|
||||
// and therefore we don't have the receiver object at our fingertips. (Though,
|
||||
// on some platforms the receiver still resides in a register...). Thus,
|
||||
// we have no choice but print an error message not containing the receiver
|
||||
// type.
|
||||
IRT_ENTRY(void, InterpreterRuntime::throw_AbstractMethodErrorWithMethod(JavaThread* thread,
|
||||
Method* missingMethod))
|
||||
ResourceMark rm(thread);
|
||||
assert(missingMethod != NULL, "sanity");
|
||||
methodHandle m(thread, missingMethod);
|
||||
LinkResolver::throw_abstract_method_error(m, THREAD);
|
||||
IRT_END
|
||||
|
||||
IRT_ENTRY(void, InterpreterRuntime::throw_AbstractMethodErrorVerbose(JavaThread* thread,
|
||||
Klass* recvKlass,
|
||||
Method* missingMethod))
|
||||
ResourceMark rm(thread);
|
||||
methodHandle mh = methodHandle(thread, missingMethod);
|
||||
LinkResolver::throw_abstract_method_error(mh, recvKlass, THREAD);
|
||||
IRT_END
|
||||
|
||||
|
||||
IRT_ENTRY(void, InterpreterRuntime::throw_IncompatibleClassChangeError(JavaThread* thread))
|
||||
THROW(vmSymbols::java_lang_IncompatibleClassChangeError());
|
||||
IRT_END
|
||||
|
||||
IRT_ENTRY(void, InterpreterRuntime::throw_IncompatibleClassChangeErrorVerbose(JavaThread* thread,
|
||||
Klass* recvKlass,
|
||||
Klass* interfaceKlass))
|
||||
ResourceMark rm(thread);
|
||||
char buf[1000];
|
||||
buf[0] = '\0';
|
||||
jio_snprintf(buf, sizeof(buf),
|
||||
"Class %s does not implement the requested interface %s",
|
||||
recvKlass ? recvKlass->external_name() : "NULL",
|
||||
interfaceKlass ? interfaceKlass->external_name() : "NULL");
|
||||
THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf);
|
||||
IRT_END
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
// Fields
|
||||
|
@ -118,7 +118,15 @@ class InterpreterRuntime: AllStatic {
|
||||
|
||||
// Exceptions thrown by the interpreter
|
||||
static void throw_AbstractMethodError(JavaThread* thread);
|
||||
static void throw_AbstractMethodErrorWithMethod(JavaThread* thread, Method* oop);
|
||||
static void throw_AbstractMethodErrorVerbose(JavaThread* thread,
|
||||
Klass* recvKlass,
|
||||
Method* missingMethod);
|
||||
|
||||
static void throw_IncompatibleClassChangeError(JavaThread* thread);
|
||||
static void throw_IncompatibleClassChangeErrorVerbose(JavaThread* thread,
|
||||
Klass* resc,
|
||||
Klass* interfaceKlass);
|
||||
static void throw_StackOverflowError(JavaThread* thread);
|
||||
static void throw_delayed_StackOverflowError(JavaThread* thread);
|
||||
static void throw_ArrayIndexOutOfBoundsException(JavaThread* thread, char* name, jint index);
|
||||
|
@ -1344,8 +1344,7 @@ void LinkResolver::runtime_resolve_virtual_method(CallInfo& result,
|
||||
|
||||
// do lookup based on receiver klass using the vtable index
|
||||
if (resolved_method->method_holder()->is_interface()) { // default or miranda method
|
||||
vtable_index = vtable_index_of_interface_method(resolved_klass,
|
||||
resolved_method);
|
||||
vtable_index = vtable_index_of_interface_method(resolved_klass, resolved_method);
|
||||
assert(vtable_index >= 0 , "we should have valid vtable index at this point");
|
||||
|
||||
selected_method = methodHandle(THREAD, recv_klass->method_at_vtable(vtable_index));
|
||||
@ -1355,7 +1354,7 @@ void LinkResolver::runtime_resolve_virtual_method(CallInfo& result,
|
||||
assert(!resolved_method->has_itable_index(), "");
|
||||
vtable_index = resolved_method->vtable_index();
|
||||
// We could get a negative vtable_index for final methods,
|
||||
// because as an optimization they are they are never put in the vtable,
|
||||
// because as an optimization they are never put in the vtable,
|
||||
// unless they override an existing method.
|
||||
// If we do get a negative, it means the resolved method is the the selected
|
||||
// method, and it can never be changed by an override.
|
||||
@ -1369,20 +1368,13 @@ void LinkResolver::runtime_resolve_virtual_method(CallInfo& result,
|
||||
|
||||
// check if method exists
|
||||
if (selected_method.is_null()) {
|
||||
ResourceMark rm(THREAD);
|
||||
THROW_MSG(vmSymbols::java_lang_AbstractMethodError(),
|
||||
Method::name_and_sig_as_C_string(resolved_klass,
|
||||
resolved_method->name(),
|
||||
resolved_method->signature()));
|
||||
throw_abstract_method_error(resolved_method, recv_klass, CHECK);
|
||||
}
|
||||
|
||||
// check if abstract
|
||||
if (check_null_and_abstract && selected_method->is_abstract()) {
|
||||
ResourceMark rm(THREAD);
|
||||
THROW_MSG(vmSymbols::java_lang_AbstractMethodError(),
|
||||
Method::name_and_sig_as_C_string(resolved_klass,
|
||||
selected_method->name(),
|
||||
selected_method->signature()));
|
||||
// Pass arguments for generating a verbose error message.
|
||||
throw_abstract_method_error(resolved_method, selected_method, recv_klass, CHECK);
|
||||
}
|
||||
|
||||
if (log_develop_is_enabled(Trace, vtables)) {
|
||||
@ -1438,53 +1430,46 @@ void LinkResolver::runtime_resolve_interface_method(CallInfo& result,
|
||||
// do lookup based on receiver klass
|
||||
// This search must match the linktime preparation search for itable initialization
|
||||
// to correctly enforce loader constraints for interface method inheritance
|
||||
methodHandle sel_method = lookup_instance_method_in_klasses(recv_klass,
|
||||
methodHandle selected_method = lookup_instance_method_in_klasses(recv_klass,
|
||||
resolved_method->name(),
|
||||
resolved_method->signature(), CHECK);
|
||||
if (sel_method.is_null() && !check_null_and_abstract) {
|
||||
if (selected_method.is_null() && !check_null_and_abstract) {
|
||||
// In theory this is a harmless placeholder value, but
|
||||
// in practice leaving in null affects the nsk default method tests.
|
||||
// This needs further study.
|
||||
sel_method = resolved_method;
|
||||
selected_method = resolved_method;
|
||||
}
|
||||
// check if method exists
|
||||
if (sel_method.is_null()) {
|
||||
ResourceMark rm(THREAD);
|
||||
THROW_MSG(vmSymbols::java_lang_AbstractMethodError(),
|
||||
Method::name_and_sig_as_C_string(recv_klass,
|
||||
resolved_method->name(),
|
||||
resolved_method->signature()));
|
||||
if (selected_method.is_null()) {
|
||||
// Pass arguments for generating a verbose error message.
|
||||
throw_abstract_method_error(resolved_method, recv_klass, CHECK);
|
||||
}
|
||||
// check access
|
||||
// Throw Illegal Access Error if sel_method is not public.
|
||||
if (!sel_method->is_public()) {
|
||||
// Throw Illegal Access Error if selected_method is not public.
|
||||
if (!selected_method->is_public()) {
|
||||
ResourceMark rm(THREAD);
|
||||
THROW_MSG(vmSymbols::java_lang_IllegalAccessError(),
|
||||
Method::name_and_sig_as_C_string(recv_klass,
|
||||
sel_method->name(),
|
||||
sel_method->signature()));
|
||||
selected_method->name(),
|
||||
selected_method->signature()));
|
||||
}
|
||||
// check if abstract
|
||||
if (check_null_and_abstract && sel_method->is_abstract()) {
|
||||
ResourceMark rm(THREAD);
|
||||
THROW_MSG(vmSymbols::java_lang_AbstractMethodError(),
|
||||
Method::name_and_sig_as_C_string(recv_klass,
|
||||
sel_method->name(),
|
||||
sel_method->signature()));
|
||||
if (check_null_and_abstract && selected_method->is_abstract()) {
|
||||
throw_abstract_method_error(resolved_method, selected_method, recv_klass, CHECK);
|
||||
}
|
||||
|
||||
if (log_develop_is_enabled(Trace, itables)) {
|
||||
trace_method_resolution("invokeinterface selected method: receiver-class:",
|
||||
recv_klass, resolved_klass, sel_method, true);
|
||||
recv_klass, resolved_klass, selected_method, true);
|
||||
}
|
||||
// setup result
|
||||
if (!resolved_method->has_itable_index()) {
|
||||
int vtable_index = resolved_method->vtable_index();
|
||||
assert(vtable_index == sel_method->vtable_index(), "sanity check");
|
||||
result.set_virtual(resolved_klass, recv_klass, resolved_method, sel_method, vtable_index, CHECK);
|
||||
assert(vtable_index == selected_method->vtable_index(), "sanity check");
|
||||
result.set_virtual(resolved_klass, recv_klass, resolved_method, selected_method, vtable_index, CHECK);
|
||||
} else {
|
||||
int itable_index = resolved_method()->itable_index();
|
||||
result.set_interface(resolved_klass, recv_klass, resolved_method, sel_method, itable_index, CHECK);
|
||||
result.set_interface(resolved_klass, recv_klass, resolved_method, selected_method, itable_index, CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1774,3 +1759,38 @@ void LinkResolver::resolve_dynamic_call(CallInfo& result,
|
||||
result.set_handle(resolved_method, resolved_appendix, resolved_method_type, THREAD);
|
||||
Exceptions::wrap_dynamic_exception(CHECK);
|
||||
}
|
||||
|
||||
// Selected method is abstract.
|
||||
void LinkResolver::throw_abstract_method_error(const methodHandle& resolved_method,
|
||||
const methodHandle& selected_method,
|
||||
Klass *recv_klass, TRAPS) {
|
||||
Klass *resolved_klass = resolved_method->method_holder();
|
||||
ResourceMark rm(THREAD);
|
||||
stringStream ss;
|
||||
|
||||
if (recv_klass != NULL) {
|
||||
ss.print("Receiver class %s does not define or inherit an "
|
||||
"implementation of the",
|
||||
recv_klass->external_name());
|
||||
} else {
|
||||
ss.print("Missing implementation of");
|
||||
}
|
||||
|
||||
assert(resolved_method.not_null(), "Sanity");
|
||||
ss.print(" resolved method %s%s%s%s of %s %s.",
|
||||
resolved_method->is_abstract() ? "abstract " : "",
|
||||
resolved_method->is_private() ? "private " : "",
|
||||
resolved_method->name()->as_C_string(),
|
||||
resolved_method->signature()->as_C_string(),
|
||||
resolved_klass->external_kind(),
|
||||
resolved_klass->external_name());
|
||||
|
||||
if (selected_method.not_null() && !(resolved_method == selected_method)) {
|
||||
ss.print(" Selected method is %s%s%s.",
|
||||
selected_method->is_abstract() ? "abstract " : "",
|
||||
selected_method->is_private() ? "private " : "",
|
||||
selected_method->name_and_sig_as_C_string());
|
||||
}
|
||||
|
||||
THROW_MSG(vmSymbols::java_lang_AbstractMethodError(), ss.as_string());
|
||||
}
|
||||
|
@ -347,5 +347,19 @@ class LinkResolver: AllStatic {
|
||||
static void resolve_invoke(CallInfo& result, Handle& recv,
|
||||
const methodHandle& attached_method,
|
||||
Bytecodes::Code byte, TRAPS);
|
||||
|
||||
public:
|
||||
// Only resolved method known.
|
||||
static void throw_abstract_method_error(const methodHandle& resolved_method, TRAPS) {
|
||||
throw_abstract_method_error(resolved_method, NULL, NULL, CHECK);
|
||||
}
|
||||
// Resolved method and receiver klass know.
|
||||
static void throw_abstract_method_error(const methodHandle& resolved_method, Klass *recv_klass, TRAPS) {
|
||||
throw_abstract_method_error(resolved_method, NULL, recv_klass, CHECK);
|
||||
}
|
||||
// Selected method is abstract.
|
||||
static void throw_abstract_method_error(const methodHandle& resolved_method,
|
||||
const methodHandle& selected_method,
|
||||
Klass *recv_klass, TRAPS);
|
||||
};
|
||||
#endif // SHARE_VM_INTERPRETER_LINKRESOLVER_HPP
|
||||
|
@ -636,12 +636,17 @@ const char* Klass::external_name() const {
|
||||
return name()->as_klass_external_name();
|
||||
}
|
||||
|
||||
|
||||
const char* Klass::signature_name() const {
|
||||
if (name() == NULL) return "<unknown>";
|
||||
return name()->as_C_string();
|
||||
}
|
||||
|
||||
const char* Klass::external_kind() const {
|
||||
if (is_interface()) return "interface";
|
||||
if (is_abstract()) return "abstract class";
|
||||
return "class";
|
||||
}
|
||||
|
||||
// Unless overridden, modifier_flags is 0.
|
||||
jint Klass::compute_modifier_flags(TRAPS) const {
|
||||
return 0;
|
||||
|
@ -546,6 +546,9 @@ protected:
|
||||
|
||||
const char* class_loader_and_module_name() const;
|
||||
|
||||
// Returns "interface", "abstract class" or "class".
|
||||
const char* external_kind() const;
|
||||
|
||||
// type testing operations
|
||||
#ifdef ASSERT
|
||||
protected:
|
||||
|
@ -839,9 +839,15 @@ address SharedRuntime::continuation_for_implicit_exception(JavaThread* thread,
|
||||
if (vt_stub->is_abstract_method_error(pc)) {
|
||||
assert(!vt_stub->is_vtable_stub(), "should never see AbstractMethodErrors from vtable-type VtableStubs");
|
||||
Events::log_exception(thread, "AbstractMethodError at " INTPTR_FORMAT, p2i(pc));
|
||||
return StubRoutines::throw_AbstractMethodError_entry();
|
||||
// Instead of throwing the abstract method error here directly, we re-resolve
|
||||
// and will throw the AbstractMethodError during resolve. As a result, we'll
|
||||
// get a more detailed error message.
|
||||
return SharedRuntime::get_handle_wrong_method_stub();
|
||||
} else {
|
||||
Events::log_exception(thread, "NullPointerException at vtable entry " INTPTR_FORMAT, p2i(pc));
|
||||
// Assert that the signal comes from the expected location in stub code.
|
||||
assert(vt_stub->is_null_pointer_exception(pc),
|
||||
"obtained signal from unexpected location in stub code");
|
||||
return StubRoutines::throw_NullPointerException_at_call_entry();
|
||||
}
|
||||
} else {
|
||||
@ -1454,7 +1460,29 @@ JRT_END
|
||||
|
||||
// Handle abstract method call
|
||||
JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method_abstract(JavaThread* thread))
|
||||
return StubRoutines::throw_AbstractMethodError_entry();
|
||||
// Verbose error message for AbstractMethodError.
|
||||
// Get the called method from the invoke bytecode.
|
||||
vframeStream vfst(thread, true);
|
||||
assert(!vfst.at_end(), "Java frame must exist");
|
||||
methodHandle caller(vfst.method());
|
||||
Bytecode_invoke invoke(caller, vfst.bci());
|
||||
DEBUG_ONLY( invoke.verify(); )
|
||||
|
||||
// Find the compiled caller frame.
|
||||
RegisterMap reg_map(thread);
|
||||
frame stubFrame = thread->last_frame();
|
||||
assert(stubFrame.is_runtime_frame(), "must be");
|
||||
frame callerFrame = stubFrame.sender(®_map);
|
||||
assert(callerFrame.is_compiled_frame(), "must be");
|
||||
|
||||
// Install exception and return forward entry.
|
||||
JRT_BLOCK
|
||||
methodHandle callee = invoke.static_target(thread);
|
||||
assert(!callee.is_null() && invoke.has_receiver(), "or we should not be here");
|
||||
oop recv = callerFrame.retrieve_receiver(®_map);
|
||||
LinkResolver::throw_abstract_method_error(callee, recv->klass(), thread);
|
||||
JRT_BLOCK_END
|
||||
return StubRoutines::forward_exception_entry();
|
||||
JRT_END
|
||||
|
||||
|
||||
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/* Method aFunctionOfMyInterface:"()Ljava/lang/String;" is missing in this implementation to cause error. */
|
||||
|
||||
class AME1_E extends AME1_B implements AME1_C {
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method AME1_B."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
public Method firstAbstractMethod:"()Ljava/lang/String;"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokevirtual Method java/lang/Object.getClass:"()Ljava/lang/Class;";
|
||||
invokevirtual Method java/lang/Class.getName:"()Ljava/lang/String;";
|
||||
areturn;
|
||||
}
|
||||
|
||||
public Method secondAbstractMethod:"()Ljava/lang/String;"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokevirtual Method java/lang/Object.getClass:"()Ljava/lang/Class;";
|
||||
invokevirtual Method java/lang/Class.getName:"()Ljava/lang/String;";
|
||||
areturn;
|
||||
}
|
||||
|
||||
/* Missing to cause error.
|
||||
public Method anAbstractMethod:"()Ljava/lang/String;"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokevirtual Method java/lang/Object.getClass:"()Ljava/lang/Class;";
|
||||
invokevirtual Method java/lang/Class.getName:"()Ljava/lang/String;";
|
||||
areturn;
|
||||
}
|
||||
*/
|
||||
|
||||
public Method firstFunctionOfMyInterface0:"()Ljava/lang/String;"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokevirtual Method java/lang/Object.getClass:"()Ljava/lang/Class;";
|
||||
invokevirtual Method java/lang/Class.getName:"()Ljava/lang/String;";
|
||||
areturn;
|
||||
}
|
||||
|
||||
public Method secondFunctionOfMyInterface0:"()Ljava/lang/String;"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokevirtual Method java/lang/Object.getClass:"()Ljava/lang/Class;";
|
||||
invokevirtual Method java/lang/Class.getName:"()Ljava/lang/String;";
|
||||
areturn;
|
||||
}
|
||||
|
||||
public Method firstFunctionOfMyInterface:"()Ljava/lang/String;"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokevirtual Method java/lang/Object.getClass:"()Ljava/lang/Class;";
|
||||
invokevirtual Method java/lang/Class.getName:"()Ljava/lang/String;";
|
||||
areturn;
|
||||
}
|
||||
|
||||
public Method secondFunctionOfMyInterface:"()Ljava/lang/String;"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokevirtual Method java/lang/Object.getClass:"()Ljava/lang/Class;";
|
||||
invokevirtual Method java/lang/Class.getName:"()Ljava/lang/String;";
|
||||
areturn;
|
||||
}
|
||||
|
||||
/* Missing to cause error.
|
||||
public Method aFunctionOfMyInterface:"()Ljava/lang/String;"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokevirtual Method java/lang/Object.getClass:"()Ljava/lang/Class;";
|
||||
invokevirtual Method java/lang/Class.getName:"()Ljava/lang/String;";
|
||||
areturn;
|
||||
}
|
||||
*/
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/* Method aFunctionOfMyInterface() is missing in this implementation to cause error. */
|
||||
|
||||
class AME2_C extends AME2_B {
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method AME2_B."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
public Method fun2:"()V"
|
||||
stack 0 locals 1
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/* Method ma() is missing in this implementation to cause error. */
|
||||
|
||||
class AME3_C extends AME3_B {
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method AME3_B."<init>":()V;
|
||||
return;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
|
||||
/* Method ma() is missing in this implementation to cause error. */
|
||||
|
||||
class AME4_E extends AME4_B {
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method AME4_B."<init>":()V;
|
||||
return;
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/* Method mc() is missing in this implementation to cause error. */
|
||||
|
||||
class AME5_B extends AME5_A {
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method AME5_A."<init>":()V;
|
||||
return;
|
||||
}
|
||||
|
||||
public Method ma:"()V"
|
||||
stack 2 locals 1
|
||||
{
|
||||
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
|
||||
ldc String "B.ma() ";
|
||||
invokevirtual Method java/io/PrintStream.print:"(Ljava/lang/String;)V";
|
||||
return;
|
||||
}
|
||||
|
||||
public Method mb:"()V"
|
||||
stack 2 locals 1
|
||||
{
|
||||
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
|
||||
ldc String "B.mb() ";
|
||||
invokevirtual Method java/io/PrintStream.print:"(Ljava/lang/String;)V";
|
||||
return;
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/* Method mc() is missing in this implementation to cause error. */
|
||||
|
||||
class AME6_B implements AME6_A {
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
public Method ma:"()V"
|
||||
stack 2 locals 1
|
||||
{
|
||||
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
|
||||
ldc String "B.ma() ";
|
||||
invokevirtual Method java/io/PrintStream.print:"(Ljava/lang/String;)V";
|
||||
return;
|
||||
}
|
||||
|
||||
public Method mb:"()V"
|
||||
stack 2 locals 1
|
||||
{
|
||||
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
|
||||
ldc String "B.mb() ";
|
||||
invokevirtual Method java/io/PrintStream.print:"(Ljava/lang/String;)V";
|
||||
return;
|
||||
}
|
||||
}
|
@ -0,0 +1,892 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Check that the verbose message of the AME is printed correctly.
|
||||
* @requires !(os.arch=="arm")
|
||||
* @library /test/lib /
|
||||
* @build sun.hotspot.WhiteBox
|
||||
* @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
* @compile AbstractMethodErrorTest.java
|
||||
* @compile AME1_E.jasm AME2_C.jasm AME3_C.jasm AME4_E.jasm AME5_B.jasm AME6_B.jasm
|
||||
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* -XX:-BackgroundCompilation -XX:-Inline
|
||||
* -XX:CompileCommand=exclude,AbstractMethodErrorTest::test_ame1
|
||||
* AbstractMethodErrorTest
|
||||
*/
|
||||
|
||||
import sun.hotspot.WhiteBox;
|
||||
import compiler.whitebox.CompilerWhiteBoxTest;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
// This test assembles an errorneous installation of classes.
|
||||
// First, compile the test by @compile. This results in a legal set
|
||||
// of classes.
|
||||
// Then, with jasm, generate incompatible classes that overwrite
|
||||
// the class files in the build directory.
|
||||
// Last, call the real test throwing an AbstractMethodError and
|
||||
// check the message generated.
|
||||
public class AbstractMethodErrorTest {
|
||||
|
||||
private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
|
||||
|
||||
private static boolean enableChecks = true;
|
||||
|
||||
public static void setup_test() {
|
||||
// Assure all exceptions are loaded.
|
||||
new AbstractMethodError();
|
||||
new IncompatibleClassChangeError();
|
||||
|
||||
enableChecks = false;
|
||||
// Warmup
|
||||
System.out.println("warmup:");
|
||||
test_ame5_compiled_vtable_stub();
|
||||
test_ame6_compiled_itable_stub();
|
||||
enableChecks = true;
|
||||
|
||||
// Compile
|
||||
try {
|
||||
Method method = AbstractMethodErrorTest.class.getMethod("test_ame5_compiled_vtable_stub");
|
||||
WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
|
||||
if (!WHITE_BOX.isMethodCompiled(method)) {
|
||||
throw new RuntimeException(method.getName() + " is not compiled");
|
||||
}
|
||||
method = AbstractMethodErrorTest.class.getMethod("test_ame6_compiled_itable_stub");
|
||||
WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
|
||||
if (!WHITE_BOX.isMethodCompiled(method)) {
|
||||
throw new RuntimeException(method.getName() + " is not compiled");
|
||||
}
|
||||
method = AME5_C.class.getMethod("c");
|
||||
WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
|
||||
if (!WHITE_BOX.isMethodCompiled(method)) {
|
||||
throw new RuntimeException("AME5_C." + method.getName() + " is not compiled");
|
||||
}
|
||||
method = AME5_D.class.getMethod("c");
|
||||
WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
|
||||
if (!WHITE_BOX.isMethodCompiled(method)) {
|
||||
throw new RuntimeException("AME5_D." + method.getName() + " is not compiled");
|
||||
}
|
||||
method = AME5_E.class.getMethod("c");
|
||||
WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
|
||||
if (!WHITE_BOX.isMethodCompiled(method)) {
|
||||
throw new RuntimeException("AME5_E." + method.getName() + " is not compiled");
|
||||
}
|
||||
method = AME6_C.class.getMethod("c");
|
||||
WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
|
||||
if (!WHITE_BOX.isMethodCompiled(method)) {
|
||||
throw new RuntimeException("AME6_C." + method.getName() + " is not compiled");
|
||||
}
|
||||
method = AME6_D.class.getMethod("c");
|
||||
WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
|
||||
if (!WHITE_BOX.isMethodCompiled(method)) {
|
||||
throw new RuntimeException("AME6_D." + method.getName() + " is not compiled");
|
||||
}
|
||||
method = AME6_E.class.getMethod("c");
|
||||
WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
|
||||
if (!WHITE_BOX.isMethodCompiled(method)) {
|
||||
throw new RuntimeException("AME6_E." + method.getName() + " is not compiled");
|
||||
}
|
||||
} catch (NoSuchMethodException e) { }
|
||||
}
|
||||
|
||||
private static String expectedErrorMessageAME1_1 =
|
||||
"Missing implementation of resolved method abstract " +
|
||||
"anAbstractMethod()Ljava/lang/String; of abstract class AME1_B.";
|
||||
private static String expectedErrorMessageAME1_2 =
|
||||
"Receiver class AME1_E does not define or inherit an implementation of the " +
|
||||
"resolved method abstract aFunctionOfMyInterface()Ljava/lang/String; of " +
|
||||
"interface AME1_C.";
|
||||
|
||||
public static void test_ame1() {
|
||||
AME1_B objectAbstract = new AME1_D();
|
||||
AME1_C objectInterface = new AME1_D();
|
||||
objectInterface.secondFunctionOfMyInterface();
|
||||
objectAbstract.anAbstractMethod();
|
||||
objectInterface.aFunctionOfMyInterface();
|
||||
|
||||
try {
|
||||
objectAbstract = new AME1_E();
|
||||
// AbstractMethodError gets thrown in the interpreter at:
|
||||
// InterpreterGenerator::generate_abstract_entry
|
||||
objectAbstract.anAbstractMethod();
|
||||
throw new RuntimeException("Expected AbstractRuntimeError was not thrown.");
|
||||
} catch (AbstractMethodError e) {
|
||||
String errorMsg = e.getMessage();
|
||||
if (errorMsg == null) {
|
||||
throw new RuntimeException("Caught AbstractMethodError with empty message.");
|
||||
} else if (!errorMsg.equals(expectedErrorMessageAME1_1)) {
|
||||
System.out.println("Expected: " + expectedErrorMessageAME1_1 + "\n" +
|
||||
"but got: " + errorMsg);
|
||||
throw new RuntimeException("Wrong error message of AbstractMethodError.");
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Caught unexpected exception: " + e);
|
||||
}
|
||||
|
||||
try {
|
||||
objectInterface = new AME1_E();
|
||||
// AbstractMethodError gets thrown in:
|
||||
// TemplateTable::invokeinterface or C-Interpreter loop
|
||||
objectInterface.aFunctionOfMyInterface();
|
||||
throw new RuntimeException("Expected AbstractRuntimeError was not thrown.");
|
||||
} catch (AbstractMethodError e) {
|
||||
String errorMsg = e.getMessage();
|
||||
if (errorMsg == null) {
|
||||
throw new RuntimeException("Caught AbstractMethodError with empty message.");
|
||||
} else if (!errorMsg.equals(expectedErrorMessageAME1_2)) {
|
||||
// Thrown via InterpreterRuntime::throw_AbstractMethodErrorVerbose().
|
||||
System.out.println("Expected: " + expectedErrorMessageAME1_2 + "\n" +
|
||||
"but got: " + errorMsg);
|
||||
throw new RuntimeException("Wrong error message of AbstractMethodError.");
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Caught unexpected exception: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String expectedErrorMessageAME2_Interpreted =
|
||||
"Missing implementation of resolved method abstract " +
|
||||
"aFunctionOfMyInterface()V of interface AME2_A.";
|
||||
private static String expectedErrorMessageAME2_Compiled =
|
||||
"Receiver class AME2_C does not define or inherit an implementation of the resolved method " +
|
||||
"abstract aFunctionOfMyInterface()V of interface AME2_A.";
|
||||
|
||||
public AbstractMethodErrorTest() throws InstantiationException, IllegalAccessException {
|
||||
try {
|
||||
AME2_B myAbstract = new ImplementsAllFunctions();
|
||||
myAbstract.fun2();
|
||||
myAbstract.aFunctionOfMyInterface();
|
||||
|
||||
// AME2_C does not implement the method
|
||||
// aFunctionOfMyInterface(). Expected runtime behavior is
|
||||
// throwing an AbstractMethodError.
|
||||
// The error will be thrown via throw_AbstractMethodErrorWithMethod()
|
||||
// if the template interpreter calls an abstract method by
|
||||
// entering the abstract method entry.
|
||||
myAbstract = new AME2_C();
|
||||
myAbstract.fun2();
|
||||
myAbstract.aFunctionOfMyInterface();
|
||||
} catch (SecurityException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Loop so that method gets eventually compiled/osred.
|
||||
public static void test_ame2() throws Exception {
|
||||
boolean seenInterpreted = false;
|
||||
boolean seenCompiled = false;
|
||||
|
||||
// Loop to test both, the interpreted and the compiled case.
|
||||
for (int i = 0; i < 10000 && !(seenInterpreted && seenCompiled); ++i) {
|
||||
try {
|
||||
// Supposed to throw AME with verbose message.
|
||||
new AbstractMethodErrorTest();
|
||||
|
||||
throw new RuntimeException("Expected AbstractMethodError was not thrown.");
|
||||
} catch (AbstractMethodError e) {
|
||||
String errorMsg = e.getMessage();
|
||||
|
||||
// Check the message obtained.
|
||||
if (errorMsg == null) {
|
||||
throw new RuntimeException("Caught AbstractMethodError with empty message.");
|
||||
} else if (errorMsg.equals(expectedErrorMessageAME2_Interpreted)) {
|
||||
seenInterpreted = true;
|
||||
} else if (errorMsg.equals(expectedErrorMessageAME2_Compiled)) {
|
||||
// Sparc and the other platforms behave differently here:
|
||||
// Sparc throws the exception via SharedRuntime::handle_wrong_method_abstract(),
|
||||
// x86, ppc and s390 via LinkResolver::runtime_resolve_virtual_method(). Thus,
|
||||
// sparc misses the test case for LinkResolver::runtime_resolve_virtual_method().
|
||||
seenCompiled = true;
|
||||
} else {
|
||||
System.out.println("Expected: " + expectedErrorMessageAME2_Interpreted + "\n" +
|
||||
"or: " + expectedErrorMessageAME2_Compiled + "\n" +
|
||||
"but got: " + errorMsg);
|
||||
throw new RuntimeException("Wrong error message of AbstractMethodError.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(seenInterpreted && seenCompiled)) {
|
||||
if (seenInterpreted) { System.out.println("Saw interpreted message."); }
|
||||
if (seenCompiled) { System.out.println("Saw compiled message."); }
|
||||
throw new RuntimeException("Test did not produce wrong error messages for AbstractMethodError, " +
|
||||
"but it did not test both cases (interpreted and compiled).");
|
||||
}
|
||||
}
|
||||
|
||||
private static String expectedErrorMessageAME3_1 =
|
||||
"Receiver class AME3_C does not define or inherit an implementation of the resolved method " +
|
||||
"ma()V of class AME3_A. Selected method is abstract AME3_B.ma()V.";
|
||||
|
||||
// Testing abstract class that extends a class that has an implementation.
|
||||
// Loop so that method gets eventually compiled/osred.
|
||||
public static void test_ame3_1() throws Exception {
|
||||
AME3_A c = new AME3_C();
|
||||
|
||||
try {
|
||||
// Supposed to throw AME with verbose message.
|
||||
c.ma();
|
||||
|
||||
throw new RuntimeException("Expected AbstractMethodError was not thrown.");
|
||||
} catch (AbstractMethodError e) {
|
||||
String errorMsg = e.getMessage();
|
||||
|
||||
// Check the message obtained.
|
||||
if (errorMsg == null) {
|
||||
throw new RuntimeException("Caught AbstractMethodError with empty message.");
|
||||
} else if (errorMsg.equals(expectedErrorMessageAME3_1)) {
|
||||
// Expected test case thrown via LinkResolver::runtime_resolve_virtual_method().
|
||||
} else {
|
||||
System.out.println("Expected: " + expectedErrorMessageAME3_1 + "\n" +
|
||||
"but got: " + errorMsg);
|
||||
throw new RuntimeException("Wrong error message of AbstractMethodError.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String expectedErrorMessageAME3_2 =
|
||||
"Receiver class AME3_C does not define or inherit an implementation of " +
|
||||
"the resolved method abstract ma()V of abstract class AME3_B.";
|
||||
|
||||
// Testing abstract class that extends a class that has an implementation.
|
||||
// Loop so that method gets eventually compiled/osred.
|
||||
public static void test_ame3_2() throws Exception {
|
||||
AME3_C c = new AME3_C();
|
||||
|
||||
try {
|
||||
// Supposed to throw AME with verbose message.
|
||||
c.ma();
|
||||
|
||||
throw new RuntimeException("Expected AbstractMethodError was not thrown.");
|
||||
} catch (AbstractMethodError e) {
|
||||
String errorMsg = e.getMessage();
|
||||
|
||||
// Check the message obtained.
|
||||
if (errorMsg == null) {
|
||||
throw new RuntimeException("Caught AbstractMethodError with empty message.");
|
||||
} else if (errorMsg.equals(expectedErrorMessageAME3_2)) {
|
||||
// Expected test case thrown via LinkResolver::runtime_resolve_virtual_method().
|
||||
} else {
|
||||
System.out.println("Expected: " + expectedErrorMessageAME3_2 + "\n" +
|
||||
"but got: " + errorMsg);
|
||||
throw new RuntimeException("Wrong error message of AbstractMethodError.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String expectedErrorMessageAME4 =
|
||||
"Missing implementation of resolved method abstract ma()V of " +
|
||||
"abstract class AME4_B.";
|
||||
|
||||
// Testing abstract class that extends a class that has an implementation.
|
||||
public static void test_ame4() throws Exception {
|
||||
AME4_C c = new AME4_C();
|
||||
AME4_D d = new AME4_D();
|
||||
AME4_E e = new AME4_E(); // Errorneous.
|
||||
|
||||
AME4_A a;
|
||||
try {
|
||||
// Test: calls errorneous e.ma() in the last iteration.
|
||||
final int iterations = 10;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
a = e;
|
||||
if (i % 2 == 0 && i < iterations - 1) {
|
||||
a = c;
|
||||
}
|
||||
if (i % 2 == 1 && i < iterations - 1) {
|
||||
a = d;
|
||||
}
|
||||
|
||||
// AbstractMethodError gets thrown in the interpreter at:
|
||||
// InterpreterGenerator::generate_abstract_entry
|
||||
a.ma();
|
||||
}
|
||||
|
||||
throw new RuntimeException("Expected AbstractMethodError was not thrown.");
|
||||
} catch (AbstractMethodError exc) {
|
||||
System.out.println();
|
||||
String errorMsg = exc.getMessage();
|
||||
|
||||
// Check the message obtained.
|
||||
if (enableChecks && errorMsg == null) {
|
||||
throw new RuntimeException("Caught AbstractMethodError with empty message.");
|
||||
} else if (errorMsg.equals(expectedErrorMessageAME4)) {
|
||||
// Expected test case.
|
||||
} else if (enableChecks) {
|
||||
System.out.println("Expected: " + expectedErrorMessageAME4 + "\n" +
|
||||
"but got: " + errorMsg);
|
||||
throw new RuntimeException("Wrong error message of AbstractMethodError.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String expectedErrorMessageAME5_VtableStub =
|
||||
"Receiver class AME5_B does not define or inherit an implementation of the resolved method abstract mc()V " +
|
||||
"of abstract class AME5_A.";
|
||||
|
||||
// AbstractMethodErrors detected in vtable stubs.
|
||||
// Note: How can we verify that we really stepped through the vtable stub?
|
||||
// - Bimorphic inlining should not happen since we have no profiling data when
|
||||
// we compile the method
|
||||
// - As a result, an inline cache call should be generated
|
||||
// - This inline cache call is patched into a real vtable call at the first
|
||||
// re-resolve, which happens constantly during the first 10 iterations of the loop.
|
||||
// => we should be fine! :-)
|
||||
public static void test_ame5_compiled_vtable_stub() {
|
||||
// Allocated the objects we need and call a valid method.
|
||||
boolean caught_ame = false;
|
||||
AME5_B b = new AME5_B();
|
||||
AME5_C c = new AME5_C();
|
||||
AME5_D d = new AME5_D();
|
||||
AME5_E e = new AME5_E();
|
||||
b.ma();
|
||||
c.ma();
|
||||
d.ma();
|
||||
e.ma();
|
||||
|
||||
try {
|
||||
final int iterations = 10;
|
||||
// Test: calls b.c() in the last iteration.
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
AME5_A a = b;
|
||||
if (i % 3 == 0 && i < iterations - 1) {
|
||||
a = c;
|
||||
}
|
||||
if (i % 3 == 1 && i < iterations - 1) {
|
||||
a = d;
|
||||
}
|
||||
if (i % 3 == 2 && i < iterations - 1) {
|
||||
a = e;
|
||||
}
|
||||
|
||||
a.mc();
|
||||
}
|
||||
System.out.println();
|
||||
} catch (AbstractMethodError exc) {
|
||||
caught_ame = true;
|
||||
System.out.println();
|
||||
String errorMsg = exc.getMessage();
|
||||
if (enableChecks && errorMsg == null) {
|
||||
System.out.println(exc);
|
||||
throw new RuntimeException("Empty error message of AbstractMethodError.");
|
||||
}
|
||||
if (enableChecks &&
|
||||
!errorMsg.equals(expectedErrorMessageAME5_VtableStub)) {
|
||||
// Thrown via SharedRuntime::handle_wrong_method_abstract().
|
||||
System.out.println("Expected: " + expectedErrorMessageAME5_VtableStub + "\n" +
|
||||
"but got: " + errorMsg);
|
||||
System.out.println(exc);
|
||||
throw new RuntimeException("Wrong error message of AbstractMethodError.");
|
||||
}
|
||||
if (enableChecks) {
|
||||
System.out.println("Passed with message: " + errorMsg);
|
||||
}
|
||||
} catch (Throwable exc) {
|
||||
|
||||
throw exc;
|
||||
}
|
||||
|
||||
// Check that we got the exception at some point.
|
||||
if (enableChecks && !caught_ame) {
|
||||
throw new RuntimeException("Expected AbstractMethodError was not thrown.");
|
||||
}
|
||||
}
|
||||
|
||||
private static String expectedErrorMessageAME6_ItableStub =
|
||||
"Receiver class AME6_B does not define or inherit an implementation of the resolved" +
|
||||
" method abstract mc()V of interface AME6_A.";
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// AbstractMethodErrors detected in itable stubs.
|
||||
// Note: How can we verify that we really stepped through the itable stub?
|
||||
// - Bimorphic inlining should not happen since we have no profiling data when
|
||||
// we compile the method
|
||||
// - As a result, an inline cache call should be generated
|
||||
// - This inline cache call is patched into a real vtable call at the first
|
||||
// re-resolve, which happens constantly during the first 10 iterations of the loop.
|
||||
// => we should be fine! :-)
|
||||
public static void test_ame6_compiled_itable_stub() {
|
||||
// Allocated the objects we need and call a valid method.
|
||||
boolean caught_ame = false;
|
||||
AME6_B b = new AME6_B();
|
||||
AME6_C c = new AME6_C();
|
||||
AME6_D d = new AME6_D();
|
||||
AME6_E e = new AME6_E();
|
||||
b.ma();
|
||||
c.ma();
|
||||
d.ma();
|
||||
e.ma();
|
||||
|
||||
try {
|
||||
final int iterations = 10;
|
||||
// Test: calls b.c() in the last iteration.
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
AME6_A a = b;
|
||||
if (i % 3 == 0 && i < iterations - 1) {
|
||||
a = c;
|
||||
}
|
||||
if (i % 3 == 1 && i < iterations - 1) {
|
||||
a = d;
|
||||
}
|
||||
if (i % 3 == 2 && i < iterations - 1) {
|
||||
a = e;
|
||||
}
|
||||
a.mc();
|
||||
}
|
||||
System.out.println();
|
||||
} catch (AbstractMethodError exc) {
|
||||
caught_ame = true;
|
||||
System.out.println();
|
||||
String errorMsg = exc.getMessage();
|
||||
if (enableChecks && errorMsg == null) {
|
||||
System.out.println(exc);
|
||||
throw new RuntimeException("Empty error message of AbstractMethodError.");
|
||||
}
|
||||
if (enableChecks &&
|
||||
!errorMsg.equals(expectedErrorMessageAME6_ItableStub)) {
|
||||
// Thrown via LinkResolver::runtime_resolve_interface_method().
|
||||
System.out.println("Expected: " + expectedErrorMessageAME6_ItableStub + "\n" +
|
||||
"but got: " + errorMsg);
|
||||
System.out.println(exc);
|
||||
throw new RuntimeException("Wrong error message of AbstractMethodError.");
|
||||
}
|
||||
if (enableChecks) {
|
||||
System.out.println("Passed with message: " + errorMsg);
|
||||
}
|
||||
} catch (Throwable exc) {
|
||||
throw exc;
|
||||
}
|
||||
|
||||
// Check that we got the exception at some point.
|
||||
if (enableChecks && !caught_ame) {
|
||||
throw new RuntimeException("Expected AbstractMethodError was not thrown.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
setup_test();
|
||||
test_ame1();
|
||||
test_ame2();
|
||||
test_ame3_1();
|
||||
test_ame3_2();
|
||||
test_ame4();
|
||||
test_ame5_compiled_vtable_stub();
|
||||
test_ame6_compiled_itable_stub();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper classes to test abstract method error.
|
||||
//
|
||||
// Errorneous versions of these classes are implemented in java
|
||||
// assembler.
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// This error should be detected interpreted.
|
||||
//
|
||||
// Class hierachy:
|
||||
//
|
||||
// C // interface, defines aFunctionOfMyInterface()
|
||||
// |
|
||||
// A | // interface
|
||||
// | |
|
||||
// B | // abstract class, defines anAbstractMethod()
|
||||
// \ /
|
||||
// E // errorneous class implementation lacks methods C::aFunctionOfMyInterface()
|
||||
// B::anAbstractMethod()
|
||||
interface AME1_A {
|
||||
|
||||
public String firstFunctionOfMyInterface0();
|
||||
|
||||
public String secondFunctionOfMyInterface0();
|
||||
}
|
||||
|
||||
abstract class AME1_B implements AME1_A {
|
||||
|
||||
abstract public String firstAbstractMethod();
|
||||
|
||||
abstract public String secondAbstractMethod();
|
||||
|
||||
abstract public String anAbstractMethod();
|
||||
}
|
||||
|
||||
interface AME1_C {
|
||||
|
||||
public String firstFunctionOfMyInterface();
|
||||
|
||||
public String secondFunctionOfMyInterface();
|
||||
|
||||
public String aFunctionOfMyInterface();
|
||||
}
|
||||
|
||||
class AME1_D extends AME1_B implements AME1_C {
|
||||
|
||||
public AME1_D() {
|
||||
}
|
||||
|
||||
public String firstAbstractMethod() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String secondAbstractMethod() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String anAbstractMethod() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String firstFunctionOfMyInterface0() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String secondFunctionOfMyInterface0() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String firstFunctionOfMyInterface() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String secondFunctionOfMyInterface() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String aFunctionOfMyInterface() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
}
|
||||
|
||||
class AME1_E extends AME1_B implements AME1_C {
|
||||
|
||||
public AME1_E() {
|
||||
}
|
||||
|
||||
public String firstAbstractMethod() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String secondAbstractMethod() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
// This method is missing in the .jasm implementation.
|
||||
public String anAbstractMethod() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String firstFunctionOfMyInterface0() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String secondFunctionOfMyInterface0() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String firstFunctionOfMyInterface() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String secondFunctionOfMyInterface() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
// This method is missing in the .jasm implementation.
|
||||
public String aFunctionOfMyInterface() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// This error should be detected interpreted.
|
||||
//
|
||||
// Class hierachy:
|
||||
//
|
||||
// A // an interface declaring aFunctionOfMyInterface()
|
||||
// |
|
||||
// B // an abstract class
|
||||
// |
|
||||
// C // errorneous implementation lacks method A::aFunctionOfMyInterface()
|
||||
//
|
||||
interface AME2_A {
|
||||
public void aFunctionOfMyInterface();
|
||||
}
|
||||
|
||||
abstract class AME2_B implements AME2_A {
|
||||
abstract public void fun2();
|
||||
}
|
||||
|
||||
class ImplementsAllFunctions extends AME2_B {
|
||||
|
||||
public ImplementsAllFunctions() {}
|
||||
|
||||
public void fun2() {
|
||||
//System.out.print("You called public void ImplementsAllFunctions::fun2().\n");
|
||||
}
|
||||
|
||||
public void aFunctionOfMyInterface() {
|
||||
//System.out.print("You called public void ImplementsAllFunctions::aFunctionOfMyInterface()\n");
|
||||
}
|
||||
}
|
||||
|
||||
class AME2_C extends AME2_B {
|
||||
|
||||
public AME2_C() {}
|
||||
|
||||
public void fun2() {
|
||||
//System.out.print("You called public void AME2_C::fun2().\n");
|
||||
}
|
||||
|
||||
// This method is missing in the .jasm implementation.
|
||||
public void aFunctionOfMyInterface() {
|
||||
//System.out.print("You called public void AME2_C::aFunctionOfMyInterface()\n");
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test AbstractMethod error shadowing existing implementation.
|
||||
//
|
||||
// Class hierachy:
|
||||
//
|
||||
// A // a class implementing m()
|
||||
// |
|
||||
// B // an abstract class defining m() abstract
|
||||
// |
|
||||
// C // an errorneous class lacking an implementation of m()
|
||||
//
|
||||
class AME3_A {
|
||||
public void ma() {
|
||||
System.out.print("A.ma() ");
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AME3_B extends AME3_A {
|
||||
public abstract void ma();
|
||||
}
|
||||
|
||||
class AME3_C extends AME3_B {
|
||||
// This method is missing in the .jasm implementation.
|
||||
public void ma() {
|
||||
System.out.print("C.ma() ");
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test AbstractMethod error shadowing existing implementation. In
|
||||
// this test there are several subclasses of the abstract class.
|
||||
//
|
||||
// Class hierachy:
|
||||
//
|
||||
// A // A: a class implementing ma()
|
||||
// |
|
||||
// B // B: an abstract class defining ma() abstract
|
||||
// / | \
|
||||
// C D E // E: an errorneous class lacking an implementation of ma()
|
||||
//
|
||||
class AME4_A {
|
||||
public void ma() {
|
||||
System.out.print("A.ma() ");
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AME4_B extends AME4_A {
|
||||
public abstract void ma();
|
||||
}
|
||||
|
||||
class AME4_C extends AME4_B {
|
||||
public void ma() {
|
||||
System.out.print("C.ma() ");
|
||||
}
|
||||
}
|
||||
|
||||
class AME4_D extends AME4_B {
|
||||
public void ma() {
|
||||
System.out.print("D.ma() ");
|
||||
}
|
||||
}
|
||||
|
||||
class AME4_E extends AME4_B {
|
||||
// This method is missing in the .jasm implementation.
|
||||
public void ma() {
|
||||
System.out.print("E.ma() ");
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// This error should be detected while processing the vtable stub.
|
||||
//
|
||||
// Class hierachy:
|
||||
//
|
||||
// A__ // abstract
|
||||
// /|\ \
|
||||
// C D E \
|
||||
// B // Bad class, missing method implementation.
|
||||
//
|
||||
// Test:
|
||||
// - Call D.mc() / E.mc() / F.mc() several times to force real vtable call constrution
|
||||
// - Call errorneous B.mc() in the end to raise the AbstraceMethodError
|
||||
|
||||
abstract class AME5_A {
|
||||
abstract void ma();
|
||||
abstract void mb();
|
||||
abstract void mc();
|
||||
}
|
||||
|
||||
class AME5_B extends AME5_A {
|
||||
void ma() {
|
||||
System.out.print("B.ma() ");
|
||||
}
|
||||
|
||||
void mb() {
|
||||
System.out.print("B.mb() ");
|
||||
}
|
||||
|
||||
// This method is missing in the .jasm implementation.
|
||||
void mc() {
|
||||
System.out.print("B.mc() ");
|
||||
}
|
||||
}
|
||||
|
||||
class AME5_C extends AME5_A {
|
||||
void ma() {
|
||||
System.out.print("C.ma() ");
|
||||
}
|
||||
|
||||
void mb() {
|
||||
System.out.print("C.mb() ");
|
||||
}
|
||||
|
||||
void mc() {
|
||||
System.out.print("C.mc() ");
|
||||
}
|
||||
}
|
||||
|
||||
class AME5_D extends AME5_A {
|
||||
void ma() {
|
||||
System.out.print("D.ma() ");
|
||||
}
|
||||
|
||||
void mb() {
|
||||
System.out.print("D.mb() ");
|
||||
}
|
||||
|
||||
void mc() {
|
||||
System.out.print("D.mc() ");
|
||||
}
|
||||
}
|
||||
|
||||
class AME5_E extends AME5_A {
|
||||
void ma() {
|
||||
System.out.print("E.ma() ");
|
||||
}
|
||||
|
||||
void mb() {
|
||||
System.out.print("E.mb() ");
|
||||
}
|
||||
|
||||
void mc() {
|
||||
System.out.print("E.mc() ");
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Test AbstractMethod error detected while processing
|
||||
// the itable stub.
|
||||
//
|
||||
// Class hierachy:
|
||||
//
|
||||
// A__ (interface)
|
||||
// /|\ \
|
||||
// C D E \
|
||||
// B (bad class, missing method)
|
||||
//
|
||||
// Test:
|
||||
// - Call D.mc() / E.mc() / F.mc() several times to force real itable call constrution
|
||||
// - Call errorneous B.mc() in the end to raise the AbstraceMethodError
|
||||
|
||||
interface AME6_A {
|
||||
abstract void ma();
|
||||
abstract void mb();
|
||||
abstract void mc();
|
||||
}
|
||||
|
||||
class AME6_B implements AME6_A {
|
||||
public void ma() {
|
||||
System.out.print("B.ma() ");
|
||||
}
|
||||
|
||||
public void mb() {
|
||||
System.out.print("B.mb() ");
|
||||
}
|
||||
|
||||
// This method is missing in the .jasm implementation.
|
||||
public void mc() {
|
||||
System.out.print("B.mc() ");
|
||||
}
|
||||
}
|
||||
|
||||
class AME6_C implements AME6_A {
|
||||
public void ma() {
|
||||
System.out.print("C.ma() ");
|
||||
}
|
||||
|
||||
public void mb() {
|
||||
System.out.print("C.mb() ");
|
||||
}
|
||||
|
||||
public void mc() {
|
||||
System.out.print("C.mc() ");
|
||||
}
|
||||
}
|
||||
|
||||
class AME6_D implements AME6_A {
|
||||
public void ma() {
|
||||
System.out.print("D.ma() ");
|
||||
}
|
||||
|
||||
public void mb() {
|
||||
System.out.print("D.mb() ");
|
||||
}
|
||||
|
||||
public void mc() {
|
||||
System.out.print("D.mc() ");
|
||||
}
|
||||
}
|
||||
|
||||
class AME6_E implements AME6_A {
|
||||
public void ma() {
|
||||
System.out.print("E.ma() ");
|
||||
}
|
||||
|
||||
public void mb() {
|
||||
System.out.print("E.mb() ");
|
||||
}
|
||||
|
||||
public void mc() {
|
||||
System.out.print("E.mc() ");
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
class ICC_B implements ICC_iA {
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
public Method a:"()V"
|
||||
stack 2 locals 1
|
||||
{
|
||||
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
|
||||
ldc String "B.a()";
|
||||
invokevirtual Method java/io/PrintStream.print:"(Ljava/lang/String;)V";
|
||||
return;
|
||||
}
|
||||
|
||||
public Method b:"()V"
|
||||
stack 2 locals 1
|
||||
{
|
||||
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
|
||||
ldc String "B.b()";
|
||||
invokevirtual Method java/io/PrintStream.print:"(Ljava/lang/String;)V";
|
||||
return;
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ImplementsSomeInterfaces extends AbstractICCE0 {
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method AbstractICCE0."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
public Method firstAbstractMethod:"()Ljava/lang/String;"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokevirtual Method java/lang/Object.getClass:"()Ljava/lang/Class;";
|
||||
invokevirtual Method java/lang/Class.getName:"()Ljava/lang/String;";
|
||||
areturn;
|
||||
}
|
||||
|
||||
public Method secondAbstractMethod:"()Ljava/lang/String;"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokevirtual Method java/lang/Object.getClass:"()Ljava/lang/Class;";
|
||||
invokevirtual Method java/lang/Class.getName:"()Ljava/lang/String;";
|
||||
areturn;
|
||||
}
|
||||
|
||||
public Method firstFunctionOfMyInterface0:"()Ljava/lang/String;"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokevirtual Method java/lang/Object.getClass:"()Ljava/lang/Class;";
|
||||
invokevirtual Method java/lang/Class.getName:"()Ljava/lang/String;";
|
||||
areturn;
|
||||
}
|
||||
|
||||
public Method secondFunctionOfMyInterface0:"()Ljava/lang/String;"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokevirtual Method java/lang/Object.getClass:"()Ljava/lang/Class;";
|
||||
invokevirtual Method java/lang/Class.getName:"()Ljava/lang/String;";
|
||||
areturn;
|
||||
}
|
||||
|
||||
public Method firstFunctionOfMyInterface:"()Ljava/lang/String;"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokevirtual Method java/lang/Object.getClass:"()Ljava/lang/Class;";
|
||||
invokevirtual Method java/lang/Class.getName:"()Ljava/lang/String;";
|
||||
areturn;
|
||||
}
|
||||
|
||||
public Method secondFunctionOfMyInterface:"()Ljava/lang/String;"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokevirtual Method java/lang/Object.getClass:"()Ljava/lang/Class;";
|
||||
invokevirtual Method java/lang/Class.getName:"()Ljava/lang/String;";
|
||||
areturn;
|
||||
}
|
||||
}
|
@ -0,0 +1,354 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Check that the verbose message of ICCE is printed correctly.
|
||||
* The test forces errors in vtable stubs and interpreter.
|
||||
* @requires !(os.arch=="arm")
|
||||
* @library /test/lib /
|
||||
* @build sun.hotspot.WhiteBox
|
||||
* @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
* @compile IncompatibleClassChangeErrorTest.java
|
||||
* @compile ImplementsSomeInterfaces.jasm ICC_B.jasm
|
||||
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* -XX:-BackgroundCompilation -XX:-Inline
|
||||
* -XX:CompileCommand=exclude,IncompatibleClassChangeErrorTest::test_iccInt
|
||||
* IncompatibleClassChangeErrorTest
|
||||
*/
|
||||
|
||||
import sun.hotspot.WhiteBox;
|
||||
import compiler.whitebox.CompilerWhiteBoxTest;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
// This test assembles an errorneous installation of classes.
|
||||
// First, compile the test by @compile. This results in a legal set
|
||||
// of classes.
|
||||
// Then, with jasm, generate incompatible classes that overwrite
|
||||
// the class files in the build directory.
|
||||
// Last, call the real tests throwing IncompatibleClassChangeErrors
|
||||
// and check the messages generated.
|
||||
public class IncompatibleClassChangeErrorTest {
|
||||
|
||||
private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
|
||||
|
||||
private static boolean enableChecks = true;
|
||||
|
||||
private static String expectedErrorMessageInterpreted =
|
||||
"Class ImplementsSomeInterfaces " +
|
||||
"does not implement the requested interface InterfaceICCE1";
|
||||
private static String expectedErrorMessageCompiled =
|
||||
"Class ICC_B does not implement the requested interface ICC_iB";
|
||||
// old message: "vtable stub"
|
||||
|
||||
public static void setup_test() {
|
||||
// Assure all exceptions are loaded.
|
||||
new AbstractMethodError();
|
||||
new IncompatibleClassChangeError();
|
||||
|
||||
enableChecks = false;
|
||||
// Warmup
|
||||
System.out.println("warmup:");
|
||||
test_iccInt();
|
||||
test_icc_compiled_itable_stub();
|
||||
enableChecks = true;
|
||||
|
||||
// Compile
|
||||
try {
|
||||
Method method = IncompatibleClassChangeErrorTest.class.getMethod("test_icc_compiled_itable_stub");
|
||||
WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
|
||||
if (!WHITE_BOX.isMethodCompiled(method)) {
|
||||
throw new RuntimeException(method.getName() + " is not compiled");
|
||||
}
|
||||
method = ICC_C.class.getMethod("b");
|
||||
WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
|
||||
if (!WHITE_BOX.isMethodCompiled(method)) {
|
||||
throw new RuntimeException("ICC_C." + method.getName() + " is not compiled");
|
||||
}
|
||||
method = ICC_D.class.getMethod("b");
|
||||
WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
|
||||
if (!WHITE_BOX.isMethodCompiled(method)) {
|
||||
throw new RuntimeException("ICC_D." + method.getName() + " is not compiled");
|
||||
}
|
||||
method = ICC_E.class.getMethod("b");
|
||||
WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
|
||||
if (!WHITE_BOX.isMethodCompiled(method)) {
|
||||
throw new RuntimeException("ICC_E." + method.getName() + " is not compiled");
|
||||
}
|
||||
} catch (NoSuchMethodException e) { }
|
||||
System.out.println("warmup done.");
|
||||
}
|
||||
|
||||
// Should never be compiled.
|
||||
public static void test_iccInt() {
|
||||
boolean caught_icc = false;
|
||||
try {
|
||||
InterfaceICCE1 objectInterface = new ImplementsSomeInterfaces();
|
||||
// IncompatibleClassChangeError gets thrown in
|
||||
// - TemplateTable::invokeinterface()
|
||||
// - LinkResolver::runtime_resolve_interface_method()
|
||||
objectInterface.aFunctionOfMyInterface();
|
||||
} catch (IncompatibleClassChangeError e) {
|
||||
String errorMsg = e.getMessage();
|
||||
if (enableChecks && !errorMsg.equals(expectedErrorMessageInterpreted)) {
|
||||
System.out.println("Expected: " + expectedErrorMessageInterpreted + "\n" +
|
||||
"but got: " + errorMsg);
|
||||
throw new RuntimeException("Wrong error message of IncompatibleClassChangeError.");
|
||||
}
|
||||
caught_icc = true;
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Caught unexpected exception: " + e);
|
||||
}
|
||||
|
||||
// Check we got the exception.
|
||||
if (!caught_icc) {
|
||||
throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown.");
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Test AbstractMethodErrors detected in itable stubs.
|
||||
// Note: How can we verify that we really stepped through the vtable stub?
|
||||
// - Bimorphic inlining should not happen since we have no profiling data when
|
||||
// we compile the method
|
||||
// - As a result, an inline cache call should be generated
|
||||
// - This inline cache call is patched into a real vtable call at the first
|
||||
// re-resolve, which happens constantly during the first 10 iterations of the loop.
|
||||
// => we should be fine! :-)
|
||||
public static void test_icc_compiled_itable_stub() {
|
||||
// Allocated the objects we need and call a valid method.
|
||||
boolean caught_icc = false;
|
||||
ICC_B b = new ICC_B();
|
||||
ICC_C c = new ICC_C();
|
||||
ICC_D d = new ICC_D();
|
||||
ICC_E e = new ICC_E();
|
||||
b.a();
|
||||
c.a();
|
||||
d.a();
|
||||
e.a();
|
||||
|
||||
try {
|
||||
final int iterations = 10;
|
||||
// Test: calls b.b() in the last iteration.
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
ICC_iB a = b;
|
||||
if (i % 3 == 0 && i < iterations - 1) {
|
||||
a = c;
|
||||
}
|
||||
if (i % 3 == 1 && i < iterations - 1) {
|
||||
a = d;
|
||||
}
|
||||
if (i % 3 == 2 && i < iterations - 1) {
|
||||
a = e;
|
||||
}
|
||||
a.b();
|
||||
}
|
||||
} catch (AbstractMethodError exc) {
|
||||
// It's a subclass of IncompatibleClassChangeError, so we must catch this first.
|
||||
System.out.println();
|
||||
System.out.println(exc);
|
||||
if (enableChecks) {
|
||||
String errorMsg = exc.getMessage();
|
||||
if (errorMsg == null) {
|
||||
throw new RuntimeException("Caught unexpected AbstractMethodError with empty message.");
|
||||
}
|
||||
throw new RuntimeException("Caught unexpected AbstractMethodError.");
|
||||
}
|
||||
} catch (IncompatibleClassChangeError exc) {
|
||||
caught_icc = true;
|
||||
System.out.println();
|
||||
String errorMsg = exc.getMessage();
|
||||
if (enableChecks && errorMsg == null) {
|
||||
System.out.println(exc);
|
||||
throw new RuntimeException("Empty error message of IncompatibleClassChangeError.");
|
||||
}
|
||||
if (enableChecks &&
|
||||
!errorMsg.equals(expectedErrorMessageCompiled)) {
|
||||
System.out.println("Expected: " + expectedErrorMessageCompiled + "\n" +
|
||||
"but got: " + errorMsg);
|
||||
System.out.println(exc);
|
||||
throw new RuntimeException("Wrong error message of IncompatibleClassChangeError.");
|
||||
}
|
||||
if (enableChecks) {
|
||||
System.out.println("Passed with message: " + errorMsg);
|
||||
}
|
||||
} catch (Throwable exc) {
|
||||
throw exc; // new RuntimeException("Caught unexpected exception: " + exc);
|
||||
}
|
||||
|
||||
// Check we got the exception at some point.
|
||||
if (enableChecks && !caught_icc) {
|
||||
throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
setup_test();
|
||||
test_iccInt();
|
||||
test_icc_compiled_itable_stub();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Helper classes to test incompatible class change in interpreter.
|
||||
//
|
||||
// The test also contains .jasm files with implementations
|
||||
// of the classes that shall generate the errors.
|
||||
|
||||
|
||||
// I0 // interface defining aFunctionOfMyInterface()
|
||||
// |
|
||||
// | I1 // interface
|
||||
// | |
|
||||
// A0 | // abstract class
|
||||
// \ /
|
||||
// C // class not implementing I1 and
|
||||
// not implementing I0::aFunctionOfMyInterface()
|
||||
//
|
||||
// Test is expected to throw error because of missing interface and not
|
||||
// because of missing method.
|
||||
|
||||
interface InterfaceICCE0 {
|
||||
public String firstFunctionOfMyInterface0();
|
||||
public String secondFunctionOfMyInterface0();
|
||||
}
|
||||
|
||||
interface InterfaceICCE1 {
|
||||
|
||||
public String firstFunctionOfMyInterface();
|
||||
|
||||
public String secondFunctionOfMyInterface();
|
||||
|
||||
public String aFunctionOfMyInterface();
|
||||
}
|
||||
|
||||
abstract class AbstractICCE0 implements InterfaceICCE0 {
|
||||
abstract public String firstAbstractMethod();
|
||||
abstract public String secondAbstractMethod();
|
||||
|
||||
abstract public String anAbstractMethod();
|
||||
}
|
||||
|
||||
class ImplementsSomeInterfaces extends
|
||||
AbstractICCE0
|
||||
// This interface is missing in the .jasm implementation.
|
||||
implements InterfaceICCE1
|
||||
{
|
||||
|
||||
public String firstAbstractMethod() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String secondAbstractMethod() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
// This method is missing in the .jasm implementation.
|
||||
public String anAbstractMethod() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String firstFunctionOfMyInterface0() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String secondFunctionOfMyInterface0() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String firstFunctionOfMyInterface() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
public String secondFunctionOfMyInterface() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
// This method is missing in the .jasm implementation.
|
||||
public String aFunctionOfMyInterface() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper classes to test incompatible class change in itable stub.
|
||||
//
|
||||
// Class hierachy:
|
||||
//
|
||||
// iA,iB (interfaces)
|
||||
// /|\ \
|
||||
// C D E \
|
||||
// B (bad class, missing interface implementation)
|
||||
|
||||
interface ICC_iA {
|
||||
public void a();
|
||||
}
|
||||
|
||||
interface ICC_iB {
|
||||
public void b();
|
||||
}
|
||||
|
||||
// This is the errorneous class. A variant of it not
|
||||
// implementing ICC_iB is copied into the test before
|
||||
// it is run.
|
||||
class ICC_B implements ICC_iA,
|
||||
// This interface is missing in the .jasm implementation.
|
||||
ICC_iB {
|
||||
public void a() {
|
||||
System.out.print("B.a() ");
|
||||
}
|
||||
|
||||
public void b() {
|
||||
System.out.print("B.b() ");
|
||||
}
|
||||
}
|
||||
|
||||
class ICC_C implements ICC_iA, ICC_iB {
|
||||
public void a() {
|
||||
System.out.print("C.a() ");
|
||||
}
|
||||
|
||||
public void b() {
|
||||
System.out.print("C.b() ");
|
||||
}
|
||||
}
|
||||
|
||||
class ICC_D implements ICC_iA, ICC_iB {
|
||||
public void a() {
|
||||
System.out.print("D.a() ");
|
||||
}
|
||||
|
||||
public void b() {
|
||||
System.out.print("D.b() ");
|
||||
}
|
||||
}
|
||||
|
||||
class ICC_E implements ICC_iA, ICC_iB {
|
||||
public void a() {
|
||||
System.out.print("E.a() ");
|
||||
}
|
||||
|
||||
public void b() {
|
||||
System.out.print("E.b() ");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user