Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Illegal hardware instruction java - when calling Rust code from Java #26

Open
tomislavhoman opened this issue Apr 3, 2023 · 1 comment

Comments

@tomislavhoman
Copy link

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.

@uvlad7
Copy link
Contributor

uvlad7 commented Jan 16, 2024

That "illegal hardware instruction" might actually be stack overflow.

It is, see #13

I wish I'd seen that issue before I spent two hours debugging

uvlad7 added a commit to uvlad7/robusta that referenced this issue Jan 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants