A bound method handle is a small struct containing one or more saved argument values, plus a target method handle which will eventually receive those arguments.
The file BoundMethodHandle.java
contains a framework for generating these small structs, one subclass per basic type.
Each distinct small struct is represented by a subclass BoundMethodHandle with a name like BoundMethodHandle$Species_LL. Examples:
struct layout | type name |
last access method |
---|---|---|
two pointers | Species_LL | argL1 |
three pointers | Species_LLL | argL2 |
pointer & int | Species_LI | argI1 |
two pointers & double | Species_LLD | argD2 |
The invoker method for a bound method handle marshals the incoming arguments with the saved arguments, and passes them all to the target method handle. By convention, this target is bound to the first word in the struct, which must therefore be a pointer.
Here is an example invoker method:
LambdaForm(a0:L,a1:L,a2:L)=>{ t3:L=BoundMethodHandle$Species_LL.argL1(a0:L); t4:L=MethodHandle.reinvokerTarget(a0:L); t5:L=MethodHandle.invokeBasic(t4:L,t3:L,a1:L,a2:L); t5:L}
Here, the bound method handle (call it BMH) that uses this invoker was built from some target method handle (say, MH0) which is stored in the argL0 field. It will be bound to a single value (of any reference type) to be stored in the argL1 field.
The resulting bound method handle will be invoked with two reference arguments named a1 and a2. (The argument a0 is the this argument for the method handle itself.) After extracting the stored value argL1 and the reinvoker target MH0 into temporary variables t3 and t4, it will use invokeBasic (an unchecked invocation entry point) to call MH0, with the stored value prepended to the incoming argument list.
The adapter code is allowed to use invokeBasic because it is trusted and private to the java.lang.invoke package. User code perform a similar task on a user-defined struct type, but it would have to use invokeExact, which includes a safety check on the MethodType of BMH.
Example execution for invokedynamic
Code which creates BMH and then invokes it might look like this:
interface ObjType { Object m2(Object a1, Object a2); } MethodHandle mh0 = lookup() .findVirtual(ObjType.class, "m2", genericMethodType(2)); ObjType val = new ObjType() { public Object m2(Object a1, Object a2) { return "present"; } }; MethodHandle bmh = mh0.bindTo(val); System.out.println(bmh.invokeExact(a1, a2)); System.out.println(mh0.bindTo(val).invokeExact(a1, a2)); //same System.out.println(mh0.invokeExact(val, a1, a2)); //same System.out.println(val.m2(a1, a2)); //same
The target of a bound method handle can be any method handle. In the simplest case it could be a direct method handle to some Java language method, as in the above example code.
Here is an example of the sequence of events and stack frames that would make such a connection, from a method MHUser.m1 to a target method LibraryCls.m2:
(MHUser.m1) | (LF adapter for invokeExact) | (LF method for BMH) | (LF method for MH0) | (ObjType.m2) |
---|---|---|---|---|
(BMH) "1" "2" > invokevirtual invokeExact(LL)L | ||||
(BMH) "1" "2" > push CPC.appendix | ||||
(BMH) "1" "2" (MT) > jump to CPC.method | ||||
... |
a0: (BMH) a1:"1" a2:"2" a3: (MT) (MH) (MT) > invokestatic Invokers.checkExactType |
|||
... |
a0: (BMH) a1:"1" a2:"2" a3: (MT) (MH) "1" "2" > invokestatic MH.invokeBasic(LL)L |
|||
... | ... |
a0: (BMH) a1:"1" a2:"2" (BMH) > invokevirtual Sepcies_LL.argl1 |
||
... | ... |
a0: (BMH) a1:"1" a2:"2" t3: (VAL) (BMH) > invokevirtual BMH.reinvokerTarget |
||
... | ... |
a0: (BMH) a1:"1" a2:"2" t3: (VAL) t4: (MH0) (MH0) (VAL) "1" "2" > invokevirtual MH.invokeBasic(LLL)L |
||
... | ... | ... |
a0: (MH0) a1: (VAL) a2: "1" a3: "2" (MH0) >invokestatic DMH.internalMemberName |
|
... | ... | ... |
a0: (MH0) a1: (VAL) a2: "1" a3: "2" t4: (MN) (VAL) "1" "2" (MN) > invokestatic MH.linkToInterface(LLL+L)L |
|
... | ... | ... | (VAL) "1" "2" (MN) > pop MN | |
... | ... | ... | (VAL) "1" "2" > jump to MN.method | |
... | ... | ... | ... |
(VAL) "1" "2" > interface dispatch for ObjType.m2 |
... | ... | ... | ... |
this: (VAL) l1:"1" l2:"2" > code for ValObjTypeImpl.m2 |
There are three internal stack frames, one for the adapter bound to the invokeExact call site, one which inserts the stored argument value, and one which implements the direct method handle.
The activity in the middle frame is specific to bound method handles. Adapters for invokeExact are described here, while the special methods for the direct method handle (internalMemberName and linkToInterface) are explained on the page about direct method handles.