从Java调用Rust
我使用的是Rust 1.0测试版,并且能够创建一个调用Java中用Rust编写的函数的小示例。我只是使用rustc在mylib.rs中编译了以下Rust代码,它在Windows上生成mylib.dll:从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
#![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业务代码不应该适应使用它的应用程序。如果你有一些资源遵循这个想法,它将是非常有用的!