背景信息:
我有这个java框架,用于运行外部脚本。为此,我使用类加载器和系统java编译器的组合来编译在我的项目的构建路径中不存在的.java
“脚本”文件。所有这些都有效,编译器黑魔法和所有。
外部加载代码的固有复杂性是难以调试。我已经通过使用java运行时的远程调试功能解决了这个问题。
所以,我有一个附加到我的可执行jar的调试配置,它在源查找路径上有外部java脚本的目录。 实际上已经工作了一段时间。实际上,它从来没有正常工作,我只是偶然在我的构建路径上有脚本。令人困惑的是,我可以在脚本中放置断点,并且调试器实际上在那里STOPS(一致的行号,-verbose:class
日志记录和所有)。 了解eclipse如何找到源文件是有帮助的。毕竟,大多数eclipse文档都包含用户手册。
我怀疑是因为我意外地复制了某些脚本文件,因此将源查找与不同步的源文件混淆了。事实并非如此,我已经删除了重复的文件,而eclipse仍然无法找到源代码。
我尝试了什么
解决方法
此处唯一的解决方法是将脚本文件添加到项目的构建路径中,这对我来说是不可接受的。
我现在在做什么
我正在慢慢爬过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文件的顶部定义)并将我的源文件移动到那些文件夹,但是当我将这些路径添加到源时,源查找仍然失败我的源查找路径。关于在源查找中使用哪些其他因素的任何其他见解将是巨大的。
答案 0 :(得分:6)
我在这方面有一些经验,通过JDT完成了调试动态编译的groovy脚本的一些工作。我从来没有完美地工作,我认为它主要是JDT的限制,它从来没有设计用于处理动态编译的代码。
TLDR:我的猜测是你的动态编译脚本在字节码中的源文件属性不正确。该属性由编译器在类文件中设置。见https://en.wikipedia.org/wiki/Java_class_file
我认为,您的混淆是调试器在您在脚本中设置的断点处正确停止,但IDE无法加载源。这当然令人困惑,但对此有一个很好的解释。
断点实际上由VM处理,VM通过完全限定名称和行号跟踪它们。这允许在不管哪个类加载器加载类文件的情况下命中断点,但是如果通过具有相同限定名称但不同源代码的不同类加载器加载多个类文件,则会导致一些混淆。用于确定何时停止VM的此算法与VM停止时实际查找源代码无关。
查找源代码由IDE处理。因为即使在静态编译的世界中,源文件名也可能与类名(内部类,匿名类等)不匹配。类名不能用于查找源文件。
以下是IDE在断点处停止时所执行操作的简化:
(请注意,我认为source属性只是源文件的简单名称(即没有目录),所以我认为IDE将包名称转换为目录结构作为查找的一部分,但我可能会这是错误的。)
因此,如果动态编译的脚本没有正确的源属性,则查找将失败。您可以通过查看字节代码来检查此理论。您将不得不以某种方式编译脚本并将位保存到磁盘。然后,您可以在其上运行javap -v myScript
。我敢打赌,这就是问题所在。我以前在其他动态编译语言中看到过这种情况。
答案 1 :(得分:-1)
我有类似但不太复杂的问题。它是由使用两个不同的编译器引起的。您写道,在您的情况下使用系统java编译器,请确保您的Eclipse使用相同的JDK,并且系统编译器通过使用-g:vars参数在编译的类中包含调试信息。