Browse documentation

Nativization

ImplementedEligible method bodies are translated to Rust, compiled into the JAR's native library, and relinked through JNI.

What it does

Every eligible method body is exported to an intermediate representation, translated to Rust, and compiled, together with every other method that translated successfully, into one shared native library for the whole JAR. Each method is flipped to a bare native declaration and bound through JNI's RegisterNatives.

A bootstrap native method, resolved by the JVM's default symbol-mangling convention and not through RegisterNatives itself, receives the caller's Class object as an argument at first use. Registration therefore always targets the right class, regardless of which class loader defined it or how many other classes share the same native library.

Scope and boundaries

Supported today: core arithmetic and control flow (branches, loops), object and array access, casting and instanceof, exceptions (throw, and an exception not caught inside a translated method propagates out of it with its original type), whole-method synchronized, lambdas and method references created via LambdaMetafactory, non-virtual calls (super.foo(), private-method calls), and null-safety matching the JVM's semantics: a null dereference throws a catchable NullPointerException instead of causing undefined behaviour in the compiled native code.

Not yet supported, and excluded, not mistranslated: multi-dimensional array literals, block-level synchronized(obj), object reference-equality branches, nested try/catch blocks, and a small number of rarer shapes. Full list: Known limitations.

Where it sits in the pipeline

Nativization runs last among the code-transforming passes (stage 4–5 of the six-stage pipeline), after class renaming, string encryption, member renaming, and control-flow flattening have already run. This means a nativized method's JNI signature and generated Rust code reflect final, post-rename identifiers, and any string literal it used has already been replaced with an encrypted payload and a runtime decrypt call, so the compiled native code never embeds plaintext.

Threat model

Removes the method's Code attribute entirely, so there is no bytecode body left for a decompiler to reconstruct. The trade-off: RegisterNatives is a name-based JNI API, so the method's exact name and JVM descriptor remain visible as plaintext strings inside the compiled native library, whatever that name currently is (a renamed method reveals its renamed name, not the original, which is the intended outcome). See Security model.

Verified. 103 of 103 translatable methods nativized (100%) on an unmodified build of Apache Commons Exec after every codegen gap found along the way was closed. On an unrelated 1,208-class application (a Fabric/Mixin Minecraft mod with Kotlin interop), 3,303 of 3,321 methods nativized (99.5%), and every remaining rejection was categorised with a reason, not a crash.