javace 2012

invokedynamic crash course

- Douglas Campos / @qmx

whoami

@qmx

JBoss

disclaimer

hardcore stuff

lingua franca

languages?

Java?

						Car car = new Car();
					

Ruby?

						car = Car.new
					

JavaScript?

						var x = new Car();
					

different?

back to

java

JVM

stack-based

Virtual Machine

1+2 = ?

(+ 1 2)

ZOMG, lisp???

le stack

PUSH 1
  • 1
PUSH 2
  • 1
  • 2
ADD
  • 3

le stack - bytecode

iconst_1
  • 1
iconst_2
  • 1
  • 2
iadd
  • 3

iadd?

integer add

^^^^------

statically typed

Java

Virtual Machine

let's go back to

Ruby

isn't this cool?

						2.days.ago
					

what is

2

in

Ruby?

2

						> 2.class
 => Fixnum
> 2.methods[1..10]
 => [:even?, :magnitude, :&, :[], :div, :<<, :modulo, :times, :==, :/]
					

object!!!

let's do this in java

						public class Fixnum {
  public Fixnum +(Fixnum f) { 
    return new Fixnum(
      this.intValue()+f.intValue()
    );
  }
}
					

oops

syntax error

fixing the java class

						public class Fixnum {
  public Fixnum op_add(Fixnum f) { 
    return new Fixnum(
      this.intValue()+f.intValue()
    );
  }
}
				

ugly, I know :(

Java

Y U NO OPERATOR OVERLOADING

Java (language) is

limited

(and that's ok)

there and back again

similar?

						car.honk();
					

similar?

						car.honk();
					

similar?

						car.honk();
					

on the jvm

it's all about

invocation

bytecodes!!!

						invokevirtual #3, #8
					

bytecode is hard

let's go shopping!

this java code

						Car car = new Car();
car.honk();
					

jitescript

						// new instance
  newobj(p(Car.class));
  dup();

					

jitescript

						// calls the default constructor
  invokespecial(p(Car.class),
    "<init>", sig(void.class));
  // calls the "honk" method
  invokevirtual(p(Car.class), 
    "honk", sig(void.class));

					

WTF?

summary

invokestatic for static methods
invokevirtual for instance methods
invokespecial for super() and constructors
invokeinterface for calling against an interface

requires

ALL THE TYPES

during compilation

What about some

JavaScript?

JavaScript

						var car = new Car();
car.honk();
					

let's do this in Java

OHWAIT

from ECMA 262 specification, Topic 4.3.3

An object is a collection of properties and has a single prototype object.

let's follow the spec

						public class JSObject {
  // yuck!
  private Map<String, Map<String, Object>> properties = ...;
  private Object prototype;
}
					

where is honk()?

The Car Object

						JSObject obj = new JSObject();
obj.getProperties().put("honk",
 new HashMap<String, Object>(){{
  put("call", SomeClass.class);
}});

					

invoking honk()

						// cast to Class!
Class honk = (Class) obj.getProperties().get("honk").get("call");
honk.getDeclaredMethod("call").invoke(obj);
					

forced casts

reflection

slow

JVM

the bytecode for this is huge

 L0
    LINENUMBER 24 L0
    ALOAD 0
    GETFIELD me/qmx/javace/indydemo/JSObjectTest.obj : Lme/qmx/javace/indydemo/JSObject;
    INVOKEVIRTUAL me/qmx/javace/indydemo/JSObject.getProperties ()Ljava/util/Map;
    LDC "honk"
    INVOKEINTERFACE java/util/Map.get (Ljava/lang/Object;)Ljava/lang/Object;
    CHECKCAST java/util/Map
						
    LDC "call"
    INVOKEINTERFACE java/util/Map.get (Ljava/lang/Object;)Ljava/lang/Object;
    CHECKCAST java/lang/Class
    ASTORE 1

   L1
    LINENUMBER 25 L1
    ALOAD 1
    LDC "honk"
    ICONST_0
    ANEWARRAY java/lang/Class
    INVOKEVIRTUAL java/lang/Class.getDeclaredMethod (Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
						
    ACONST_NULL
    ICONST_0
    ANEWARRAY java/lang/Object
    INVOKEVIRTUAL java/lang/reflect/Method.invoke (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
    POP
   L2
    LINENUMBER 26 L2
    RETURN
	

what if we could

help the JVM?

what if we could

avoid slow reflection?

what if we could

teach the JVM the **fast** path?

what if we could

						// put a car object on the stack
invokedynamic("dyn:call:honk", sig(Object.class, Object.class));
					

YES, WE CAN

(almost)

avoiding reflection

int (*foo)(int)

MethodHandles

aka method pointers

MethodHandles

						MethodHandle honk = MethodHandles
  .lookup()
  .findVirtual(Car.class, "honk", 
MethodType.methodType(void.class));
honk.invoke(new Car());
					

reflection is slow because of

security checks

at each call

MethodHandles do all

security checks

during creation

and they are immutable too :)

what about the types?

polymorphic signatures

polymorphic signatures

						handle.invoke(1,2.0,3L,"lol");
					

teaching the JVM

who calls who?

main calls car.honk()

the place where the caller calls the callee is called a

Call Site

(inception?)

a Call Site

						public static void main(...) {
  new Car().honk();
}
					

to help the JVM, we need to

create the CallSites

back to the invokedynamic instruction

						// put a car object on the stack
invokedynamic("dyn:call:honk", sig(Object.class, Object.class), ?, ?);
					

we need a

bootstrap method

bootstrap method (BSM)

						public class MyBootstrapper {
public static CallSite(caller, name, methodType) {
if (name.equals("dyn:call:honk")) {
  // lookup a Method Handle
 }
}
}
					

back to the JS code, invokedynamic version

JS

  • invokedynamic("dyn:call:honk", signature, bootstrap, bootstrap_signature)
  • bootstrap method
  • MethodHandles that maps straight to the desired object
  • CallSite

Inline Caching

monomorphic CallSite

						Object[] objects = {1L, 2L, 3L};
for(Object o : objects {
  System.out.println(o);
}
					

polymorphic CallSite

						Object[] objects = {1L, 2.0, 3f};
for(Object o : objects {
  System.out.println(o);
}
					

megamorphic CallSite

						Runnable[] objects = {.....};
for(Runnable o : objects {
	o.run();
}
					

that's all, folks!

?

thanks!