从Java调用Rust

从Java调用Rust,java,java-native-interface,rust,Java,Java Native Interface,Rust,我使用的是Rust 1.0测试版,并且能够创建一个调用Java中用Rust编写的函数的小示例。我只是使用rustc在mylib.rs中编译了以下Rust代码,它在Windows上生成mylib.dll: #![crate_type = "dylib"] use std::any::Any; #[no_mangle] pub extern fn Java_tests_Test_hello(env: *const Any, jclass: *const Any) { println!("h

我使用的是Rust 1.0测试版,并且能够创建一个调用Java中用Rust编写的函数的小示例。我只是使用rustc在mylib.rs中编译了以下Rust代码,它在Windows上生成mylib.dll:

#![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;
}
然后我可以从Java类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));
    }
}
运行Java main将打印预期结果:

hello from rust
42
在Rust方法中,我将
env
声明为指向
Any
类型的指针,但实际上它是一个C结构,带有指向中所述函数的指针,这些函数是与Java运行时交换数据所必需的

从这一点我了解到,这种带有函数指针的结构应该在Rust代码中有一个对应的结构来调用这些函数。因此,我尝试实现这样一个结构,将所有字段值设置为
*mut Any
,除了
GetVersion
字段:

#[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,  
    …
当我从Java调用以下函数(该函数应调用GetVersion函数)时,JVM崩溃:

#[no_mangle]
pub extern fn Java_tests_Test_helloJre(jre: *mut JavaEnv, class: *const Any) {
    unsafe {
        let v = ((*jre).GetVersion)(jre);
        println!("version: {:?}", v);
    }
}

如何正确调用GetVersion函数?请注意,我对这类内容非常陌生,因此如果需要,请随时编辑此问题。

除了
*mut Any
/
*const Any
是胖指针的问题外,还有一个事实,即本机JNI函数在访问
JNITINATIVEINTERFACE
结构时使用双间接寻址:

structjniationiveinterface;
typedef const struct jniationavienterface_*JNIEnv;
jint(JNICALL*GetVersion)(JNIEnv*env);
在这里,您可以看到
JNIEnv
是一个指向
jniativeinterface
结构的指针,该结构实际上包含您提供的字段,
GetVersion
接受指向
JNIEnv
的指针,也就是说,它需要指向
jniativeinterface
的指针。这个Rust程序在我的机器上运行(Rust nightly被使用,但相同的代码在beta版中使用外部libc板条箱):

#![板条箱类型=“dylib”]
#![专题(libc)]
外部板条箱libc;
使用libc::c_void;
#[报告员(C)]
发布结构JNINativeInterface{
保留:*多个c_void,
预留1:*多个c_void,
预留2:*多个c_void,
预留3:*mut c_void,
GetVersion:externfn(env:*mutjnienv)->i32,
_不透明数据:[u8;1824]
}
发布类型JNIEnv=*const JNINativeInterface;
#[没有损坏]
pub extern fn Java_tests_Test_helloJre(jre:*mut JNIEnv,class:*const c_void){
println!(“调用的本机方法,jre:{:p},类:{:p}”,jre,类);
不安全{
设v=(**jre.GetVersion)(jre);
println!(“版本:{:?}”,v);
}
}
Java对应项:

包测试;
导入java.nio.file.Path;
导入java.nio.file.path;
公开课考试{
公共静态本机void helloJre();
公共静态void main(字符串[]args){
Path p=Path.get(“libtest.dylib”);
系统加载(p.ToAbsolutionPath().toString());
Test.helloJre();
}
}
调用:

%javac tests/Test.java
%java测试
调用的本机方法,jre:0x7f81240011e0,类:0x10d9808d8
版本:65544
65544是0x10008,实际上,我在Oracle JVM 1.8下运行它


我想您可以省略
\u opaque\u data
字段,因为
jnitiveinterface
结构总是通过指针传递的,所以如果您只需要结构中的前几个字段,您可以只声明它们,而忽略其余字段。

更简单的方法是使用。JRuby项目大量使用JnrFFI,它很可能成为新Java的基础。这基本上消除了编写所有JNI的废话。下面是使用JnrFFI从Java调用Rust函数的示例:

Java代码

  public static interface RustLib {
        int double_input(int i);
    }
    public static String getLibraryPath(String dylib) {
        File f = new File(JavaRustFFI.class.getClassLoader().getResource(mapLibraryName(dylib)).getFile());
        return f.getParent();
    }
    public static void main(String[] args) {
        String dylib = "double_input";
        System.setProperty("jnr.ffi.library.path", getLibraryPath(dylib));
        RustLib rlib = LibraryLoader.create(RustLib.class).load(dylib);
        int r = rlib.double_input(20);
        System.out.println("Result from rust double_input:  " + r);
    }
#[no_mangle]
pub extern fn double_input(input: i32) -> i32 {
    input * 2
}
防锈代码

  public static interface RustLib {
        int double_input(int i);
    }
    public static String getLibraryPath(String dylib) {
        File f = new File(JavaRustFFI.class.getClassLoader().getResource(mapLibraryName(dylib)).getFile());
        return f.getParent();
    }
    public static void main(String[] args) {
        String dylib = "double_input";
        System.setProperty("jnr.ffi.library.path", getLibraryPath(dylib));
        RustLib rlib = LibraryLoader.create(RustLib.class).load(dylib);
        int r = rlib.double_input(20);
        System.out.println("Result from rust double_input:  " + r);
    }
#[no_mangle]
pub extern fn double_input(input: i32) -> i32 {
    input * 2
}

这是一个

我无法给出完整答案,但您似乎假设
*const Any
是一个常规指针:它不是。它是指向trait对象的指针,这意味着它的大小是常规指针的两倍。不应使用
*const Any
表示“任意指针”;为此,您应该使用
*const()
*const c_void
(来自
libc
包)。顺便提一下:您可以使用
::std::mem::size_of()
来获取给定类型的大小。请注意
任何
都是一个特征,任何指向裸特征的指针都是一个特征对象,也就是说,它实际上是一个“胖”指针。例如,程序打印16而不是8。因此,它不适合用作类似
void*
的指针。例如,您需要使用
*mut()
。感谢这个例子,Rust业务代码不应该适应使用它的应用程序。如果你有一些资源遵循这个想法,它将是非常有用的!