The Foreign Function Interface is a new JDK Enhancement-Proposal posted by Charles Oliver Nutter in a Java community website. It described a Foreign Function Interface that can combine native functions and handle blocks of native memory.
What are their goals?
- To produce a Java level foreign function interface comparable to JNA (Java Native Access) or JNR (Java Native Runtime).
- To improve native functions and management of native memory call at the JVM level (optional).
- To support a JSR for a standard Java FFI.
The measure for success is to have an FFI API at the Java level that can execute native-backed features such as NIO, advanced filesystem metadata, process management, and the like. When a new native-level feature is required, they wanted FFI API to be a preference for back-end use.
The reason for this new feature is because over the years Java Native Interface (JNI) is the only reliable process being used by Java and JDK developers to add native-level features to Java application. JNI provide a firm barrier which is difficult to pass through between managed and native code. The following are some of the reported issues experienced by developers with JNI:
- Required expertise in C.
- Need to have at little knowledge of how the JVM manage object lifecycles, GC complications, Java class layout, the JVM lifecycle, etc.
- Aside from writing C code, developers also need to create code for each platform they will support.
- Performance of JNI-based libraries is usually very poor.
- JNI serves as a dense border for security .
It is said that a built-in FFi API at the JDK level will address these issues. The JDK FFI offers the following benefits to JDK developers:
- A metadata system to define native library calls (call protocol, argument list structure, argument types, return type) and native memory structure (size, layout, typing, lifecycle).
- Tool for determining and loading native libraries. These abilities may be offered by current System.loadLibrary or may comprise extra improvements for searching platform or version-specific binaries suitable to the host system.
- Use for binding, based on metadata, a set library/function coordinate to a Java endpoint, likely via a user-defined interface backed by plumbing to create the native downcall.
- Mechanisms for binding, based on metadata, a specific memory structure (layout, endianness, logical types) to a Java endpoint, either via a user-defined interface or a user-defined class, in both cases backed by plumbing to manage a real block of native memory.
- Suitable support code for marshaling Java data types to native data types and vice-versa. In some cases, this will involve the creation of FFI-specific types to support bit widths and numeric signs that Java can’t represent.
He also mentioned that optionally, this JEP will build extra support for the above features through:
- JVM-level responsiveness of FFI downcalls which could include: JIT optimization of those calls, JVM/GC-level awareness of native memory, protection against illegal native memory accesses (SEGV faults), and mechanisms to opt out of JNI safeguards known to be unnecessary in specific cases (safepoint boundaries, blocking call guarantees, object lifecycle management, etc.).
- Tooling at either build time or run time for reflectively gathering function and memory metadata from native libraries. This would help the initial binding of a library by providing a way to generate that binding at the Java level rather than requiring hand-implementation and tweaking for each platform. Previous work here includes the ffi-gen library for (J)Ruby, which uses clang (LLVM C compiler) metadata APIs for generating Ruby FFI code.
- The JVM security subsystem should understand specific library/function coordinates. It should be possible to set up security policies that allow binding only specific functions in specific libraries, rather than just the coarse-grained library-level permissions that exist today.
The level of abstraction for the JDK FFI is not yet determined, however, it must at least able to:
- Load a library;
- Summon a function at some offset in that library;
- Pass bit-appropriate arguments to that library (width, endianness) and receive bit-appropriate values back (typing is not relevant at this level, beyond describing bit width and structure); and
- Minimally manage native memory: allocation, deallocation, access, and passing to/receiving from native calls.
Nutter explained that to test the API, they will need to add representative native endpoints (as C code) to deliver sufficient coverage of all call protocols, type marshaling, and memory management features. Many of these capabilities may exist in current JDK and Java test suites. The JNR library also has an existing suite of tests that could (will) be incorporated into JDK.
One of the risks and assumptions they looking into is that JNR’s original author has recently gone on hiatus from open-source development. However, he believes he will be able to assist us in this effort. In addition, JNR’s license may or may not be compatible with the OpenJDK Community’s standard licensing terms, but it is negotiable.
There are no known dependencies on other JEPs or JSRs. It is also not known to block any other JEPs or JSRs.
Lastly, he listed the following impact of this JEP:
- Compatibility: FFI will increase the work required to guarantee the cross-platform compatibility of the JDK. However, the JDK’s supported platforms already support FFI via JNR.
- Security: FFI opens up the potential for user-level code to access untrusted native functions and read or write normally-inaccessible areas of native memory. This capability is no more extreme than that provided by existing APIs (sun.misc.Unsafe, et al). It will be possible to explicitly define security controls based on library/function coordinates; this capability represents an improvement over coarse-grainedSystem.loadLibrary controls.
- Performance/scalability: Performance of native bindings should improve, if those bindings are written to use FFI. It is an open question whether existing JNI-based features should be rewritten to use FFI, and in what timeframe that should happen.
- Portability: There are obvious portability concerns, but no more than exist in current builds of the JDK. JDK code that uses FFI will still need testing across supported platforms to ensure functionality.
- Documentation: Ideally this API will become the preferred way to bind native code and memory, and so developer documentation should provide everything needed for JDK developers to use this API instead of JNI.
- TCK: A future Java FFI JSR will obviously need additions to the TCK, but ideally this will be little more than the testing provided by the JDK-level FFI.