Configuration & Features
This page covers the three ways you control what Nativify does: the
@Nativify annotation (which methods to protect), the
feature catalog (-enable), and the optional
config file.
The @Nativify annotation
Add the nativify-annotations dependency to your project and mark the methods you
want protected:
import dev.haedus.nativify.annotation.Nativify;
public class LicenseCheck {
@Nativify
public static boolean validate(String key) {
// sensitive logic — compiled to native, removed from your .class files
...
}
}
Rules:
- Methods may be
staticor instance methods, with any visibility. - Methods cannot be abstract, constructors (
<init>), or static initializers (<clinit>). - The annotation has
CLASSretention and is stripped from the final JAR.
To protect methods without annotating them, enable
disable-annotation-check and Nativify will protect every
eligible method.
Feature catalog
Pass features to -enable as a comma-separated list. No features are enabled by
default — a plain run compiles your selected methods to native code without extra
obfuscation passes. Enable passes explicitly to harden the output.
Method selection
| Feature | What it does |
|---|---|
disable-annotation-check |
Protect every eligible method without requiring the @Nativify annotation. |
Obfuscation passes
Each pass makes the native code harder to analyse. Combine the ones you need.
| Feature | Protection |
|---|---|
native-llvm-passes-bogus-control-flow |
Adds fake control-flow branches that never execute. |
native-llvm-passes-substitution |
Replaces simple operations with more complex equivalents. |
native-llvm-passes-string-encryption |
Encrypts string constants; they are decrypted at runtime. |
native-llvm-passes-split-basic-block |
Splits code blocks to increase complexity. |
native-llvm-passes-indirect-call |
Routes function calls through pointers instead of direct calls. |
native-llvm-passes-indirect-global-variable |
Accesses global data indirectly. |
native-llvm-passes-flattening |
Flattens control flow into a dispatcher. (experimental) |
native-llvm-passes-indirect-branch |
Routes branches through jump tables. (experimental) |
native-llvm-all-passes |
Legacy switch that enables all passes at once. Prefer selecting individual passes. (not recommended) |
Performance & behaviour
| Feature | Effect |
|---|---|
native-unsafe |
Faster field access via sun.misc.Unsafe. Requires sun.misc.Unsafe to be available. |
native-llvm-direct-call |
Faster native-to-native calls that skip JNI overhead (static methods only). |
native-llvm-aggressive-opt |
Applies extra optimization for faster native code. |
native-stdlib-replacement |
Replaces some Java standard-library calls with optimized native implementations. |
dynamic-registration |
Binds native methods at runtime so the shared library can be stripped of symbols — extra protection against inspection. |
native-ffm |
Optimized upcall stubs using the Foreign Function & Memory API. Requires Java 21+. (experimental) |
native-cfe-extraction |
Enables protection of some otherwise-unsupported bytecode patterns. (experimental) |
native-unsafe and native-llvm-direct-call are mutually exclusive
Enabling both at once is rejected at compile time. Choose native-unsafe for
faster field access or native-llvm-direct-call for faster method calls —
not both. Each works correctly on its own.
Experimental features
Features marked experimental work in common cases but may have edge-case issues. Test the protected JAR thoroughly before relying on them in production.
Field access: Unsafe vs JNI
How protected code reads and writes object fields depends on whether native-unsafe
is enabled:
- Default (JNI): maximum compatibility, no dependency on
sun.misc.Unsafe. Recommended for Android and locked-down runtimes. native-unsafe: faster field access through direct memory operations, at the cost of requiringsun.misc.Unsafe.
Config file
-c/--config <file> points at a JSON file that supplies defaults for the
command-line options. Anything you also pass on the command line overrides the file.
{
"compileFor": "linux-x86_64,windows-x86_64",
"enable": "native-llvm-passes-bogus-control-flow,native-llvm-passes-string-encryption",
"llvmDir": "/usr/lib/llvm-19/bin",
"verbose": "true"
}
Supported keys mirror the CLI option names: inputJar, outputJar, compileFor,
enable, irOutput, compilerFlags, linkerFlags, llvmDir, outputDir,
skipIROpt, lib.
See also
- CLI reference — every flag and platform target
- Processor reference — GUI/headless processors
- Limitations