Page 14 - 《软件学报》2026年第7期
P. 14
赵英全 等: 基于大语言模型的 Java 新特性测试程序生成 2699
将其提交给 ECJ 的开发人员. 经开发人员确认, 该缺陷的根因在于, 由于 DEBUG 变量恒为 false, 因此, if 代码体中
的语句被 ECJ 编译器识别为死代码. ECJ 在编译过程中未对死代码中的 Lambda 表达式进行语义检查, 最终导致
了该缺陷的产生. 目前, 开发人员已经对该缺陷进行了确认和修复, 并表示该缺陷可能存在于 Lambda 最初的实现
版本中 (“Problem goes back a long ways, I could see it in 4.18 the earliest version I have but it likely goes back all the
way to the original lambda implementation!”), 是一个长期存在的编译器缺陷.
1 private static final boolean DEBUG = false;
2 public static void main (String[] args) {
3 Object o = new Object();
4 for (int i = 1; i < 10; i++) {
5 o = new Object();
6 if (DEBUG) { //Identified as dead code in ecj
7 Thread virtualThread = Thread.startVirtualThread(() → {
8 try {
9 java.lang.reflect.Method method = Test.class.getMethod (“funcName”, Object.class);
10 method.invoke(Object.class, o);
11 } catch (Exception e) {…});
12 try {virtualThread.join(); }
13 catch (InterruptedException e) {e.printStackTrace();}
14 }
15 }
16 }
图 1 ECJ 编译器缺陷 (Bug #3792)
通过上述示例程序可以看出, 新引入的语言特性同样会引入新的编译器缺陷. 然而, 现有的测试程序生成方法
均未能有效地检测出此类缺陷. 首先, 随机差分测试方法 JavaFuzzer 无法检测到此类缺陷, 其所设计的生成规则仅
针对基础的语法, 无法涵盖 Lambda 表达式等新特性的测试. 其次, 基于程序合成的测试程序生成方法 JavaTailor
无法检测到此类缺陷, 其通过将历史揭错测试程序中的代码片段插入至新的上下文中生成新的测试程序. 然而, 历
史揭错测试程序中并不包含覆盖新特性的测试程序, 无法对其进行有效的测试. 最后, 基于变异的测试程序方法
classming 同样无法检测到此类缺陷, 其通过改变种子程序的控制流和数据流来生成新的测试程序. 然而, 此类变
异方法同样无法使变异后的种子程序具备针对新特性的测试能力.
3 方法介绍
本文提出一种基于大语言模型的 Java 新特性测试程序生成方法 LumiX. LumiX 旨在利用大模型强大的文本
理解与代码生成能力, 结合程序分析方法, 生成能够覆盖 Java 新特性的测试程序, 以检测 Java 编译器及 JVM 对新
特性的解析和执行是否正确. 具体来说, LumiX 主要包含以下 3 个步骤: (1) 使用大模型总结 Java 新特性的使用描
述; (2) 结合大模型与程序分析工具生成能够覆盖 Java 新特性的代码片段, 并插入至种子程序中生成新测试程序;
(3) 采用双层差分测试方法, 分别利用不同的 Java 编译器和 JVM 对生成的测试程序进行编译和执行, 以检测潜在
的缺陷.
图 2 展示了 LumiX 方法的整体流程图. 为获取 Java 新特性的使用描述, LumiX 首先以 Java 增强提案 (Java
enhancement proposal, JEP) 作为输入, 利用大模型的文本理解能力, 总结出不同 Java 新特性的使用描述, 构建出新
特性的使用描述池, 如图 2 中①②所示. 随后, LumiX 从使用描述池以及历史揭错种子程序池中分别随机选择一个
新特性使用描述和一个历史揭错种子程序 (如图 2 中③所示), 用于生成新的代码片段. 为了提升生成代码片段与
种子程序之间的交互, LumiX 利用程序分析工具对种子程序进行解析, 提取可复用的程序元素 (即变量及函数定
义), 如图 2 中④⑤所示. 在此基础上, LumiX 利用大模型的代码生成能力, 生成能够覆盖新特性的代码片段, 并插
入种子程序中生成新的测试程序, 如图 2 中⑥⑦所示. 最后, LumiX 首先利用不同的 Java 编译器 (即 javac 和 ECJ)
对新生成的测试程序进行编译, 通过对比编译结果来检测是否存在潜在的编译器缺陷. 如果编译成功, LumiX 则会
使用不同的 JVM (即 HotSpot 和 OpenJ9) 来分别执行两个 Java 编译器编译后的字节码文件, 通过对比不同 JVM

