JSR 292 goodness: Lambda to SAM type conversion using invokedynamic
Happy new year !
We made some interresting progress toward JSR 292 completion. As John already blogged, the latest build of jdk7 (b123) comes with a nearly up to date version of JSR 292 API. This build introduces a new mechanism that allow to send constant arguments to the bootstrap method of an invokedynamic. This blog entry is about how to use this feature to implement lambda to SAM type conversion.
Let suppose I want to translate this following code to bytecode.
Comparator<Object, Object> c = #{ o1, o2 -> 1 };
Currently (*), the prototype compiler of the lambda project, first create a static method (lambda$1) corresponding to the lambda then create a MethodHandle on this method. This method handle is after translated into an instance of class implementing the interface using a java.lang.reflect.Proxy.
Here, the lambda is not bound to any local arguments so the instance of the class Comparator can be created once. Using a trick similar to the <a href="http://weblogs.java.net/blog/forax/archive/2010/11/04/jsr-292-goodness-singleton-pattern">one use to implement the DCL Pattern</a>, it can be implemented using invokedynamic.
With invokedynamic it's simpler. But because there is no Java syntax for invokedynamic, I can't explain the translation using real Java code but in pseudo Java code, it's something like that:
Comparator c = (Comparator) invokedynamic [Lambdas#asSamBSM, #lambda$1, Comparator.class] ();
Or using ASM 4, the future version of ASM that supports invokedynamic's boostrap arguments:
mv.visitIndyMethodInsn("_", "()Ljava/util/Comparator;",
new MHandle(MHandle.REF_invokeStatic, "Lambdas", "asSamBSM",
"(Ljava/dyn/MethodHandles$Lookup;Ljava/lang/String;Ljava/dyn/MethodType;[Ljava/lang/Object;)Ljava/dyn/CallSite;"),
new Object[] {
new MHandle(MHandle.REF_invokeStatic, "LambdaTest", "lambda$1",
"(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I"),
Type.getObjectType("java/util/Comparator")
});
mv.visitVarInsn(ASTORE, 1);
And the code of the bootstrap method is
public class Lambdas {
public static CallSite asSamBSM(Lookup lookup, String name, MethodType methodType, Object[] bsmArgs) {
MethodHandle lambda = (MethodHandle)bsmArgs[0];
Class<?> samInterface = (Class<?>)bsmArgs[1];
if (methodType.returnType() != samInterface) {
throw new InvokeDynamicBootstrapError("asSam incompatible return type "+methodType);
}
int parameterCount = methodType.parameterCount();
if (parameterCount == 0) { // constant case
Object samInstance = MethodHandles.asInstance(lambda, samInterface);
return new ConstantCallSite(
MethodHandles.constant(samInterface, samInstance));
}
if (parameterCount == 1) { // bind case
MethodHandle combiner = BIND_TO.bindTo(lambda);
MethodHandle target = MethodHandles.insertArguments(AS_INSTANCE, 1, samInterface);
target = MethodHandles.dropArguments(target, 1, methodType.parameterType(0));
return new ConstantCallSite(
MethodHandles.foldArguments(target, combiner).asType(methodType));
}
throw new InvokeDynamicBootstrapError("asSam incompatible method type "+methodType);
}
private static final MethodHandle AS_INSTANCE;
private static final MethodHandle BIND_TO;
static {
try {
Lookup lookup = MethodHandles.publicLookup();
AS_INSTANCE = lookup.findStatic(MethodHandles.class, "asInstance",
MethodType.methodType(Object.class, MethodHandle.class, Class.class));
BIND_TO = lookup.findVirtual(MethodHandle.class, "bindTo",
MethodType.methodType(MethodHandle.class, Object.class));
} catch (NoAccessException e) {
throw new LinkageError("linkage error", e);
}
}
}
The main advantage of this code compared to the code currently emitted by the compiler prototype of lambda is that SAM convertions of a constant method handle are computed once on demand.
cheers,
Rémi
* Update: in fact, as pointed by Tomasz Kowalczewski, the compiler prototype code was changed 3 month ago to use a translation using inner classes.
- Login or register to post comments
- Printer-friendly version
- forax's blog
- 736 reads






Comments
JSR 292 goodness: Lambda to
by 4bugzilla - 2011-01-06 10:17
I'm sorrybut I don't get it.
>With invokedynamic it's simpler. But because there is no Java syntax for invokedynamic ...
Why there is no Java syntax for invokedynamic? Is it planned for JDK 7 or 8. It's great to have vm support but to start using invokedynamic you need to have java language support.
Could you please comment on this?
JSR 292 goodness: Lambda to
by forax - 2011-01-27 16:36
Sorry for the late answer.
It's planned for JDK 8 because of its interraction with lambdas.
Rémi