Thursday, February 2, 2012

Java 7: A complete invokedynamic example

Another blog entry in my current Java 7 series. This time it's dealing with invokedynamic, a new bytecode instruction on the JVM for method invocation. The invokedynamic instruction allows dynamic linkage between a call site and the receiver of the call. That means you can link the class that is performing a method call to the class (and method) that is receiving the call at run-time. All the other JVM bytecode instructions for method invocation, like invokevirtual, hard-wire the target type information into your compilation, i.e. into your class file. Let's look at an example.


The bytecode snippet above shows an invokevirtual method call of java.lang.String -> length() in line 20. It refers to item 65 in the contsant pool table which is a MethodRef entry (see line 6). Items 42 and 66 in the constant pool table refer to the class and the method descriptor entries. As you can see, the target type and method of the invokevirtual call is completely resolved and hard-wired into the bytecode. Now, let's return to invokedynamic!

It is important to notice that it is not possible to compile Java code into bytecode that contains an invokedynamic instruction. Java is statically typed. That means that Java performs type checking at compile time. Therefore, in Java, it is possible (and wanted!) to hard-wire all type information of method call receivers into the callers class file. The caller knows the type name of the call target, as demonstrated in our example above. The use of invokedynamic - on the other hand - enables the JVM to resolve exactly that type information at run-time. This is only required (and wanted!) for dynamic languages, such as JRuby or Rhino.

Now, suppose you want to implement a new language on the JVM that is dynamically typed. I am not suggesting you should invent *another* language on the JVM, but *suppose* you would, and *suppose* your new language should be dynamically typed. That would mean, in your new language, the linking between a caller and a receiver of a method call is performed at run-time. Since Java 7 this is possible on the bytecode level using the invokedynamic instruction.

Because I cannot create an invokedynamic instruction using a Java compiler, I will create a class file that contains invokedynamic myself. Once this class file is created I will run that class file's main method using an ordinary java launcher. How can you create a class file without a compiler? This is possible by using bytecode manipulation frameworks like ASM or Javassist.


The following code snippet shows the SimpleDynamicInvokerGenerator that can generate a class file SimpleDynamicInvoker.class which contains an invokedynamic instruction.


I am using ASM here, an all purpose Java bytecode manipulation and analysis framework, to do the job of creating a correct class file format. In line 30 the visitInvokeDynamicInsn creates the invokedynamic instruction. Generating a class that does an invokedynamic call is only half of the story. You also need some code that links the dynamic call site to the actual target, this is the real purpose of invokedynamic. Here is an example.


The bootstrap method in line 9-14 selects the actual target of the dynamic call. In our case the target is the sayHello() method. To learn how the bootstrap method is linked to the invokedynamic instruction we need to dive into the bytecode of SimpleDynamicInvoker that we've generated with SimpleDynamicInvokerGenerator.


In line 49 you can see the invokedynamic instruction. The logical name of the dynamic method is runCalculation, this is a fictitious name. You can use any name that makes sense, also names like "+" are allowed. The instruction refers to item 20 in the contant pool table (see line 33). This in turn refers to index 0 in the BootstrapMethods attribute (see line 8). There you can see the link to the SimpleDynamicLinkageExample.bootstrapDynamic method that links the invokedynamic instruction to the call target.

Now if you call the SimpleDynamicInvoker using the java launcher, then the invokedynamic call is executed.


The following sequence diagram illustrates what's happening when the SimpleDynamicInvoker is called using the java launcher.


The first call of runCalculation using invokedynamic issues a call to the bootstrapDynamic method. This method does the dynamic linkage between the calling class (SimpleDynamicInvoker) and the receiving class (SimpleDynamicLinkageExample). The bootstrap method returns a MethodHandle that targets the receiving class. This method handle is cached for repetitive invocations of the runCalculation method.

That's all in terms of invokedynamic. I have some more sophisticated examples published here in my Git repo. I hope you've enjoyed reading this - in times of shortage!

Cheers, Niklas

References:

http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html
http://asm.ow2.org/
http://java.sun.com/developer/technicalArticles/DynTypeLang/
http://asm.ow2.org/doc/tutorial-asm-2.0.html
http://weblogs.java.net/blog/forax/archive/2011/01/07/calling-invokedynamic-java
http://nerds-central.blogspot.com/2011/05/performing-dynamicinvoke-from-java-step.html





10 comments:

  1. Very good post on invokedynamic, keep them coming!

    ReplyDelete
  2. Great article. Will try it in the weekend.

    ReplyDelete
  3. This seems to be very powerful feature given its ability to support dynamic language thanks for posting this in detail as always.

    Javin
    String in Switch in JDK7 uses hashCode

    ReplyDelete
  4. Hi Niklas,

    Thanks for this post. I didn't know about this before reading this article. Please let me know if i can add some of your post in my website.

    http://www.javabeginnerstutorial.com/

    Thanks

    ReplyDelete
  5. Very Good Article..

    Can I use it in spring-mvc?

    I want to use it...

    i got a parameter that Class name and Method name..

    i want to find that method(Class and Method name)..

    and Run it..

    ReplyDelete
    Replies
    1. Thx for the comment. You cannot use it at all from within ordinary Java. The javac compiler will not generate an invokedynamic instruction. So, I suggest you'll use reflection instead?
      Cheers, Niklas

      Delete
  6. perhaps i'm being dense, but where are MethodType, CallSite and MethodHandles defined? I'd assume asm 4 but don't see them.

    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);

    ReplyDelete
  7. Hello Niklas,

    I just stumbled upon your blog - thank you very much for your interesting articles :)

    I think that your statement "It is important to notice that it is not possible to compile Java code into bytecode that contains an invokedynamic instruction."
    doesn't apply anymore if you take Java 8 into account. In Java 8 Lambda Expressions make use of the invokedynamic bytecode instruction.

    If one compiles the following program with Java 8 javac (with an lambda enabled build (e.g. b75+)):

    package de.tutorials.training.dynamic;

    import java.util.function.Consumer;

    public class LambdaInvokeDynamicExample {
    public static void main(String[] args) {
    Consumer c = (s) -> System.out.println(s);
    c.accept("Hello InvokeDynamic!");
    }
    }

    You get the following bytecode:
    (Notice the bytecode instruction in main(...) at position 0 :)

    C:\development\workspaces\intellij\de.tutorials.training\target\classes\de\tutorials\training\dynamic>%JAVA_HOME%\bin\javap -c LambdaInvokeDynamicExample.class
    Compiled from "LambdaInvokeDynamicExample.java"
    public class de.tutorials.training.dynamic.LambdaInvokeDynamicExample {
    public de.tutorials.training.dynamic.LambdaInvokeDynamicExample();
    Code:
    0: aload_0
    1: invokespecial #1 // Method java/lang/Object."":()V
    4: return

    public static void main(java.lang.String[]);
    Code:
    0: invokedynamic #2, 0 // InvokeDynamic #0:lambda$:()Ljava/util/function/Consumer;
    5: astore_1
    6: aload_1
    7: ldc #3 // String Hello InvokeDynamic!
    9: invokeinterface #4, 2 // InterfaceMethod java/util/function/Consumer.accept:(Ljava/lang/Object;)V
    14: return
    }

    Best regards,
    Thomas

    ReplyDelete
  8. REllay interesting tutorials of JAVA!

    ReplyDelete