Интерфейс Java Debug, Lambdas и номера строк

У меня возникают проблемы с обновлением отладчика для работы с Java 8. Рассмотрим следующую программу, например:

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

Как и ожидалось, Java 8 компилирует лямбда для чего-то вроде этого:

> 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

Это похоже на обычный код. Тем не менее, я пытаюсь использовать интерфейс Java Debugger (JDI) для перехвата каждого шага программы. Первая вещь, которая идет не так, - это когда я обрабатываю ClassPrepareEvent event, соответствующий классу лямбда. Просить event.referenceType() дает мне что-то вроде Lam$$Lambda$1.1464642111, которое круто. Но затем вызов .allLineLocations() в .referenceType() дает AbsentInformationException, что, по-видимому, противоречит LineNumberTable в скомпилированном файле.

Похоже, что перешагнуть лямбда-тела в Java 8 возможно. Но кто-нибудь знает, как это можно сделать в JDI?

Обновления:

  • когда .allLineLocations вызывается в классе Lam, он отражает все эти номера строк.
  • когда JDI Event происходит в классе лямбда (например, от шага), .sourceName() местоположения выбрасывает AbsentInformationException
  • похоже, что jdk.internal.org.objectweb.asm.* делает кучу вещей, связанных с копированием лямбда
  • Я не уверен, сохранена ли карта из исходных строк в байт-коды в Java или в JDI

Итак, моя рабочая гипотеза заключается в том, что когда класс lambda создается во время выполнения, JDI должен что-то сделать, чтобы признать, что новый байт-код класса происходит из старого байт-кода класса (который, в свою очередь, исходит от Lam.java), Я не знаю достаточно о внутреннем представлении java.lang.Class или com.sun.jdi.ClassType, чтобы знать, с чего начать.

Почему я пытаюсь это сделать:

Ответ 1

Кажется, что вы сбиваете скомпилированный класс с классом лямбда, созданным во время выполнения. Последний содержит только клей, который соединяет функциональный интерфейс с реализацией в методе лямбда класса компилятора - здесь нет ничего, что вы хотите пройти, за исключением, возможно, только имени метода без источника. Для класса лямбда нет sourceName, потому что нет источника. Код ASM строит сгенерированный лямбда-класс. Карта из местоположения байт-кода в исходные строки находится в файле класса.