11

我在更新调试器以使用 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 将 lambda 编译为如下所示:

> 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 调试器接口 (JDI) 来拦截程序的每一步。出错的第一件事是当我处理ClassPrepareEvent event对应于 lambda 类的时候。问这个event.referenceType()给了我一些Lam$$Lambda$1.1464642111很酷的东西。但是随后调用a .allLineLocations(),这似乎与编译文件中的 the不一致。.referenceType()AbsentInformationExceptionLineNumberTable

看起来在 Java 8 中单步执行 lambda 体是可能的。但是有谁知道如何在 JDI 中完成它?

更新

  • .allLineLocations在类上调用时Lam,它确实反映了所有这些行号。
  • 当 JDIEvent在 lambda 类中发生时(例如,从步进),.sourceName()该位置的AbsentInformationException
  • 看起来jdk.internal.org.objectweb.asm.*正在做一堆与复制 lambda 相关的事情
  • 我不确定从源代码行到字节码的映射是否保存在 Java 或 JDI 中

所以我的工作假设是,当 lambda 的类在运行时创建时,JDI 需要做一些事情来识别新类的字节码来自旧类的字节码(而旧类的字节码又来自Lam.java)。我对内部表示的了解不够多,也不知道java.lang.Classcom.sun.jdi.ClassType哪里开始。

我为什么要这样做

4

1 回答 1

7

您似乎将编译的类与运行时生成的 lambda 类混淆了。后者仅包含将功能接口与编译器类 lambda 方法中的实现连接起来的胶水——这里没有您想要单步执行的任何内容,可能只有方法名称没有源代码。lambda 类没有 sourceName,因为没有源。ASM 代码正在构建生成的 lambda 类。从字节码位置到源代码行的映射在类文件中。

于 2014-03-31T21:03:25.497 回答