8197405: Improve messages of AbstractMethodErrors and IncompatibleClassChangeErrors

Reviewed-by: coleenp, dholmes, mdoerr, njian
This commit is contained in:
Goetz Lindenmaier 2018-02-08 09:23:49 +01:00
parent 10259cf594
commit 507c62fc76
35 changed files with 1981 additions and 79 deletions

View File

@ -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();

View File

@ -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;

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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

View File

@ -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)

View File

@ -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();

View File

@ -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();

View File

@ -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;

View File

@ -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);

View File

@ -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();

View File

@ -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

View File

@ -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();

View File

@ -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();
}

View File

@ -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();

View File

@ -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();

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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());
}

View File

@ -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

View File

@ -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;

View File

@ -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:

View File

@ -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(&reg_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(&reg_map);
LinkResolver::throw_abstract_method_error(callee, recv->klass(), thread);
JRT_BLOCK_END
return StubRoutines::forward_exception_entry();
JRT_END

View File

@ -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;
}
*/
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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() ");
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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() ");
}
}