JNA: comfortable but slow
Panama: comfortable & fast
Soon I'll start working on similar features for my own language. This could give me a massive head start.
JNA relies on JNI, and exposes a better Java interface.
This means that every call sets up some libffi data structures and libffi uses this information to perform the native call. Likewise in the other direction for return values. With JNI (and Panama), Hotspot can directly emit the argument/return code a the call, not too dissimilar from what a C or C++ compiler would do. There is still some overhead from maintaining JVM invariants. For example, I think a thread blocked in an FFI call can still participate in a safepoint. But that applies to JNI as well.
FFM should offer the perf of JNI with the usability of JNA.
Also, because symbol resolution is done at compile-time, this is a non starter for something like JRuby, wherein one would want to write plain Ruby code to call a C function — rather than writing Ruby, Java and JNI flavored C code. Ruby is an interpreted language, so calling into or out of C requires dynamic symbol resolution, and for the C->Ruby callback case, a closure must be created on the fly (that is, a C ABI function must be JIT compiled for that method or proc). This isn’t particular to Ruby — any interpreted language will run into this issue on the JVM (picture Python and its ctypes and/or cffi libraries).
You can see some of the hoops that JRuby had to jump through to support this use case, in order to provide a JRuby implementation of the ‘ffi’ gem interface:
https://javadoc.io/doc/org.jruby/jruby-core/9.2.13.0/org/jru...
https://www.igvita.com/2009/01/15/bridging-mri-jruby-rubiniu...
It’s been a minute, so my recollection may be wrong, but IIRC JRuby uses Java and JNI to call into a C shim that then calls into libffi to handle things like creating C ABI conforming closures and such on the fly (the ffi gem on CRuby also uses libffi for the same purposes).
Correction: I believe JRuby uses JNA, which in turn is Java+JNI to call C that then calls into libffi, which is then leveraged to call whatever other functions (or create C callbacks back into the JVM). So I’m pretty sure FFM would be an officially supported replacement (maybe superset?) of JNA. (I’m on vacation and typed all of this out on my phone, so forgive me if I got some details a bit off)
I’m pretty sure that just about all of that complexity can be ripped out once FFM is generally available.
We are working with folks on the FFM team to re-implement the core jnr back end. It's very exciting work, and I will present it this weekend at RubyConf Taiwan!
Back when IronRuby (the Ruby implementation for .NET) was still actively developed, I decided to work on a port of the ffi gem for it. The JRuby code base provided some great ideas, thus my familiarity (but that was well over a decade ago now — time flies and memories fade, haha). Sadly, I reached 100% test success rate the same week Jimmy Schementi announced his departure and the writing was on the wall for IronRuby and the other IronX languages, so I never released my work.
Thanks for all you’ve done, btw! Your efforts and general enthusiasm definitely inspired me to dig into language implementation and other explorations.
> One correction though, JRuby uses a reimplementation of jna called jnr, which is generally much faster (close to jni for simple calls).
Thanks for the correction. I might have to take a look at JNR sometime!
> We are working with folks on the FFM team to re-implement the core jnr back end. It's very exciting work, and I will present it this weekend at RubyConf Taiwan!
That’s exciting news - I’ll keep an eye out for the talk once it’s available online.
>>> The FFM API allows Java programs to interoperate safely and more easily with code and data outside the Java runtime. Unlike JNI, which often involves brittle and error-prone boilerplate code, the FFM API aims to provide a more straightforward and safer method for calling native libraries and manipulating native data.