I was playing with Java8 Lambda last night and I was wondering if it is possible to retrieve the Lambda expression at runtime. In short and as far as I understood, Lambda expression are converted into (static) methods at runtime and then called using InvokeDynamics.
Let's take an example like this:
people.filter(person -> person.getAge() >= minAge);
where filter
would be a custom method taking a Predicate<T>
as a parameter.
Inside this filter
method, how could I retrieve the argument in a form similar (or identical) to the Lambda expression (person -> person.getAge() >= minAge
) in this case ?
I tried to read the generated bytecode of the argument's class using ASM5_BETA but I couldn't go further than using a ClassVisitor and a MethodVisitor to reach the method associated with the Lambda expression.
public <T> List<T> filter(Filter<T> expression) {
try {
Class<? extends Filter> expressionClass = expression.getClass();
byte[] content = getClassContent(expressionClass);
ClassReader classReader = new ClassReader(content);
classReader.accept(new PredicateClassVisitor(), 0);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
private byte[] getClassContent(Class<? extends Filter> expressionClazz) throws
IOException {
InputStream stream = Thread.currentThread().getContextClassLoader()
.getResourceAsStream(getClassName(expressionClazz.getName()));
return IOUtils.toByteArray(stream);
}
private String getClassName(String expressionClazz) {
return expressionClazz.substring(0, expressionClazz.indexOf('$'))
.replace('.', '/') + ".class";
}
static class PredicateClassVisitor extends ClassVisitor {
public PredicateClassVisitor() {
super(Opcodes.ASM4);
}
@Override
public MethodVisitor visitMethod(int i, String s, String s2, String s3,
String[] strings) {
return new PredicateMethodVisitor();
}
}
static class PredicateMethodVisitor extends MethodVisitor {
public PredicateMethodVisitor() {
super(Opcodes.ASM4);
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
Object... bsmArgs) {
for (Object object : bsmArgs) {
System.out.println(" " + object.toString());
}
}
}
I'm not sure this is the right path to follow, and I was wondering if there were more appropriate tooling in ASM or in JDK8 for such a purpose.
Thanks for any advice ;-) Best regards, Xavier
For Lambda expressions, the compiler doesn't translate them into something which is already understood by JVM. Lambda syntax that is written by the developer is desugared into JVM level instructions generated during compilation, which means the actual responsibility of constructing lambda is deferred to runtime.
A return statement is not an expression in a lambda expression. We must enclose statements in braces ({}). However, we do not have to enclose a void method invocation in braces. The return type of a method in which lambda expression used in a return statement must be a functional interface.
Lambda expression is compiled using invokedynamic bytecode. Lambda implementation is stored in the same class file as a special private method. Whether an object is created to invoke lambda depends on the situation. In the trivial cases lambda gets translated to a constant method handle.
Oracle claims that use of lambda expressions also improve the collection libraries making it easier to iterate through, filter, and extract data from a collection. In addition, new concurrency features improve performance in multicore environments.
You already know that lambda expressions are usually compiled into a synthetic method so you already know which code to decompile to get the lambda’s source code, or, well, something similar to the original code or even something looking completely different, depending on the particular code.
There is no reason why decompiling a lambda expressions should be easier than decompiling any other Java expression. Simple expressions might be easy to recover, especially when the code has debugging information, complex expressions are very likely to look different when decompiling, especially when the compiler applies optimizations to the code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With