A custom Rust compiler backend that compiles Rust directly to Java Virtual Machine (JVM) bytecode, enabling you to compile crates into a runnable .jar compatible with Java 8+.

This backend transparently compiles Rust constructs to Java classes and interfaces, enabling rich interop between JVM and Rust code at a level mostly unreachable by traditional FFI solutions.
It also enables modern Rust code to run on older platforms outside the reach of current native targets, and has integrated upstream changes into OpenJDK's C2 JIT compiler which can make the JVM faster for everyone, including ~1.85x faster 128-bit multiplication on x86.
By leveraging a "virtual MMU" translation layer, it supports raw pointers with complex pointer arithmetic, transmute, and unions. It also supports key parts of the Rust standard library, including networking, async/await, threading, unwinding, allocation, as well as file system operations, STDIO, and more.
Every selected official Rust coretests and alloctests test passes in CI in both debug and release mode. CI currently verifies 2,812 of 2,817 coretests and 1,474 of 1,477 alloctests, which is roughly 99.8% of the upstream test suites.
[!NOTE] This project is in an active mid-stage of development. While it supports the vast majority of the Rust language, edge-case bugs are continually being ironed out. The ultimate goal is potential upstreaming into main
rustc.
Stars, contributions, and feedback are highly welcome and appreciated!
cargo-jvmIf on Windows, please use PowerShell so $PWD will work.
git clone https://github.com/IntegralPilot/rustc_codegen_jvm
cd rustc_codegen_jvm
cargo install --path cargo-jvm
cargo jvm setup "$PWD"
cargo-jvm installs and selects the backend's pinned Rust nightly automatically;
your default toolchain can remain stable.
cargo new hello_world --bin
cd hello_world
cargo jvm run
You should see "Hello, world!" printed to the console.
Then, head down to Usage to learn how to integrate it into your project.
Rust enums, structs, traits, and function pointers lower to ordinary JVM classes and interfaces rather than opaque native handles (see Interop Model). This enables unusually direct interop:
&dyn Trait (test and demo).Fn closures
(test and demo).async functions from Kotlin suspend code while retaining
Kotlin's coroutine dispatcher
(test and demo).For example, one Rust API can expose an enum and accept both a JVM implementation of a Rust trait and a standard JVM lambda. Its result can then cross the Rust/Kotlin async bridge. The complete example is kept executable in the Kotlin interop test suite:
Rust
pub trait BatchObserver {
fn accept(&mut self, processed: u32) -> bool;
}
pub enum PipelineResult {
Success { count: u32, elapsed_ms: u64 },
Rejected(i32),
}
pub fn process_batch(
batch_size: u32,
transform: &dyn Fn(u32) -> u32,
observer: &mut dyn BatchObserver,
) -> PipelineResult {
let processed = transform(batch_size);
if observer.accept(processed) {
PipelineResult::Success {
count: processed,
elapsed_ms: u64::from(batch_size),
}
} else {
PipelineResult::Rejected(-1)
}
}
pub async fn confirm_batch(result: PipelineResult) -> PipelineResult {
result
}
Kotlin
import my_crate.BatchObserver
import my_crate.PipelineResult
import org.rustlang.runtime.await
class LimitObserver(private val limit: Int) : BatchObserver {
override fun accept(processed: Int): Boolean = processed <= limit
}
suspend fun main() {
val prepared = my_crate.my_crate.process_batch(
40,
{ value -> value + 2 },
LimitObserver(100),
)
val outcome = my_crate.my_crate.confirm_batch(prepared)
.await<PipelineResult>()
when (outcome) {
is PipelineResult.Success -> {
val (count, elapsedMs) = outcome
println("completed: $count in ${elapsedMs}ms")
}
is PipelineResult.Rejected -> {
val (code) = outcome
println("rejected: $code")
}
else -> error("unknown PipelineResult implementation")
}
}
Single tuple payloads use value, multi-field tuples use _0, _1, and so on,
and struct-like variants retain their field names. Kotlin can destructure any payload variant.
Java
import org.rustlang.runtime.Utf8View;
import my_crate.NamedCounter;
import my_crate.Accumulator;
import my_crate.Calculation;
import my_crate.NetworkEvent;
import my_crate.AppEvent;
import java.time.LocalDate;
import static my_crate.my_crate.*;
public class Main {
// Implement a Rust trait directly on any Java class
private static class JavaAccumulator implements Accumulator {
private int sum = 0;
@Override
public int add(int amount) {
this.sum += amount;
return this.sum;
}
}
// Ordinary Java fields and constructors can be imported by Rust.
public static int sharedCount = 10;
public static final class JavaCounter {
public int value;
public JavaCounter(int value) {
this.value = value;
}
}
public static void main(String[] args) {
// 1. Interact with Rust types and methods
NamedCounter counter = NamedCounter.new(Utf8View.fromJavaString("JVM-Counter"));
counter.increment();
System.out.println("Counter: " + counter.count);
// 2. Construct, inspect, compare, and call methods on Rust enums
Calculation calculation = new Calculation.Success(42);
Calculation sameCalculation = new Calculation.Success(42);
int payload = ((Calculation.Success) calculation).value;
System.out.println("Enum payload: " + payload);
System.out.println("Enum method: " + calculation.value_or(-1));
System.out.println("Enum equality: " + Calculation.eq(calculation, sameCalculation));
// A transparent enum subtype needs no AppEvent.Network wrapper.
NetworkEvent network = new NetworkEvent.Connected(8080);
AppEvent event = network;
System.out.println("Outer variant: " + AppEvent.variantIndex(event));
System.out.println("Trait method: " + event.code());
System.out.println("Rust match: " + inspect_event(event));
// 3. Pass a standard Java lambda directly to a Rust Fn closure
int result = apply_twice(val -> val * 3, 2);
System.out.println("Lambda output: " + result);
// 4. Pass a Java trait implementation to Rust dynamic dispatch
JavaAccumulator acc = new JavaAccumulator();
int finalSum = run_accumulation(acc);
System.out.println("Accumulator sum: " + finalSum);
// 5. Construct and call a standard Java API object in Rust
LocalDate leapDay = make_java_date(2024, 2, 29);
System.out.println("Leap day: " + leapDay);
System.out.println("Leap year: " + java_date_year(leapDay));
// 6. Construct a Java object and access its fields from Rust
System.out.println("Java field result: " + update_java_counter());
}
}
Rust
#![feature(extern_types, register_tool)]
#![register_tool(jvm)]
unsafe extern "C" {
#[link_name = "java/time/LocalDate"]
pub type JavaLocalDate;
#[link_name = "jvm:static:java/time/LocalDate:of"]
fn java_local_date_of(year: i32, month: i32, day: i32) -> *const JavaLocalDate;
#[link_name = "jvm:virtual:getYear"]
fn java_local_date_get_year(date: &JavaLocalDate) -> i32;
#[link_name = "Main$JavaCounter"]
pub type JavaCounter;
#[link_name = "jvm:new:Main$JavaCounter"]
fn java_counter_new(value: i32) -> *mut JavaCounter;
// A return value makes this an instance-field getter.
#[link_name = "jvm:field:value"]
fn java_counter_value(counter: &JavaCounter) -> i32;
// A value parameter and () return make this an instance-field setter.
#[link_name = "jvm:field:value"]
fn java_counter_set_value(counter: &mut JavaCounter, value: i32);
#[link_name = "jvm:static-field:Main:sharedCount"]
fn shared_count() -> i32;
#[link_name = "jvm:static-field:Main:sharedCount"]
fn set_shared_count(value: i32);
}
impl JavaLocalDate {
pub fn year(&self) -> i32 {
unsafe { java_local_date_get_year(self) }
}
}
pub struct NamedCounter {
pub name: &'static str,
pub count: u32,
}
impl NamedCounter {
pub fn new(name: &'static str) -> Self {
NamedCounter { name, count: 0 }
}
pub fn increment(&mut self) {
self.count += 1;
}
}
pub enum Calculation {
Success(i32),
Failure(i32),
}
impl Calculation {
pub fn value_or(&self, fallback: i32) -> i32 {
match self {
Calculation::Success(value) => *value,
Calculation::Failure(_) => fallback,
}
}
}
pub enum NetworkEvent {
Connected(i32),
Disconnected,
}
pub enum AppEvent {
// NetworkEvent extends AppEvent on the JVM; AppEvent$Network is omitted.
#[jvm::subtype]
Network(NetworkEvent),
Calculation(Calculation),
}
pub trait EventCode {
fn code(&self) -> i32;
}
impl EventCode for AppEvent {
fn code(&self) -> i32 {
match self {
AppEvent::Network(NetworkEvent::Connected(port)) => *port,
AppEvent::Network(NetworkEvent::Disconnected) => -1,
AppEvent::Calculation(value) => value.value_or(-1),
}
}
}
pub fn inspect_event(event: AppEvent) -> i32 {
event.code()
}
pub fn apply_twice(callback: &dyn Fn(i32) -> i32, value: i32) -> i32 {
callback(callback(value))
}
pub trait Accumulator {
fn add(&mut self, value: i32) -> i32;
}
pub fn run_accumulation(acc: &mut dyn Accumulator) -> i32 {
acc.add(10) + acc.add(5)
}
pub fn make_java_date(year: i32, month: i32, day: i32) -> *const JavaLocalDate {
unsafe { java_local_date_of(year, month, day) }
}
pub fn java_date_year(date: &JavaLocalDate) -> i32 {
date.year()
}
pub fn update_java_counter() -> i32 {
unsafe {
let counter = java_counter_new(5);
java_counter_set_value(&mut *counter, java_counter_value(&*counter) + 1);
set_shared_count(shared_count() + 1);
java_counter_value(&*counter) + shared_count()
}
}
Because the compiler targets standard JVM bytecode rather than native machine code, compiled output can run on platforms far outside the reach of modern native Rust targets. It supports any environment with JVM 8+ compatibility.
| Operating System | Native Rust Minimum | JVM 8 (rustc_codegen_jvm) |
|---|---|---|
| Windows | Windows 10 | Windows Vista SP2 / 7 SP1 |
| macOS | 10.12 Sierra | 10.8.3 Mountain Lion |
| Linux | Kernel 3.2, glibc 2.17 | Kernel 2.6.28, glibc 2.9+ |
| Solaris | Solaris 11.4 | Solaris 10 |
Compiling directly to JVM bytecode also avoids the deployment friction of native shared libraries in restricted environments. This makes compiled JARs highly portable across sandboxed environments (such as Minecraft mod loaders) and Android platforms (via DEX conversion).
Developing this backend helps inspire me to find opportunities to optimise OpenJDK's upstream HotSpot C2 compiler. Contributions benefit the entire JVM ecosystem (including Java and Kotlin).
One merged optimisation (OpenJDK PR #30174) sped up 128-bit multiplication by ~1.85x on x86 targets. Another contribution under review (OpenJDK PR #30485) introduces internal range-check elimination in loops for common compiled patterns.
Transitioning a large production JVM codebase to native Rust is rarely feasible in a single step. rustc_codegen_jvm enables an incremental migration path where new or refactored components are written in Rust while remaining fully compatible with the existing JVM application. Once a rewrite is complete, the Rust code can either be target-switched to native or kept on the JVM target for fast iteration and cross-platform consistency.
Once shared standard-library artifacts are cached, incremental compilation for crates is fast. Leveraging the JVM's mature debugging, hot-reloading, and tracing ecosystem (such as JFR and IDE debuggers) opens up rapid iteration workflows that are traditionally difficult with native Rust targets.
Additionally, the virtual MMU layer can catch raw pointer Undefined Behavior (UB) early, throwing structured Java exceptions with accurate stack traces and LineNumberTable information.
The following example programs live in tests/, are compiled with the standard library to JVM bytecode, and are verified in CI on every commit:
| Example | Demonstrates |
|---|---|
| Alloc | Complex allocations: binary trees, heaps, linked lists, vectors, strings, Arc/atomics, and drop/cleanup semantics. |
| Threads | Multi-threading, scoped threads, mutexes (with poisoning), RWLocks, barriers, condition variables, and TLS. |
| Panic | Unwinding, catching static/dynamic panic payloads, resuming unwinds, and custom panic hooks. |
| Async / Await | Multi-poll futures, nested and recursive async work, async closures and trait methods, dyn Future, cross-thread execution, cancellation, and unwinding across suspension points. |
| STD | File system operations, command-line arguments, environment variables, standard I/O, and runtime context. |
| Network | Loopback TCP and UDP, DNS, timeouts, nonblocking sockets, peeking, vectored I/O, multicast, cloning, shutdown, and socket options. |
| Example | Demonstrates |
|---|---|
| Raw Pointers | Pointer identity, dereferencing, casts, offset arithmetic, fat pointers, and DST handling. |
| Unions | unsafe union storage, field nesting, and reinterpretation. |
| Enums & Structs | Complex nested data structures, tuples, arrays, and slices. |
| Traits | Trait implementations, trait objects, and dynamic dispatch. |
| Function Pointers | Function pointers as values, struct fields, parameters, returns, and generics. |
| Iterators | Combinators (map, zip, chain, flatten), double-ended traversal, and custom iterators. |
| Example | Demonstrates |
|---|---|
| Rich Enums | Constructing, inspecting, comparing, and dispatching through Rust enum interfaces and transparent subtypes. |
| Lambda Callbacks | Passing native Java lambdas directly into Rust functions expecting Fn closures. |
| Trait Implementors | Implementing a Rust trait on a Java class and passing it to Rust dynamic dispatch (&dyn Trait). |
| JVM Link Names | Calling JVM constructors and accessing instance and static fields directly from Rust. |
| Kotlin Async | Awaiting Rust async functions from Kotlin suspend code. |
The vast majority of the Rust language is supported, including generics, traits, coroutines, closures, control flow, data structures, and unsafe features (raw pointer arithmetic, transmutes, and unions).
The following pass rates are a quality gate enforced by CI, in both debug and release mode.
| Suite | Passed | Project skips (too slow) | Upstream ignored | Total | Verified |
|---|---|---|---|---|---|
coretests | 2,812 | 3 | 2 | 2,817 | 99.82% |
alloctests | 1,474 | 3 | 0 | 1,477 | 99.80% |
Beyond core and alloc, a large amount of the std is supported too, though a JVM OS implementation which is overlayed on top of upstream.
| Subsystem | Status | Details |
|---|---|---|
| Threads & Sync | Supported | Thread spawning, scoped threads, Mutex, RwLock, Condvar, TLS |
| Async & Futures | Supported | Async functions, blocks, closures and trait methods; boxed/recursive dyn Future; Kotlin suspend interop; cancellation |
| Panic Unwinding | Supported | Complete unwinding stack, catch_unwind, panic hooks, and abort-on-double-panic semantics |
| Stdio & Env | Supported | println!, eprintln!, stdin, env::args, env::vars |
| Time & Random | Supported | SystemTime, Instant, standard entropy seeds |
File System (std::fs) | Supported | Java NIO files, directories, positional and vectored I/O, nanosecond timestamps, opaque file identity, atomic POSIX creation permissions, links, locks, and paths |
Networking (std::net) | Supported | Java NIO TCP/UDP, IPv4/IPv6, DNS, timeouts, nonblocking sockets, vectored I/O, peeking, multicast, and socket cloning |
Processes (std::process) | Planned | Spawning, managing, interacting with, and terminating child processes |
Compiled JAR files emit rich JVM metadata (LineNumberTable, parameter names, nested class info), ensuring seamless IDE integration (autocomplete, tooltips, refactoring) in IntelliJ IDEA and detailed stack traces during debugging or profiling with JFR.
DirectoryStream does not expose the native directory-entry type (d_type). DirEntry::file_type therefore performs a no-follow metadata lookup.IP_TTL or IPV6_V6ONLY. The corresponding std::net getters and setters return ErrorKind::Unsupported; multicast TTL is supported.coretests pass, niche compiler edge cases may still trigger Internal Compiler Errors (ICEs).quote!() proc macro is currently unsupported.std::traceback which is currently unimplemented and emits some warnings during std compile.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
rustc Frontend: Parses and type-checks code, lowering it to Mid-level IR (MIR).lower1: Transforms MIR into a custom "Object-Oriented MIR" (OOMIR) matching JVM constructs.optimise1: Applies constant folding, constant propagation, dead code elimination, and algebraic simplification.lower2: Translates OOMIR to bytecode, computes stack map frames, and serialises .class files via ristretto_classfile.java-linker: Bundles generated .class files and the runtime environment into a self-contained .jar with manifest metadata.To enable unsafe Rust features without violating JVM bytecode verification or breaking garbage collection, the runtime uses a custom translation layer (Pointer.java). The basics are:
ALLOCATION_RANGES).WeakReference entries and ReferenceQueue hooks to prevent tracking metadata memory leaks.ATOMIC_STRIPES), maintaining thread safety up to SeqCst.Because the JVM uses garbage collection, rustc_codegen_jvm preserves Rust's deterministic RAII semantics by emitting explicit drop calls at compile time.
rustc's frontend. The backend emits direct bytecode calls at every MIR Drop terminator, executing cleanup synchronously at scope exit rather than relying on GC finalisation.Pointer.dropSlice) for slices, and enum-scoped per-variant methods for enums to prevent dropping inactive variant payloads. Scoping the generated method names also makes transparent nested enum subtypes safe.public interface RustDrop { void rustDrop(); }). Dynamic cases like dyn Trait objects or pointers use runtime instanceof checks (Pointer.dropRustValue) to dispatch destructors safely.Drop side effects (closing handles, releasing locks) run eagerly as normal Java method calls at standard Rust scope boundaries.Rust constructs map directly to JVM structures without requiring JNI wrapper code:
| Rust Construct | JVM Representation |
|---|---|
struct | Standard Java class with 1:1 mapped fields and methods |
enum | Java interface with a final concrete class per variant. More info below. |
union | Class backed by contiguous byte-array storage with reinterpretation helpers |
trait | Java interface |
fn(A, B) -> R | Single-method Java interface (Functional Interface) |
async fn(...) -> T | Generated state-machine class implementing RustFuture; awaitable from Kotlin with await<T>() |
impl methods | JVM instance/default methods, with owner-qualified static entry points for Rust dispatch |
&dyn Trait | Java interface reference |
str / &str | UTF-8-preserving org.rustlang.runtime.Utf8View |
*const T / *mut T | Shared pointer wrapper (org.rustlang.runtime.Pointer) |
Rust enums become unsealed Java interfaces, with a final class and public payload fields for each variant.
#[jvm::subtype] lets a one-field variant use its nested enum directly, without
a wrapper class:
#![feature(register_tool)]
#![register_tool(jvm)]
pub enum Leaf {
A(i32),
B,
}
pub enum Root {
#[jvm::subtype]
Leaf(Leaf),
Other(i32),
}
Conceptually, this generates:
public interface Root {
static int variantIndex(Root value) { /* instanceof-based tag */ }
static boolean eq(Root left, Root right) { /* structural equality */ }
final class Other implements Root {
public int value;
public Other(int value) { this.value = value; }
public int component1() { return value; }
}
}
// Leaf is itself the Root.Leaf case: no Root$Leaf wrapper is emitted.
public interface Leaf extends Root {
final class A implements Leaf {
public int value;
public A(int value) { this.value = value; }
public int component1() { return value; }
}
final class B implements Leaf {
public B() {}
}
}
// Ordinary Java code can use the generated hierarchy directly.
Leaf leaf = new Leaf.A(42);
Root root = leaf;
int outerVariant = Root.variantIndex(root); // 0: Root.Leaf
int payload = ((Leaf.A) root).value; // 42
boolean equal = Root.eq(root, new Leaf.A(42));
rust-toolchain.toml)java, javac, and jar must be available on PATH)The Kotlin compiler is only needed for tests/kotlin; CI installs the pinned version with tests/kotlin/install_kotlin.py.
cargo-jvm is used to make building and running Rust projects on the JVM as seamless as possible. It wraps the standard Cargo workflow, forwarding all ordinary Cargo selection and feature arguments. For instructions on installing cargo-jvm, see its README.
The following commands assume you are within a Rust project directory that you
wish to compile/run using the JVM. cargo-jvm forces the backend's pinned
nightly for its Cargo and rustc subprocesses, regardless of that project's
default toolchain.
cargo jvm build
cargo jvm build --release --features serde
cargo jvm build --workspace -j 8
Build artifacts are placed under target/jvm-unknown-jvm/debug or release, just as with an explicit Cargo target.
Binary and cdylib artifacts are JARs, but ordinary Rust libraries remain .rlib inputs.
Binaries, cdylib and ordinary libraries can all be packaged into fully self-contained JARs using cargo jvm package (see below).
Build and launch a binary with the correct JAR and classpath automatically:
cargo jvm run
cargo jvm run --release
The launcher defaults to a 16 MiB JVM thread stack. It can be adjusted, and arbitrary Java options and program arguments can be provided:
cargo jvm run --stack 32m --java-arg=-ea -- program-argument
Create a self-contained distributable JAR with all required org.rustlang.runtime classes.
cargo jvm package --release
cargo jvm package --output dist/my-app.jar
Default outputs go to target/jvm-package/<profile>.
Use --bin or --lib when a package contains both and a single --output is requested:
cargo jvm package --lib --output dist/my-library.jar
cargo jvm package --bin my-app --output dist/my-app.jar
Rust test targets can also run on the JVM:
cargo jvm test
cargo jvm test --release --workspace
cargo jvm test -- --nocapture
This compiles Cargo's test targets with --no-run, then launches every reported test JAR on the JVM.
cargo jvm doctor reports the cargo-jvm version and source commit (when
available), the configured backend's current Git commit, Java, Cargo, rustc,
target and runtime paths. Please run this if you are reporting a bug.
cargo jvm update pulls and rebuilds the backend. If the backend changes its
nightly pin, cargo-jvm installs that dated nightly and its required components
before rebuilding.
The pin in rust-toolchain.toml is the single source of truth for local builds,
cargo-jvm, and CI. A scheduled GitHub Actions workflow checks Rust's latest
published nightly once per day. It skips an already-tested date, advances the
pin only after the backend, compiler unit tests, and debug/release self-tests
pass, and opens or updates an issue when compatibility fails.
Run cargo jvm --help for all options.
Run the binary, multi-crate, Rust/Java integration, and cargo-jvm workflow self-test suite:
python3 Tester.py # Debug build testing
python3 Tester.py --release # Release build testing
Run the upstream coretests verification suite (add --include-default-ignored to run really slow cases too):
python3 Coretests.py # Debug mode
python3 Coretests.py --release # Release mode
Run the upstream alloctests verification suite (add --include-default-ignored to run the explicitly skipped cases too):
python3 Alloctests.py # Debug mode
python3 Alloctests.py --release # Release mode
Inspect compiler work amplification without replacing a native CPU profiler:
python3 Metrics.py --debug --only-run fibonacci
OOMIR: 936 -> 891 instructions; 22 shard-local data-type definitions
optimise2: 137 methods, 3,298 -> 2,800 bytecode instructions
liveness: 47 analyses, 14,072 matrix words allocated, 5,621 worklist pops
classfiles: 36 built, 33 emitted, 3 exact duplicates discarded
The full JSON report also contains per-pass input/removal counts, repeated data
types, classfile amplification, type-cache effectiveness, and linker fragment
merging. Set RCGJ_METRICS_DIR directly to collect the same records from any
build.
.
├── src/ # Compiler backend implementation
│ ├── lower1/ # MIR -> OOMIR lowering
│ ├── optimise1/ # OOMIR optimisation passes
│ ├── lower2/ # OOMIR -> Bytecode generator
│ ├── metrics.rs # Structural compiler performance metrics
│ └── oomir.rs # OOMIR definitions
├── java-linker/ # JAR packaging and manifest utility
├── cargo-jvm/ # `cargo jvm` build, run, test and package command
├── runtime/ # Core Java runtime support library
├── std/ # Standard library JVM patch overlays
├── tests/ # Integration, binary, and multicrate tests
│ └── cargo_jvm/ # Real build/run/test/package demo projects
├── build.py # Master build script
├── test_harness.py # Shared test execution utilities
├── Tester.py # Main test suite runner
├── Metrics.py # Compiler work-amplification report runner
├── Coretests.py # Upstream rustc coretests runner
└── Alloctests.py # Upstream rustc alloctests runner
Contributions, bug reports, and feature requests are welcome!
If you are interested in contributing but unsure where to start, feel free to open a thread on the Discussions board and I can point you in the right direction about what's useful right now.
For significant changes or architecture proposals, please open an issue and/or discussion first to discuss the design.
Dual-licensed under either of:
at your option.
Rust
78.8%
Java
18.3%
Python
2.5%
A custom Rust compiler backend that compiles Rust directly to Java Virtual Machine (JVM) bytecode, enabling you to compile crates into a runnable .jar compatible with Java 8+.

