背景信息:
我有这个用于运行外部脚本的 java 框架。为此,我结合使用类加载器和系统 java 编译器来编译 .java
我的项目的构建路径中不存在的“脚本”文件。所有这些都有效,编译器黑魔法等等。
外部加载代码固有的复杂性是调试困难。我已经通过使用 java 运行时的远程调试功能解决了这个问题。
因此,我有一个附加到我的可执行 jar 的调试配置,它在源查找路径上具有包含外部 java 脚本的目录。这实际上工作了一段时间。实际上,它从来没有正常工作,我只是在我的构建路径上意外地有脚本。令人困惑的是,我可以在脚本中放置断点,而调试器实际上在那里停止(一致的行号,-verbose:class
日志记录和所有内容)。 不过,了解 eclipse 如何找到源文件会有所帮助。毕竟,大多数 Eclipse 文档都是由用户手册组成的。
我怀疑的是什么 是我不小心复制了某些脚本文件,从而将源查找与不同步的源文件混淆。事实并非如此,我已经删除了重复的文件,而 eclipse 仍然无法找到源。
我试过的
解决方法
这里唯一的解决方法是将脚本文件添加到项目的构建路径中,这对我来说是 Not Acceptable 。
我现在在做什么
我正在慢慢地在 eclipse 开源项目基础存储库中寻找答案。事实证明,Eclipse 是一个相当大的项目。
问题
任何人都可以提供 Eclipse 源查找如何工作的准确算法表示吗?
知道了这一点,我可能想出一种方法来强制 Eclipse 调试器使用反射来使用正确的路径。据我所知,没有技术防止动态编译的代码被调试的限制。我知道这一点是因为我的断点正在按照我的预期暂停我的线程,源代码似乎不想加载:(
相关研究:
看来this might be linked with how the class is defined with a null CodeSource location ,但显然,在将代码动态编译到内存中时,正确的做法是提供 null arg……问题仍然存在于 eclipse 的调试器如何/为什么这很重要。
4/22 3:30 更新:
于是我追了
CodeSource
上面链接的解决方案。现在,我看到我的类正在从正确的文件路径位置加载 -verbose:class
切换,但源查找仍然失败。断点仍然被正确捕获,但我遇到了熟悉的Source not found
红色字体。5/6 3:15 更新:
我追了
javap
安德鲁的回答中讨论了解决方案。结果是,我的 .class 字节码中的源文件属性与我的源查找路径中存在的文件完全匹配。这让我感到困惑,因为这暗示文件夹层次结构对源查找有影响。但是,我已经创建了代表“真实”包(定义在我的 .java 文件顶部)的“幻影”包层次结构并将我的源文件移动到这些文件夹,但是当我将这些路径添加到时,源查找仍然失败我的源查找路径。任何关于哪些额外因素影响源查找的额外见解都将是巨大的。
最佳答案
我在这方面有一些经验,在通过 JDT 调试动态编译的 groovy 脚本方面做了一些工作。我从来没有让它完美地工作,我认为这主要是 JDT 的一个限制,它从来没有被设计用来处理动态编译的代码。
TLDR:我的猜测是您的动态编译脚本在字节码中具有不正确的源文件属性。该属性由编译器在类文件中设置。见 https://en.wikipedia.org/wiki/Java_class_file
我认为,您的困惑在于调试器正确地在您在脚本中设置的断点处停止,但 IDE 无法加载源代码。这当然令人困惑,但对此有一个很好的解释。
断点实际上由 VM 处理,VM 通过完全限定名称和行号跟踪它们。这允许无论哪个类加载器加载类文件都可以命中断点,但是如果多个类文件是通过具有相同限定名称但不同源代码的不同类加载器加载的,则可能会导致一些困惑。这种用于确定何时停止 VM 的算法与在 VM 停止时实际查找源代码无关。
寻找源代码由IDE 处理。因为即使在静态编译的世界中,源文件名也可能与类名(内部类、匿名类等)不匹配。类名不能用于查找源文件。
以下是 IDE 在断点处停止时所做操作的简化:
(注意,我认为源属性只是源文件的简单名称(即没有目录),所以我认为 IDE 将包名转换为目录结构作为查找的一部分,但我可能错了那)。
因此,如果动态编译的脚本没有正确的源属性,查找将失败。你可以通过查看字节码来检查这个理论。您必须以某种方式编译脚本并将这些位保存到磁盘。然后你可以运行
javap -v myScript
在上面。我敢打赌这就是问题所在。我以前在其他动态编译语言中看到过这种情况。
关于java - 使用动态编译和加载的代码了解 Eclipse 调试源查找,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23095119/