Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
182 views
in Technique[技术] by (71.8m points)

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.

question from:https://stackoverflow.com/questions/30258427/calling-rust-from-java

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...