Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Debug Interface, Lambdas and Line Numbers

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:

  • when .allLineLocations is called on the Lam class, it does reflect all of these line numbers.
  • when a JDI Event happens within the lambda class (e.g. from stepping), the .sourceName() of the location throws an AbsentInformationException
  • it looks like jdk.internal.org.objectweb.asm.* is doing a bunch of stuff related to copying the lambda
  • I'm not sure if the map from source lines to bytecodes is kept in Java, or in the JDI

So 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:

  • to update the Java Visualizer to work with lambdas
like image 715
daveagp Avatar asked Mar 29 '14 19:03

daveagp


People also ask

How to debug lambda expressions in Java?

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.

What are Lambdas in Java?

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:

Is it possible to debug multiple lines of variables in Java?

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.

How do I run a lambda expression in a variable?

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:


1 Answers

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.

like image 142
Robert Field Avatar answered Oct 18 '22 10:10

Robert Field