Ist es möglich, Lambda-Ausdruck zur Laufzeit abzurufen

Ich habe letzte Nacht mit Java8 Lambda gespielt und ich habe mich gefragt, ob es möglich ist, den Lambda-Ausdruck zur Laufzeit wiederherzustellen. Kurz und soweit ich verstanden habe, werden Lambda-Ausdrücke zur Laufzeit in (statische) Methoden umgewandelt und dann mit InvokeDynamics aufgerufen.

Nehmen wir ein Beispiel wie folgt:

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 ?

Ich habe versucht, den erzeugten Bytecode der Klasse des Arguments mit ASM5_BETA zu lesen, aber ich konnte nicht weiter gehen, als mit einem ClassVisitor und einem MethodVisitor die Methode zu erreichen, die mit dem Lambda-Ausdruck verbunden ist.

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

Ich bin mir nicht sicher, ob dies der richtige Weg ist, und ich frage mich, ob es in ASM oder in JDK8 geeignetere Werkzeuge für diesen Zweck gibt.

Danke für jeden Hinweis ;-) Freundliche Grüße, Xavier

0
Sie könnten Procyon verwenden, um die Lambdas zu dekompilieren.
hinzugefügt der Autor Antimony, Quelle
Wenn Sie den Bytecode für das Lambda haben, dann ist es ja möglich, es in ein äquivalentes Stück Java-Quelle umzuwandeln. Die ASM-Tools passen genau dazu (auch asm-util und asm-tree).
hinzugefügt der Autor OrangeDog, Quelle
Was versuchst du eigentlich hier zu erreichen? Bis Sie das erklären, ist es schwierig, Sie zu beraten.
hinzugefügt der Autor Stephen C, Quelle
Ich möchte den Lambda-Ausdruck erfassen, der im aufrufenden Code bereitgestellt wurde, zum Beispiel für den Zweck der Protokollierung oder für andere Verwendungszwecke später. Ich spreche nicht über die Generierung des Bytecodes anstelle der JVM. Aus dem oben genannten Beispiel möchte ich in der Methode filter (Filter expression) das angegebene Argument expression auf den zurücksetzen können person -> person.getAge ()> = minAge der Lambda-Ausdruck. Ist das machbar?
hinzugefügt der Autor Xavier Coulon, Quelle
Mit "Lambda-Ausdruck abrufen" nehme ich an, dass Sie "erzeugen" meinen. BTW der Lambda-Aufruf selbst ist nicht über InvokeDynamic, dies wird nur bei der Erstellung des Lambda Invocator-Objekts verwendet.
hinzugefügt der Autor Marko Topolnik, Quelle

2 Antworten

Sie wissen bereits, dass Lambda-Ausdrücke normalerweise in eine synthetische Methode kompiliert werden, so dass Sie bereits wissen, welcher Code zu dekompilieren ist, um den Quellcode des Lambdas zu erhalten, oder etwas, das dem ursprünglichen Code ähnelt oder sogar etwas völlig anders aussieht .

Es gibt keinen Grund, warum das Dekompilieren eines Lambda-Ausdrucks einfacher sein sollte als das Dekompilieren eines anderen Java-Ausdrucks. Einfache Ausdrücke können leicht wiederhergestellt werden, besonders wenn der Code Debugging-Informationen enthält, werden komplexe Ausdrücke beim Dekompilieren höchstwahrscheinlich anders aussehen, insbesondere wenn der Compiler Optimierungen am Code anwendet.

