Page 13 - 《软件学报》2026年第7期
P. 13

2698                                                       软件学报  2026  年第  37  卷第  7  期


                 中多个与类型相关的编译器缺陷. Chen           等人  [15] 提出的  Java 测试程序变异方法   classfuzz, 其设计了一系列针对字
                 节码文件的变异规则, 例如, 修改变量类型及函数返回值类型等. 通过在种子程序上应用不同的变异规则生成新的
                 测试程序, 从而对     JVM  的启动阶段    (即加载、链接及初始化) 进行测试. 在此基础上, Chen            等人  [32] 进一步提出基
                 于活字节码    (live bytecode) 的测试程序生成方法    classming, 其首先获取已有测试程序中被执行的字节码序列, 随
                 后通过修改被执行字节码的控制流和数据流, 生成大量语义不同的变异体. 最后基于差分测试检测                               JVM  执行引擎
                 中潜在的缺陷.
                    其他基于变异的       Java 测试程序生成方法均针对        JVM  中的  JIT  编译器进行测试. 例如, Wu    等人  [12] 提出了针
                 对  JVM  中  JIT  编译器的测试程序变异方法      JITFuzz, 其针对  JIT  编译器中不同的代码优化设计相应的变异规则,
                 例如, 函数内联及逃逸分析等. Jia 等人       [34] 提出从两个维度对    JIT  编译器进行测试的方法      JOpFuzzer, 其利用  TBar [35]
                 来对已有的测试程序进行变异, 涉及的变异规则包括修改数据类型以及函数调用等. 此外, JOpFuzzer 同时考虑生
                 成  JIT  编译器的不同优化选项配置, 通过打开或关闭不同的优化选项, 使得生成的测试程序能够覆盖更多的                               JIT
                 编译器功能模块. Xie 等人     [13] 提出了基于优化交互的      JIT  编译器测试方法    MopFuzzer, 其同样基于  JIT  编译器的优
                 化功能设计不同的变异规则, 例如, 循环展开调用及锁消除调用等. 随后, 利用                     JVM  的运行时数据引导生成触发不
                 同  JIT  优化组合的测试程序, 从而对      JIT  编译器不同优化的交互进行测试. Li 等人         [14] 提出了编译空间的概念, 并提
                 出基于变异的编译空间探索方法            Artemis, 其通过对测试程序中的函数调用进行修改, 改变其执行状态, 即解释执
                 行或  JIT  编译执行. 通过对比不同执行状态的执行结果, 从而检测              JIT  编译器中潜在的缺陷.
                    相似地, 现有基于变异的测试程序生成方法同样无法对新特性进行有效覆盖. 一方面, 现有工作的变异规则主
                 要围绕   JVM  中的特定组件    (例如, JIT  编译器) 而设计, 无法覆盖新特性       [12–14] ; 另一方面, 现有工作大多在字节码层
                 面直接生成    JVM  可以运行的测试程序       [15,32,34] . 然而, 语言新特性往往以语法糖的形式体现于        Java 源码层, 这导致
                 现有方法无法生成具备新特性触发条件的测试程序, 从而限制了其在新特性测试中的应用. 不同于上述方法, 本方
                 法一方面提升了对新特性行为的覆盖能力, 另一方面能够有效暴露                      Java 编译器和  JVM  在支持新特性过程中可能
                 存在的实现缺陷, 弥补了语言新特性测试的不足.
                  2   动机示例

                    本节通过一个示例来展示          LumiX  的研究动机. 同时, 基于该示例分析为何现有研究方法无法对新特性进行有
                 效的测试.
                    图  1 展示了由  LumiX  生成的测试程序, 该测试程序成功检测出了            Java 编译器  ECJ 中关于  Java 8 新特性  Lambda
                 表达式的未知缺陷 (Bug #3792    [36] ). 具体来说, 该测试程序在第    1 行定义了一个值为      false 的布尔类型变量    DEBUG,
                 并在第   3  行定义了一个类型为      Object 的局部变量   o, 并在第  5  行为其重新赋值为一个新的        Object 类型对象. 随后
                 在第  6  行, 测试程序通过   if 语句判断   DEBUG  变量是否为    true, 若条件成立, 则执行    if 语句的代码体. 该代码体中
                 创建了一个新的线程, 并在线程中通过            Lambda 表达式指定了线程的执行逻辑. 然而, Lambda 表达式使用了局部
                 变量  o, 而  o  并未被声明为  final 变量, 且不满足  effectively final 条件  (即变量没有显式声明为  final, 但其值在初始
                 化后未被修改). 根据      Java 8  中  Lambda  的语法规范, 表达式中引用的局部变量必须是显式声明为                final 或者是
                 effectively final, 否则将被视为非法引用, 从而可能导致线程安全问题或内存泄漏等隐患. 在对该测试程序进行编
                 译时, javac 编译器能够成功检测到此类非法引用并抛出编译错误, 而                  ECJ 编译器则未能识别出该错误, 并且生成
                 了一个错误的     class 文件.
                    需要注意的是, 图     1 中浅蓝色标记部分为      LumiX  向种子程序中插入的包含       Java 新特性的代码片段. 其中, Lambda
                 表达式是    Java 8  中引入的新特性, 用于简洁地表示匿名函数, 可作为参数传递给方法或赋值给变量, 是                       Java 向函
                 数式编程迈出的关键一步. LumiX        首先利用程序分析工具识别出种子程序中可以复用的程序元素, 例如, 本示例中
                 的局部变量    o. 随后, 利用大模型生成      Lambda 表达式的代码片段, 并在生成的过程中复用了种子程序中的局部变
                 量  o, 如测试程序第   10  行所示. 该测试程序最终成功触发了          javac 编译器和  ECJ 编译器的不一致行为. 本工作随后
   8   9   10   11   12   13   14   15   16   17   18