A custom Rust compiler backend that compiles Rust directly to Java Virtual Machine (JVM) bytecode.
Compile your Rust code into a self-contained, runnable .jar compatible with JVM 8+. By transparently mapping Rust constructs onto Java classes and interfaces, this backend bridges the safety and ergonomics of Rust with the reach of the JVM.
- Fast iteration
- Fresh compilation for most small test crates takes under 1 second, making the backend practical for rapid experimentation compared to native compilation. Due to the rich hot reload and debugging ecosystem of the JVM, my vision is that this project can in future make rapidly iterating on Rust code (which is a known drawback of Rust) fast and enjoyable.
- Interop without the boilerplate
- Rust enums, generics, function pointers, unions and other supported constructs map directly onto JVM classes and interfaces (see Interop Model). Because of this,
rustc_codegen_jvmcan achieve a level of ergonomic interop with Java that comparable native solutions can't. For example, a Java class can implement a Rust trait and be passed directly into Rust code as a&dyn Traitobject and no bindings layer is required.
- Rust enums, generics, function pointers, unions and other supported constructs map directly onto JVM classes and interfaces (see Interop Model). Because of this,
- Run anywhere a JVM runs
- Because the output is standard bytecode rather than a native binary, your Rust code can target environments where native FFI is difficult or unavailable, including sandboxed environments like Minecraft mod loaders and Android (if you convert to DEX files).
- Sandboxed by construction
- Native code loaded via JNI can crash the entire JVM on a bad memory access. Rust compiled to JVM bytecode is checked by the JVM's own bytecode verifier, so those failures stay within the managed runtime. Future visions for the project include it being able to help you leverage the JVM's safety to debug undefined behaviour, like Miri but faster because of the JVM's JIT.
It should be noted that this project is still in early development, but is supporting more of the Rust language as time goes on! The eventual goal is a potential upstreaming with main rustc.
I am so grateful for any stars and support!
- Demos
- Features
- How It Works
- Interop Model
- Target Platforms
- Prerequisites
- Installation & Build
- Usage
- Running Tests
- Project Structure
- Contributing
- License
These examples live in tests/binary, are compiled to JVM bytecode, and are verified on every CI run as part of the integration test suite. Most small examples cold-compile and run in under 1 second - verify it yourself with Instrument.py.
| Example | Demonstrates |
|---|---|
| RSA | Encryption / decryption |
| Binary search | Classic search algorithm |
| Fibonacci | Recursive sequence generation |
| Collatz conjecture | Iterative mathematical verification |
| Large prime generator | Numeric computation at scale |
| Enums / Structs | Nested data structures - tuples, arrays, slices |
| Impl blocks / Traits | Trait implementations, including dynamic dispatch |
| Function pointers | Function pointers as values, fields, parameters, returns, and generic members |
| Unions | unsafe union handling, running on the JVM |
- Constant folding & propagation
- Evaluates constant expressions and known values at compile time.
- Dead code elimination
- Strips unreachable paths for clean, efficient bytecode.
- Algebraic simplification
- Reduces expressions using algebraic identities.
- Control flow
if/else,match,for,while, andloop.
- Data structures
- arrays, slices, structs, tuples, and enums (both C-like and Rust-style).
- Functions & closures
- calls, recursion, function pointers (as values, parameters, return types, and in generics), and closure capture.
- OOP constructs
implblocks for ADTs, includingself,&self, and&mut self.
- Traits
- dynamic dispatch via
&dyn Trait.
- dynamic dispatch via
- Memory management
- mutable borrowing, references, and dereferencing.
- Unions
- supported for primitive types (
bool,i8–f64) and structs composed of them.
- supported for primitive types (
- Output
- executable, self-contained
.jargeneration for binary crates.
- executable, self-contained
- Testing
- integration coverage across debug and release modes for all of the above.
Current milestone: full support for the Rust core crate.
graph TD
A[Rust Source Code] -->|rustc frontend| B(MIR)
B -->|lower1| C(OOMIR)
C -->|optimise1| D(Optimised OOMIR)
D -->|lower2| E[JVM .class files]
E -->|java-linker| F[Executable .jar]
style A fill:#f9d0c4,stroke:#333,stroke-width:2px
style C fill:#d4e6f1,stroke:#333,stroke-width:2px
style F fill:#d5f5e3,stroke:#333,stroke-width:2px
rustcfrontend parses and type-checks your code, lowering it to Mid-level IR (MIR).- lower1 generates a custom "Object-Oriented MIR" (OOMIR) by reshaping MIR into constructs closer to the JVM's object model.
- optimise1 applies constant folding, constant propagation, dead code elimination, and algebraic simplification.
- lower2 translates OOMIR into
.classfiles viaristretto_classfile, including stack map frame generation. - java-linker bundles the
.classfiles with a small runtime shim into a self-contained, runnable.jarwith an appropriateMETA-INF/MANIFEST.MF.
Rust types map onto the JVM's class model directly, which is what makes interop feel native from both sides:
| Rust construct | JVM representation |
|---|---|
struct |
A standard JVM class, with fields and methods generated 1:1 |
enum |
An abstract parent class with an abstract getVariantIdx, and one concrete subclass per variant |
union |
A JVM class over the union's shared memory layout |
trait |
A Java interface - any type implementing the trait implements the interface |
fn(A, B) -> R |
A generated single-method Java interface for that signature, with adapter classes for Rust function definitions |
impl methods (self, &self, &mut self) |
Instance methods on the generated class |
&dyn Trait |
The generated Java interface type, usable as a normal Java argument or return type |
For supported constructs, there is no manual marshalling and no bindings layer to maintain, unlike JNI or Project Panama.
The generated classfiles also carry extra metadata so that IDEs like IntelliJ IDEA offer autocomplete, tooltips, and refactoring support for Rust-defined types directly from Java.
Because output is standard JVM bytecode rather than a native binary, rustc_codegen_jvm targets environments where native compilation isn't practical or allowed:
- Any JVM 8+, including older or constrained runtimes without modern FFI features.
- Android, by routing the
.classoutput through an external DEX conversion pipeline. - Sandboxed or embedded JVM environments, such as Minecraft mod loaders, where loading native libraries is restricted or undesirable.
- Rust Nightly -
rustup default nightly - JDK 8+ -
java,javac, andjarmust be onPATH - Python 3 -
python3must be onPATH
Clone the repository and build all components with the provided build script:
git clone https://github.com/IntegralPilot/rustc_codegen_jvm.git
cd rustc_codegen_jvm
# On Linux or macOS:
./build.py all
# On Windows:
python build.py allThis builds the following, in dependency order:
- The Java library shim (
library/) - The shim metadata file (
core.json) - The
java-linkerexecutable - The
rustc_codegen_jvmbackend library - Configuration files (
config.toml,jvm-unknown-unknown.json)
build.py checks file timestamps on subsequent runs, so only modified components are rebuilt.
-
Configure your project In your target Rust project, create or update
.cargo/config.tomlusing the template provided in the root of this repository. YourCargo.tomlmust also enable per-profile compilation flags:cargo-features = ["profile-rustflags"]
-
Build with Cargo
cargo build # Debug build cargo build --release # Optimised build
-
Run the generated JAR
java -jar target/debug/deps/your_crate*.jar # Debug build java -jar target/release/deps/your_crate*.jar # Release build
Ensure the toolchain is built first:
# On Linux/macOS:
./build.py all
# On Windows:
python build.py allThen run the test suite:
python Tester.py # Debug mode
python Tester.py --release # Release modeResults are printed to the console, and temporary test artifacts are written to .generated/ for inspection. The runner defaults to your local CPU core count; override it with -j / --jobs.
.
├── src/ # rustc_codegen_jvm compiler backend
│ ├── lib.rs
│ ├── lower1/ # MIR -> OOMIR conversion
│ ├── optimise1/ # OOMIR optimiser
│ ├── lower2/ # OOMIR -> JVM bytecode translation
│ └── oomir.rs # OOMIR data definitions
├── java-linker/ # Bundles compiled .class files into .jar archives
├── tests/binary/ # Integration tests and example source crates
├── library/ # Java shim implementation for the Rust core library
├── shim-metadata-gen/ # Tool to generate core.json metadata
├── build.py # Orchestrator build script
├── config.toml.template # Cargo configuration template
├── jvm-unknown-unknown.json.template
├── Tester.py # Automated test runner
└── LICENSE, LICENSE-Apache
Issues and pull requests are welcome and would be greatly appreciated!
If you'd like to get involved but aren't sure where to start, open a thread on the Discussions page - I am happy to help scope out a task list. For larger changes, opening an issue first to discuss the approach is appreciated.
This project is dual-licensed under your choice of:
- MIT License - https://opensource.org/licenses/MIT
- Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0