robusta
robusta copied to clipboard
Illegal hardware instruction java - when calling Rust code from Java
I am making a test project to get familiar with the library before using it in production and I have a problem when calling a Rust function that returns custom struct to the Java code. I have a simple java client called JavaClient
and the error I get when calling Rust code is illegal hardware instruction java JavaClient
.
Here is the sample Java client:
// JavaClient.java
public class JavaClient {
private static native RustResult2 stringLengthComplexGenerated(String input);
static {
System.loadLibrary("java_bridge");
}
public static void main(String[] args) {
System.out.println("Hello from Java");
String input = "12345678";
String shortInput = "1234";
// With generated models
RustResult2 result4 = stringLengthComplexGenerated(input);
System.out.printf("Length of the long string (gen, models) %s is: %d, returned error is %s\n", input, result4.length, result4.error);
RustResult2 result5 = stringLengthComplexGenerated(shortInput);
System.out.printf("Length of the short string (gen, models) %s is: %d, returned error is %s\n", shortInput, result5.length, result5.error);
}
}
// RustResult2.java
public class RustResult2 {
public int length;
public String error;
public RustResult2(int length, String error) {
this.length = length;
this.error = error;
}
}
And here is the Rust lib:
// Cargo.toml
[package]
name = "java-bridge"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib"]
[dependencies]
rust-lib = { path = "../rust-lib" }
# robusta_jni = "0.2.1"
robusta_jni = { path = "../../robusta" }
jni = "0.19.0"
// lib.rs
use robusta_jni::bridge;
#[bridge]
mod jni {
use robusta_jni::convert::{FromJavaValue, IntoJavaValue, Signature, TryFromJavaValue, TryIntoJavaValue};
use robusta_jni::jni::errors::Error as JniError;
use robusta_jni::jni::errors::Result as JniResult;
use robusta_jni::jni::objects::{AutoLocal, JValue};
use robusta_jni::jni::sys::jobject;
use robusta_jni::jni::JNIEnv;
use rust_lib::{string_length_complex_internal, string_length_internal};
#[derive(Signature, IntoJavaValue, TryIntoJavaValue, FromJavaValue, TryFromJavaValue)]
#[package()]
pub struct RustResult2<'env: 'borrow, 'borrow> {
#[instance]
raw: AutoLocal<'env, 'borrow>,
length: i32,
error: String,
}
// impl<'env: 'borrow, 'borrow> IntoJavaValue<'env> for RustResult2<'env, 'borrow> {
// type Target = jobject;
// fn into(self, env: &JNIEnv<'env>) -> Self::Target {
// let length = self.length;
// let error = self.error;
// let env: &'_ ::robusta_jni::jni::JNIEnv<'_> = env;
// let res = env
// .new_object(
// "RustResult2",
// [
// "(",
// <i32 as ::robusta_jni::convert::TryIntoJavaValue>::SIG_TYPE,
// <String as ::robusta_jni::convert::TryIntoJavaValue>::SIG_TYPE,
// ")",
// "V",
// ]
// .join(""),
// &[
// ::std::convert::Into::into(
// <i32 as ::robusta_jni::convert::TryIntoJavaValue>::try_into(
// length,
// &env,
// ).unwrap(),
// ),
// ::std::convert::Into::into(
// <String as ::robusta_jni::convert::TryIntoJavaValue>::try_into(
// error,
// &env,
// ).unwrap(),
// ),
// ],
// );
// res.unwrap().into_inner()
// }
// }
impl<'env: 'borrow, 'borrow> RustResult2<'env, 'borrow> {
#[constructor]
pub extern "java" fn new(env: &'borrow JNIEnv<'env>, length: i32, error: String) -> JniResult<Self> {}
}
#[derive(Signature)]
#[package()]
pub struct JavaClient;
impl<'env: 'borrow, 'borrow> JavaClient {
#[call_type(unchecked)]
pub extern "jni" fn stringLengthComplexGenerated(
env: &'borrow JNIEnv<'env>,
input: String,
) -> RustResult2<'env, 'borrow> {
let result = string_length_complex_internal(&input);
let (length, error) = match result {
Result::Ok(length) => {
(length.try_into().unwrap(), "".to_owned())
}
Result::Err(error) => {
(-1, error.to_owned())
}
};
let res = RustResult2::new(env, length, error).unwrap();
println!("Result is (length: {}, error: {})", res.length, res.error);
res
}
}
}
I get the error when using IntoJavaValue
in derive attribute. If I remove it and manually do the conversion (commented code) then it works. The expanded code for the IntoJavaValue
is this:
#[automatically_derived]
impl<'env: 'borrow, 'borrow> ::robusta_jni::convert::IntoJavaValue<'env>
for RustResult2<'env, 'borrow> {
type Target = ::robusta_jni::jni::objects::JObject<'env>;
fn into(self, env: &::robusta_jni::jni::JNIEnv<'env>) -> Self::Target {
::robusta_jni::convert::IntoJavaValue::into(self, env)
}
}
If I copy that code instead of my manual implementation I get the same error in the runtime and recursion warning from Rust, so that might be the issue. That "illegal hardware instruction" might actually be stack overflow.