Browse documentation
Documentation / Known limitations

Known limitations

Every entry below is either rejected with a reason or, for the rare case that can't be caught at translation time, raises a runtime exception. None of them produces a silent mistranslation or a crash. This list is a snapshot: entries are removed as coverage grows.

Bytecode shapes not yet translated

  • Multi-dimensional array literals (new int[a][b]), single-dimension arrays, including of a reference element type, are supported.
  • A synchronized(obj) { ... } block: distinct from whole-method synchronized, which is fully supported since it's carried by the method's access flag, not bytecode instructions.
  • Object reference-equality branches (if (a == b) on two references) and null checks (if (x == null)), only integer-comparison branches are covered today.
  • Wide-value stack manipulation beyond plain DUP and POP, a category-2 (long/double) value's stack semantics aren't modelled yet.
  • Old-style bytecode subroutines (JSR/RET), dead in any class file compiled by JDK 6 or later, so low priority, but unhandled if encountered.
  • invokedynamic bootstraps other than LambdaMetafactory and StringConcatFactory: lambdas, method references and string concatenation are supported; any other bootstrap method is excluded.
  • A direct LDC of a Class, MethodHandle, or MethodType constant. A plain string literal is handled, but indirectly: string encryption rewrites it into a decrypt call before the translator ever sees a bare LDC of a string.
  • A constructor reference (Foo::new), detected and rejected, since it needs a different construction mechanism than an ordinary method reference.
  • Nested try/catch: a handler whose range itself contains another handler's range. Non-nested exception handling is supported.

Excluded by design, not a gap

  • Interface methods (default or static) are never attempted for nativization, the JVM's own class-file verifier rejects ACC_NATIVE on an interface method outright, regardless of whether it has a body. Interfaces are still eligible for renaming.

A gap that can't be caught at translation time

Invoking a default method on a synthesized closure (for example Comparator.thenComparing(...) called on a translated lambda) throws UnsupportedOperationException at the point of the first such call, not at translation time, a call targeting a default method looks identical, at translation time, to one targeting the functional interface's single abstract method.

Native-loading design gaps

  • Windows delete-on-close cleanup for the extracted native library is a documented no-op today, Windows can't unlink an in-use file the way POSIX systems can, and cleanup needs a Windows-specific mechanism not yet implemented.
  • Multi-release JAR variant disambiguation is designed but not implemented, a multi-release JAR isn't specifically detected today.

Scope boundaries, not bugs

These are documented in full on their own pages: method/field renaming's closed-hierarchy requirement, string encryption's LDC-only scope, resource encryption's literal-path requirement, and unverified compatibility with other obfuscators.