我使用的是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结构,带有指向documentation (Code example 4-1)中描述的函数的指针,这些函数是与Java运行时交换数据所必需的。
从这个answer中,我理解了这样的带有函数指针的结构应该在Rust代码中有一个对应的东西来调用这些函数。因此,我尝试实现这样一个结构,将除GetVersion字段之外的所有字段值设置为*mut Any:
#[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中调用下面的函数时,JVM崩溃了,这个函数应该调用GetVersion函数:
#[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函数?请注意,我对这类东西真的很陌生,所以如果需要,请随时编辑这个问题。
发布于 2015-05-15 20:08:53
除了*mut Any/*const Any是胖指针的问题之外,还有一个事实是原生JNINativeInterface函数在访问JNI结构时使用了双重间接:
struct JNINativeInterface_;
typedef const struct JNINativeInterface_ *JNIEnv;
jint (JNICALL *GetVersion)(JNIEnv *env);在这里,您可以看到JNIEnv是指向JNINativeInterface_结构的指针,而GetVersion接受指向JNIEnv的指针-也就是说,它需要指向JNINativeInterface_的指针。这个Rust程序可以在我的机器上运行(使用Rust nightly,但相同的代码可以在beta版中使用外部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对应物:
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();
}
}调用:
% javac tests/Test.java
% java tests.Test
Invoked native method, jre: 0x7f81240011e0, class: 0x10d9808d8
version: 6554465544是0x10008,实际上,我是在Oracle JVM1.8下运行它的。
我猜你可以省略_opaque_data字段,因为JNINativeInterface结构总是通过指针传递的,所以如果你只需要结构中的前几个字段,你可以只声明它们而忽略其余的。
发布于 2016-02-20 12:45:42
一种更简单的方法是使用JnrFFI。JRuby项目大量使用了JnrFFI,它很可能成为新的FFI JEP的基础。这基本上消除了编写JNI的所有废话。下面是使用JnrFFI从Java中调用Rust函数的sample code:
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
}https://stackoverflow.com/questions/30258427
复制相似问题