Just An Application

November 24, 2009

InvokeDynamic And MethodHandles On Snow Leopard: Part Four

The Even More Magical java.dyn.InvokeDynamic

java.dyn.InvokeDynamic is a final class with a private constructor and no other no declared methods, but you can do this with it


    package test;

    import java.dyn.InvokeDynamic;

    public final class Bar
    {
        void foo()
        {
            System.out.println(InvokeDynamic.bar("bar invoked dynamically"));
        }
    }

and the compiler will compile it to this


    Macintosh:tmp simon$ build/bsd-i586/bin/javap -c test.Bar
    Compiled from "Bar.java"
    public final class test.Bar extends java.lang.Object {
      public test.Bar();
        Code:
          0: aload_0       
          1: invokespecial #1                  // Method java/lang/Object."":()V
          4: return        

      void foo();
        Code:
          0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
          3: ldc           #3                  // String bar invoked dynamically
          5: invokedynamic #4,  0              // NameAndType bar:(Ljava/lang/String;)Ljava/lang/String;
         10: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         13: return        
    }

java.dyn.InvokeDynamic is a Java language level marker for the JVM instruction formerly known as

    xxxunusedxxx

and now known as

    invokedynamic

The invokedynamic Instruction

The instruction is five bytes long. The one byte opcode, 186 (0xBA), is followed by two bytes which give the index into the constant pool of a name and type, in this case,

    bar

and

    (Ljava/lang/String;)Ljava/lang/String

followed by two zero bytes.

And that is it. Unlike the original invoke instructions

  • invokevirtual
  • invokespecial
  • invokestatic
  • invokeinterface

there is not enough information in the instruction to enable the VM to determine what it should be invoking or how.

Hence, if you run this,


    package test;

    public final class InvokeDynamicTest
    {
        public static void main(String[] theArgs)
        {
            try 
            {
                Bar bar = new Bar();
			
                bar.foo();
            }
            catch (Throwable t) 
            {
                t.printStackTrace();
            }
        }
    }

this happens


    Macintosh:tmp simon$ build/bsd-i586/bin/java -classpath . -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic test.InvokeDynamicTest
    java.dyn.InvokeDynamicBootstrapError: class has no bootstrap method: class test.Bar
	    at sun.dyn.CallSiteImpl.makeSite(CallSiteImpl.java:63)
	    at test.Bar.foo(Bar.java:10)
	    at test.InvokeDynamicTest.main(InvokeDynamicTest.java:13)

The Bootstrap Method

The first time the VM encounters an invokedynamic instruction it looks for the bootstrap method for the class of the caller.

The bootstrap method is a static method of the form

    static CallSite bootstrapMethod(Class theCallerClass, String theMethodName, MethodType theMethodType)

The bootstrap method for a class is registered either by name or as a MethodHandle using a static method on the class

    java.dyn.Linkage

For example

    ...
	
    static void init()
    {
        Linkage.registerBootstrapMethod(
                    Bar.class, 
                    MethodHandles.
                        lookup().
                            findStatic(
                                Bootstrap.class,
                                "bootstrap",
                                MethodType.make(
                                               CallSite.class, 
                                               Class.class, 
                                               String.class, 
                                               MethodType.class)));
    }
	
    ...

registers the static method

     private static CallSite bootstrap(Class theClass, String theName, MethodType theType)

of the class Bootstrap as the bootstrap method for the class Bar.

The CallSite

The bootstrap method is passed the class of the caller, and the name and type of the method specified by the instance of the invokedynamic instruction, and is responsible for returning an instance of the class

    java.dyn.CallSite

The returned CallSite instance contains a MethodHandle. The JVM uses the method referenced by the MethodHandle as the method to invoke.

Using this as the bootstrap method.


    private static CallSite bootstrap(Class<?> theClass, String theName, MethodType theType)
    {
        MethodHandle mh = MethodHandles.lookup().findStatic(Methods.class, "generic", theType);
        CallSite     cs = new CallSite(theClass, theName, mh.type());
		
        cs.setTarget(mh);
        return cs;
    }

this as the definition of the class Methods


    package test;

    final class Methods
    {
        static String generic(String theArgument)
        {
            return theArgument + ": brought to you by Methods.generic()";
        }
    }

and invoking Bar.foo() like this


    package test;


    public final class InvokeDynamicTest
    {
        public static void main(String[] theArgs)
        {
            try 
            {
                Bootstrap.init();
			
                Bar bar = new Bar();
			
                bar.foo();
            }
            catch (Throwable t) 
            {
                t.printStackTrace();
            }
        }
    }

gets you this.


    Macintosh:tmp simon$ build/bsd-i586/bin/java -classpath . -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic test.InvokeDynamicTest
    bar invoked dynamically: brought to you by Methods.generic()

While this is not of itself a particularly exciting example there are few things worth noting

  • most importantly, what happens when the method Bar.foo() is invoked is determined entirely at runtime, i.e., dynamically

  • the whole thing was done in Java. Systems which generate bytecode themselves can obviously generate invokedynamic instructions directly as well as generating bytecode on the basis of the behaviour which results

  • in the example the bootstrap method produces a single CallSite. In practice bootstrap methods would produce different instances of CallSite depending upon their arguments, and would also use different MethodHandles depending on their arguments.

  • the CallSite class can be sub-classed, which enables further specialization of certain aspects of its behavior

  • in the example the method actually invoked does not do anything particularly interesting. In practice the behaviour of invoked methods can be dependent on their argument(s), and in the case of systems generating bytecode dynamically, invoked methods can feedback information to the code generation stage.


Copyright (c) 2009 By Simon Lewis. All Rights Reserved.

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: