¿Es posible recuperar la expresión lambda en tiempo de ejecución?

Estuve jugando con Java8 Lambda anoche y me preguntaba si es posible recuperar la expresión Lambda en tiempo de ejecución. En resumen, y hasta donde yo entiendo, la expresión Lambda se convierte en métodos (estáticos) en tiempo de ejecución y luego se llama usando InvokeDynamics.

Tomemos un ejemplo como este:

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

where filter would be a custom method taking a Predicateas 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 ?

Intenté leer el bytecode generado de la clase del argumento usando ASM5_BETA, pero no pude ir más allá de usar ClassVisitor y MethodVisitor para llegar al método asociado con la expresión Lambda.

public  List filter(Filter 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());
        }
    }
} 

No estoy seguro de que este sea el camino correcto a seguir, y me preguntaba si había herramientas más adecuadas en ASM o en JDK8 para tal fin.

Gracias por cualquier consejo ;-) Atentamente, Javier

0
Podría intentar usar Procyon para descompilar las lambdas.
agregado el autor Antimony, fuente
Si tiene el bytecode para el lambda, entonces sí, es posible transformarlo en una fuente equivalente de Java. Las herramientas de ASM son las adecuadas para esto (también asm-util y asm-tree).
agregado el autor OrangeDog, fuente
¿Qué estás tratando de lograr aquí? Hasta que no explique esto, es difícil aconsejarlo.
agregado el autor Stephen C, fuente
Me gustaría capturar la expresión Lambda que se proporciona en el código de llamada, para fines de registro, por ejemplo, u otros usos posteriores, tal vez. No estoy hablando de generar el bytecode en lugar de la JVM. Según el ejemplo anterior, en el método filter (Filter expression) , me gustaría poder revertir el argumento dado expression al person -> person.getAge ()> = minAge la expresión lambda. ¿Es esto factible?
agregado el autor Xavier Coulon, fuente
Por "recuperar la expresión lambda", lo tomo como "generar". Por cierto, la llamada lambda no ha terminado InvokeDynamic, esto se usa solo en el proceso de creación del objeto lambda invocator.
agregado el autor Marko Topolnik, fuente

2 Respuestas

Ya sabes que las expresiones lambda generalmente se compilan en un método sintético para que ya sepas qué código descompilar para obtener el código fuente de lambda o, bueno, algo similar al código original o incluso algo que se ve completamente diferente, dependiendo del código particular .

No hay ninguna razón por la que decompilar expresiones lambda sea más fácil que descompilar cualquier otra expresión Java. Las expresiones simples pueden ser fáciles de recuperar, especialmente cuando el código tiene información de depuración, es muy probable que las expresiones complejas se vean diferentes al descompilar, especialmente cuando el compilador aplica optimizaciones al código.

0
agregado
Afirmé que el objeto Filter era el lambda porque lo es. Lee el código!
agregado el autor OrangeDog, fuente
Eso no es relevante, demostrable ni estadísticamente significativo.
agregado el autor OrangeDog, fuente
Por lo tanto, debe eliminar el primer párrafo como engañoso y ambiguo, y realmente poner el punto de la respuesta en la respuesta.
agregado el autor OrangeDog, fuente
Sí, no ha comprendido los comentarios del mismo modo que no ha comprendido la pregunta. La frase "ya sabes qué código descompilar para obtener el código fuente de lambda" es incorrecta o ambigua. Si sabe cómo lo saben (las respuestas anteriores indican que no lo hace), debe hacerlo explícito.
agregado el autor OrangeDog, fuente
Es habitual comentar las respuestas para explicar qué es lo que falla o qué les falta.
agregado el autor OrangeDog, fuente
Si no sabe qué es Filter ni si es un lambda, entonces claramente no leyó la pregunta con mucho cuidado. Es el método de destino de la clase generada que tiene el código; de eso se trata la pregunta. Y la clase pasada al ClassReader no es sintética, por lo que no hay razón para no esperar encontrar invookedynamic en algún lugar de ella.
agregado el autor OrangeDog, fuente
¿En qué estás basando esto? El código en la pregunta comienza desde un objeto Filter que es un lambda y se repite sobre el bytecode de su clase adjunta. En ninguna parte hay ninguna sugerencia de que haya alguna consideración para tratar el caso en el que hay múltiples lambdas en esa clase.
agregado el autor OrangeDog, fuente
¿Cómo han encontrado el sitio de creación de instancias? A partir de mi lectura de la pregunta, tienen una sola lambda en la clase adjunta, por lo que expressionClass debe ser esa.
agregado el autor OrangeDog, fuente
¿Cómo sabes qué código es? El nombre de la clase lambda (por ejemplo, $$ Lambda $ 58/918965208 ) no corresponde a los nombres de los métodos lambda, que están numerados por separado.
agregado el autor OrangeDog, fuente
Entonces, ¿qué pasa con mi respuesta, excepto que no aborda su pregunta completamente diferente? Casi todo lo que escribiste es sobre el código de pregunta </​​i>. Si crees que el código del interrogador debe cumplir tu objetivo, discútelo con él .
agregado el autor Holger, fuente
@OrangeDog: si descompilas esa clase, descubrirás qué método invocará. En el caso de esta pregunta, el OP ya rastreó el método a través del sitio de creación de instancias. Sin embargo, el objetivo de esta respuesta es que es no fácil o incluso imposible.
agregado el autor Holger, fuente
@OrangeDog: el objetivo de la respuesta es que no se puede obtener el código fuente . Eso es. Cómo encontrar el método de destino de una clase generada nunca fue el tema. La introducción solo sugiere al OP que "si conoce el método, ya sabe, es un código ordinario compilado, no es algo especial si se conoce el código fuente original". Decirle cómo encontrar el método de destino de una clase generada, no tenía sentido, ya que aún no proporciona el código fuente, solicitó OP.
agregado el autor Holger, fuente
@OrangeDog: usted es el primero en los últimos 3½ años que lo encuentra engañoso y ambiguo.
agregado el autor Holger, fuente

Puede hacerlo en algunos casos con Groovy, si esto lo ayudara: Obtener el contenido del cierre, en groovy . Geb en realidad usa esta función para resaltar un error de aserción dentro de una expresión evaluada.

0
agregado