Linker methods are four sub-primitives used to implement Direct method handles.
Linker methods
Certain non-public methods in MethodHandle
are used as sub-primitives inside certain lambda-form bodies, to provide the ultimate connection between lambda-form code and the machine-level entry points of arbitrary JVM methods. In particular, direct method handles (such as are formed as CONSTANT_MethodHandle constants) are represented with lambda forms which perform argument checks and marshaling and then invoke the JVM method underlying the direct method handle.
These so-called linker methods are linkToStatic
, linkToSpecial
, linkToVirtual
, and linkToInterface
. They correspond to the invocation modes of the first four invoker instructions. (There is no linkToDynamic
; use see CallSite.dynamicInvoker
instead.) These four methods are signature-polymorphic when used from bytecode instructions. They cannot be used meaningfully from Java source code.
Each method takes an arbitrary sequence of arguments, followed by a MemberName
argument which wraps a Method*
metadata pointer and/or a vtable index. The type of the MemberName
must exactly match the type of the invocation.
The code which uses these linker methods is java.lang.invoke.DirectMethodHandle.makePreparedLambdaForm
.
Example
String x = "123"; assert(s.length() == 3); MethodHandle MH_length = ...String::length...; int y = (int) MH_length.invokeExact(s); assert(y == 3); MemberName MN_length = ...String::length/REF_invokeSpecial...; int z = (int) MethodHandle.linkToSpecial(s, MN_length); //N.B. in bytecode only assert(z == 3);
In the third invocation, the linker method linkToSpecial
is performing a low-level indirect call to String.length. It does this by first marshaling the arguments for String.length
, plus a trailing reference to the MemberName
for the method being called. Next, when linkToSpecial
is called, it consults the trailing argument, discards it, and jumps directly to the entry point of String.length
.
Note that String.length
expects only one argument (String.this
). Neither its compiled nor its interpreted form expects a second argument (the trailing MemberName
).
The interpreter code for linkToSpecial
simply pops the interpreter TOS, pulls out the method and/or vtable index, performs any needed dispatch, and jumps to the interpreter entry point of the indicated method. The compiled code for linkToSpecial
picks up the Nth argument for the member name, does any needed dispatch, and jumps through a pointer to the method's compiled entry point. This process does not (normally) re-order the arguments in transit. The final target method does not know or care about the trailing MemberName
argument.
interaction with SharedRuntime::java_calling_conventions
To make this work, the implementation of JSR 292 assumes that linker method calls can transparently drop their trailing reference parameter. This is a reasonable assumption, since Java calling conventions are usually based on (though not identical to) the platform's C calling conventions. C conventions, in turn, usually have the property that trailing arguments can safely be disregarded by callees. The reason for this is to ease the implementation of varargs functions like printf
, so that leading parameters (like the format string) will be placed in the same register regardless of how many arguments the varargs function receives in a given call.
For the JVM, this means that if java_calling_convention
assigns String.this
to R1 for the normal call to String.length
, it must also assign String.this
to R1 for the indirect call via linkToSpecial
. The trailing MemberName
argument must be assigned elsewhere, say some register R2.
This is generally true on OpenJDK platforms. However, if a platform were to assign arguments in reverse, or in an order compatible with the interpreter, there could be trouble. For example, consider the call above to linkToSpecial
. It uses the signature "(Ljava/lang/String;Ljava/lang/invoke/MemberName;)I"
. If the string argument were assigned to R2 and the MemberName
to R1, then when the linkToSpecial
code jumps into the compiled entry point of String.length
, that code expects String.this
to be in R1, but instead it has been put into R2. Something bad will happen when String.length
attempts to load a length field from the MemberName
.
To prevent problems like this, platforms should define java_calling_convention
in such a way that adding an extra trailing argument (of basic type T_OBJECT) must not disturb the register (or stack) assignments of preceding arguments (such as String.this
in the above case). The assertion SharedRuntime::check_member_name_argument_is_last_argument
checks for this.
Alternatively, if the above cannot be made true, the code generated by gen_special_dispatch
(in sharedRuntime_<arch>.cpp
) needs to reorder in-transit arguments so that, however they are presented to the linker routine, they will be moved into the expected position by the time they are presented to the compiled target.