Skip to content

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 static or instance methods, with any visibility.
  • Methods cannot be abstract, constructors (<init>), or static initializers (<clinit>).
  • The annotation has CLASS retention 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.

-enable native-llvm-passes-bogus-control-flow,native-llvm-passes-string-encryption

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 requiring sun.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