Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling Rust from Java

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:

#![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 42 

In 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 {         let v = ((*jre).GetVersion)(jre);         println!("version: {:?}", v);     } } 

How should I call the GetVersion function correctly? Note that I am really new to this kind of stuff so please feel free to edit this question if required.

like image 859
Michael Avatar asked May 15 '15 11:05

Michael


People also ask

Is Rust compatible with Java?

If you are a Java developer, you'll find Rust relatively easy to get a grip on, thanks to the similarity of the two languages.

Is Rust better than Java?

When it comes to Java, this programming language is significantly slower than Rust. Rust delivers faster startup times and smaller memory footprint on top of it. Java uses Garbage Collection for memory management, which decreases performance.

Can you call Rust from C++?

Our Rust program now has a C interface that the C++ benchmark program can call. To avoid repetition, we wrap it into a Rust macro create_extern_c_wrapper . To create a C interface named step that wraps a Rust implementation named _step , we simply evaluate the macro: create_extern_c_wrapper!(


1 Answers

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.

like image 143
Vladimir Matveev Avatar answered Oct 07 '22 09:10

Vladimir Matveev