首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从Java调用Rust

从Java调用Rust
EN

Stack Overflow用户
提问于 2015-05-15 19:31:08
回答 2查看 11.9K关注 0票数 39

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

代码语言:javascript
复制
#![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调用这些函数:

代码语言:javascript
复制
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将打印预期的结果:

代码语言:javascript
复制
hello from rust
42

在Rust方法中,我将env声明为指向Any类型的指针,但实际上它是一个C结构,带有指向documentation (Code example 4-1)中描述的函数的指针,这些函数是与Java运行时交换数据所必需的。

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

代码语言:javascript
复制
#[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函数:

代码语言:javascript
复制
#[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函数?请注意,我对这类东西真的很陌生,所以如果需要,请随时编辑这个问题。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-05-15 20:08:53

除了*mut Any/*const Any是胖指针的问题之外,还有一个事实是原生JNINativeInterface函数在访问JNI结构时使用了双重间接:

代码语言:javascript
复制
struct JNINativeInterface_;
typedef const struct JNINativeInterface_ *JNIEnv;
jint (JNICALL *GetVersion)(JNIEnv *env);

在这里,您可以看到JNIEnv是指向JNINativeInterface_结构的指针,而GetVersion接受指向JNIEnv的指针-也就是说,它需要指向JNINativeInterface_的指针。这个Rust程序可以在我的机器上运行(使用Rust nightly,但相同的代码可以在beta版中使用外部libc crate):

代码语言:javascript
复制
#![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对应物:

代码语言:javascript
复制
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();
    }
}

调用:

代码语言:javascript
复制
% javac tests/Test.java
% java tests.Test
Invoked native method, jre: 0x7f81240011e0, class: 0x10d9808d8
version: 65544

65544是0x10008,实际上,我是在Oracle JVM1.8下运行它的。

我猜你可以省略_opaque_data字段,因为JNINativeInterface结构总是通过指针传递的,所以如果你只需要结构中的前几个字段,你可以只声明它们而忽略其余的。

票数 21
EN

Stack Overflow用户

发布于 2016-02-20 12:45:42

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

Java代码

代码语言:javascript
复制
  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);
    }

防锈代码

代码语言:javascript
复制
#[no_mangle]
pub extern fn double_input(input: i32) -> i32 {
    input * 2
}

这是full code

票数 19
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30258427

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档