2015-05-15 2 views
24

Я использую бета-версию Rust 1.0 и смог создать небольшой пример для вызова функций, написанных на Rust с Java. Я просто составил следующий код Rust в mylib.rs с помощью rustc, который производит MyLib.dll на Windows:Вызов ржавчины из Java

#![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 ожидаемый результат:

hello from rust 
42 

в методах Rust я объявил env как указатель на Any типа, но в действительности это C s truct с указателями на функции, как описано в documentation (Code example 4-1), которые необходимы для обмена данными со средой выполнения Java.

От этого answer Я понял, что такие структуры с указателями функций должны иметь аналог кода 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, виртуальная машину Аварию:

#[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? Обратите внимание, что я действительно новичок в подобных материалах, поэтому, пожалуйста, не стесняйтесь редактировать этот вопрос, если это необходимо.

+1

Я могу» t дать полный ответ, но вы, кажется, предполагаете, что '* const Any' i s обычный указатель: ** это не **. Это указатель на объект-признак, что означает, что он * в два раза больше обычного указателя *. Вы не должны * использовать '* const Any' для представления« произвольного указателя »; для этого вы должны либо использовать '* const()', либо '* const c_void' (из пакета' libc'). В стороне: вы можете использовать ':: std :: mem :: size_of ()', чтобы получить размер данного типа. –

+1

Обратите внимание, что символ «Любой» является признаком, и любой указатель на голый признак является объектом признаков, то есть он на самом деле является «толстым» указателем. Например, программа [this] (http://is.gd/tjze8D) печатает 16 вместо 8. Следовательно, она не подходит для использования в качестве указателя 'void *'. Вам нужно, например, '* mut()' для этого. –

ответ

16

Помимо проблемы, что *mut Any/*const Any жира указатели, есть и то, что родные функции JNI использовать двойной косвенность при доступе JNINativeInterface структуру:

struct JNINativeInterface_; 
typedef const struct JNINativeInterface_ *JNIEnv; 
jint (JNICALL *GetVersion)(JNIEnv *env); 

Здесь вы можете увидеть, что JNIEnv является указатель на JNINativeInterface_ структура, которая фактически содержит поля, которые вы представили, и GetVersion принимает указатель на JNIEnv - то есть для этого требуется указатель на указатель на JNINativeInterface_. Эта программа Ржавчина работает на моей машине (Rust по ночам используется, но тот же код будет работать в тестовом режиме с внешним LibC обрешетке):

#![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: 65544 

65544 - 0x10008, и действительно, я запускаю это под Oracle JVM 1.8.

Я думаю, вы можете опустить _opaque_data поле как JNINativeInterface структура всегда передается указатель, так что если вам нужно всего лишь несколько первые поля из структуры, можно объявить только их и игнорировать остальные.

8

Простой подход заключается в использовании JnrFFI.Проект JRuby сильно использует JnrFFI, и он, вероятно, станет основой для новой Java FFI JEP. Это в основном исключает возможность написания всей взлома JNI. Вот sample code, который использует JnrFFI для вызова функции Rust от Java:

код 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); 
    } 

Rust Код

#[no_mangle] 
pub extern fn double_input(input: i32) -> i32 { 
    input * 2 
} 

Вот full code

Смежные вопросы