Apart from the problem that *mut Any/*const Any are fat pointers, there is also a fact that native JNI functions use double indirection when accessing JNINativeInterface structure:
struct JNINativeInterface_;
typedef const struct JNINativeInterface_ *JNIEnv;
jint (JNICALL *GetVersion)(JNIEnv *env);
Here, you can see that JNIEnv is a pointer to JNINativeInterface_ structure which actually contains the fields you presented, and GetVersion accepts a pointer to JNIEnv – that is, it requires a pointer to a pointer to JNINativeInterface_. This Rust program works on my machine (Rust nightly is used but the same code would work in beta with an external libc crate):
#![crate_type="dylib"]
#![feature(libc)]
extern crate libc;
use libc::c_void;
#[repr(C)]
pub struct JNINativeInterface {
reserved0: *mut c_void,
reserved1: *mut c_void,
reserved2: *mut c_void,
reserved3: *mut c_void,
GetVersion: extern fn(env: *mut JNIEnv) -> i32,
_opaque_data: [u8; 1824]
}
pub type JNIEnv = *const JNINativeInterface;
#[no_mangle]
pub extern fn Java_tests_Test_helloJre(jre: *mut JNIEnv, class: *const c_void) {
println!("Invoked native method, jre: {:p}, class: {:p}", jre, class);
unsafe {
let v = ((**jre).GetVersion)(jre);
println!("version: {:?}", v);
}
}
Java counterpart:
package tests;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Test {
public static native void helloJre();
public static void main(String[] args) {
Path p = Paths.get("libtest.dylib");
System.load(p.toAbsolutePath().toString());
Test.helloJre();
}
}
Invocation:
% javac tests/Test.java
% java tests.Test
Invoked native method, jre: 0x7f81240011e0, class: 0x10d9808d8
version: 65544
65544 is 0x10008, and indeed, I’m running this under Oracle JVM 1.8.
I guess you can omit _opaque_data field as JNINativeInterface structure is always passed by pointer, so if you only need several first fields from the structure, you can declare only them and ignore the rest.