I've been experimenting with the low-level plumbing of JNR, trying to
replace the multitude of code generators (there's different ones in
JRuby's FFI, jnr-ffi and jython's ctypes), with a common API.
To use it, you do something like:
// Create a function signature for long getpid();
CallContext callContext =
CallContext.getCallContext(ResultType.primitive(NativeType.ULONG,
long.class),
new ParameterType[0], CallingConvention.DEFAULT, false);
Library libc =
Library.open(Platform.getPlatform().mapLibraryName("c"), Library.LAZY
| Library.LOCAL);
MethodHandle mh = Native.getMethodHandle(callContext,
libc.getFunction("getpid"));
System.out.println("pid = " + (long) mh.invokeExact());
That generates a class with a single static method that looks like:
public final class jnr/invoke/Native$x86asm$0 {
public final static native long invokeNative();
}
with an asm trampoline that implements the native method:
jnr.invoke.Native$x86asm$0.invokeNative ()J
0: sub rsp, 0x8
4: mov rax, 0x0
b: call 0x15
10: add rsp, 0x8
14: ret
15: <indirect call trampolines>
and returns a MethodHandle bound to the java class's native method.
The interface is somewhat low level - intentionally. Upper layers
should interpret the user-friendly API, convert that into low-level
types (e.g. based on meta-data from clang), and then feed that to
jnr-invoke.
There is also
https://github.com/wmeissner/crazy-ivan which uses
jnr-invoke to wire up x86_86 asm into the jvm.