Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to retrieve lambda expression at runtime

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

like image 304
Xavier Coulon Avatar asked Dec 17 '13 08:12

Xavier Coulon


People also ask

How is lambda represented at runtime?

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.

Can lambda expressions return?

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.

How is lambda Expresssion represented by the JVM at runtime?

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.

How does lambda expression improve the performance in runtime?

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.


1 Answers

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.

like image 134
Holger Avatar answered Nov 16 '22 02:11

Holger