RJNI runs six stages over your JAR, in this order, every time. The order is fixed, so behaviour is predictable and reproducible.
You supply a built JAR, an optional trial fraction, optional class-level keep patterns, and an optional watermark string. See Configuration.
Every class is parsed with ASM and checked against the registered exclusion profiles
(see Exclusions) and an interface check, an interface
method can never legally carry ACC_NATIVE, so whole interfaces are excluded
from nativization eligibility here (though not from renaming, see
Class name obfuscation). Every
LDC string constant and static final String value across the
whole JAR is also collected here, so later passes know what's unsafe to rename.
On the surviving, eligible classes: class renaming, string encryption, method/field renaming, and control-flow flattening each run as an independent pass over the same class set, in that order. String encryption runs before nativization's own eligibility scan, so a nativized method's compiled Rust code never embeds a plaintext string literal either.
Every remaining method body (skipping <init>/<clinit>,
already-native, and abstract methods) is exported to an
intermediate representation and checked for translatability. A method that throws here
is excluded and recorded with a reason. It does not stop the run.
Every method that exported successfully is compiled, in one batch, into a single
shared native library for the whole JAR, not one library per method. Each method that
compiled is flipped to native via bytecode rewriting and bound through
RegisterNatives, resolved against exactly the calling class regardless of
which class loader defined it.
In this exact order: decompiler-resistant wrapping of whatever's left as ordinary bytecode; anti-tamper and anti-debug injection into every protected class's static initialiser; resource call-site rewriting for matched files; an optional watermark embedded; RJNI's own bundled runtime classes renamed per build; the native library obfuscated and bundled under a randomised path; anti-tamper checksums computed last, over the exact bytes that ship. The result is written out as a single JAR.
A method that can't be safely translated, and a bytecode shape RJNI doesn't yet handle, are both excluded with a recorded reason. Neither is mistranslated or left to crash at runtime. See Known limitations for the current list.