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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…