mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 14:24:46 +02:00
6990212: JSR 292 JVMTI MethodEnter hook is not called for JSR 292 bootstrap and target methods
Check for single stepping when dispatching invokes from method handles Reviewed-by: coleenp, twisti, kvn, dsamersoff
This commit is contained in:
parent
cf91e8dbca
commit
8acdd5ce55
7 changed files with 139 additions and 40 deletions
|
@ -524,6 +524,30 @@ void MethodHandles::verify_klass(MacroAssembler* _masm,
|
||||||
}
|
}
|
||||||
#endif // ASSERT
|
#endif // ASSERT
|
||||||
|
|
||||||
|
|
||||||
|
void MethodHandles::jump_from_method_handle(MacroAssembler* _masm, Register method, Register target, Register temp) {
|
||||||
|
assert(method == G5_method, "interpreter calling convention");
|
||||||
|
__ verify_oop(method);
|
||||||
|
__ ld_ptr(G5_method, in_bytes(methodOopDesc::from_interpreted_offset()), target);
|
||||||
|
if (JvmtiExport::can_post_interpreter_events()) {
|
||||||
|
// JVMTI events, such as single-stepping, are implemented partly by avoiding running
|
||||||
|
// compiled code in threads for which the event is enabled. Check here for
|
||||||
|
// interp_only_mode if these events CAN be enabled.
|
||||||
|
__ verify_thread();
|
||||||
|
Label skip_compiled_code;
|
||||||
|
|
||||||
|
const Address interp_only(G2_thread, JavaThread::interp_only_mode_offset());
|
||||||
|
__ ld(interp_only, temp);
|
||||||
|
__ tst(temp);
|
||||||
|
__ br(Assembler::notZero, true, Assembler::pn, skip_compiled_code);
|
||||||
|
__ delayed()->ld_ptr(G5_method, in_bytes(methodOopDesc::interpreter_entry_offset()), target);
|
||||||
|
__ bind(skip_compiled_code);
|
||||||
|
}
|
||||||
|
__ jmp(target, 0);
|
||||||
|
__ delayed()->nop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Code generation
|
// Code generation
|
||||||
address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm) {
|
address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm) {
|
||||||
// I5_savedSP/O5_savedSP: sender SP (must preserve)
|
// I5_savedSP/O5_savedSP: sender SP (must preserve)
|
||||||
|
@ -1105,9 +1129,6 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||||
guarantee(java_lang_invoke_MethodHandle::vmentry_offset_in_bytes() != 0, "must have offsets");
|
guarantee(java_lang_invoke_MethodHandle::vmentry_offset_in_bytes() != 0, "must have offsets");
|
||||||
|
|
||||||
// Some handy addresses:
|
// Some handy addresses:
|
||||||
Address G5_method_fie( G5_method, in_bytes(methodOopDesc::from_interpreted_offset()));
|
|
||||||
Address G5_method_fce( G5_method, in_bytes(methodOopDesc::from_compiled_offset()));
|
|
||||||
|
|
||||||
Address G3_mh_vmtarget( G3_method_handle, java_lang_invoke_MethodHandle::vmtarget_offset_in_bytes());
|
Address G3_mh_vmtarget( G3_method_handle, java_lang_invoke_MethodHandle::vmtarget_offset_in_bytes());
|
||||||
|
|
||||||
Address G3_dmh_vmindex( G3_method_handle, java_lang_invoke_DirectMethodHandle::vmindex_offset_in_bytes());
|
Address G3_dmh_vmindex( G3_method_handle, java_lang_invoke_DirectMethodHandle::vmindex_offset_in_bytes());
|
||||||
|
@ -1136,24 +1157,23 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||||
case _raise_exception:
|
case _raise_exception:
|
||||||
{
|
{
|
||||||
// Not a real MH entry, but rather shared code for raising an
|
// Not a real MH entry, but rather shared code for raising an
|
||||||
// exception. Since we use the compiled entry, arguments are
|
// exception. For sharing purposes the arguments are passed into registers
|
||||||
// expected in compiler argument registers.
|
// and then placed in the intepreter calling convention here.
|
||||||
assert(raise_exception_method(), "must be set");
|
assert(raise_exception_method(), "must be set");
|
||||||
assert(raise_exception_method()->from_compiled_entry(), "method must be linked");
|
assert(raise_exception_method()->from_compiled_entry(), "method must be linked");
|
||||||
|
|
||||||
__ mov(O5_savedSP, SP); // Cut the stack back to where the caller started.
|
|
||||||
|
|
||||||
Label L_no_method;
|
|
||||||
// FIXME: fill in _raise_exception_method with a suitable java.lang.invoke method
|
|
||||||
__ set(AddressLiteral((address) &_raise_exception_method), G5_method);
|
__ set(AddressLiteral((address) &_raise_exception_method), G5_method);
|
||||||
__ ld_ptr(Address(G5_method, 0), G5_method);
|
__ ld_ptr(Address(G5_method, 0), G5_method);
|
||||||
|
|
||||||
const int jobject_oop_offset = 0;
|
const int jobject_oop_offset = 0;
|
||||||
__ ld_ptr(Address(G5_method, jobject_oop_offset), G5_method);
|
__ ld_ptr(Address(G5_method, jobject_oop_offset), G5_method);
|
||||||
|
|
||||||
__ verify_oop(G5_method);
|
adjust_SP_and_Gargs_down_by_slots(_masm, 3, noreg, noreg);
|
||||||
__ jump_indirect_to(G5_method_fce, O3_scratch); // jump to compiled entry
|
|
||||||
__ delayed()->nop();
|
__ st_ptr(O0_code, __ argument_address(constant(2), noreg, 0));
|
||||||
|
__ st_ptr(O1_actual, __ argument_address(constant(1), noreg, 0));
|
||||||
|
__ st_ptr(O2_required, __ argument_address(constant(0), noreg, 0));
|
||||||
|
jump_from_method_handle(_masm, G5_method, O1_scratch, O2_scratch);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1161,7 +1181,6 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||||
case _invokespecial_mh:
|
case _invokespecial_mh:
|
||||||
{
|
{
|
||||||
__ load_heap_oop(G3_mh_vmtarget, G5_method); // target is a methodOop
|
__ load_heap_oop(G3_mh_vmtarget, G5_method); // target is a methodOop
|
||||||
__ verify_oop(G5_method);
|
|
||||||
// Same as TemplateTable::invokestatic or invokespecial,
|
// Same as TemplateTable::invokestatic or invokespecial,
|
||||||
// minus the CP setup and profiling:
|
// minus the CP setup and profiling:
|
||||||
if (ek == _invokespecial_mh) {
|
if (ek == _invokespecial_mh) {
|
||||||
|
@ -1171,8 +1190,7 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||||
__ null_check(G3_method_handle);
|
__ null_check(G3_method_handle);
|
||||||
__ verify_oop(G3_method_handle);
|
__ verify_oop(G3_method_handle);
|
||||||
}
|
}
|
||||||
__ jump_indirect_to(G5_method_fie, O1_scratch);
|
jump_from_method_handle(_masm, G5_method, O1_scratch, O2_scratch);
|
||||||
__ delayed()->nop();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1204,9 +1222,7 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||||
Address vtable_entry_addr(O0_klass, base + vtableEntry::method_offset_in_bytes());
|
Address vtable_entry_addr(O0_klass, base + vtableEntry::method_offset_in_bytes());
|
||||||
__ ld_ptr(vtable_entry_addr, G5_method);
|
__ ld_ptr(vtable_entry_addr, G5_method);
|
||||||
|
|
||||||
__ verify_oop(G5_method);
|
jump_from_method_handle(_masm, G5_method, O1_scratch, O2_scratch);
|
||||||
__ jump_indirect_to(G5_method_fie, O1_scratch);
|
|
||||||
__ delayed()->nop();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1237,9 +1253,7 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||||
O3_scratch,
|
O3_scratch,
|
||||||
no_such_interface);
|
no_such_interface);
|
||||||
|
|
||||||
__ verify_oop(G5_method);
|
jump_from_method_handle(_masm, G5_method, O1_scratch, O2_scratch);
|
||||||
__ jump_indirect_to(G5_method_fie, O1_scratch);
|
|
||||||
__ delayed()->nop();
|
|
||||||
|
|
||||||
__ bind(no_such_interface);
|
__ bind(no_such_interface);
|
||||||
// Throw an exception.
|
// Throw an exception.
|
||||||
|
@ -1283,9 +1297,7 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||||
|
|
||||||
if (direct_to_method) {
|
if (direct_to_method) {
|
||||||
__ load_heap_oop(G3_mh_vmtarget, G5_method); // target is a methodOop
|
__ load_heap_oop(G3_mh_vmtarget, G5_method); // target is a methodOop
|
||||||
__ verify_oop(G5_method);
|
jump_from_method_handle(_masm, G5_method, O1_scratch, O2_scratch);
|
||||||
__ jump_indirect_to(G5_method_fie, O1_scratch);
|
|
||||||
__ delayed()->nop();
|
|
||||||
} else {
|
} else {
|
||||||
__ load_heap_oop(G3_mh_vmtarget, G3_method_handle); // target is a methodOop
|
__ load_heap_oop(G3_mh_vmtarget, G3_method_handle); // target is a methodOop
|
||||||
__ verify_oop(G3_method_handle);
|
__ verify_oop(G3_method_handle);
|
||||||
|
|
|
@ -221,4 +221,8 @@ public:
|
||||||
"reference is a MH");
|
"reference is a MH");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Similar to InterpreterMacroAssembler::jump_from_interpreted.
|
||||||
|
// Takes care of special dispatch from single stepping too.
|
||||||
|
static void jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp, Register temp2);
|
||||||
|
|
||||||
static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN;
|
static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN;
|
||||||
|
|
|
@ -403,9 +403,9 @@ void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register
|
||||||
// interp_only_mode if these events CAN be enabled.
|
// interp_only_mode if these events CAN be enabled.
|
||||||
get_thread(temp);
|
get_thread(temp);
|
||||||
// interp_only is an int, on little endian it is sufficient to test the byte only
|
// interp_only is an int, on little endian it is sufficient to test the byte only
|
||||||
// Is a cmpl faster (ce
|
// Is a cmpl faster?
|
||||||
cmpb(Address(temp, JavaThread::interp_only_mode_offset()), 0);
|
cmpb(Address(temp, JavaThread::interp_only_mode_offset()), 0);
|
||||||
jcc(Assembler::zero, run_compiled_code);
|
jccb(Assembler::zero, run_compiled_code);
|
||||||
jmp(Address(method, methodOopDesc::interpreter_entry_offset()));
|
jmp(Address(method, methodOopDesc::interpreter_entry_offset()));
|
||||||
bind(run_compiled_code);
|
bind(run_compiled_code);
|
||||||
}
|
}
|
||||||
|
|
|
@ -402,7 +402,7 @@ void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register
|
||||||
// interp_only is an int, on little endian it is sufficient to test the byte only
|
// interp_only is an int, on little endian it is sufficient to test the byte only
|
||||||
// Is a cmpl faster?
|
// Is a cmpl faster?
|
||||||
cmpb(Address(r15_thread, JavaThread::interp_only_mode_offset()), 0);
|
cmpb(Address(r15_thread, JavaThread::interp_only_mode_offset()), 0);
|
||||||
jcc(Assembler::zero, run_compiled_code);
|
jccb(Assembler::zero, run_compiled_code);
|
||||||
jmp(Address(method, methodOopDesc::interpreter_entry_offset()));
|
jmp(Address(method, methodOopDesc::interpreter_entry_offset()));
|
||||||
bind(run_compiled_code);
|
bind(run_compiled_code);
|
||||||
}
|
}
|
||||||
|
|
|
@ -546,6 +546,28 @@ void MethodHandles::verify_klass(MacroAssembler* _masm,
|
||||||
}
|
}
|
||||||
#endif //ASSERT
|
#endif //ASSERT
|
||||||
|
|
||||||
|
void MethodHandles::jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp) {
|
||||||
|
if (JvmtiExport::can_post_interpreter_events()) {
|
||||||
|
Label run_compiled_code;
|
||||||
|
// JVMTI events, such as single-stepping, are implemented partly by avoiding running
|
||||||
|
// compiled code in threads for which the event is enabled. Check here for
|
||||||
|
// interp_only_mode if these events CAN be enabled.
|
||||||
|
#ifdef _LP64
|
||||||
|
Register rthread = r15_thread;
|
||||||
|
#else
|
||||||
|
Register rthread = temp;
|
||||||
|
__ get_thread(rthread);
|
||||||
|
#endif
|
||||||
|
// interp_only is an int, on little endian it is sufficient to test the byte only
|
||||||
|
// Is a cmpl faster?
|
||||||
|
__ cmpb(Address(rthread, JavaThread::interp_only_mode_offset()), 0);
|
||||||
|
__ jccb(Assembler::zero, run_compiled_code);
|
||||||
|
__ jmp(Address(method, methodOopDesc::interpreter_entry_offset()));
|
||||||
|
__ bind(run_compiled_code);
|
||||||
|
}
|
||||||
|
__ jmp(Address(method, methodOopDesc::from_interpreted_offset()));
|
||||||
|
}
|
||||||
|
|
||||||
// Code generation
|
// Code generation
|
||||||
address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm) {
|
address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm) {
|
||||||
// rbx: methodOop
|
// rbx: methodOop
|
||||||
|
@ -1120,9 +1142,6 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||||
guarantee(java_lang_invoke_MethodHandle::vmentry_offset_in_bytes() != 0, "must have offsets");
|
guarantee(java_lang_invoke_MethodHandle::vmentry_offset_in_bytes() != 0, "must have offsets");
|
||||||
|
|
||||||
// some handy addresses
|
// some handy addresses
|
||||||
Address rbx_method_fie( rbx, methodOopDesc::from_interpreted_offset() );
|
|
||||||
Address rbx_method_fce( rbx, methodOopDesc::from_compiled_offset() );
|
|
||||||
|
|
||||||
Address rcx_mh_vmtarget( rcx_recv, java_lang_invoke_MethodHandle::vmtarget_offset_in_bytes() );
|
Address rcx_mh_vmtarget( rcx_recv, java_lang_invoke_MethodHandle::vmtarget_offset_in_bytes() );
|
||||||
Address rcx_dmh_vmindex( rcx_recv, java_lang_invoke_DirectMethodHandle::vmindex_offset_in_bytes() );
|
Address rcx_dmh_vmindex( rcx_recv, java_lang_invoke_DirectMethodHandle::vmindex_offset_in_bytes() );
|
||||||
|
|
||||||
|
@ -1163,8 +1182,8 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||||
assert(raise_exception_method(), "must be set");
|
assert(raise_exception_method(), "must be set");
|
||||||
assert(raise_exception_method()->from_compiled_entry(), "method must be linked");
|
assert(raise_exception_method()->from_compiled_entry(), "method must be linked");
|
||||||
|
|
||||||
const Register rdi_pc = rax;
|
const Register rax_pc = rax;
|
||||||
__ pop(rdi_pc); // caller PC
|
__ pop(rax_pc); // caller PC
|
||||||
__ mov(rsp, saved_last_sp); // cut the stack back to where the caller started
|
__ mov(rsp, saved_last_sp); // cut the stack back to where the caller started
|
||||||
|
|
||||||
Register rbx_method = rbx_temp;
|
Register rbx_method = rbx_temp;
|
||||||
|
@ -1172,11 +1191,15 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||||
|
|
||||||
const int jobject_oop_offset = 0;
|
const int jobject_oop_offset = 0;
|
||||||
__ movptr(rbx_method, Address(rbx_method, jobject_oop_offset)); // dereference the jobject
|
__ movptr(rbx_method, Address(rbx_method, jobject_oop_offset)); // dereference the jobject
|
||||||
__ verify_oop(rbx_method);
|
|
||||||
|
|
||||||
NOT_LP64(__ push(rarg2_required));
|
__ movptr(rsi, rsp);
|
||||||
__ push(rdi_pc); // restore caller PC
|
__ subptr(rsp, 3 * wordSize);
|
||||||
__ jmp(rbx_method_fce); // jump to compiled entry
|
__ push(rax_pc); // restore caller PC
|
||||||
|
|
||||||
|
__ movptr(__ argument_address(constant(2)), rarg0_code);
|
||||||
|
__ movptr(__ argument_address(constant(1)), rarg1_actual);
|
||||||
|
__ movptr(__ argument_address(constant(0)), rarg2_required);
|
||||||
|
jump_from_method_handle(_masm, rbx_method, rax);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1195,7 +1218,7 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||||
__ null_check(rcx_recv);
|
__ null_check(rcx_recv);
|
||||||
__ verify_oop(rcx_recv);
|
__ verify_oop(rcx_recv);
|
||||||
}
|
}
|
||||||
__ jmp(rbx_method_fie);
|
jump_from_method_handle(_masm, rbx_method, rax);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1228,7 +1251,7 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||||
__ movptr(rbx_method, vtable_entry_addr);
|
__ movptr(rbx_method, vtable_entry_addr);
|
||||||
|
|
||||||
__ verify_oop(rbx_method);
|
__ verify_oop(rbx_method);
|
||||||
__ jmp(rbx_method_fie);
|
jump_from_method_handle(_masm, rbx_method, rax);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1263,7 +1286,7 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||||
no_such_interface);
|
no_such_interface);
|
||||||
|
|
||||||
__ verify_oop(rbx_method);
|
__ verify_oop(rbx_method);
|
||||||
__ jmp(rbx_method_fie);
|
jump_from_method_handle(_masm, rbx_method, rax);
|
||||||
__ hlt();
|
__ hlt();
|
||||||
|
|
||||||
__ bind(no_such_interface);
|
__ bind(no_such_interface);
|
||||||
|
@ -1311,7 +1334,7 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||||
Register rbx_method = rbx_temp;
|
Register rbx_method = rbx_temp;
|
||||||
__ load_heap_oop(rbx_method, rcx_mh_vmtarget);
|
__ load_heap_oop(rbx_method, rcx_mh_vmtarget);
|
||||||
__ verify_oop(rbx_method);
|
__ verify_oop(rbx_method);
|
||||||
__ jmp(rbx_method_fie);
|
jump_from_method_handle(_masm, rbx_method, rax);
|
||||||
} else {
|
} else {
|
||||||
__ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
|
__ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
|
||||||
__ verify_oop(rcx_recv);
|
__ verify_oop(rcx_recv);
|
||||||
|
|
|
@ -291,6 +291,10 @@ public:
|
||||||
"reference is a MH");
|
"reference is a MH");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Similar to InterpreterMacroAssembler::jump_from_interpreted.
|
||||||
|
// Takes care of special dispatch from single stepping too.
|
||||||
|
static void jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp);
|
||||||
|
|
||||||
static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN;
|
static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN;
|
||||||
|
|
||||||
static Register saved_last_sp_register() {
|
static Register saved_last_sp_register() {
|
||||||
|
|
56
hotspot/test/compiler/6990212/Test6990212.java
Normal file
56
hotspot/test/compiler/6990212/Test6990212.java
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @bug 6990212
|
||||||
|
* @summary JSR 292 JVMTI MethodEnter hook is not called for JSR 292 bootstrap and target methods
|
||||||
|
*
|
||||||
|
* @run main Test6990212
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.invoke.*;
|
||||||
|
|
||||||
|
interface intf {
|
||||||
|
public Object target();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Test6990212 implements intf {
|
||||||
|
public Object target() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Throwable {
|
||||||
|
// Build an interface invoke and then invoke it on something
|
||||||
|
// that doesn't implement the interface to test the
|
||||||
|
// raiseException path.
|
||||||
|
MethodHandle target = MethodHandles.lookup().findVirtual(intf.class, "target", MethodType.methodType(Object.class));
|
||||||
|
try {
|
||||||
|
target.invoke(new Object());
|
||||||
|
} catch (ClassCastException cce) {
|
||||||
|
// everything is ok
|
||||||
|
System.out.println("got expected ClassCastException");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue