simple Java linkage: an invokedynamic apéritif

As is abundantly documented elsewhere (and in this blog), the JVM performs all of its method calls with a suite of four bytecode instructions, all of which are statically typed in all arguments and return values. The object-oriented aspects of Java are served by the JVM’s ability to dynamically select a resolved method based not only on the static type of the call, but also on the dynamic type of the receiver (the first stacked argument).

Here is a quick summary of the invocation instructions: Both invokestatic and invokespecial resolve the target method based only on static types at the call site. They differ in that invokestatic is the only receiverless invocation instruction. Both invokevirtual and invokeinterface resolve the target method based also on the dynamic type of the reciever, which must be a subtype of its static type. They differ in the static type of the receiver; only invokeinterface accepts a receiver of an interface type.

JSR 292 is adding a fifth invocation instruction, invokedynamic. Like all the other instructions, it is statically typed. Like invokestatic, it is receiverless. What is new is that an invokedynamic instruction is dynamically linked (and even re-linked) under program control. There are many applications for such a thing, and in this blog I will be giving “recipes” to demonstrate some of them. For today, here is a light aperitif showing how invokedynamic could be used to simulate the other invocation instructions. This of course is relatively useless as-is, but it is an apt demonstration that invokedynamic can be used as a building block for more complicated call sites that include the standard JVM invocation behaviors as special cases. (Caution: This blog post is for people who enjoy their bytecodes full strength and without mixers.)

Here is a code fragment that creates a File and performs two calls on it, an invokevirtual call and an invokedynamic:

java.io.File file = ...;
String result1 = file.getName();
String result2 = java.dyn.Dynamic.<String>getName(file);
The static type of both calls is exactly the same. Their symbolic descriptors differ, but only because the first (and only) argument is explicit in the second call, but implicitly determined by the symbolic method reference in the first call. Here is the disassembled bytecode:
$ MH=$PROJECTS/MethodHandle/dist/MethodHandle.jar
$ LT=$DAVINCI/sources/langtools
$ cd $PROJECTS/InvokeDynamicDemo
$ $LT/dist/bin/javac -target 7 -d build/classes -classpath $MH src/GetNameDemo.java
$ $LT/dist/bin/javap -c -classpath build/classes GetNameDemo
...
   26:	aload_1
   27:	invokevirtual	#6; //Method java/io/File.getName:()Ljava/lang/String;
   30:	astore_2
...
   38:	aload_1
   39:	invokedynamic	#9,  0; //NameAndType getName:(Ljava/io/File;)Ljava/lang/String;
   44:	astore_3
...

Since invokedynamic is dynamically linked under program control, there is no guarantee in the code above that the second invocation does the same thing as a the first. In order to provide the required semantics, an invokedynamic instruction requires a bootstrap method to help it link itself. In the present recipe, the required bootstrap method splits neatly into a link step and a continuation step, and looks like this:

private static Object bootstrapDynamic(CallSite site, Object... args) {
    MethodHandle target = linkDynamic(site);
    site.setTarget(target);
    return MethodHandles.invoke_1(target, site, args);
}
The link step, handled by the linkDynamic and setTarget statements, ensures that the call site is supplied with a non-null target method. The continuation step (the third statement) simply invokes target method on the given arguments.

The middle statement (with setTarget) installs the target on the call site, so that if that particular invokedynamic instruction is ever executed a second time, the JVM itself will execute the target method on the stacked arguments, without a trip through the bootstrap method. This is why we say that invokedynamic is dynamically linked by the bootstrap method, not just dynamically interpreted. If the setTarget call were left out, the program would perform the same operations, but interpretively, with the linkage step performed every time.

The interesting part of this example is the linkage routine itself. It is handed a call site with a specific name and resolved type descriptor, and is expected to produce a target method to fully implement the call site. In this present example, that consists of deciding which virtual method to call, and asking the JVM for a handle on it:

private static MethodHandle linkDynamic(CallSite site) {
    String       name         = site.name();
    MethodType   type         = site.type();  // static type of call
    Class<?>     recvType     = type.parameterType(0);
    MethodType   dropRecvType = type.dropParameterType(0);
    MethodHandle target       = MethodHandles.findVirtual(recvType, name, dropRecvType);
    if (target == null) {
	throw new InvokeDynamicBootstrapError("linkage failed: "+site);
    }
    return target;
}

In this example, the value of name will be the method name supplied at the call site, "getName", and the value of type will be the method type resolved from the symbolic descriptor (Ljava/io/File;)Ljava/lang/String; (as found in the bytecodes). The first (and only) argument type is dropped and used as the class to search for the matching method.

This provides a faithful emulation of the invokevirtual bytecode. The other bytecodes could also be emulated by small variations. For example, since findVirtual works for interface types as well, if the invokedynamic call has stacked a first argument of an interface type, the end result would have been the same as the corresponding invokeinterface call. To emulate an invokespecial or invokestatic call, the call to findVirtual would change to findSpecial or findStatic, respectively.

Since one bootstrap method serves an entire class, with any number and variety of invokedynamic call sites, the method names at the call sites must in some way encode not only the target method name, but also other information relevant to specifying the target. In the case of invokestatic, the intended target class is not represented anywhere within the descriptor (there is no stacked argument of that class), the target class must also be encoded. Here are some examples emulating additional types of calls, with the linkDynamic logic left as an exercise to the reader:

String result3 = java.dyn.Dynamic.<String>toString((CharSequence)"foo");  // invokeinterface
String result4 = java.dyn.Dynamic.<String>#"static:java\\|lang\\|Integer:toHexString"(123);  // invokestatic

Using invokedynamic merely to emulate the other instructions does not have a direct use, unless the linkDynamic logic is varied in interesting ways. But that is the point: There is no end to such variations. Here are some of the degrees of freedom:

  • use the call site name as a starting point but link to a method of a different name (e.g., "GET-NAME" links to "getName")
  • use the call site type as a starting point but link to a method of a different type, via a method handle adapter (e.g., supply a default value for a missing argument)
  • combine some or all of the actual arguments into an array or list, and pass them as a unit to the target method, via an adapter
  • use the actual, dynamic types of any or all reference arguments to dispatch to a variable target method (e.g., implement multiple dispatch for generic arithmetic)

For the curious, I have uploaded a NetBeans project which presents the code fragments mentioned above. It will not run without an updated JVM built from patching in the Da Vinci Machine Project. To track that project, please join the mlvm-dev mailing list.

There is one final detail which commenters have asked about. The JVM needs to be informed where to find the bootstrap method for any given class containing an invokedynamic instruction. This is done by a static initializer in the class itself:

static {
    MethodType bootstrapType = MethodType.make(Object.class, CallSite.class, Object[].class);
    MethodHandle bootstrapDynamic
	= MethodHandles.findStatic(GetNameDemo.class, "bootstrapDynamic", bootstrapType);
    Linkage.registerBootstrapMethod(GetNameDemo.class, bootstrapDynamic);
}

Watch this space for more invokedynamic recipes. Next up, Duck Typée à la Invokedynamic.

Comments:

Very interesting post. Thanks.

After reading it I was wondering how the boostrapDynamic method was related to the class using invokedynamic, but a quick look at your demo source showed that it needs to be registered. Maybe that would be nice to have in the actual blog post too?

Posted by Frz on February 17, 2009 at 04:44 PM PST #

[Trackback] First patches from the Da Vinci Machine project hit the hotspot repository.

Posted by R&#233;mi Forax's Blog on April 25, 2009 at 12:11 AM PDT #

What does it bring ? Can you give us a use case of something we can do with invokedynamic , and that we could not do without invokedynamic ?

Posted by Curious on April 28, 2009 at 08:44 PM PDT #

[Trackback] ASM 3.2 is released with the support of the new bytecode invokedynamic.

Posted by R&#233;mi Forax's Blog on June 11, 2009 at 03:43 PM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

John R. Rose

Java maven, HotSpot developer, Mac user, Scheme refugee.

Once Sun and present Oracle engineer.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today