0
hinzugefügt
Ich behauptete, dass das Objekt Filter das Lambda war, weil es das ist. Lies den Code!
hinzugefügt der Autor OrangeDog, Quelle
Das ist weder relevant, beweisbar noch statistisch signifikant.
hinzugefügt der Autor OrangeDog, Quelle
Daher sollten Sie den ersten Absatz als irreführend und mehrdeutig entfernen und den Punkt der Antwort in die Antwort einfügen.
hinzugefügt der Autor OrangeDog, Quelle
Ja, Sie haben die Kommentare nicht so verstanden, wie Sie die Frage nicht verstanden haben. Die Anweisung "Sie wissen bereits, welcher Code zu dekompilieren ist, um den Quellcode des Lambda zu erhalten" ist entweder falsch oder mehrdeutig. Wenn Sie wissen, wie sie es wissen (frühere Antworten geben an, dass Sie es nicht tun), sollten Sie es explizit machen.
hinzugefügt der Autor OrangeDog, Quelle
Es ist üblich, Antworten zu kommentieren, um zu erklären, was mit ihnen falsch oder falsch ist.
hinzugefügt der Autor OrangeDog, Quelle
Wenn Sie nicht wissen, was Filter ist oder ob es ein Lambda ist, dann haben Sie die Frage eindeutig nicht genau gelesen. Es ist die Zielmethode der generierten Klasse, die den Code enthält - darum geht es in der Frage. Und die Klasse, die an den ClassReader übergeben wird, ist nicht synthetisch, also gibt es keinen Grund, nicht zu erwarten, vokedynamic irgendwo zu finden.
hinzugefügt der Autor OrangeDog, Quelle
Worauf gründest du das? Der Code in der Frage beginnt mit einem Filter -Objekt, das ein Lambda ist und über den Bytecode seiner einschließenden Klasse iteriert. Nirgends gibt es einen Hinweis darauf, dass man sich mit dem Fall befassen sollte, in dem es in dieser Klasse mehrere Lambdas gibt.
hinzugefügt der Autor OrangeDog, Quelle
Wie haben sie die Instanziierungsstelle gefunden? Nach dem Lesen der Frage haben sie zufällig nur ein Lambda in der einschließenden Klasse, also muss expressionClass das sein.
hinzugefügt der Autor OrangeDog, Quelle
Woher weißt du, welcher Code es ist? Der Name der Lambda-Klasse (z. B. $$ Lambda $ 58/918965208 ) entspricht nicht den Namen der Lambda-Methoden, die separat nummeriert sind.
hinzugefügt der Autor OrangeDog, Quelle
Was stimmt also nicht mit meiner Antwort, außer dass es Ihre ganz andere Frage nicht behandelt? Fast alles, was Sie geschrieben haben, handelt vom Code der -Frage. Wenn du denkst, dass der Code des Fragestellers deinem Ziel dienen muss, besprich es mit ihm .
hinzugefügt der Autor Holger, Quelle
@OrangeDog: Wenn Sie diese Klasse dekompilieren, werden Sie herausfinden, welche Methode sie aufrufen wird. Bei dieser Frage hat das OP die Methode bereits über die Instanziierungsstelle verfolgt. Der Sinn dieser Antwort ist jedoch, dass es nicht einfach oder sogar unmöglich ist.
hinzugefügt der Autor Holger, Quelle
@OrangeDog: Der Hauptpunkt der Antwort ist, dass Sie den Quellcode nicht bekommen können . Das ist es. Wie die Zielmethode einer generierten Klasse zu finden war, war nie das Thema. Die Einleitung schlägt dem OP nur vor, "wenn Sie die Methode kennen, wissen Sie, dass es gewöhnlicher kompilierter Code ist, nicht etwas besonderes, den ursprünglichen Quellcode zu kennen". Zu sagen, wie die Zielmethode einer generierten Klasse zu finden sei, hatte keinen Sinn, da der Quellcode immer noch nicht gefunden wurde, verlangte das OP.
hinzugefügt der Autor Holger, Quelle
@OrangeDog: Du bist der erste in den letzten 3½ Jahren, der es irreführend und zweideutig findet.
hinzugefügt der Autor Holger, Quelle

Das können Sie in manchen Fällen mit Groovy tun, wenn Ihnen das weiterhilft: Getting the Inhalt der Schließung, in groovy . Geb verwendet diese Funktion, um einen Assertionsfehler innerhalb eines ausgewerteten Ausdrucks hervorzuheben.

0
hinzugefügt