10 unstable releases (3 breaking)
Uses old Rust 2015
0.4.0 | Aug 25, 2018 |
---|---|
0.3.2 | Mar 29, 2018 |
0.3.0 | Jan 27, 2018 |
0.2.0 | Jan 21, 2018 |
0.1.2 | Sep 30, 2017 |
#454 in Programming languages
570KB
6K
SLoC
rsjni
Rust bindings to the Java Native Interface (JNI)
lib.rs
:
Rust JNI Bindings for Java Interop.
Examples
Java
We will be exercising the following Java class via Rust.
//
// Test Class
//
import java.lang.System;
import java.util.Arrays;
import java.util.stream.Stream;
import java.util.stream.IntStream;
public class Test {
public int current = 0;
public static String message = "Hello!";
public Test(int current) {
this.current = current;
}
public void incrementCurrent() {
this.current += 1;
}
public int getCurrent() {
return this.current;
}
public static int add(int a, int b) {
return a + b;
}
public static String append(String input) {
return input + " there from Java!";
}
public static void printMessage() {
System.out.println("The message is: " + message);
}
public boolean allOnes(int iarr[]) {
final IntStream stream1 = Arrays.stream(iarr);
return stream1.allMatch(i -> i == 1);
}
public int[] iArr() {
int[] a = {1,2,3,4,5,9};
return a;
}
}
Rust
#
#
#
// Setup the `Classpath`. These are the directories or jar files to search for .class files.
let manifest = env::var("CARGO_MANIFEST_DIR")?;
let path = PathBuf::from(manifest).join("examples");
let mut classpath: Classpath = Default::default();
classpath.add(path);
// Setup the `Opts`, i.e. -Xms256m
let mut opts: Opts = Default::default();
opts.set_initial_heap_size(256);
opts.set_max_heap_size(256);
opts.set_version(Version::V18);
opts.set_classpath(classpath);
// Create the JVM.
let jvm = Jvm::new(opts)?;
// Get the JNI environment from the JVM.
let env = jvm.env();
// Load the class `Test` into the JVM if it is found on the classpath.
let test_class = env.find_class("Test")?;
// We are Java-ing. We need to construct an instance of our class.
//
// The proper constructor is found based on the argument signature, `Test(int)` in this case.
// We are trying to call `Test(5)` here, which stores 5 in the `current` field.
let constructor_id = test_class.get_method_id("<init>", &[Kind::Int], &Kind::Void)?;
let test_object = test_class.new_object(&constructor_id, &[Input::Int(5)])?;
let inc_curr_method_id = test_class.get_method_id("incrementCurrent", &[], &Kind::Void)?;
// Lets change some internal state. Call 'public void incrementCurrent()'. Internally, the
// `incrementCurrent` method increments the `current` field by 1.
test_object.call_method(&inc_curr_method_id, &[])?;
// Lets see what our `current` value is via a getter. Call 'public int getCurrent()'
let get_curr_method_id = test_class.get_method_id("getCurrent", &[], &Kind::Int)?;
let current = test_object.call_method(&get_curr_method_id, &[])?;
// Here we are converting the `Output` object into the type we expect.
assert_eq!(6, current.int());
// Set the 'public int current' field to 10.
let current_field_id = test_class.get_field_id("current", &Kind::Int)?;
test_object.set_field(¤t_field_id, &Input::Int(10))?;
// Read back the field to check that it is now 10.
let updated_current = test_object.get_field(¤t_field_id)?;
assert_eq!(10, updated_current.int());
// Call 'public static int add(int a, int b)'.
// Notice that we call static methods on the class, not the object instance.
let args = [Input::Int(1), Input::Int(8)];
let add_method_id = test_class.get_static_method_id("add", &[Kind::Int, Kind::Int], &Kind::Int)?;
let value1 = test_class.call_static_method(&add_method_id, &args)?;
assert_eq!(9, value1.int());
// Get the value of the 'public static String message' field.
let message_field_id = test_class.get_static_field_id("message", &Kind::Object("java/lang/String".to_string()))?;
let message = test_class.get_static_field(&message_field_id)?;
// Convert the output object into a string.
let java_str: strng::JavaString = TryFrom::try_from(message.object())?;
let message_str: String = TryFrom::try_from(java_str)?;
assert_eq!(message_str, "Hello!");
// Set the value of the 'public static String message' field to "Hi!".
// We need to create the 'java/lang/String' in the JVM first.
let str_obj = strng::JavaString::new(&env, "Hi!")?;
test_class.set_static_field(&message_field_id, &Input::StringObj(str_obj))?;
// Get the value again to see that the static field is changed.
let new_message = test_class.get_static_field(&message_field_id)?;
let java_str: strng::JavaString = TryFrom::try_from(new_message.object())?;
let new_message_str: String = TryFrom::try_from(java_str)?;
assert_eq!(new_message_str, "Hi!");
// Work with an array input.
// Create an int[5] in the JVM.
let mut int_arr = array::Int::new(&env, 5)?;
// Setup the array in the JVM.
int_arr.set_slice(0, &[1; 5])?;
// Check that the array now contains all 1's.
let args = [Input::IntArr(int_arr)];
let all_ones_method_id = test_class.get_method_id("allOnes", &[Kind::IntArr], &Kind::Boolean)?;
let value = test_object.call_method(&all_ones_method_id, &args)?;
assert!(value.boolean());
// Now we are calling a method that returns an int[].
let int_arr_method_id = test_class.get_method_id("intArr", &[], &Kind::IntArr)?;
let int_arr_ret = test_object.call_method(&int_arr_method_id, &[])?;
// Get the object out of the `Output`.
let int_arr: array::Int = TryFrom::try_from(int_arr_ret.object())?;
// Convert it to an array we can work with.
let mut full_buf = [0; 6];
int_arr.as_slice(0, 6, &mut full_buf)?;
// Release it back to the JVM to allow for GC.
drop(int_arr);
Dependencies
~4–5MB
~110K SLoC