What It Is
RustJava is not a re-implementation of OpenJDK. It's not trying to keep up with modern Java releases, run a full standard library, or pass the TCK. The goal is much narrower: provide a small, embeddable Java runtime that can be linked into other Rust applications, compiled to WebAssembly, and extended with custom classes defined in Rust rather than Java.
The target version is Java 1.2. That's deliberate. Most of the historical software the project was built to support was written against 1.2-era APIs, and freezing the language target at that level avoids a long tail of features โ generics, lambdas, modules, value types โ that would massively expand the implementation surface without changing what RustJava can actually run.
Why It Exists
RustJava grew out of a concrete need. The
WIE emulator needed a JVM that
could be linked directly into the emulator binary, run
in a no_std environment so it could be
compiled to WebAssembly, and let WIPI and MIDP APIs be
implemented in Rust rather than as a pile of Java glue
code. Standard JVMs failed all three requirements: they
expect a host operating system, they expect to be the
top-level process, and they expect new classes to be
defined as .class files at runtime.
The host environment for WIE is the browser, where shipping a native JVM is not practical to begin with, and the runtime requirements include features โ like callback-driven scheduling and cooperative async tasks โ that don't map cleanly onto an off-the-shelf JVM's threading model. RustJava became reusable enough that it now stands on its own outside the emulator.
Workspace Layout
The project is split into a small set of crates, each owning one piece of the runtime.
-
jvmโ the JVM core. Class loading, classes and instances, method and field metadata, value representations, the garbage collector, and the thread abstraction. Compiled asno_std; everything dynamic goes throughalloc. -
jvm_rustโ the execution engine. Bytecode interpretation, stack frame management, and the Rust-backed implementations of the JVM core traits. This is where actual Java code gets executed instruction by instruction. -
classfileโ the.classfile parser. Constant pool, attributes, fields, methods, and the opcode table. Decoupled from the rest of the runtime so it can also be used as a standalone analysis tool. -
java_class_protoโ the Rust-defined class prototype system. This is the layer that lets a host application register Java classes whose methods are actually Rust functions. -
java_runtimeโ the bundled Java standard library, implemented as Rust-defined class prototypes. Coversjava.lang,java.io,java.util, and the other 1.2-era packages an embedded app is likely to touch.
An embedding application can pull in just the pieces
it needs. A user that only wants to parse class files
can depend on classfile alone; a user
that wants a full embedded JVM pulls in
jvm, jvm_rust, and
java_runtime.
Rust-Defined Class Prototypes
The most distinctive part of RustJava is how it lets
host applications add classes to the runtime. In a
normal JVM, every class either comes from a
.class file on the classpath or from a
JNI-implemented native library. RustJava adds a third
option: a class prototype defined in Rust, where the
class's fields and method signatures are declared as
Rust data and the method bodies are async Rust
functions.
From the running Java code's perspective, these
classes are indistinguishable from classes loaded
from a .class file. They participate in
method dispatch, can be referenced by name, can be
extended, and can throw and catch Java exceptions.
Underneath, when the interpreter dispatches a method
call to a prototype class, it hands off to the
registered Rust function instead of fetching a
bytecode array to interpret.
For embedders, this is the feature that makes the
project worth building. Implementing
java.lang.String or
org.kwis.msp.lcdui.Graphics in Java and
then having to plumb every primitive operation back
out to the host through JNI is enormously more
painful than writing the class once in Rust and
registering it. The standard library shipped with
RustJava itself is built this way.
The no_std and WebAssembly Story
Core crates are written as no_std, using
the alloc crate for heap-allocated
types. This means there's no implicit dependency on
filesystems, threading primitives, or any other
capability that the operating system would normally
provide. The host application supplies whatever
external integrations the JVM needs through
explicit trait implementations.
On native targets the project links against
tokio with the multi-threaded runtime;
on WebAssembly it links against tokio
with the single-threaded runtime instead. Apart from
the executor swap, the same code runs in both
environments. This matters because WIE's
browser-hosted use case needs the JVM to coexist
with the rest of the emulator on a single thread
without needing host-level threading.
Async All the Way Through
Method dispatch in RustJava is async. The
Method::run trait method returns a
future, and the interpreter awaits the result.
Bytecode interpretation itself is async; calls into
Rust-defined prototype methods are async; class
loading is async. This is unusual for a JVM and is
a direct consequence of the embedding model.
The host needs to be able to suspend Java execution
from outside โ for example, when a WIPI API call
has to wait on a host-side audio buffer to drain,
or when ARM code on the other side of an emulator
boundary needs to run before Java can continue.
Making the whole call stack async makes those
suspensions cheap to express. A Rust-defined Java
method just .awaits whatever it needs
and the interpreter handles the rest. No additional
thread-juggling, no callback inversion, no separate
"blocking Java thread" abstraction.
What It Doesn't Try to Be
It's worth being explicit about scope. RustJava is not aiming to run modern Java applications. It does not aim for spec-conformance. It is not a replacement for OpenJDK or for any other production JVM. The standard library implementation is exactly as large as the embedding applications have required, and no larger.
Within that scope, though, the project does the thing it set out to do: run early-era Java code from inside another Rust program, on either native or WebAssembly, with the ability to extend the runtime entirely from the Rust side. That's a narrow niche, but it's one that no existing JVM fills well.
This page is a best-effort write-up โ some details may be inaccurate or out of date. If you spot an error, please flag it via the email on the home page.
Source
RustJava is MIT-licensed and developed on GitHub. The code, issue tracker, and contribution guidelines live there.