patternjavaModerate
Calling Rust from Java
Viewed 0 times
javafromcallingrust
Problem
I am using Rust 1.0 beta and was able to create a small example for calling functions written in Rust from Java. I simply compiled the following Rust code in mylib.rs using rustc which produces a mylib.dll on Windows:
Then I can call these functions from a Java class tests.Test:
Running the Java main prints the expected result:
In the Rust methods I declared
From this answer I understood that such structs with function pointers should have a counterpart in the Rust code to call these functions. So I tried to implement such a struct setting all field values to
When I call the following function from Java which should call the GetVersion function, the JVM crashes:
```
#[no_mangle]
pub extern fn Java_tests_Test_helloJre(jre: mut JavaEnv, class: const Any) {
unsafe {
#![crate_type = "dylib"]
use std::any::Any;
#[no_mangle]
pub extern fn Java_tests_Test_hello(env: *const Any, jclass: *const Any) {
println!("hello from rust");
}
#[no_mangle]
pub extern fn Java_tests_Test_sum(env: *const Any, jclass: *const Any, a: i32, b: i32) -> i32 {
return a + b;
}Then I can call these functions from a Java class tests.Test:
package tests;
import java.io.File;
public class Test {
public static native void hello();
public static native int sum(int a, int b);
public static void main(String[] args) {
File f = new File("mylib.dll");
System.load(f.getAbsolutePath());
Test.hello();
System.out.println(Test.sum(20, 22));
}
}Running the Java main prints the expected result:
hello from rust
42In the Rust methods I declared
env as a pointer to the Any type but in reality it is a C struct with pointers to functions as described in the documentation (Code example 4-1) which are required to exchange data with the Java runtime. From this answer I understood that such structs with function pointers should have a counterpart in the Rust code to call these functions. So I tried to implement such a struct setting all field values to
*mut Any except for the GetVersion field: #[repr(C)]
pub struct JavaEnv {
reserved0: *mut Any,
reserved1: *mut Any,
reserved2: *mut Any,
reserved3: *mut Any,
GetVersion: extern "C" fn(env: *mut JavaEnv) -> i32,
DefineClass: *mut Any,
FindClass: *mut Any,
…When I call the following function from Java which should call the GetVersion function, the JVM crashes:
```
#[no_mangle]
pub extern fn Java_tests_Test_helloJre(jre: mut JavaEnv, class: const Any) {
unsafe {
Solution
Apart from the problem that
Here, you can see that
Java counterpart:
Invocation:
65544 is 0x10008, and indeed, I'm running this under Oracle JVM 1.8.
I guess you can omit
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.Context
Stack Overflow Q#30258427, score: 24
Revisions (0)
No revisions yet.