robusta icon indicating copy to clipboard operation
robusta copied to clipboard

Illegal hardware instruction java - when calling Rust code from Java

Open tomislavhoman opened this issue 1 year ago • 1 comments

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.

tomislavhoman avatar Apr 03 '23 11:04 tomislavhoman