Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you inspect the byte code of a Java 8 lambda at runtime?

Tags:

If you have an anonymous class like

Predicate<String> isEmpty = new Predicate<String>() {     public boolean test(String t) {         return t.isEmpty();     } }; 

A library which is passed the reference to isEmpty can inspect the byte code to see what it does and possibly manipulate it. Is there a way you can do this for lambdas?

Predicate<String> isEmpty = String::isEmpty; 

e.g Say have this code and byte code

public class Main {     public static void test(Predicate<String> tester) {         System.out.println("tester.getClass()= " + tester.getClass());         System.out.println("tester.getClass().getClassLoader()="+ tester.getClass().getClassLoader());     }     public static void main(String... args) {         Predicate<String> isEmpty = String::isEmpty;         test(isEmpty);     } }  $ javap -cp . -c -private Main.class Compiled from "Main.java" public class Main {   public Main();     Code:        0: aload_0               1: invokespecial #1                  // Method java/lang/Object."<init>":()V        4: return            public static void test(java.util.function.Predicate<java.lang.String>);     Code:        0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;        3: new           #3                  // class java/lang/StringBuilder        6: dup                   7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V       10: ldc           #5                  // String tester.getClass()=        12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;       15: aload_0              16: invokevirtual #7                  // Method java/lang/Object.getClass:()Ljava/lang/Class;       19: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;       22: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;       25: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V       28: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;       31: new           #3                  // class java/lang/StringBuilder       34: dup                  35: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V       38: ldc           #11                 // String tester.getClass().getClassLoader()=       40: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;       43: aload_0              44: invokevirtual #7                  // Method java/lang/Object.getClass:()Ljava/lang/Class;       47: invokevirtual #12                 // Method java/lang/Class.getClassLoader:()Ljava/lang/ClassLoader;       50: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;       53: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;       56: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V       59: return            public static void main(java.lang.String...);     Code:        0: invokedynamic #13,  0             // InvokeDynamic #0:test:()Ljava/util/function/Predicate;        5: astore_1              6: aload_1               7: invokestatic  #14                 // Method test:(Ljava/util/function/Predicate;)V       10: return         } 

With a reference to tester in test how do I find which method is called?

like image 411
Peter Lawrey Avatar asked Jul 04 '14 18:07

Peter Lawrey


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 we see the bytecode?

Also, we can see various details of the bytecode like constant pool, fields, and methods using the Jclasslib plugin dialog: Similarly, we have the Bytecode Visualizer Plugin to view the bytecode of a class file using the Eclipse IDE.

Which provides runtime environment for Java byte code to be a executed?

JVM (Java Virtual Machine) is an abstract machine. It is a specification that provides runtime environment in which java bytecode can be executed.

Does Java 8 have lambda?

Lambda Expressions were added in Java 8. A lambda expression is a short block of code which takes in parameters and returns a value. Lambda expressions are similar to methods, but they do not need a name and they can be implemented right in the body of a method.


2 Answers

If you just want to SEE the bytecode:

javap -c -p -v classfile       ^disassemble          ^private methods             ^verbose, including constant pool and bootstrap methods attribute 

But if you want to try to do this at runtime, you're pretty much out of luck (by design, we don't have anything like Expression Trees), as the other answer suggests.

like image 106
Brian Goetz Avatar answered Sep 22 '22 11:09

Brian Goetz


The simple answer is: You can't. There is a related answer of Brian Goetz on this matter. For implementing lambda expressions, javac creates an INVOKEDYNAMIC instruction which delegates the invocation to the LambdaMetafactory's bootstrap method. For the OpenJDK, this bootstrap method is then creating an implementation of the required interface at runtime using ASM.

Within the test method, the retreived instance tester is of this ASM-generated class such that you have no class file to read for finding out which method the Predicate represents. In the general case, the exact decision of how to map lambda expressions to interface implementations is left to the runtime environment what makes your problem even harder. The only way to find out which method a lambda expression represents is to read the byte code of its creation, i.e. interpreting the main method, for your example.

like image 25
Rafael Winterhalter Avatar answered Sep 22 '22 11:09

Rafael Winterhalter