I am having some problems updating a debugger to work with Java 8. Consider the following program for example:
public class Lam {
public static void main(String[] args) {
java.util.function.Function<Integer, Integer> square =
x -> {
int result = 0;
for (int i=0;
i<x;
i++)
result++;
return result;
};
System.out.println(square.apply(5));
}
}
As expected, Java 8 compiles the lambda to something like this:
> javap -c -p -v -s -constants Lam
Classfile Lam.class
...
private static java.lang.Integer lambda$main$0(java.lang.Integer);
...
Code:
stack=2, locals=3, args_size=1
0: iconst_0
1: istore_1
...
LineNumberTable:
line 5: 0
line 6: 2
line 7: 4
line 9: 12
line 8: 15
line 10: 21
This looks pretty much like normal code. However, I am trying to use the Java Debugger Interface (JDI) to intercept every step of the program. The fist thing that goes wrong is when I handle the ClassPrepareEvent event
corresponding to the lambda class. Asking the event.referenceType()
gives me something like Lam$$Lambda$1.1464642111
which is cool. But then calling .allLineLocations()
on the .referenceType()
gives a AbsentInformationException
, which seems at odds with the LineNumberTable
in the compiled file.
It looks like stepping through lambda bodies in Java 8 is possible. But does anyone know how it can be done in JDI?
Updates:
.allLineLocations
is called on the Lam
class, it does reflect all of these line numbers. Event
happens within the lambda class (e.g. from stepping), the .sourceName()
of the location throws an AbsentInformationException
jdk.internal.org.objectweb.asm.*
is doing a bunch of stuff related to copying the lambdaSo my working hypothesis is that when the lambda's class is created at runtime, the JDI needs to do something to recognize that the new class's bytecode is coming from the old class's bytecode (which is in turn coming from from Lam.java
). I don't know enough about the internal representation of java.lang.Class
or com.sun.jdi.ClassType
to know where to begin.
Why am I trying to do this:
We can use different IDE's like Netbeans, IntelliJ, and Eclipse to debug the lambda expressions in Java. It is always possible to create multi-line lambda expressions and use print statements to display the values of a variable.
Lambda expressions are the couriers via which Java moves around a set of behavior. Then again, this structure is subject to change. Let's see the lambdas in action first and elaborate on the adapted versions of their syntax later on. We'll start off by defining a functional interface:
It is always possible to create multi-line lambda expressions and use print statements to display the values of a variable. The debugger can also provide additional information about the state of a java program. It allows some variables to be modified while the debugger is executing.
Use Java's Consumer interface to store a lambda expression in a variable: To use a lambda expression in a method, the method should have a parameter with a single-method interface as its type. Calling the interface's method will run the lambda expression:
You seem to be confusing the compiled class with the runtime generated lambda class. The latter contains only the glue that connects the functional interface with the implementation in the compiler class lambda method -- there isn't anything here you want to step through, with the possible exception of just the method name with no source. There is no sourceName for the lambda class because there is no source. The ASM code is building the generated lambda class. Map from bytecode location to source lines is in the class file.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With