2013-12-17 3 views
5

Вчера вечером я играл с Java8 Lambda, и мне было интересно, можно ли получить выражение Lambda во время выполнения. Короче говоря, насколько я понял, выражение Lambda преобразуется в (статические) методы во время выполнения, а затем вызывается с использованием InvokeDynamics.Можно ли получить выражение лямбда во время выполнения

Давайте рассмотрим пример, как это:

people.filter(person -> person.getAge() >= minAge); 

где filter будет пользовательский метод принятия Predicate<T> в качестве параметра. Внутри этого метода filter, как я мог получить аргумент в форме, подобной (или идентичной) выражению Lambda (person -> person.getAge() >= minAge) в этом случае?

Я попытался прочитать сгенерированный байт-код класса аргумента, используя ASM5_BETA, но я не мог пойти дальше, чем использовать ClassVisitor и MethodVisitor, чтобы достичь метода, связанного с выражением Lambda.

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()); 
     } 
    } 
} 

Я не уверен, что это правильный путь, чтобы следовать, и мне было интересно, если бы было более подходящим инструментов в ASM или в JDK8 для такой цели.

Спасибо за любые советы ;-) С наилучшими пожеланиями, Ксавье

+2

Что вы на самом деле пытаетесь достичь здесь? Пока вы не объясните это, вам трудно посоветовать. –

+0

«Извлеките лямбда-выражение», я понимаю, вы имеете в виду «сгенерировать». Кстати, сам вызов лямбда не является над InvokeDynamic, это используется только в процессе создания объекта лямбда-invocator. –

+1

Я хотел бы захватить выражение Лямбда, которое было предоставлено в вызывающем коде, например, для целей ведения журнала или, например, для других способов использования. Я не говорю о создании байт-кода вместо JVM. Из приведенного выше примера, в методе 'filter (Filter )', я хотел бы вернуть данное выражение для выражения 'person -> person.getAge()> = minAge' лямбда выражение. Это выполнимо? –

ответ

4

Вы уже знаете, что лямбда-выражение, как правило, скомпилированные в синтетический метод, так что вы уже знаете, какой код декомпилировать, чтобы получить исходный код лямбды, либо , ну, что-то похожее на исходный код или даже на что-то совершенно иное, в зависимости от конкретного кода.

Нет причин, по которым декомпиляция лямбда-выражений должна быть проще, чем декомпиляция любого другого выражения Java. Простые выражения могут быть легко восстановлены, особенно когда код имеет отладочную информацию, при декомпиляции с большой вероятностью будут выглядеть разные выражения, особенно когда компилятор применяет оптимизацию к коду.

+0

Откуда вы знаете, какой именно код? Имя класса лямбда (например, '$$ Lambda $ 58/918965208') не соответствует именам лямбда-методов, которые пронумерованы отдельно. – OrangeDog

+0

@OrangeDog: если вы декомпилируете этот класс, вы узнаете, какой метод он будет вызывать. В случае этого вопроса OP уже отслеживает этот метод через сайт-экземпляр. Тем не менее, точка этого ответа заключается в том, что он не * легко или может быть даже невозможным. – Holger

+0

Как они нашли сайт-экземпляр? Из моего чтения вопроса у них есть только одна лямбда в охватывающем классе, поэтому 'expressionClass' должен быть таким. – OrangeDog

0

Вы можете сделать это в некоторых случаях с Groovy, если это вам поможет: Getting the contents of closure, in groovy. Geb действительно использует эту функцию, чтобы выделить ошибку утверждения внутри оцениваемого выражения.

Смежные вопросы