JEP 191: Foreign Function Interface

AuthorCharles Oliver Nutter
OwnerJohn Rose
TypeFeature
ScopeJDK
StatusClosed / Withdrawn
Componenttools
JSRTBD
Discussioncore dash libs dash dev at openjdk dot java dot net
EffortM
DurationL
Created2014/01/28 20:00
Updated2021/04/06 21:54
Issue8046181

Summary

Define a Foreign Function Interface that can bind native functions, such as those found in shared libraries and operating-system kernels, to Java methods, and also directly manage blocks of native memory.

Goals

Non-Goals

Success Metrics

Success will be having an FFI API at the Java level sufficient for implementing large-scale native-backed features like NIO, advanced filesystem metadata, process management, and the like. This FFI API should ideally become preferred over writing JNI backends for each platform when a new native-level feature is required.

Motivation

The enhancement of the JDK (and of Java in general) by adding native-level features (new filesystem structures, different I/O channels, new crypto backends) has been stymied by the need to write JNI code to back those features for every new platform. The expertise necessary to write proper JNI is orthogonal to the expertise needed to write good Java code or call native libraries, but JNI has been the only path toward bridging those two worlds. This API will provide a tool enabling any JDK developer with understanding of Java and of a specific native library to bind that library as a JDK API in furtherance of JDK and Java features.

This JEP should make it easier to add new OS, hardware, and native library-level features to Java and the JDK as well as provide a standard Java FFI for use in the wider Java world.

Description

For years, Java and JDK developers have had only one trusted way to add native-level features to Java applications: the Java Native Interface (JNI). This interface governs the boundary between the managed environment of the JVM and the unmanaged environment of native code, providing explicit protocols for data marshaling, object lifecycle management, upcalls back into Java, and native JVM tooling and management APIs. JNI provides a strict, unmistakable demarcation between managed and native code; it also makes that boundary incredibly painful to cross, even for the most trivial cases.

The following aspects of JNI are most painful to developers:

All the difficulties of JNI would be addressed by providing a built-in FFI API at the JDK level. An FFI would be easier for Java developers to write, not require as much knowledge of JVM internals, favor correct implementation or fast-fail over latent bugs, and eliminate the requirement for per-platform build expertise.

The JDK FFI API will provide the following to JDK developers:

  1. A metadata system to describe native library calls (call protocol, argument list structure, argument types, return type) and native memory structure (size, layout, typing, lifecycle).

  2. Mechanisms for discovering and loading native libraries. These capabilities may be provided by current System.loadLibrary or may include additional enhancements for locating platform or version-specific binaries appropriate to the host system.

  3. Mechanisms for binding, based on metadata, a given library/function coordinate to a Java endpoint, likely via a user-defined interface backed by plumbing to make the native downcall.

  4. 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.

  5. Appropriate support code for marshaling Java data types to native data types and vice-versa. This will in some cases require the creation of FFI-specific types to support bit widths and numeric signs that Java can't represent.

Optionally, this JEP will build additional support for the above features via:

  1. JVM-level awareness of FFI downcalls. This 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.).

  2. Tooling at either build time or run time for reflectively gathering function and memory metadata from native libraries. This would aid 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. Prior work here includes the ffi-gen library for (J)Ruby, which uses clang (LLVM C compiler) metadata APIs for generaing Ruby FFI code.

  3. The JVM security subsystem should understand specific library/fuction 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 TBD; at minimum, it must be able to:

  1. Load a library;

  2. Invoke a function at some offset in that library;

  3. 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

  4. Minimally manage native memory: allocation, deallocation, access, and passing to/receiving from native calls.

There will, no doubt, be further discussion on how far to go with this API.

Alternatives

The need for a Java FFI has spawned several libraries. Among these, Java Native Access (JNA) is the most pervasively used, and Java Native Runtime (JNR) is perhaps the most comprehensive and advanced. JNR is likely to form the basis for this JEP, since it implements various levels of abstraction, provides function and memory metadata, abstracts away library and function binding, and has been in heavy use by at least the JRuby project (and its users) for at least the last five years.

Testing

In order to test this API, we will need to add representative native endpoints (as C code) to provide 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.

Risks and Assumptions

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.

JNR's license may or may not be compatible with the OpenJDK Community's standard licensing terms, but it is negotiable.

Dependences

No known dependencies on other JEPs or JSRs. Not known to block any other JEPs or JSRs.

Impact