This backend transparently compiles Rust constructs to Java classes and interfaces, enabling rich interop between JVM and Rust code at a level mostly unreachable by traditional FFI solutions.
It also enables modern Rust code to run on older platforms outside the reach of current native targets, and has integrated upstream changes into OpenJDK's C2 JIT compiler which can make the JVM faster for everyone, including ~1.85x faster 128-bit multiplication on x86.
By leveraging a "virtual MMU" translation layer, it supports raw pointers with complex pointer arithmetic, transmute, and unions. It also supports key parts of the Rust standard library, including networking, async/await, threading, unwinding, allocation, as well as file system operations, STDIO, and more.
Every selected official Rust coretests and alloctests test passes in CI in both debug and release mode. CI currently verifies 2,812 of 2,817 coretests and 1,474 of 1,477 alloctests, which is roughly 99.8% of the upstream test suites.
[!NOTE] This project is in an active mid-stage of development. While it supports the vast majority of the Rust language, edge-case bugs are continually being ironed out. The ultimate goal is potential upstreaming into main
rustc.
Stars, contributions, and feedback are highly welcome and appreciated!
cargo-jvmIf on Windows, please use PowerShell so $PWD will work.
git clone https://github.com/IntegralPilot/rustc_codegen_jvm
cd rustc_codegen_jvm
cargo install --path cargo-jvm
cargo jvm setup "$PWD"
cargo-jvm installs and selects the backend's pinned Rust nightly automatically;
your default toolchain can remain stable.
cargo new hello_world --bin
cd hello_world
cargo jvm run
You should see "Hello, world!" printed to the console.
Then, head down to Usage to learn how to integrate it into your project.
Rust enums, structs, traits, and function pointers lower to ordinary JVM classes and interfaces rather than opaque native handles (see Interop Model). This enables unusually direct interop:
&dyn Trait (test and demo).Fn closures
(test and demo).async functions from Kotlin suspend code while retaining
Kotlin's coroutine dispatcher
(test and demo).For example, one Rust API can expose an enum and accept both a JVM implementation of a Rust trait and a standard JVM lambda. Its result can then cross the Rust/Kotlin async bridge. The complete example is kept executable in the Kotlin interop test suite:
Rust
pub trait BatchObserver {
fn accept(&mut self, processed: u32) -> bool;
}
pub enum PipelineResult {
Success { count: u32, elapsed_ms: u64 },
Rejected(i32),
}
pub fn process_batch(
batch_size: u32,
transform: &dyn Fn(u32) -> u32,
observer: &mut dyn BatchObserver,
) -> PipelineResult {
let processed = transform(batch_size);
if observer.accept(processed) {
PipelineResult::Success {
count: processed,
elapsed_ms: u64::from(batch_size),
}
} else {
PipelineResult::Rejected(-1)
}
}
pub async fn confirm_batch(result: PipelineResult) -> PipelineResult {
result
}
Kotlin
import my_crate.BatchObserver
import my_crate.PipelineResult
import org.rustlang.runtime.await
class LimitObserver(private val limit: Int) : BatchObserver {
override fun accept(processed: Int): Boolean = processed <= limit
}
suspend fun main() {
val prepared = my_crate.my_crate.process_batch(
40,
{ value -> value + 2 },
LimitObserver(100),
)
val outcome = my_crate.my_crate.confirm_batch(prepared)
.await<PipelineResult>()
when (outcome) {
is PipelineResult.Success -> {
val (count, elapsedMs) = outcome
println("completed: $count in ${elapsedMs}ms")
}
is PipelineResult.Rejected -> {
val (code) = outcome
println("rejected: $code")
}
else -> error("unknown PipelineResult implementation")
}
}
Single tuple payloads use value, multi-field tuples use _0, _1, and so on,
and struct-like variants retain their field names. Kotlin can destructure any payload variant.
Java
import org.rustlang.runtime.Utf8View;
import my_crate.NamedCounter;
import my_crate.Accumulator;
import my_crate.Calculation;
import my_crate.NetworkEvent;
import my_crate.AppEvent;
import java.time.LocalDate;
import static my_crate.my_crate.*;
public class Main {
// Implement a Rust trait directly on any Java class
private static class JavaAccumulator implements Accumulator {
private int sum = 0;
@Override
public int add(int amount) {
this.sum += amount;
return this.sum;
}
}
// Ordinary Java fields and constructors can be imported by Rust.
public static int sharedCount = 10;
public static final class JavaCounter {
public int value;
public JavaCounter(int value) {
this.value = value;
}
}
public static void main(String[] args) {
// 1. Interact with Rust types and methods
NamedCounter counter = NamedCounter.new(Utf8View.fromJavaString("JVM-Counter"));
counter.increment();
System.out.println("Counter: " + counter.count);
// 2. Construct, inspect, compare, and call methods on Rust enums
Calculation calculation = new Calculation.Success(42);
Calculation sameCalculation = new Calculation.Success(42);
int payload = ((Calculation.Success) calculation).value;
System.out.println("Enum payload: " + payload);
System.out.println("Enum method: " + calculation.value_or(-1));
System.out.println("Enum equality: " + Calculation.eq(calculation, sameCalculation));
// A transparent enum subtype needs no AppEvent.Network wrapper.
NetworkEvent network = new NetworkEvent.Connected(8080);
AppEvent event = network;
System.out.println("Outer variant: " + AppEvent.variantIndex(event));
System.out.println("Trait method: " + event.code());
System.out.println("Rust match: " + inspect_event(event));
// 3. Pass a standard Java lambda directly to a Rust Fn closure
int result = apply_twice(val -> val * 3, 2);
System.out.println("Lambda output: " + result);
// 4. Pass a Java trait implementation to Rust dynamic dispatch
JavaAccumulator acc = new JavaAccumulator();
int finalSum = run_accumulation(acc);
System.out.println("Accumulator sum: " + finalSum);
// 5. Construct and call a standard Java API object in Rust
LocalDate leapDay = make_java_date(2024, 2, 29);
System.out.println("Leap day: " + leapDay);
System.out.println("Leap year: " + java_date_year(leapDay));
// 6. Construct a Java object and access its fields from Rust
System.out.println("Java field result: " + update_java_counter());
}
}
Rust
#![feature(extern_types, register_tool)]
#![register_tool(jvm)]
unsafe extern "C" {
#[link_name = "java/time/LocalDate"]
pub type JavaLocalDate;
#[link_name = "jvm:static:java/time/LocalDate:of"]
fn java_local_date_of(year: i32, month: i32, day: i32) -> *const JavaLocalDate;
#[link_name = "jvm:virtual:getYear"]
fn java_local_date_get_year(date: &JavaLocalDate) -> i32;
#[link_name = "Main$JavaCounter"]
pub type JavaCounter;
#[link_name = "jvm:new:Main$JavaCounter"]
fn java_counter_new(value: i32) -> *mut JavaCounter;
// A return value makes this an instance-field getter.
#[link_name = "jvm:field:value"]
fn java_counter_value(counter: &JavaCounter) -> i32;
// A value parameter and () return make this an instance-field setter.
#[link_name = "jvm:field:value"]
fn java_counter_set_value(counter: &mut JavaCounter, value: i32);
#[link_name = "jvm:static-field:Main:sharedCount"]
fn shared_count() -> i32;
#[link_name = "jvm:static-field:Main:sharedCount"]
fn set_shared_count(value: i32);
}
impl JavaLocalDate {
pub fn year(&self) -> i32 {
unsafe { java_local_date_get_year(self) }
}
}
pub struct NamedCounter {
pub name: &'static str,
pub count: u32,
}
impl NamedCounter {
pub fn new(name: &'static str) -> Self {
NamedCounter { name, count: 0 }
}
pub fn increment(&mut self) {
self.count += 1;
}
}
pub enum Calculation {
Success(i32),
Failure(i32),
}
impl Calculation {
pub fn value_or(&self, fallback: i32) -> i32 {
match self {
Calculation::Success(value) => *value,
Calculation::Failure(_) => fallback,
}
}
}
pub enum NetworkEvent {
Connected(i32),
Disconnected,
}
pub enum AppEvent {
// NetworkEvent extends AppEvent on the JVM; AppEvent$Network is omitted.
#[jvm::subtype]
Network(NetworkEvent),
Calculation(Calculation),
}
pub trait EventCode {
fn code(&self) -> i32;
}
impl EventCode for AppEvent {
fn code(&self) -> i32 {
match self {
AppEvent::Network(NetworkEvent::Connected(port)) => *port,
AppEvent::Network(NetworkEvent::Disconnected) => -1,
AppEvent::Calculation(value) => value.value_or(-1),
}
}
}
pub fn inspect_event(event: AppEvent) -> i32 {
event.code()
}
pub fn apply_twice(callback: &dyn Fn(i32) -> i32, value: i32) -> i32 {
callback(callback(value))
}
pub trait Accumulator {
fn add(&mut self, value: i32) -> i32;
}
pub fn run_accumulation(acc: &mut dyn Accumulator) -> i32 {
acc.add(10) + acc.add(5)
}
pub fn make_java_date(year: i32, month: i32, day: i32) -> *const JavaLocalDate {
unsafe { java_local_date_of(year, month, day) }
}
pub fn java_date_year(date: &JavaLocalDate) -> i32 {
date.year()
}
pub fn update_java_counter() -> i32 {
unsafe {
let counter = java_counter_new(5);
java_counter_set_value(&mut *counter, java_counter_value(&*counter) + 1);
set_shared_count(shared_count() + 1);
java_counter_value(&*counter) + shared_count()
}
}
Because the compiler targets standard JVM bytecode rather than native machine code, compiled output can run on platforms far outside the reach of modern native Rust targets. It supports any environment with JVM 8+ compatibility.
| Operating System | Native Rust Minimum | JVM 8 (rustc_codegen_jvm) |
|---|---|---|
| Windows | Windows 10 | Windows Vista SP2 / 7 SP1 |
| macOS | 10.12 Sierra | 10.8.3 Mountain Lion |
| Linux | Kernel 3.2, glibc 2.17 | Kernel 2.6.28, glibc 2.9+ |
| Solaris | Solaris 11.4 | Solaris 10 |
Compiling directly to JVM bytecode also avoids the deployment friction of native shared libraries in restricted environments. This makes compiled JARs highly portable across sandboxed environments (such as Minecraft mod loaders) and Android platforms (via DEX conversion).
Developing this backend helps inspire me to find opportunities to optimise OpenJDK's upstream HotSpot C2 compiler. Contributions benefit the entire JVM ecosystem (including Java and Kotlin).
One merged optimisation (OpenJDK PR #30174) sped up 128-bit multiplication by ~1.85x on x86 targets. Another contribution under review (OpenJDK PR #30485) introduces internal range-check elimination in loops for common compiled patterns.
Transitioning a large production JVM codebase to native Rust is rarely feasible in a single step. rustc_codegen_jvm enables an incremental migration path where new or refactored components are written in Rust while remaining fully compatible with the existing JVM application. Once a rewrite is complete, the Rust code can either be target-switched to native or kept on the JVM target for fast iteration and cross-platform consistency.
Once shared standard-library artifacts are cached, incremental compilation for crates is fast. Leveraging the JVM's mature debugging, hot-reloading, and tracing ecosystem (such as JFR and IDE debuggers) opens up rapid iteration workflows that are traditionally difficult with native Rust targets.
Additionally, the virtual MMU layer can catch raw pointer Undefined Behavior (UB) early, throwing structured Java exceptions with accurate stack traces and LineNumberTable information.
The following example programs live in tests/, are compiled with the standard library to JVM bytecode, and are verified in CI on every commit:
| Example | Demonstrates |
|---|---|
| Alloc | Complex allocations: binary trees, heaps, linked lists, vectors, strings, Arc/atomics, and drop/cleanup semantics. |
| Threads | Multi-threading, scoped threads, mutexes (with poisoning), RWLocks, barriers, condition variables, and TLS. |
| Panic | Unwinding, catching static/dynamic panic payloads, resuming unwinds, and custom panic hooks. |
| Async / Await | Multi-poll futures, nested and recursive async work, async closures and trait methods, dyn Future, cross-thread execution, cancellation, and unwinding across suspension points. |
| STD | File system operations, command-line arguments, environment variables, standard I/O, and runtime context. |
| Network | Loopback TCP and UDP, DNS, timeouts, nonblocking sockets, peeking, vectored I/O, multicast, cloning, shutdown, and socket options. |
| Example | Demonstrates |
|---|---|
| Raw Pointers | Pointer identity, dereferencing, casts, offset arithmetic, fat pointers, and DST handling. |
| Unions | unsafe union storage, field nesting, and reinterpretation. |
| Enums & Structs | Complex nested data structures, tuples, arrays, and slices. |
| Traits | Trait implementations, trait objects, and dynamic dispatch. |
| Function Pointers | Function pointers as values, struct fields, parameters, returns, and generics. |
| Iterators | Combinators (map, zip, chain, flatten), double-ended traversal, and custom iterators. |
| Example | Demonstrates |
|---|---|
| Rich Enums | Constructing, inspecting, comparing, and dispatching through Rust enum interfaces and transparent subtypes. |
| Lambda Callbacks | Passing native Java lambdas directly into Rust functions expecting Fn closures. |
| Trait Implementors | Implementing a Rust trait on a Java class and passing it to Rust dynamic dispatch (&dyn Trait). |
| JVM Link Names | Calling JVM constructors and accessing instance and static fields directly from Rust. |
| Kotlin Async | Awaiting Rust async functions from Kotlin suspend code. |
The vast majority of the Rust language is supported, including generics, traits, coroutines, closures, control flow, data structures, and unsafe features (raw pointer arithmetic, transmutes, and unions).
The following pass rates are a quality gate enforced by CI, in both debug and release mode.
| Suite | Passed | Project skips (too slow) | Upstream ignored | Total | Verified |
|---|---|---|---|---|---|
coretests | 2,812 | 3 | 2 | 2,817 | 99.82% |
alloctests | 1,474 | 3 | 0 | 1,477 | 99.80% |
Beyond core and alloc, a large amount of the std is supported too, though a JVM OS implementation which is overlayed on top of upstream.
| Subsystem | Status | Details |
|---|---|---|
| Threads & Sync | Supported | Thread spawning, scoped threads, Mutex, RwLock, Condvar, TLS |
| Async & Futures | Supported | Async functions, blocks, closures and trait methods; boxed/recursive dyn Future; Kotlin suspend interop; cancellation |
| Panic Unwinding | Supported | Complete unwinding stack, catch_unwind, panic hooks, and abort-on-double-panic semantics |
| Stdio & Env | Supported | println!, eprintln!, stdin, env::args, env::vars |
| Time & Random | Supported | SystemTime, Instant, standard entropy seeds |
File System (std::fs) | Supported | Java NIO files, directories, positional and vectored I/O, nanosecond timestamps, opaque file identity, atomic POSIX creation permissions, links, locks, and paths |
Networking (std::net) | Supported | Java NIO TCP/UDP, IPv4/IPv6, DNS, timeouts, nonblocking sockets, vectored I/O, peeking, multicast, and socket cloning |
Processes (std::process) | Planned | Spawning, managing, interacting with, and terminating child processes |
Compiled JAR files emit rich JVM metadata (LineNumberTable, parameter names, nested class info), ensuring seamless IDE integration (autocomplete, tooltips, refactoring) in IntelliJ IDEA and detailed stack traces during debugging or profiling with JFR.
DirectoryStream does not expose the native directory-entry type (d_type). DirEntry::file_type therefore performs a no-follow metadata lookup.IP_TTL or IPV6_V6ONLY. The corresponding std::net getters and setters return ErrorKind::Unsupported; multicast TTL is supported.coretests pass, niche compiler edge cases may still trigger Internal Compiler Errors (ICEs).quote!() proc macro is currently unsupported.std::traceback which is currently unimplemented and emits some warnings during std compile.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
rustc Frontend: Parses and type-checks code, lowering it to Mid-level IR (MIR).lower1: Transforms MIR into a custom "Object-Oriented MIR" (OOMIR) matching JVM constructs.optimise1: Applies constant folding, constant propagation, dead code elimination, and algebraic simplification.lower2: Translates OOMIR to bytecode, computes stack map frames, and serialises .class files via ristretto_classfile.java-linker: Bundles generated .class files and the runtime environment into a self-contained .jar with manifest metadata.To enable unsafe Rust features without violating JVM bytecode verification or breaking garbage collection, the runtime uses a custom translation layer (Pointer.java). The basics are:
ALLOCATION_RANGES).WeakReference entries and ReferenceQueue hooks to prevent tracking metadata memory leaks.ATOMIC_STRIPES), maintaining thread safety up to SeqCst.Because the JVM uses garbage collection, rustc_codegen_jvm preserves Rust's deterministic RAII semantics by emitting explicit drop calls at compile time.
rustc's frontend. The backend emits direct bytecode calls at every MIR Drop terminator, executing cleanup synchronously at scope exit rather than relying on GC finalisation.Pointer.dropSlice) for slices, and enum-scoped per-variant methods for enums to prevent dropping inactive variant payloads. Scoping the generated method names also makes transparent nested enum subtypes safe.public interface RustDrop { void rustDrop(); }). Dynamic cases like dyn Trait objects or pointers use runtime instanceof checks (Pointer.dropRustValue) to dispatch destructors safely.Drop side effects (closing handles, releasing locks) run eagerly as normal Java method calls at standard Rust scope boundaries.Rust constructs map directly to JVM structures without requiring JNI wrapper code:
| Rust Construct | JVM Representation |
|---|---|
struct | Standard Java class with 1:1 mapped fields and methods |
enum | Java interface with a final concrete class per variant. More info below. |
union | Class backed by contiguous byte-array storage with reinterpretation helpers |
trait | Java interface |
fn(A, B) -> R | Single-method Java interface (Functional Interface) |
async fn(...) -> T | Generated state-machine class implementing RustFuture; awaitable from Kotlin with await<T>() |
impl methods | JVM instance/default methods, with owner-qualified static entry points for Rust dispatch |
&dyn Trait | Java interface reference |
str / &str | UTF-8-preserving org.rustlang.runtime.Utf8View |
*const T / *mut T | Shared pointer wrapper (org.rustlang.runtime.Pointer) |
Rust enums become unsealed Java interfaces, with a final class and public payload fields for each variant.
#[jvm::subtype] lets a one-field variant use its nested enum directly, without
a wrapper class:
#![feature(register_tool)]
#![register_tool(jvm)]
pub enum Leaf {
A(i32),
B,
}
pub enum Root {
#[jvm::subtype]
Leaf(Leaf),
Other(i32),
}
Conceptually, this generates:
public interface Root {
static int variantIndex(Root value) { /* instanceof-based tag */ }
static boolean eq(Root left, Root right) { /* structural equality */ }
final class Other implements Root {
public int value;
public Other(int value) { this.value = value; }
public int component1() { return value; }
}
}
// Leaf is itself the Root.Leaf case: no Root$Leaf wrapper is emitted.
public interface Leaf extends Root {
final class A implements Leaf {
public int value;
public A(int value) { this.value = value; }
public int component1() { return value; }
}
final class B implements Leaf {
public B() {}
}
}
// Ordinary Java code can use the generated hierarchy directly.
Leaf leaf = new Leaf.A(42);
Root root = leaf;
int outerVariant = Root.variantIndex(root); // 0: Root.Leaf
int payload = ((Leaf.A) root).value; // 42
boolean equal = Root.eq(root, new Leaf.A(42));
rust-toolchain.toml)java, javac, and jar must be available on PATH)The Kotlin compiler is only needed for tests/kotlin; CI installs the pinned version with tests/kotlin/install_kotlin.py.
cargo-jvm is used to make building and running Rust projects on the JVM as seamless as possible. It wraps the standard Cargo workflow, forwarding all ordinary Cargo selection and feature arguments. For instructions on installing cargo-jvm, see its README.
The following commands assume you are within a Rust project directory that you
wish to compile/run using the JVM. cargo-jvm forces the backend's pinned
nightly for its Cargo and rustc subprocesses, regardless of that project's
default toolchain.
cargo jvm build
cargo jvm build --release --features serde
cargo jvm build --workspace -j 8
Build artifacts are placed under target/jvm-unknown-jvm/debug or release, just as with an explicit Cargo target.
Binary and cdylib artifacts are JARs, but ordinary Rust libraries remain .rlib inputs.
Binaries, cdylib and ordinary libraries can all be packaged into fully self-contained JARs using cargo jvm package (see below).
Build and launch a binary with the correct JAR and classpath automatically:
cargo jvm run
cargo jvm run --release
The launcher defaults to a 16 MiB JVM thread stack. It can be adjusted, and arbitrary Java options and program arguments can be provided:
cargo jvm run --stack 32m --java-arg=-ea -- program-argument
Create a self-contained distributable JAR with all required org.rustlang.runtime classes.
cargo jvm package --release
cargo jvm package --output dist/my-app.jar
Default outputs go to target/jvm-package/<profile>.
Use --bin or --lib when a package contains both and a single --output is requested:
cargo jvm package --lib --output dist/my-library.jar
cargo jvm package --bin my-app --output dist/my-app.jar
Rust test targets can also run on the JVM:
cargo jvm test
cargo jvm test --release --workspace
cargo jvm test -- --nocapture
This compiles Cargo's test targets with --no-run, then launches every reported test JAR on the JVM.
cargo jvm doctor reports the cargo-jvm version and source commit (when
available), the configured backend's current Git commit, Java, Cargo, rustc,
target and runtime paths. Please run this if you are reporting a bug.
cargo jvm update pulls and rebuilds the backend. If the backend changes its
nightly pin, cargo-jvm installs that dated nightly and its required components
before rebuilding.
The pin in rust-toolchain.toml is the single source of truth for local builds,
cargo-jvm, and CI. A scheduled GitHub Actions workflow checks Rust's latest
published nightly once per day. It skips an already-tested date, advances the
pin only after the backend, compiler unit tests, and debug/release self-tests
pass, and opens or updates an issue when compatibility fails.
Run cargo jvm --help for all options.
Run the binary, multi-crate, Rust/Java integration, and cargo-jvm workflow self-test suite:
python3 Tester.py # Debug build testing
python3 Tester.py --release # Release build testing
Run the upstream coretests verification suite (add --include-default-ignored to run really slow cases too):
python3 Coretests.py # Debug mode
python3 Coretests.py --release # Release mode
Run the upstream alloctests verification suite (add --include-default-ignored to run the explicitly skipped cases too):
python3 Alloctests.py # Debug mode
python3 Alloctests.py --release # Release mode
Inspect compiler work amplification without replacing a native CPU profiler:
python3 Metrics.py --debug --only-run fibonacci
OOMIR: 936 -> 891 instructions; 22 shard-local data-type definitions
optimise2: 137 methods, 3,298 -> 2,800 bytecode instructions
liveness: 47 analyses, 14,072 matrix words allocated, 5,621 worklist pops
classfiles: 36 built, 33 emitted, 3 exact duplicates discarded
The full JSON report also contains per-pass input/removal counts, repeated data
types, classfile amplification, type-cache effectiveness, and linker fragment
merging. Set RCGJ_METRICS_DIR directly to collect the same records from any
build.
.
├── src/ # Compiler backend implementation
│ ├── lower1/ # MIR -> OOMIR lowering
│ ├── optimise1/ # OOMIR optimisation passes
│ ├── lower2/ # OOMIR -> Bytecode generator
│ ├── metrics.rs # Structural compiler performance metrics
│ └── oomir.rs # OOMIR definitions
├── java-linker/ # JAR packaging and manifest utility
├── cargo-jvm/ # `cargo jvm` build, run, test and package command
├── runtime/ # Core Java runtime support library
├── std/ # Standard library JVM patch overlays
├── tests/ # Integration, binary, and multicrate tests
│ └── cargo_jvm/ # Real build/run/test/package demo projects
├── build.py # Master build script
├── test_harness.py # Shared test execution utilities
├── Tester.py # Main test suite runner
├── Metrics.py # Compiler work-amplification report runner
├── Coretests.py # Upstream rustc coretests runner
└── Alloctests.py # Upstream rustc alloctests runner
Contributions, bug reports, and feature requests are welcome!
If you are interested in contributing but unsure where to start, feel free to open a thread on the Discussions board and I can point you in the right direction about what's useful right now.
For significant changes or architecture proposals, please open an issue and/or discussion first to discuss the design.
Dual-licensed under either of:
at your option.
Rust
78.8%
Java
18.3%
Python
2.5%