Question

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

Was it helpful?

Solution

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.

OTHER TIPS

You can do this in some cases with Groovy, if this would help you: Getting the contents of closure, in groovy. Geb actually uses this feature to highlight an assertion error inside an evaluated expression.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top