CrossLangFuzzer: Differential Testing of Cross-Language JVM Compilers Xiaotian Ma
Nanjing University of Science and Technology Nanjing, China [email protected]
Qiong Feng
Nanjing University of Science and Technology Nanjing, China [email protected]
arXiv:2606.28132v1 [cs.SE] 26 Jun 2026
Wei Song
Yongqiang Tian
Monash University Melbourne, Australia [email protected]
Peng Liang
Nanjing University of Science and Technology Nanjing, China [email protected]
Wuhan University School of Computer Science Wuhan, China [email protected]
Abstract
1
Modern JVM software increasingly integrates multiple programming languages, such as Java, Kotlin, Groovy, and Scala, within a single application. Supporting such interoperability requires JVM compilers to perform cross-language compilation while reconciling subtle semantic differences across language boundaries. Errors in this process can lead to critical miscompilations, yet existing compiler testing techniques focus exclusively on isolated, singlelanguage compilation. To address this gap, we present CrossLangFuzzer, the first differential testing framework for cross-language JVM compilation. CrossLangFuzzer leverages the Kotlin compiler’s unified intermediate representation (IR) to synthesize cross-language test programs. It further applies seven mutation operators to diversify generated test programs and improve bug-finding capability. Evaluated on the latest versions of five major JVM compilers, CrossLangFuzzer uncovered 32 confirmed bugs, including 15 in Kotlin, 4 in Groovy, 7 in Scala 3, 2 in Scala 2, and 4 in Java.
On the Java Virtual Machine (JVM), multiple programming languages—including Kotlin, Java, Groovy, and Scala—coexist and interoperate seamlessly [2, 13]. Modern software systems increasingly exploit this multilingual ecosystem, routinely combining components written in different JVM languages within the same application [20]. Supporting such interoperability requires JVM compilers to perform cross-language compilation: translating heterogeneous source programs into a common bytecode representation while correctly resolving dependencies and interactions across language boundaries. This process is inherently challenging. Unlike singlelanguage compilation, cross-language compilation must reconcile semantic discrepancies among languages, including differences in type systems, nullability models, generics, variance rules, and method dispatch semantics. Subtle mistakes in handling these interactions can lead to miscompilations that are difficult to detect and diagnose. Despite the growing importance of multilingual JVM software, existing compiler testing techniques predominantly target isolated single-language compilation pipelines [4, 6, 10–12, 14, 16–19, 21]. Although several studies have explored testing language interoperability [5, 7, 8], they do not systematically exercise the diverse semantic interactions arising in modern cross-language JVM applications. Consequently, current approaches lack the multi-language context necessary to expose faults at language boundaries, leaving a substantial class of cross-language compilation bugs largely unexplored. To address this gap, we propose CrossLangFuzzer, a differential testing framework for cross-language JVM compilation. CrossLangFuzzer constructs semantically rich test programs using the Kotlin compiler’s internal intermediate representation (IR), which provides a unified representation of program semantics independent of any specific JVM language[3, 15]. The generated IR programs are subsequently rendered into Kotlin, Java, Groovy, or Scala source code, enabling the systematic creation of multilingual programs that exercise interactions across language boundaries. To further improve test diversity, CrossLangFuzzer applies seven mutation operators to the generated programs after the initial synthesis phase.
CCS Concepts • Software and its engineering → Source code generation; Parsers; Compilers.
Keywords Cross-Language, Code Generator, JVM, Differential Testing ACM Reference Format: Xiaotian Ma, Qiong Feng, Yongqiang Tian, Wei Song, and Peng Liang. 2026. CrossLangFuzzer: Differential Testing of Cross-Language JVM Compilers. In . ACM, New York, NY, USA, 5 pages. https://doi.org/10.1145/nnnnnnn. nnnnnnn Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. Copyrights for components of this work owned by others than the author(s) must be honored. Abstracting with credit is permitted. To copy otherwise, or republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee. Request permissions from [email protected]. Conference’17, Washington, DC, USA © 2026 Copyright held by the owner/author(s). Publication rights licensed to ACM. ACM ISBN 978-x-xxxx-xxxx-x/YYYY/MM https://doi.org/10.1145/nnnnnnn.nnnnnnn
Introduction
Conference’17, July 2017, Washington, DC, USA
2
Research Motivation
Although Kotlin is designed to interoperate seamlessly with Java, there are syntactic and semantic differences between Kotlin and Java (e.g., the latest Java versions still use raw types for backward compatibility with pre-generics Java code, while Kotlin does not support raw types). These differences pose challenges for Kotlin and Java compilers when processing cross-language programs. 1 // Java Files 2 public interface A <T > { 3 void foo ( List <T > list ) ; 4 } 5 public abstract class B implements A < String > { 6 @Override 7 public final void foo ( List list ) {} 8 } 9 public class C extends B implements A < String > {} 10 // Kotlin File 11 class X : C () // compiler error
3
Design and Implementation
This section details the architecture of CrossLangFuzzer, its unified intermediate representation (IR), and its mutation strategies. CrossLangFuzzer extends our prior prototype [9] by refactoring and enriching the IR, introducing additional mutations, and adding automated test-case reduction. These extensions enabled CrossLangFuzzer to discover 8 additional bugs, raising the total from 24 to 32. We also package the tool as Docker images for ease of use.
3.1
Overall Framework
As illustrated in Figure 2, CrossLangFuzzer executes an automated pipeline spanning four major components:
.Generate Valid and Unverified Cross-Lang Program
Generator
Unverified Cross-Lang Program in IR
Valid Cross-Lang Program in Intermediate Representation (IR)
Printer
Cross-Lang Program
.Conduct Normal and Differential Test for Compilers Test Runners
Valid Cross-Lang Program
Normal Testing
Figure 1: KT-55822: The Kotlin compiler rejects a valid Java program with raw type arguments Figure 1 illustrates a bug in the Kotlin compiler when handling raw types from Java. In Class B, the method foo overrides A.foo using a raw type. Class C extends Class B and implements interface A with the generic argument String. When a Kotlin Class X inherits from Class C, the Kotlin compiler rejects this valid program, reporting: “Class ‘X’ is not abstract and does not implement abstract base class member public abstract fun foo(list: (Mutable)List<String!>!): Unit”. If Class X were a Java class, the Java compiler would correctly accept this code. In this example, the Kotlin compiler fails to recognize B.foo as an override of A.foo because it treats the raw type List as a wildcard type List<*> (for the reader’s understanding, this is a simplified explanation; in actual Kotlin-Java interoperability, the raw type List is treated by Kotlin as a flexible type List<*>..MutableList<*>?). Since the wildcard * type does not match the generic argument String in interface A<String>, the compiler does not recognize B.foo as an override of A.foo. The Kotlin development team has previously located and fixed this issue (KT-55822); we use it here purely as an illustrative example of the bug class, and it is not among the 32 bugs newly reported in this work. This case demonstrates a Kotlin compiler bug caused by improper handling of the inconsistency between Kotlin and Java raw types. Beyond raw types, Kotlin, Scala, Groovy, and Java also differ in inheritance and generics design, null handling, and declaration-site variance [1]. Identifying such cross-language compilation issues is crucial for assessing the robustness of Kotlin/Java compilers. Therefore, establishing systematic cross-language compilation testing is essential for improving the interoperability and reliability of the JVM ecosystem.
Mutator
Config
Unverified Cross-Lang Program
Differential Testing
Latest Kotlin/ Scala/Groovy Compiler
Latest Java Compiler
Earlier Kotlin/ Scala/Groovy Compiler
Earlier Java Compiler
Result Analyzer IR Serializer
IR Program that triggers bug
.Reduce the Bug Triggering Program and Report Roll Back IR Reductor bug still exist
raeppasid gub
By differentially testing multiple versions of JVM compilers, CrossLangFuzzer has uncovered 32 confirmed bugs in the latest versions of five major JVM compilers.
Xiaotian Ma, Qiong Feng, Yongqiang Tian, Wei Song, and Peng Liang
until no further reduction can make bug disappear
Minimized Trigger IR Program
Printer
Report to compiler teams Minimized Trigger Source Program
Figure 2: CrossLangFuzzer Overall Framework (1) Generator: Given a configuration file which specifies target JVM languages, the generator synthesizes an initial cross-language program modeled in our custom IR. The generator is valid by construction: integrated semantic constraint-checking guarantees that the generated class hierarchies and type assignments strictly conform to JVM inheritance rules. (2) Mutator: To enhance test-case diversity, CrossLangFuzzer passes the initial valid IR to a mutation engine, which deliberately applies structural modifications without enforcing semantic validation. This lack of validation concerns semantic well-typedness, not syntactic validity. For programs taken directly from the generator without mutation—which are well-typed by construction—any divergence in accept/reject behavior across compilers is flagged as a candidate bug. For programs altered by mutation, which may be ill-typed, we likewise treat accept-versus-reject divergence as the trigger, but only as a candidate: because compilers may legitimately
CrossLangFuzzer: Differential Testing of Cross-Language JVM Compilers
Conference’17, July 2017, Washington, DC, USA
Table 1: Mutation Operators in CrossLangFuzzer Mutation Operator
Description
mutateGenericArgumentInParent
Replaces a type argument within a parent class or interface. Example: List<String> → List<Int>
removeOverrideMemberFunction
Strips an overridden method body, transforming it into an empty stub while preserving its signature.
mutateGenericArgumentInMemberFunctionParameter
Replaces a nested type argument inside a function parameter. Example: Map<K, V> → Map<Int, String>
mutateParameterNullability
Toggles the nullability modifier of a targeted function parameter. Example: x: String → x: String?
mutateClassTypeParameterUpperBoundNullability
Toggles whether a class type parameter’s upper bound allows null values. Example: T <: Any → T <: Any?
mutateClassTypeParameterUpperBound
Replaces the upper bound of a class type parameter with an entirely distinct type. Example: T <: Any → T <: Comparable<T>
shuffleLanguage
Reassigns the target language of IR classes. Example: (Kotlin,Java) → (Scala,Java)
differ on invalid input, each divergence is minimized and then manually inspected together with the respective compiler developers, who ultimately confirm whether it is a real bug. (3) Printer and Runner: CrossLangFuzzer employs three distinct language printers to transform the abstract IR into concrete, interacting source code across different target languages: KtIrClassPrinter for Kotlin, JavaIrClassPrinter for Java and Groovy, and ScalaIrClassPrinter for Scala. The runner then invokes the respective compilers in the following modes: • NormalTest: Compiles the code using a single compiler version to detect crashes or internal errors. • DifferentialTest: Executes cross-version or cross-compiler trials, where behavioral or bytecode mismatches indicate potential bugs. (4) IR Reductor: When a bug is detected, CrossLangFuzzer serializes the generated test program into the unified IR and minimizes it using our IR reducer. The reducer is based on an optimized Delta Debugging Minimization (DDMin) algorithm and employs an active validator to ensure that the IR remains structurally valid after each element removal. After each reduction step, the resulting IR is translated back into source code and executed to determine whether the bug is still reproducible. If the bug persists, the reduced IR is retained for the next iteration; otherwise, the change is rolled back. This process continues until no further reduction is possible without eliminating the bug. The final minimized IR is then translated into source programs and reported to the corresponding compiler development team for confirmation.
3.2
Unified Intermediate Representation (IR)
To decouple test generation from language-specific syntax, CrossLangFuzzer introduces a unified intermediate representation. The
type system and structure of our IR are inspired by the Kotlin compiler’s backend IR, which models the rich semantic features necessary for multi-language JVM interoperability—such as advanced generics, mixed nullability, and platform-specific types. Rather than invoking the Kotlin compiler during code generation, CrossLangFuzzer implements these structural abstractions independently, enabling flexible, multi-target code generation. 3.2.1 Program Structure. The IR models a multi-language program as a hierarchical declaration tree. The root Program node acts as a container for multiple ClassDeclaration nodes. Each class maintains explicit metadata, including a target-language tag, an inheritance chain (extends/implements), optional type parameters, and its member functions. For instance, a cross-language boundary where a Scala class extends a parameterized Java interface is captured as a unified declaration tree before code generation. 3.2.2 Type System Representation. To comprehensively stress-test modern JVM compiler front-ends, the IR supports five type forms that map directly to internal compiler metadata structures: • Simple types: Unparameterized base classifiers or primitive representations (e.g., Int, String, Any). • Parameterized types: Generic types bound to explicit type arguments (e.g., List<Int>). • Nullable types: Explicitly nullable forms denoted by a suffix modifier (e.g., String?). • Platform types: Types with ambiguous nullability denoted by an exclamation mark (e.g., String!), replicating how the Kotlin compiler treats unannotated Java types at crosslanguage boundaries. • Type parameters: Generic variables constrained by optional upper bounds (e.g., T <: Comparable<T>). 3.2.3 Traversal Mechanisms. The pipeline interacts with the IR through two unified traversal components: a top-down visitor
Conference’17, July 2017, Washington, DC, USA
for structural metadata collection and verification, and an in-place transformer for subtree replacement. The generator and the reduction validator use the visitor to enforce structural soundness, while the mutator uses the transformer to transform the IR.
3.3
Table 2: Overview of the bugs discovered by CrossLangFuzzer. Compiler
Bug IDs
#
Status
Kotlinc
KT-74109, KT-74147, KT-74148, KT-74151, KT-74156, KT-74160, KT-74174, KT-74188, KT-74202, KT-74209, KT-74288, KT-78819, KT-79508, KT-80382, KT-80387 GROOVY-11548, GROOVY11549, GROOVY-11550, GROOVY-11579
15
1 fixed, 14 confirmed
4
4 fixed (100%)
SCALA3-22307, SCALA3-22308, SCALA3-22309, SCALA3-22310, SCALA3-22311, SCALA3-22312, SCALA3-22717 SCALA2-13074, SCALA2-13075 JDK-8347330, JDK-8352290, JDK-8361835, JDK-8370716
7
confirmed
2 4
confirmed confirmed
Mutation Strategies
CrossLangFuzzer invokes its mutation engine to increase program diversity. Each mutation operator targets a specific semantic feature known to be error-prone during cross-language compilation. As summarized in Table 1, CrossLangFuzzer’s seven mutation operators target four areas of cross-language divergence. • Generic Subtyping. mutateGenericArgumentInParent, mutateGenericArgumentInMemberFunctionParameter, and mutateClassTypeParameterUpperBound mutate type arguments and bounds, forcing compilers to reason about variance, substitution, and type erasure. • Nullability. Operators like mutateParameterNullability and mutateClassTypeParameterUpperBoundNullability toggle nullability on parameters and bounds, exercising cross-language nullability coercion, where missing or misinterpreted metadata frequently triggers compiler errors. • Override Resolution. removeOverrideMemberFunction reduces an overridden method to an empty stub, exercising cross-language method resolution and linkage rules, where Java, Kotlin, and Scala diverge. • Language placement. shuffleLanguage reassigns class languages while holding structure fixed, re-exposing the same pattern under different language combinations and amplifying the other operators. During execution, operators are selected probabilistically from a weighted distribution. To increase test-case complexity, multiple mutations can be chained onto a single IR program before it is passed to the printing layer.
4
Xiaotian Ma, Qiong Feng, Yongqiang Tian, Wei Song, and Peng Liang
Tool Availability and Running the Tool
Tool Availability: CrossLangFuzzer is open-source at https:// github.com/XYZboom/CrossLangFuzzer, with an archived snapshot at https://doi.org/10.5281/zenodo.20925432. A video demonstration is available at https://youtu.be/XBG6dUO0Adk.
Docker (Quick Run) docker pull xyzboom123/clf:dev docker run -it xyzboom123/clf:dev ./quick_run.sh This runs the Kotlin compiler differential testing in a single command. When a bug is found, the runner stops and reports the minimized reproducer in out/min/.
Gradle Build and Run Clone the repository and build the project using Gradle. Each JVM compiler is tested through its own Gradle runner. The following example shows how to execute the Kotlin compiler runner: # Kotlin compiler (JDK 17 required) ./gradlew :runners:kotlin-runner:run --args="-s" \ -Dorg.gradle.java.home=/path/to/jdk17
Groovyc
Scala3c
Scala2c Javac
Commands for the Groovy and Scala runners, along with detailed command-line options, are provided in the repository’s README. For each detected bug, the corresponding runner generates a bug report under the out/min/ directory, including the minimized source files, the serialized IR, and the corresponding compiler error output.
5
Results
Table 2 summarizes the 32 bugs uncovered by CrossLangFuzzer. All reports were validated by the corresponding compiler teams, confirming the effectiveness of CrossLangFuzzer in revealing real cross-language compilation defects. Notably, these bugs were found in the latest actively maintained compiler versions, demonstrating that cross-language inconsistencies remain a significant challenge and that CrossLangFuzzer can effectively expose defects missed by existing testing efforts. Beyond discovery, the reports have proven actionable. Every issue was verified by the respective development team, and several have already been fixed—most notably, the Groovy maintainers resolved all four reported bugs, and one Kotlinc bug has been patched, with the remaining 14 confirmed and awaiting fixes.
6
Summary and Future work
In summary, we address the challenge of validating cross-language JVM compilation with CrossLangFuzzer, which synthesizes crosslanguage IR and programs, applies seven mutation operators to diversify test cases, and automatically reduces bug-triggering programs. Evaluated on five JVM compilers, CrossLangFuzzer uncovered 32 confirmed bugs, demonstrating its effectiveness in exposing cross-language compilation defects. Because generation and mutation operate on an abstract IR in CrossLangFuzzer, the serialized IR provides a natural interface for LLM-assisted fuzzing. For example, an LLM could directly manipulate the serialized IR or guide mutation selection based on observed error logs. We leave such integrations to future work.
CrossLangFuzzer: Differential Testing of Cross-Language JVM Compilers
References [1] Marat Akhin and Mikhail Belyaev. 2021. Kotlin Language Specification. https: //kotlinlang.org/spec/pdf/kotlin-spec.pdf [2] Luca Ardito, Riccardo Coppola, Giovanni Malnati, and Marco Torchiano. 2020. Effectiveness of Kotlin vs. Java in android app development tasks. Information and Software Technology 127 (2020), 106374. [3] Berke Ates, Filip Dobrosavljević, Theodoros Theodoridis, and Zhendong Su. 2026. MLIR-Smith: A Novel Random Program Generator for Evaluating Compiler Pipelines. arXiv preprint arXiv:2601.02218 (2026). [4] Stefanos Chaliasos, Thodoris Sotiropoulos, Georgios-Petros Drosos, Charalambos Mitropoulos, Dimitris Mitropoulos, and Diomidis Spinellis. 2021. Well-typed programs can go wrong: A study of typing-related bugs in jvm compilers. Proceedings of the ACM on Programming Languages 5, OOPSLA (2021), 1–30. [5] Stefanos Chaliasos, Thodoris Sotiropoulos, Diomidis Spinellis, Arthur Gervais, Benjamin Livshits, and Dimitris Mitropoulos. 2022. Finding typing compiler bugs. In Proceedings of the 43rd ACM SIGPLAN International Conference on Programming Language Design and Implementation (PLDI). ACM, 183–198. [6] Junjie Chen, Jibesh Patra, Michael Pradel, Yingfei Xiong, Hongyu Zhang, Dan Hao, and Lu Zhang. 2020. A survey of compiler testing. Comput. Surveys 53, 1 (2020), 1–36. [7] Kyle Dewey, Jared Roesch, and Ben Hardekopf. 2014. Language fuzzing using constraint logic programming. In Proceedings of the 29th ACM/IEEE International Conference on Automated Software Engineering (ASE). ACM, 725–730. [8] Kyle Dewey, Jared Roesch, and Ben Hardekopf. 2015. Fuzzing the Rust typechecker using CLP (T). In Proceedings of the 30th IEEE/ACM International Conference on Automated Software Engineering (ASE). IEEE, 482–493. [9] Qiong Feng, Xiaotian Ma, Ziyuan Feng, Marat Akhin, Wei Song, and Peng Liang. 2025. Finding Compiler Bugs through Cross-Language Code Generator and Differential Testing. Proceedings of the ACM on Programming Languages 9, OOPSLA2 (2025), 2843–2869. [10] Călin Georgescu, Mitchell Olsthoorn, Pouria Derakhshanfar, Marat Akhin, and Annibale Panichella. 2024. Evolutionary generative fuzzing for differential testing of the kotlin compiler. In Proceedings of the 32nd ACM International Conference on the Foundations of Software Engineering (FSE): Companion. ACM, 197–207. [11] Ben Limpanukorn, Jiyuan Wang, Hong Jin Kang, Zitong Zhou, and Miryung Kim. 2025. Fuzzing mlir compilers with custom mutation synthesis. In Proceedings of
Conference’17, July 2017, Washington, DC, USA
the 47th IEEE/ACM International Conference on Software Engineering (ICSE). IEEE, 217–229. [12] Vsevolod Livinskii, Dmitry Babokin, and John Regehr. 2020. Random testing for C and C++ compilers with YARPGen. Proceedings of the ACM on Programming Languages 4, OOPSLA (2020), 1–25. [13] Bruno Gois Mateus and Matias Martinez. 2020. On the adoption, usage and evolution of Kotlin features in Android development. In Proceedings of the 14th ACM/IEEE International Symposium on Empirical Software Engineering and Measurement (ESEM). ACM, 1–12. [14] Xianfei Ou, Cong Li, Yanyan Jiang, and Chang Xu. 2024. The Mutators Reloaded: Fuzzing Compilers with Large Language Model Generated Mutation Operators. In Proceedings of the 29th ACM International Conference on Architectural Support for Programming Languages and Operating Systems (ASPLOS). ACM, 1–15. [15] Yuyang Rong, Zhanghan Yu, Zhenkai Weng, Stephen Neuendorffer, and Hao Chen. 2024. IRFuzzer: Specialized fuzzing for LLVM backend code generation. arXiv preprint arXiv:2402.05256 (2024). [16] Yongqiang Tian, Zhenyang Xu, Yiwen Dong, Chengnian Sun, and Shing-Chi Cheung. 2023. Revisiting the Evaluation of Deep Learning-Based Compiler Testing. In Proceedings of the 32nd International Joint Conference on Artificial Intelligence (IJCAI). IJCAI, 4873–4882. [17] Bo Wang, Chong Chen, Ming Deng, Junjie Chen, Xing Zhang, Youfang Lin, Dan Hao, and Jun Sun. 2025. Fuzzing C++ Compilers via Type-Driven Mutation. Proceedings of the ACM on Programming Languages 9, OOPSLA2 (2025), 1232– 1260. [18] Bo Wang, Pengyang Wang, Chong Chen, Ming Deng, Jieke Shi, Qi Sun, Chengran Yang, Youfang Lin, Zhou Yang, Junjie Chen, et al. 2025. Mut4All: Fuzzing Compilers via LLM-Synthesized Mutators Learned from Bug Reports. arXiv preprint arXiv:2507.19275 (2025). [19] Theodore Luo Wang, Yongqiang Tian, Yiwen Dong, Zhenyang Xu, and Chengnian Sun. 2023. Compilation Consistency Modulo Debug Information. In Proceedings of the 28th ACM International Conference on Architectural Support for Programming Languages and Operating Systems (ASPLOS). ACM, 146–158. [20] Haoran Yang, Yu Nong, Shaowei Wang, and Haipeng Cai. 2024. Multi-language software development: Issues, challenges, and solutions. IEEE Transactions on Software Engineering 50, 3 (2024), 512–533. [21] Xuejun Yang, Yang Chen, Eric Eide, and John Regehr. 2011. Finding and understanding bugs in C compilers. ACM SIGPLAN Notices 46, 6 (2011), 283–294.