giovanniberti / robusta

Easy interop between Rust and Java
MIT License
328 stars 14 forks source link

Exit code 11 (segfault?) when converting a java class to a JObject #13

Open theoparis opened 2 years ago

theoparis commented 2 years ago

So I have this java class binded through rust:

    #[derive(Signature, TryIntoJavaValue, IntoJavaValue, TryFromJavaValue)]
    #[package(com.theoparis.elementalbridge.core)]
    pub struct Item<'env: 'borrow, 'borrow> {
        #[instance]
        raw: AutoLocal<'env, 'borrow>,
        #[field]
        pub id: Field<'env, 'borrow, String>,
        #[field]
        pub name: Field<'env, 'borrow, String>,
    }

    impl<'env: 'borrow, 'borrow> Item<'env, 'borrow> {
        #[constructor]
        pub extern "java" fn new(
            env: &'borrow JNIEnv<'env>,
            id: String,
            name: String,
        ) -> JniResult<Self> {
        }
    }

And I have a rust function that needs to convert the Item struct to a JObject so it can be used in another struct method (that I'm using to "register the item").

pub fn register_item<'a>(ctx: &ModContext<'a>, mod_info: ModInfo, item: &Item) {
    let registry =
        Registry::new(ctx.jni, mod_info.id, "item".to_string()).unwrap();

    let java_item =
        crate::jni::Item::new(ctx.jni, item.id.clone(), item.name.clone())
            .unwrap();

    debug!("Registering item: {}", item.name);

    let java_obj =
        robusta_jni::convert::IntoJavaValue::into(java_item, ctx.jni);
    debug!("Java object: {:?}", java_obj);
    registry.register(ctx.jni, java_obj).unwrap();
    info!("Registered item: {}", item.name);
}

It does print the registering item message, but then after a second or two the whole java process crashes with exit code 11 - which I'm assuming is a segmentation fault since im on linux.

Any ideas as to why this could be happening? There isn't a discussions page so I assumed creating an issue would be the best place to get help...

giovanniberti commented 2 years ago

There isn't a discussions page so I assumed creating an issue would be the best place to get help...

It seems like a bug, so issues are definitely the best place!

First of all could you try to use TryIntoJavaValue instead and unwrap? This way rust generates a panic (if it's a problem from Rust) and we can inspect the backtrace (make sure to have the environment variable RUST_BACKTRACE set to 1)

uvlad7 commented 7 months ago

I reproduced this problem when tried to pass User struct as an argument in tests to check its signature.

Initialized env logger with level: info
error: test failed, to rerun pass `--test mod`

Caused by:
  process didn't exit successfully: `/home/vladimir/robusta/target/debug/deps/mod-f46496b2644f0906 --test-threads=1` (signal: 11, SIGSEGV: invalid memory reference)
uvlad7 commented 7 months ago

So, it appears to be a stack overflow. This is how that TryIntoJavaValue derive expands:

    #[automatically_derived]
    impl<'env: 'borrow, 'borrow> ::robusta_jni::convert::TryIntoJavaValue<'env>
    for User<'env, 'borrow> {
        type Target = ::robusta_jni::jni::objects::JObject<'env>;
        fn try_into(
            self,
            env: &::robusta_jni::jni::JNIEnv<'env>,
        ) -> ::robusta_jni::jni::errors::Result<Self::Target> {
            ::robusta_jni::convert::TryIntoJavaValue::try_into(self, env)
        }
    }
    #[automatically_derived]
    impl<'env: 'borrow, 'borrow> ::robusta_jni::convert::TryIntoJavaValue<'env>
    for &User<'env, 'borrow> {
        type Target = ::robusta_jni::jni::objects::JObject<'env>;
        fn try_into(
            self,
            env: &::robusta_jni::jni::JNIEnv<'env>,
        ) -> ::robusta_jni::jni::errors::Result<Self::Target> {
            Ok(self.raw.as_obj())
        }
    }
    #[automatically_derived]
    impl<'env: 'borrow, 'borrow> ::robusta_jni::convert::TryIntoJavaValue<'env>
    for &mut User<'env, 'borrow> {
        type Target = ::robusta_jni::jni::objects::JObject<'env>;
        fn try_into(
            self,
            env: &::robusta_jni::jni::JNIEnv<'env>,
        ) -> ::robusta_jni::jni::errors::Result<Self::Target> {
            ::robusta_jni::convert::TryIntoJavaValue::try_into(self, env)
        }
    }

I just copy-pasted it and debugger stuck on the first conversion Screenshot from 2024-01-16 02-59-33 Screenshot from 2024-01-16 02-59-45

And then I replaced it with Ok(self.raw.as_obj()) and tests passed, both in embedded java in tests/mod.rs and inside a "jni" method called from java

        pub extern "jni" fn getInt(self, _env: &JNIEnv, v: i32) -> i32 {
            User::selfSignatureCheck(&_env,self).expect("selfSignatureCheck failed");
            v
        }